By: James Keats | 16 February 2018
Could it really be blog post time again already?
First, let’s take a look at the new HUD:
For the most part this included few code-related changes. We’re still using the “BoundProperty” system which uses C# events to auto-push updates to UI elements without the “user code” having to know how that data is being used and displayed. The durability indicators, which used to exist in the ammo panel, have been removed and will be replaced in favor of a durability display directly on the model, coming next sprint. Luckily, the same system that used to display the durability has been repurposed to show the “ammo type” whenever the mechanism changes. A similar system was used to change the center reticle whenever the barrel changes.
The other change included writing a new BoundPropertyListener for the draining bars. This binding element takes a “current value” property, a “max value” property or an inspector-set max value, then calculates a fill percentage based on this whenever either item changes. Note: in the video above, these bars drain vertically, but we decided after it was recorded to have them drain outwards horizontally instead for clearer visual distinction.
In the above video, you might’ve noticed a new robot hand holding the weapon. One of my tasks this sprint also involved helping our lead artist, Tyler, implement the first pass of the hand into the game. For the most part, that was simply a matter of changing around some parenting in the hierarchy and ensuring the correct items get enabled/disabled depending on if a player is the local player or a remote/networked player.
There was one catch for implementing this hand, though. Going forward, we are planning on having team play with team colors. One option was to have separate texture sets for each team, but the preferred way was to do it in a more programmatic fashion so that we could easily change things later on. Tyler came up with a clever solution for this; in addition to the standard PBR maps, he also provided me with a one-channel “color mask” map. Wherever this mask was white, our Material-defined team/player color would be multiplied onto the diffuse. Wherever it was black, the color would be ignored. This obviously required a custom shader. I’m not a graphics person, but thankfully Unity made this easy, as it was just a surface shader.
My first attempt at this was the following surface shader:
It essentially determines the value of the color mask and then conditionally multiplies based on that. However, if I’ve learned anything in my graphics class, it’s that conditional branching in shaders is a death knell for the GPU. So, after a bit of thought and discussion with some other programmers, we were able to get it down to the following:
It’s a small change and a minor improvement, but profiling through Unity revealed that it saves a conditional branch (obviously) and about 10 operations on the GPU total. I consider that a great optimization considering how easy it was to do! All-in-all, this was a successful sprint (including the weapon view refactoring from my last blog post) and I’m looking forward to sharing what I’m working on this sprint next Friday!