Search code examples
javalibgdxscreentransparencygame-development

How to refresh a transparent screen in libGdx


I am working on a small libGdx project at the moment and I am facing now some trouble concerning a transparent screen. This transparent screen is called 'GameMenuScreen' because it can be called throughout the game. If it is called the 'GameMenuScreen' is rendered above the GameScreen which is still visible in the background. Now when I press e.g. the 'Save' button, all of the buttons duplicate. I think it occurs because the screen isn't refreshed every frame. But for now I have no idea how to refresh the screen and preserve its transparency. Here is the Code of the '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);
    }

The normal way to refresh the screen every frame is to call this two code pieces:

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

But as mentioned before: the screen loses its transparency when it is called.

For better understanding one picture showing the doupling:

I hope you can help me.


Solution

  • Removing Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); from the render() method means you never clear the pixel buffer of your screen, which means all of the stuff you draw will be drawn over the pixels of the previously rendered frames.

    If you need to save a picture of the previous screen and use it as a background for your menu, you should take some kind of 'screenshot' of it and save it into a texture. A very convenient method already exists in LibGDX - ScreenUtils.getFrameBufferTexture(): LibGDX ScreenUtils source code. Just call this method before you switch to the menu and use it as a regular texture for your background image (but remember to dispose() of the old textures or they'll pollute your memory).

    You can also call this method inside your render() method right before you render the UI and it'll pick up a picture of your game without UI elements, which may be more suitable for a menu background.