Day and Night Cycle Filter - UQdeco2800/2022-studio-1 GitHub Wiki
Jump to a section or return to Day and Night Cycle Summary here!
The day and night cycle component or filter is in charge of rendering the shader effects that show the effects of dawn,day,dusk and night to the player.
It's called during the render cycle. Renderer.java
makes sure to revert to default shader when rendering UI elements thus leaving UI elements unshaded.
Astah - Fig. 1 - Green are the ones added by feature team
It's coupled to InputComponent
as we wanted to be able to toggle the VFX on or off depending on a key press. The key in question is the period key. When pressed it will toggle the effect on or off. This feature is useful for those that wish to inspect the visuals of their designs without the shader in the way.
Although not shown in the UML. The RenderService.java
uses the class to apply the shader. Internally the class sets the correct shade colour and the intensity corresponding with that part of the day which it receives through the event handler. The justification for coupling it with RenderService
is due to the fact that that's where rendering takes place and it seems like the right place to apply the shader. Furthermore, in the future the render service could be used to hold different sprite batches for different renderables and use those batches instead to avoid shading.
DayNightCycleComponent
is used inside RenderService
it's applied to the batch only for none UI elements as shown below:
public void render(SpriteBatch batch) {
for (Array<Renderable> layer : renderables) {
// Sort into rendering order
layer.sort();
for (Renderable renderable : layer) {
if (dayNightCycleComponent != null) {
if (AtlantisSinks.gameRunning) {
dayNightCycleComponent.render(batch);
}
}
renderable.render(batch);
}
}
}
Shading was achieved through 2 OPenGL Shader Language files. One file known as base.vert chooses which parts of the pixels to be shaded while the fragmentation bit sits on top of it. These files are then compiled by ligGDX ShaderProgram
and then applied to sprite batches.
./core/assets/shaders
├── base.vert
└── light.frag
ShaderProgram.pedantic = false;
var vertexShader = Gdx.files.internal("shaders/base.vert");
var lightShader= Gdx.files.internal("shaders/light.frag");
this.dayNightCycleShader = new ShaderProgram(vertexShader, lightShader); // --> ready to use shader
For more technical details about implementation please see the JavaDoc.
One of the challenges faced while coming up with this component was how to get other UI elements not to be shaded. For instance the terrain (map) was not correctly shaded - or to be more precise, not shaded at all. Several hours of debugging revealed that TiledMapRenderer
used in TerrainComponent.java
, used it's own internal sprite batch. So to overcome this, the internal sprite batch was modified to use the custom shader:
if (renderer != null) {
try {
this.batchedMapTileSpriteBatch = (SpriteBatch) ((BatchTiledMapRenderer) renderer).getBatch();
} catch(ClassCastException e) {
// issue caused when being mocked
this.batchedMapTileSpriteBatch = null;
}
Then the shader was modified in draw()
@Override
public void draw(SpriteBatch batch) {
tiledMapRenderer.setView(camera);
// render night affect (using tiledmapRenderer batch)
if (dayNightCycleComponent != null && batchedMapTileSpriteBatch != null) {
dayNightCycleComponent.render(batchedMapTileSpriteBatch);
}
tiledMapRenderer.render();
}
What was learned from this challenge was useful. It showed that if you didn't want the shader applied to some elements you can use a different sprite batch that hasn't got the shader set - or in other words, that uses the default shader.
To improve the game's overral experience we decided to let the different parts of day slowly fade into each other for a seamless experience. The abrupt change can catch users by surprise and so the gradual phase reminds users that it's about to be night or day.
To make this possible DayNigtCycleComponent
was modified with the addition of a few new methods, highlighted in blue in the above class diagram. A method was added to return the amount of the gradual fade would have to progress. The fade is then stepped through based on the difference between it and the target. It can either get darker or lighter. The fade step only occurs for a set interval of milliseconds which are configurable in the class. So for instance we can make a fade step every 500ms and so on.