Over the past month or so, I’ve been slowly working away on a new project, and I’ve decided that maybe it’s a good idea to start posting about some of the challenges I’ve been facing. Quite often I’ll see interesting posts put up by other developers about what they’re doing, how they’re doing it, and what they have done to overcome obstacles, so I keep thinking that maybe I should join in. So, here I am, ready to share some of my experiences. Hopefully this won’t be a complete bore, and maybe, if I’m lucky, it may also teach others how to tackle similar problems.
This first post is all about the health system I’ve created for my new game (which has the incredibly thoughtful working title of “Tank Demo”). It’s a small game where two players drive around in tanks and blow each other up. In early prototypes of this game players had a single health bar, which was displayed at the bottom of the screen. It was simply a coloured box which indicated to the player roughly how many more shots their tank could take before blowing up. The display was scaled directly to the players health value, making it easy to read.
It was probably about a week later that I decided to implement a shield to the player. I wanted to bring the shield into the game so that the players had something that they could regenerate in an attempt to survive a little longer. Collecting a pickup changes your current weapon, and doing so gives the player a percentage of their shield back. I found this a nice way to encourage players to frequently change weapons. My problem was that I didn’t want to have the shield bar sitting at the bottom of the screen like the health bar currently was. I wanted them both together, but I wanted them in a place where the player could always see them without having to be too distracted from the action. I decided that having two rings around the tank (one for health, and one for shield) could be a nice way bringing the display into the playing field.
After doing some research, I wasn’t surprised to find that others had attempted to do similar things before. I was eventually led to an article which explained a very clean way of doing this using a gradient texture, with an alpha cutoff at specific values (see Article and Demo). With this as a starting point, I was able to change the gradient to something that better fit my needs, and hook up the cutoff value to the desired value (either health or shield).
Here is the gradient I used. I made it like this so that as health went down, the circle opened at the top and came down both sides to meet in the middle at the bottom.
Inside my playerController script, I had the following:
//The health bar object (which is just a plane with the texture on it)
public GameObject healthBar;
//My tank's health value
public float health;
//Inside the update function, we set the cutoff value to match our health value
At this point I knew nothing about shaders (and not much has really changed from that) so I was quite happy when everything worked. From the small understanding I have gathered on shaders (and please correct me if I’m wrong), this is how it works:
“_Cutoff” is the value which determines which pixels are drawn, based on their level of transparency.
Mathf.Inverselerp has the following values (FromValue,ToValue,ValueToSet). Since the max health for the player is 100, health/100 will always give a value between 1 and 0.
Once the health bar was working, I implemented the shield, and everything seemed great. Well, at least until I started seeing flaws in my idea. The health/shield bars functioned as I had intended, however since this was all being displayed in the play area, it meant that it was now being visually interrupted by the environment.
After doing some testing, it was clear that something had to change. I had already been experimenting with the use of multiple cameras, so I though that might be the answer to my problem.
I created a new camera, and sat it at the same position as the main one. Then I made the following changes to my new camera:
– Set the camera to Orthographic – this way the bars always look round, and flat.
– Set the Culling Mask to “HealthDisplay” – A layer I had created and put the two bars on, making sure that these were the only two objects drawn by the camera.
– Set Clear Flags to “Depth Only” – A necessary step when using multiple cameras, as it stops the extra camera from drawing a background
– Set the “Depth” to 0 – Since the main camera is -1, a higher value would make sure the bars were drawn above the rest of the game objects, regardless of their actual depth.
– Removed “HealthDisplay” from the Culling Mask on the main camera – this made sure the main camera couldn’t draw anything on that layer.
Here’s a shot of the camera setup:
And here is what they now looked like in-game:
This was a much better approach, as it no longer felt that the environment was affecting the way the player could read their health/shield values. The health/shield bars stayed like this for several weeks, and everything seemed fine, until I implemented a new weapon into the game – the Flamethrower.
The Flamethrower is designed to be a high-damage, close range weapon. It’s set up to constantly shoot a number of large, slow moving projectiles within a small cone area in front of the player. Functionally, it worked great, but it had re-surfaced an earlier problem – now that the health/shield bars were being drawn on top of the everything else, the ‘flames’ (or spheres, as they currently look) are visually being cut off.
This just wasn’t good enough. It now felt that the health/shield bars were interfering with the game play, but I didn’t want to move them. It wasn’t just that I had spent a lot of time implementing them, I thought they were a nice way of displaying information to the player, but now they had become invasive. I spent some time re-visiting feedback that I had got from earlier versions of the game (I sometimes do this to make sure I’ve cleaned up all the bugs that people have told me about) and found one comment which suggested that both the health and shield bars should be semi-transparent. Perfect! If I could get this working then they could become less invasive, but still just as useful for the player. Then I struck another problem; I still knew nothing about shaders, so how long would it take to get this working? Was it going to be worth all the time, or would it be easier just to move them back to the bottom of the screen?
I did some more research, and as usual, someone else had stumbled across this exact problem. Actually, not only had someone else had the same problem, but they had manged to work it out, and share their results. Now generally speaking, I don’t like using other peoples work code unless I completely understand how it all works. I find that it helps me make any necessary alterations so that it can be efficiently use for my own purposes, and also saves me time later on when I come across a similar problem. With the shader, however, I was happy to accept that it worked, and give all credit to the person who made it possible (see this post for the details about the shader).
Here is an image of what the new shader looks like in the inspector.
Looking at this, we can see that the shader isn’t too complex. It works almost identical to the shader we were already using, however the top texture will control overall transparency of the image (take not that although the textures look the same, the top texture is just a solid green square, and the bottom texture is the one with our gradient) and the bottom texture controls our alpha cutoff for our gradient. Using the colour picker at the top, we can set the overall transparency of the material. Here are the results:
As you can see, the ‘flames’ are now visible through the shield/health bars. This was a big step forward, and a great achievement, however I felt it could use a little more improvement. There was one last thing which I needed to do; reduce the size of the health/shield bars. By scaling them down, and bringing them closer to the tank, it would mean that they take up less space in the cone that the Flamethrower shoots its projectiles.
This decision made several important changes to Flamethrower:
– Most of the ‘flames’ move past the health/shield bar at the start of their life, meaning the majority of their time is spent in open space.
– The Flamethrower, as a weapon, is visually more appealing now that it’s not completely overlapping with the health/shield bars.
Overall, I’m happy at how the health/shield bars have turned out. I’m sure they still might need a bit of tweaking, but I’ll address that as I come to it.
I also hope that this post hasn’t been a complete bore, and that I may have even helped educate some readers. If anyone found this interesting at all, please let me know, as I’d be happy to post more about challenges I’ve faced (or am still facing) through the development of this project!
Screenshot of the game in action after all the changes had been made: