You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在libGdx中刷新透明屏幕并解决按钮重复问题

LibGdx透明菜单屏幕按钮重复问题解决

我在开发小型libGdx项目时遇到了透明屏幕的问题:GameMenuScreen作为可全局调用的透明菜单,渲染在GameScreen上方,背景可见,但点击"Save"等按钮后,所有按钮会出现重复残影。推测是屏幕未逐帧刷新导致,但直接用常规的glClear会失去透明度。

问题代码(GameMenuScreen)

public class GameMenuScreen extends ScreenAdapter {

    private TextureAtlas uiskinAtlas;
    private Skin skin;
    private OrthographicCamera camera;
    private Viewport viewport;
    private Stage stage;
    private Table table;
    private Vector3 position;
    private SpriteBatch batch;

    private TextButton continueGame;
    private TextButton loadGame;
    private TextButton settings;
    private TextButton mainMenu;
    private TextButton quit;
    private TextButton save;

    public GameMenuScreen() {
        
        uiskinAtlas = GameWindow.manager.get(Paths.uiskinAtlas, TextureAtlas.class);
        skin = new Skin(Gdx.files.internal(Paths.uiskinJson), uiskinAtlas);
        camera = new OrthographicCamera();
        position = new Vector3(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, 0);
        camera.position.set(position);
        viewport = new ScreenViewport(camera);
        stage = new Stage(viewport);
        batch = new SpriteBatch();

        table = new Table();
        
        continueGame = new TextButton("Continue Game", skin);
        loadGame = new TextButton("Load Game", skin);
        settings = new TextButton("Settings", skin);
        mainMenu = new TextButton("Main Menu", skin);
        quit = new TextButton("Quit", skin);
        save = new TextButton("Save", skin);
        
        
        continueGame.setColor(Color.ROYAL);
        loadGame.setColor(Color.ROYAL);
        settings.setColor(Color.ROYAL);
        mainMenu.setColor(Color.ROYAL);
        quit.setColor(Color.ROYAL);
        save.setColor(Color.ROYAL);
        
        createUI();
    }

    @Override
    public void show() {
        Gdx.input.setInputProcessor(stage);
    }

    @Override
    public void render(float delta) {

        Gdx.gl.glClearColor(0, 0, 0, 0f);
        
        batch.enableBlending();
        batch.begin();
        continueGame.draw(batch, 0);
        loadGame.draw(batch, 0);
        settings.draw(batch, 0);
        mainMenu.draw(batch, 0);
        quit.draw(batch, 0);
        save.draw(batch, 0);
        batch.end();
        
        stage.act();
        stage.draw();
    }

    private void createUI() {
        
        table.setFillParent(true);
        table.center();

        setButtonInput();

        table.add(continueGame).width(250);
        table.row().space(5, 5, 0, 0);
        table.add(loadGame).width(250);
        table.row().space(5, 5, 0, 0);
        table.add(save).width(250);
        table.row().space(5, 5, 0, 0);
        table.add(settings).width(250);
        table.row().space(5, 5, 0, 0);
        table.add(mainMenu).width(250);
        table.row().space(5, 5, 0, 0);
        table.add(quit).width(250);
        table.row().space(5, 5, 0, 0);

        stage.addActor(table);
        
    }

    private void setButtonInput() {

        continueGame.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                GameWindow.INSTANCE.setScreen(Screens.getGameScreen());
            }
        });

        loadGame.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Savegame.load();
                Screens.getGameScreen().setWorldCreated(false);
                GameWindow.INSTANCE.setScreen(Screens.getGameScreen());
            }
        });

        save.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Savegame.save();
            }
        });

        mainMenu.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                GameWindow.INSTANCE.setScreen(Screens.getMainMenuScreen());
            }
        });

        quit.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Gdx.app.exit();
            }
        });
    }
    
    @Override
    public void resize(int width, int height) {
        viewport.update(width, height);
        position = new Vector3(viewport.getScreenWidth() / 2, viewport.getScreenHeight() / 2, 0);
        camera.position.set(position);
    }
}

常规刷新代码的问题

下面的代码会导致屏幕失去透明度:

Gdx.gl.glClearColor(0.35f, 0, 0f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

按钮重复现象表现为点击后按钮出现多层重叠的残影。


问题根源与解决方法

问题根源

  1. 重复绘制按钮:render方法中既通过batch手动绘制所有按钮,又调用stage.draw()——按钮已经被添加到Table并加入Stage,这导致按钮被绘制两次,点击后按钮状态变化(如按下效果),两次绘制状态不一致就会出现残影。
  2. 未清理缓冲区:没有调用glClear(GL20.GL_COLOR_BUFFER_BIT),每一帧画面叠加在前一帧上,加剧残影问题。

解决步骤

  1. 删除手动绘制按钮的代码:移除render中batch.begin()到batch.end()之间的所有按钮绘制逻辑,Stage会自动绘制所有添加的Actor。
  2. 保留透明背景并清理缓冲区:设置透明清除颜色后调用glClear,既清空缓冲区又不遮挡下方的GameScreen。

修改后的render方法:

@Override
public void render(float delta) {
    // 设置透明清除颜色,alpha值为0
    Gdx.gl.glClearColor(0, 0, 0, 0f);
    // 清空颜色缓冲区,避免画面叠加
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    
    // 仅保留Stage的更新与绘制
    stage.act(delta);
    stage.draw();
}
  1. 额外优化:删除类中的batch成员变量,因为Stage内部已维护自己的SpriteBatch,无需手动创建。

内容的提问来源于stack exchange,提问作者Morgoth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 03:01:10