Making a Hack’n’slash #22: Improving our player statistics

Let’s keep working on our Hack’n’slash and give some power to our equipment!

This article is also available on Medium.

For the past few weeks, we’ve worked on the inventory and the equipment systems: this means that we now have a way to play around with our items, loot them, drop them back, put them on our hero or unequip them to the bag.

But those items don’t really do anything at the moment. They are just icons and 3D meshes that we enable and pop, here and there, but they don’t have any impact on the gameplay whatsoever.

So, today, let’s see how to take our equipment into account for our player’s statistics, and how to better compute some interesting variables.

Reorganising our statistics definition

Before diving into the real topic, let’s first refactor slightly the way we store our player statistics. You might remember that, for now, we’ve just prepared a few values by hand in our PlayerData class:

We could of course keep this as-is, and then use some string codes in our items to optionally boost one of those values… but this is risky, and we could easily get something wrong. To avoid inconsistencies, it is better to use an enum!

So let’s create a fixed list of our possible player statistics:

Now, the tricky part is to actually use this enum in our PlayerData class. The natural choice would be to have some dictionary matching a statistic value to an integer value. The problem is that Unity cannot serialise dictionaries, which means that it wouldn’t work properly in our Scriptable Object and wouldn’t even display in the inspector.

To avoid this problem, we need to find a workaround. And this workaround is to rather rely on a simple structure with two parameters, that we can use in our PlayerData class to make an array of statistics.

The downside of this method is that to find the value matching a specific statistic, we’ll have to iterate through the array. But since this shouldn’t happen “too frequently” (in particular it is not a critical path that is called again and again at a high rate), it shouldn’t cause too much performance issues. The upside is, of course, that it will get rid of our serialisation headaches since both the stat and value are serialisable, and we’ve defined the class itself to be too!

We can also prepare some additional quick accessors to our different statistic values:

And fix the definition of our AttackDamage property so that it uses these new stats:

Of course, we’ll have to update our InventoryManager script to use our new shorthands, too:

Now, with all that settled, time to get to today’s main topic!

Updating the stats based on the equipment

Preparing our modifiers

Thanks to our little preparation, we can define a similar array of PlayerStatisticValue for our items, in which we’ll store the bonus or malus the item gives for various statistics. This is done in our InventoryItemData class:

I can use that on one of my item, like a sword – and to have a bit of variety, I’ll have one positive modifier (= a bonus) and one negative modifier (= a malus):

To let the players know, however, we’ll have to integrate something in our game UI to tell of these modifiers. The straight-forward way is to update our GetDetailsDisplay() function (the base one in the InventoryItemData class) to also show those modifiers.

And to make it clear what’s a bonus and what’s a malus, we can keep using Unity’s rich text format to auto-change the colour of these substrings, like before:

Another cool thing would be to show how the player stats would be modified by the item directly in our statistics listing, in the top-right corner of the inventory panel. To do this, we just need to make sure that our method _UpdatePlayerStats allows for an optional slot index as input, to display some additional info if we have selected an item with stat modifiers.

Remember however that we only want to display this data if the object is not already equipped:

Applying the modifiers

The next step is to update our equip/unequip logic so that whenever we add or remove a piece of equipment, if it has some stat modifier(s), we change the current player’s statistics.

This is fairly straight-forward to implement: we will simply write one function in our PlayerData class to apply or unapply the modifiers:

And then, we’ll call it in our InventoryManager script when we equip our unequip an item (also, we should make sure we reset the UI display of the stats after unapplying the modifiers):

With this quick change, we now have updated stats that we can use to compute more advanced logic variables, like the attack damage, and they’ll evolve with our equipment!

Bonus: changing the attack range depending on the weapon

To wrap up this little tutorial, let’s have a look at another quick-win feature – we’re going to see how to update the attack range of our character so that it is not hardcoded anymore. Rather, it should depend on the weapon currently equipped.

Let’s first add a range field in our WeaponItemData:

Now, we’ll keep the previous value for when the hero is unarmed, or else use a dynamic variable that changes with the equipment:

Here, I’ve used the [HideInInspector] attribute so it doesn’t show up in the inspector: this way, there is no risk we try and set a value that will then be overridden. Instead, we want to make sure we properly initialised this variable when the game first starts, for example in the AddressablesLoader:

And finally, we can change this currentAttackRange when we equip our unequip a weapon item, and update the UI display:

Conclusion

In this short episode, we saw how to setup statistics with an enum to avoid inconsistencies, and we also made sure they impact our advanced variables like the attack damage. We finished up with a quick way to change the attack range of our hero depending on the weapon he is carrying currently.

Next time, we’ll continue our journey and talk about our character’s special skills…

Leave a Reply

Your email address will not be published.