Why, hello there! Bet you didn't see me standing here, all cloaked in shadow.
What's that? You want a flashlight? To see me better?
Wait, you want to know the whole story behind the flashlights? Well then, gather round.
Starting point
Almost a year ago to the day, (wow time flies) I added fancy lighting.
Over the next 9 months I rewrote my rendering entirely, and mildly improved the lighting, ending up at something like this:
I also had the ability to set the light levels a bit lower when a level was set underground:
But as time wore on, I saw the imperfections more and more:
- Levels had to be bright or dark - I couldn't support "drop down onto surface, descend into caves".
- Lighting couldn't affect terrain, so I was totally faking that inner darkness on terrain - look at the sand in the top right getting unnaturally darkened.
- And because of that last point, and me not having enough light sources in-level, I was applying a minimum level of lighting to both open space and to terrain - so I couldn't have any secret areas to reward exploration!
- Worst of all, players kept complaining that the dark underground level was, well, too dark - but at the same time, that's when the lighting looked the best!
Something had to be done.
Rewriting Lighting
So, over the last 3 months, I, err, I rewrote the rendering again, and then I (with a bit of help1) redid the lighting system entirely too.
Much of game lighting boils down to maths and performance optimization, but I have a strict No Maths, No Code rule here.
So instead, let's zoom in on some of the game design problems. I had 4 goals:
- Support above-ground and below-ground areas with a different feel each
- Stop players complaining about darkness
- Support secret areas hidden in darkness
- Enable watching chaos - if you set off a chain reaction, you gotta be able to watch it!
Notice these conflict with each other! I could make everything well-lit, but then there are no secrets and underground areas feel the same as above-ground.
Or I could make a traditional "fog of war" hide unexplored areas, but that would obscure cool chain reactions!
Supporting Underground Areas
What makes something feel underground?
- It's darker (no sun underground!)
- The background is closer to you
I came up with a system whereby I can paint a "middleground"; any pixel painted as middleground doesn't emit sunlight:2
The blocking of light makes nice dark areas, and also allows neat rays of sunshine to come in "from outside".
Except.. it's maybe a little too dark?
Let There Be Light
Let's give our player a flashlight, which helps a bit:
You can see the flashlight only lights the area directly in front of you.
Turning on bounce lighting3 (halfway through the video above) improves things a bit, but it's still kinda hard to see, ya know?
Especially if you're on a ladder.
I fixed it by putting a light on the player:4
But there was one problem left: any area you weren't shining a light onto was still too dark.
That could be fun sometimes, but in general that made it too hard to use your grappling hook.5
Hey, you know what? If this seems cool, you should...
Anti-Fog of War
I was mulling over adding a fog of war,6 when it hit me: what if I added a reverse fog of war?
I could artificially boost the lighting, but only for explored areas of the level!
That way I could have secrets, keep the game readable, and let players see chain reactions (which usually emit light), while also giving cues to players about already explored areas - a rare quadruple win:
Of course, it's all tunable per level.
Here's that original dark level from above, redone:
I'm pretty happy with this approach, and it's quite flexible too.
For example, here's an emulation of a Real Time Strategy-style fog of war, as suggested by @RaR in the Slow Rush Discord 7
Lighting isn't done! For example, all of the background (and middleground) is lit evenly, even though you wouldn't expect mountains (or the moons) in the background to be lit much at all.
But I figured it was well past time for a progress update, and a...
Very Updated Playable Web Build!!!
It has been 3 months (wow, also whoops) so many things have changed since last time - most notably there's some approximation of a Risk of Rain-style core game loop now.
But I won't spoil everything - have a play and give me feedback please.
Tip: press Esc to see controls, and you can now rebind them in the Settings menu.
After being nerd-sniped by @oooks from Meatbat in the Slow Rush Discord (psst, join us!), I experimented with upgrading my lighting system to EntropyLost's implementation of Holographic Radiance Cascades.
I got a prototype working and then @xzil mentioned that he was working on the same thing based on yazaraai's HRC impl for his own game. We started swapping code & techniques, and eventually joined forces to work on his prototype since he had cooler stuff overall.
In particular, the prototyping of light bouncing around corners and the sun causing shadows below terrain came from him, and I just ported the ideas & maths into my own implementation - thanks for the collaboration & help @xzil!
You might be thinking: wait a second, doesn't sunlight only come from the sun, not from arbitrary open pixels that might be positioned under terrain?
And you'd be right: that was actually what I had to start with, but it cast everything above the top terrain layer into shadow too strongly, and didn't play nice with the camera moving down (the blocking terrain would disappear and suddenly let in new light).
This approach is a decent tradeoff - maybe I'll add in "correct" sun lighting again in future, after I have a better solution for off-screen terrain.
In real life, light that hits one surface bounces off and hits another, which prevents areas around corners from becoming completely dark.
In games, it's usually very computationally expensive, so most games don't simulate it - but this game does!
The way it works under the hood is that pixels adjacent to terrain have their previous lighting result fed back in, so they become dimmer light-emitting pixels themselves.
Credit to @xzil from the Slow Rush Discord for the original bounce prototyping!
It was harder than it sounds: if you just put a circular point light on the player, its radius is very obvious. Make the radius smaller than the player, and it can flicker at lower graphics settings. I reworked things so that every pixel of the character sprite emits even light, but then if the light intensity is too high, the player glows! So I use low intensity lights together with a hack that ray-traces a minimum-lighting circular area around the player.
Grappling hook is a work in progress - try it out on the playable web build below with Left Shift and let me know what you think!
A black fog drawn over unexplored areas of the map, sometimes combined with a light grey overlay that covers enemies.
Popular in real time strategy games, like Total Annihilation - and if you don't know that game, by god you gotta try it (better 30-odd years late than never at all!).
Or at least it's what I thought they were suggesting - it later turned out they were suggesting a bullet that reveals the fog of war, which is also a neat idea :)