When implementing walls, I wanted 3 things:
- Walls would be one tile wide and two tiles tall. Too many base building games already exist with a top-down view.
- Building walls would be simple. The player would only need to worry about building the foundation. Roofs would appear automatically.
- The player can see what’s behind walls.
So I chose to draw the outlines of things that existed behind walls, similar to Age of Empires II.
And here’s the result. So happy it works!
The relevant code is below. I had to divide some lengths by 16, due to (I think) how my spritesheets are split. The outline shader is from http://www.allpiper.com/2d-selection-outline-shader-in-libgdx/
// draws outlines of objects that are behind other objects
private void drawOutlines(GameData data) {
// make the off screen Frame Buffer Object the current buffer
fbo.begin();
drawLayer(data, fboBatch, SortingLayer.CHARACTER);
// unbind the FBO
fbo.end();
// set the shader
float outlineSize = .5f;
startOutlineShader(outlineShader, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), outlineSize);
startStencilTest(false); // set to true for visual testing
batch.begin();
batch.draw(fboRegion, 0, 0, fbo.getWidth() / 16f, fbo.getHeight() / 16f);
batch.end();
// end stencil test
Gdx.gl20.glDisable(GL20.GL_STENCIL_TEST);
}
private void startOutlineShader(ShaderProgram shaderProgram, float width, float height, float outlineSize) {
ShaderProgram.pedantic = true;
shaderProgram.begin();
shaderProgram.setUniformf("u_stepX", outlineSize / width);
shaderProgram.setUniformf("u_stepY", outlineSize / height);
shaderProgram.setUniformf("u_color", new Vector3(1, 1, 0)); // yellow
shaderProgram.end();
batch.setShader(shaderProgram);
if (!shaderProgram.isCompiled()) {
throw new GdxRuntimeException("Couldn't compile shader: " + shaderProgram.getLog());
}
}
// draw only where the over furniture (roofs, bed covers, etc) HAS been drawn
private void startStencilTest(boolean alwaysOn) {
Gdx.gl20.glColorMask(true, true, true, true);
Gdx.gl20.glStencilOp(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);
if (alwaysOn) {
Gdx.gl20.glStencilFunc(GL20.GL_ALWAYS, 0x1, 0xff);
} else {
Gdx.gl20.glStencilFunc(GL20.GL_EQUAL, 0x1, 0xff);
}
Gdx.gl20.glEnable(GL20.GL_STENCIL_TEST);
}