In game screeens, it's common to have a static background alongside interactive elements and characters. A typical approach is to group background items into a Group for easier management. How ever, sometimes this design isn't foreseen initially, or the background involves more than a simple image, requiring blending between background and interactive actors across two stages.
Instead of rewriting code, note that both stages ultimately render via SpriteBatch. Implementing dual stages can be achieved by using separate cameras. Let's examine the Stage.draw() method:
/** Renders the stage */
public void draw () {
camera.update();
if (!root.visible) return;
batch.setProjectionMatrix(camera.combined);
batch.begin();
root.draw(batch, 1);
batch.end();
}
Both stages can share the same SpriteBatch. Instantiate the second stage using Stage(width, height, stretch, batch).
Example code:
package com.example.game;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
public class MyGame implements ApplicationListener {
Stage backgroundStage;
Stage foregroundStage;
float screenWidth;
float screenHeight;
@Override
public void create() {
screenWidth = Gdx.graphics.getWidth();
screenHeight = Gdx.graphics.getHeight();
backgroundStage = new Stage(screenWidth, screenHeight, true);
foregroundStage = new Stage(screenWidth, screenHeight, true, backgroundStage.getSpriteBatch());
Image bgImage = new Image(new TextureRegion(new Texture(Gdx.files.internal("sky.jpg")), 50, 50, 480, 320));
backgroundStage.addActor(bgImage);
Image character = new Image(new TextureRegion(new Texture(Gdx.files.internal("character.png")), 217, 157));
character.x = (screenWidth - character.width) / 2;
character.y = (screenHeight - character.height) / 2;
foregroundStage.addActor(character);
}
@Override
public void dispose() { }
@Override
public void pause() { }
@Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
backgroundStage.act(Gdx.graphics.getDeltaTime());
foregroundStage.act(Gdx.graphics.getDeltaTime());
backgroundStage.draw();
foregroundStage.draw();
}
@Override
public void resize(int width, int height) { }
@Override
public void resume() { }
}
Rendering order matters: the background stage must be drawn first, then foreground elements. Otherwise, the background will overlay the foreground.
For performance optimizaton, consider tuning the SpriteBatch buffer size.
Handling input across both stages cannot be done with simple assignments like:
Gdx.input.setInputProcessor(stage1);
Gdx.input.setInputProcessor(stage2);
Instead, use an InputMultiplexer:
InputMultiplexer inputMultiplexer = new InputMultiplexer();
inputMultiplexer.addProcessor(foregroundStage);
inputMultiplexer.addProcessor(backgroundStage);
Gdx.input.setInputProcessor(inputMultiplexer);