For the past few months, I have been working on Godkin, a pixel-art online co-op combat RPG. This is a collaboration between Psychic Software and Goblin Portal (our second collaboration, in fact, following up on last year’s release Goblins & Grottos).
Godkin takes a “faked 3D” view (this style is often referred to as isometric, although actually we’re not adhering correctly to the strict viewpoint that would make the game isometric.) Getting the lighting and shadows looking good in this style is somewhat challenging, since there’s no real 3D geometry for the game engine to work with. I’m having a lot of fun programming this game in Unity (it’s my first Unity game) and figured a blog post about shadows and lights was in order!
At the core of my shadowing system are the FakeShadowCaster and LightAnimator components.
A LightAnimator component is attached to any objects that have lights – camp fires, torches, explosions, etc. These search for nearby objects which have FakeShadowCaster components, and register with them. They also notify the FakeShadowCaster objects whenever changes happen (e.g. the light moving, flickering, brightening, dimming, turning off).
A FakeShadowCaster component is attached to any objects that we need to cast shadows – basically, anything that should appear to have some 3D ‘height’ – characters, monsters, rocks, trees. This component maintains a list of nearby light sources, and creates a fake shadow sprite to associate with each of them. Whenever a light notifies a change, or if the FakeShadowCaster itself moves, its list of shadows are re-calculated. Each shadow is rotated to face away from the associated light source, and its opacity is set based on distance from the light (plus other variables). I also wrote a ‘shadow skew’ shader which spreads apart the vertices of the shadow sprite which are further away from the light source – with a stronger effect the closer it is to the light; this adds quite well to the overall feeling of 3D. Another nice touch is that we can use whatever sprites we like for the shadows – so trees, for example, can have their shape baked into their file.
‘3D’ Object Shader
The standard Unity sprite shaders look great for terrain, and sprites which don’t have much ‘height’ – however, since the amount that each of their pixels is lit is simply based on the distance they are from light sources, this starts to look strange for objects that are supposed to be tall. The problem is that the top pixels and bottom pixels of the object will be at quite a different distance from the light – but to look like a proper object with height, the vertical position of all pixels should be considered the same, for lighting purposes. So here we have a shader that attentuates light to all pixels based on the position of the vertically lowest ones. This shader also reduces the brightness of any object which is in front of lights (since in this case the majority of the light should be cast on the non-visible ‘back’ of the objects).