Making a Hack’n’slash #23: Adding some skills and powers! 1/2

Let’s continue our Hack’n’slash and grant our hero some special skills!

This article is also available on Medium.

In the previous episode, we worked on our hero’s base statistics, and how to derive a few interesting variables from them. This is a good way to have the power of our character increase throughout the adventure, in particular with the loot, but this is usually not the only tool hack’n’slash players have at their disposal…

… another crucial element, are special skills and super powers!

These can be very diverse, ranging from a pure attack boost to an instant heal, or an area confusion spell, or some teleportation mechanism. Finding the right skills is up to you, and the type of universe you settled your game in.

However, we can still discuss how to implement such a system, and go through a simple example to have hands-on practice 🙂

So, in this couple of episodes, let’s dive into special spells and skills!

Defining data for our skills

First of all, let’s think about how we could define our skills. In this article, we will take a basic example: a “power strike”, i.e. a special melee attack that deals extra damage to the enemy. We will want this skill to have a unique reference, a display name, a value type (that can be either “Damage” or “Heal” for example) and a way to actually compute this value.

The first three parameters can be easily packed in a Scriptable Object, as usual:

For the value computation function, however, it’s a bit more complex. Since we can’t edit C# in the inspector, we have to write it down somewhere and link it to the rest of our data. But it would be better to avoid having a big if-else if check in our SkillData class, so how can we “extract” this info elsewhere?

A cool trick here is to use delegates. Basically, C# delegates are a way of declaring the prototype of a function as a type so that you can then pass functions as inputs to another function.

For example, suppose we create another script called SkillEffects that contains a static class. This is where I want to least all of my actual skill computation functions. Well, I can declare some global delegate in my Skills namespace that has no input parameters and returns no value:

And then use it in my class to create a dictionary mapping a specific skill code to its computation function:

What I’ve done here is I told the program that any skill computation function will have a prototype like the one defined in the SkillEffect delegate: no input parameters, and no return value.

Now, I’ll just need to add some pass-through method to the SkillData object to have it automatically pick the right logic:

Just to be thorough, we can make all of this system a bit more robust by keeping a list of the allowed skill codes: this will allow typo errors and other silly mistakes from crippling the behaviour. To do this, let’s once again define an enum with our skill code – let’s put it in a third file called SkillCodes since, in a full-fledged project, this list would probably end up being quite long:

Then, in our SkillData, we’ll change the type of the code field to this enum type:

Finally, we just need to replace the type in our SkillEffects class as well, and we’re done!

This means that, now, in the inspector, we have a dropdown with the possible values, which makes it easier to get it right 😉

Alright, with this quick setup out of the way, let’s get to the fun part and actually start using this skill!

Using our skill to one-shot the enemy!

Triggering a specific animation

Here, we’ll keep things simple and say that our Power Strike instantly throws a punch in front of our character, and deals an insane amount of damage. This way, if we’re facing the Brute enemy from the previous episodes, we should be able to kill it in one punch.

What we want is to have our skill computation value trigger all this chain of events. In other words, it should get a reference to the player controller and have it start a specific action that, in turn, will use an event like our normal attack actions to attack the enemy at one point.

Actually, to keep things simple, I’ll reuse my first attack animation – but I will however copy it to another state in my animator called “PowerStrike”. This way, I can link it to the “Any State” tool: this means that no matter which state the animator is currently in, we’ll be able to directly transition to the “PowerStrike” state.

To do this transition, I’ll use a new trigger variable called “PowerStrike”; then, for the final transition back to the “Idle” state, I’ll leave it blank and simply wait for the end of the animation.

Here’s my updated animator with all these additions:

To allow the skill to force this trigger, let’s add a new function to our PlayerController called TriggerState():

Note that this logic does assume your skill state and the trigger to go to it are named the same.

Then, in the SkillEffects script, let’s update our PowerStrike() function to have it find the player, get its PlayerController script component and call the function. I’ll reset the attack combo, too, because skills should probably interrupt any chain of punches. We’ll also need to have a way of telling the player that it should deal an extra amount of damage, but we’ll see that in a sec:

The ResetAttackCombo() I called here is easy to code in our PlayerController:

Testing it out!

Now, suppose that we add some test GUI button in our PlayerManager script to try out this workflow. I’ll use Unity’s IMGUI to do it quickly – the point is to have a button on the screen I can click to call my Power Strike skill, like this:

Where my _skills array is filled in by hand in the inspector (ultimately, there should of course be a logic for unlocking/learning new skills, which would fill this array gradually):

If I click the button in my game, I see that the character indeed punches, as expected:

Dealing extra damage!

But, for now, nothing differentiates this punch from a normal attack. We will see in the next tutorial how to add some visual effects to show all the magical and whimsical power of this one-shot punch – right now, though, we’ll focus on the data and the logic.

We need to tell the player that the punch-to-be is a special one, with a specific amount of damage. Let’s do this in our PowerStrike() function: we’ll set some static variable on our PlayerController (there’s no risk of polluting the ecosystem since we have just one copy of this script in the whole game), and define some “override damage”:

Then, in our PlayerAttackManager, we will check if this value is set. If it is, we use it; else, we compute the damage as before:

If you re-run the game, lo-and-behold! You’ll see that your Power Strike really is powerful 🙂

Resetting the damage override

Of course, we also need to reset this damage override so that the next normal punches don’t suddenly get this override damage too. To do this, we can re-use our callback system from before and reset the value at the end of the animation, when the skill has been cast:

If you try to cast the Power Strike away from the enemy, and then do a normal punch on the Brute, you’ll see that it is indeed back to its normal amount of damage 😉

Conclusion

In this first part, we implemented our basic skill data structure and we saw a quick example of how to define the logic to execute when one is cast.

Next time, we will continue working on this system and add several features like a cooldown, some UI displays and nice VFX!

2 thoughts on “Making a Hack’n’slash #23: Adding some skills and powers! 1/2”

  1. Hello,
    Read through you article here and liked it as great starting and learning platform to build a base rpg. Was wondering if you are going to make the files/ asset available at all??

Leave a Reply

Your email address will not be published.