Hi there fellow Makers of Games and indie game aficionados.
I’ve had some positive comments from people about the lighting effects in my game, along with a few asking about how I did it. So, I thought I’d write an article about how my lighting system works, pitched at a fairly basic level (Translation: sometimes lightly technical, but no specific code, and with pretty pictures.) My hope is to….
- Give people interested in the game some insight into the work going on behind the scenes.
- Give people working on 2D games a few ideas they might want to try out.
So let’s just jump in, and start looking at some examples. First, window light.
Not too bad looking, right? The secret is that this part is actually technically pretty simple.
The first thing that is going on is that each bedroom has three sprites.
What is going on here is that we draw the dark version of the bedroom, then on top of that, we draw the sunset and daytime versions, and as the sun sets in the evening we drop down the transparency of the daytime, and then the sunset sprites. That way you have a nice smooth transition from daytime to evening. It needs a few more effects to really polish it up, but this is the foundation. (Now, as this is something I knocked up a few years ago, I made the mistake of “baking in” some daytime and sunset glow with those first two images. That means that the glow is not a separate image that can be layered on and tweaked, but is fixed in place. Not really a good idea! Keep your layers separate and you have more control.)
But of course, that’s not all that is going on. As you can see in that first GIF, the room gets darker than what we see with the final bedroom sprite. And of course, the player is also getting darker. This is where shaders come in. That’s just a fancy way of saying that we are “drawing” the regular sprites on the screen, but in an irregular way, using some code.
In this case, the code is super simple. All I am doing, is drawing a dark layer over everything.
Here you get the basic idea. We have the regular sprite, and then we use a shader to draw a pitch black version of that sprite, the transparency adjusted, on top of that regular sprite. The effect is that we have a darker looking player. This is then being done for every sprite that you can see. What I have in the background is a simple value that is used to determine how strong that dark layer should be. I call it “Ambient Darkness”. So when it is very late at night, we are drawing at 0.8 transparency (think, 80%, with a value of 1 being 100%.) But during the day, we are drawing at 0 transparency. Everything is very visible.
Now of course, for some games with a side-on platformer perspective, you could cut some major corners. Think Terraria for example. You can just layer a black square over the whole screen, and “cut out” holes in it for lights. With Innkeep though, we have a perspective that requires the treatment of lighting at the level of individual sprites. This does pay off down the road. Take a look at this.
So, here you see that the player is getting lighter when they stand in front of the window. How does that work? Well, as we have direct control of how bright each individual sprite is, we can find ways to tweak that brightness in a way that works together with ambient light. In this case, I am talking about lightmaps.
These gray sprites are not usually visible in game of course. But behind the scenes the player sprite is reading how the value of the particular pixel in that sprite (dark or light), and using that to help determine how bright the player should be. Here, the dark parts are actually brighter when it comes to drawing the player!
We can use the same system for candles.
Here, we have a light map just like by the windows. The game is drawing the player quite dark already, because it is late at night, and so the “ambient darkness” value is around .78 (The candle being lit does help! If everything in the inn was lit, including the fire, then then ambient darkness would only be around .56 or something.) The game then checks the shadow map to find out how much “variation” from the base level should be occurring.
Fitting these two things together is a liitle bit more complicated. The issue is that we cant simply add or subtract the difference, as those “gray” parts of the shadowmap are still returning a value of 0.5. If you were standing in that gray, and then it got dark, and you simply added more darkness, the player would be pitch black. So instead, we need to calculate how much “divergence” is going on from that 0.5 gray value, either positively or negatively, and work with that. Turning 0.5 into a “neutral” point where we are just going by what the ambient darkness level is telling us. Here’s a bit more detail for those who are interested in the technical side of things.
Building on these principles, you can do some pretty neat stuff. For example, you can fluctuate a little the light difference caused by the candles and the fireplace, so that when the player or the guest are standing nearby, they flicker together in the same way. I think lighting like this really helps to make it feel like these different sprites are sharing the same space.
And that about wraps it up! If you are interested in following the development of the game more closely, consider following me on twitter, or joining our discord channel. You can even support the development of the game over at Patreon and receive some neat rewards.
Thank you all for reading! See you again in 2019.