You know that horrible sinking feeling when you realise you’ve done something really, really stupid and have just spent the best part of an hour ignoring something that was staring you straight in the face all along? Doh! Not good is it.
I’ve been looking at adding some kind of health/hp to both the enemies and main character in Jetboard Joust. I think the game is going to prove far too difficult if I go for a ‘instant death’ mechanic every time you hit an enemy, plus I want to add a variety of weapons so adding health gives me more scope for different weapons doing different levels of damage. It also gives a very obvious upgrade path for weapons should I decide to add an weapon upgrade system.
So I needed something visual to indicate when a collision has occurred, or when an enemy takes damage but is not destroyed altogether, but I didn’t want to go down the route of creating a specific animation per enemy as this would have proved extremely time consuming (and very difficult to change globally).
Initially I looked at creating a type of ‘white out’ effect using the stencil buffer in MonoGame. I felt sure this was the way to go and sunk a lot of time into this approach, not ever having used a stencil buffer before. Unfortunately I just couldn’t get this to work – part of the reason was due to my own stupidity (the ‘doh’ moment I mentioned above when I realised the code that initialised the DepthStencilState objects I was using wasn’t getting called), but part of the reason was undoubtedly to do with quirks of this feature in Monogame. When I saw that my partially-working code wasn’t running consistently on Android and iOS I decided to abandon this approach as it seemed likely to lead to too much pain and grief.
That left custom shaders as the only possible approach. Fortunately getting these to work was a lot easier than I’d been anticipating. You can’t compile the shaders natively under OSX (which is a pain) but they compile easily using the Monogame Pipeline tool under Windows and I can flip back and forward between Windows and OSX easily with VMWare Fusion. I’ll probably write a separate post explaining how to do this in detail.
You can see on the right the results of my custom shader experiments. Doing a simple ‘white out’ effect was easy. Slightly more tricky was getting the texture to invert in the way I wanted. A simple colour inversion is easy but produced colours that were way out of range of the limited palette I’m using and looked really odd. What I needed to do was invert the brightness of the texture but keep the colour balance intact – I seem to have got there with the following HLSL code. Probably very hacky but it seems to work (Maths was never really my strong suit).
float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0 { float4 color = tex2D(s0, coords); if ( color.a>0 ) { float r2 = color.r; float g2 = color.g; float b2 = color.b; float t = r2+g2+b2; float rp = (r2/t)/0.3333; float gp = (g2/t)/0.3333; float bp = (b2/t)/0.3333; color.rgb = dot(color.rgb, float3(0.33, 0.33, 0.33)); color.rgb = 1.0-color.rgb; color.rgb *= float3(rp, gp, bp); color.a *= alpha; } return color; }
So the final shader does a simple ‘white out’ followed by alternating inverted and normal frames. The ‘normal’ frames have increased brightness which gradually fades out. I think it’ll do the job for now at least. I imagine I’ll be implementing a few more of these type of effects – HLSL seems like a rabbit hole I can’t stop myself from entering.
Finally I added some more particle effects (another slight addition to the particle generator required to get that ‘halo’ effect) and mini health bars which I think look kind of cool. When health is depleted these animate down rather than just jumping to the next value.
Dev Time: 1.5 days
Total Dev Time: approx 14 days

Before And After – My ‘Invert Brightness’ Shader

The Final Shader Animated

Custom Shader Effect In-Game With Health Bars