Implementing Walls

When implementing walls, I wanted 3 things:

  1. Walls would be one tile wide and two tiles tall. Too many base building games already exist with a top-down view.
  2. Building walls would be simple. The player would only need to worry about building the foundation. Roofs would appear automatically.
  3. The player can see what’s behind walls.

20160807aoeoutlinesSo I chose to draw the outlines of things that existed behind walls, similar to Age of Empires II.

ezgif.com-gif-maker(1)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);
}