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

Time to wrap up our Hack’n’slash tutorial and finish our skills system!

This article is also available on Medium.

Alright, we are nearing the end of this hack’n’slash tutorial! We’ve seen a lot of things, implemented several RPG-related systems and looked at plenty of cool optimisation tricks. So, today, we need to finish our last system: the special skills!

Last time, we prepared our data and our basic logic, so now our hero is able to unleash a devastating attack to one-shot any enemy.

That’s pretty cool, but there are obviously some things missing here 😉

In this article, we are going to extend our system by implementing the following features:

  • setting up a cooldown… or a cost: yeah, cause, for now, there is nothing stopping you from just spamming the final attack again and again, which would make your RPG ridiculously easy to speed-run!
  • adding a UI and input actions: so far, we’ve just setup a debug UI in the top-left corner – this is handy, but not that prod-ready, right?
  • showing up some great VFX: because that’s what spells are all about – flashy glowy bursts of light and joy! 🙂

Adding a cooldown

Ok, first things first, let’s take care of giving a cooldown to our spells.

Whenever a game grants you that strong an ability, it seems logical to it should come at a price. This price can be some energy or mana bar that reduces slightly, or just time that you have to wait until you can cast the spell again. Many games actually use both at the same time!

Here, I’m just going to focus on the cooldown, because I think it’s the most tricky to implement. In a nutshell, we’ll need to have some mechanism that marks a spell as currently unavailable and then resets it when the cooldown is done.

The first step is to define this cooldown in our SkillData class:

Now, when we cast it, we’ll store a flag in the object that marks it as unavailable and we’ll make sure that, if this flag is on, then the Cast() function just aborts early and doesn’t do anything. I’ll also transform our return value to a boolean so that we can now if the skill was indeed cast or not:

By the way, since this variable is private but also seralised by Unity, we’ll have to make sure it is properly set back to false when the game first starts, like this:

The complex part of this process is to find a way to trigger some reset of this _inCooldown boolean flag after the cooldown delay has passed. When we want to wait for some time, we usually have two options:

  • using Invoke() to call a function with no parameters in the same class
  • or starting a coroutine

The problem is that both these techniques require us to be inside of a MonoBehaviour class! And here, we’d like to call it in our SkillData script, which inherits from the ScriptableObject class.

So, instead, we need to use a third option: the C# asynchronous tasks system.

As a quick refresher, the idea with async code is that, rather than starting a process and having it run entirely in a single shot, you run the logic in little baby steps from time to time and every so often you give the hand back to your main routine to simulate parallel processes.

C# tasks are another way of doing asynchronous programming. This built-in C# tool lets you create async code with the await/async keywords, just like Promises in Javascript or the newest iterations of Python for example, making it really easy to code and to read.

Then, basically, by queuing tasks in an internal buffer and piping them together or placing them in parallel, you can easily have your code execute after a while, when a condition is met, once another task has finished running, or if it has failed… And to do all this, you simply need to define your functions as async and, from that point on, you can use the await keyword inside of the function body to get the result of an asynchronous task.

Here, we don’t actually have to chain tasks or get return values, so we can use it like so:

If you try this out and add some debugs, you’ll see that the spell is indeed blocked while the cooldown is not over. However, since we currently have the same animation when we use our Power Strike and we throw a normal punch, we might get confused when you left-click…

… so first let’s replace the animation in our Animator! I’ll use the second animation in my chain of punches, like this:

And now, if we try it out, we’ll see that we can cast our special skill, but then for 3 seconds we can’t trigger it again 🙂

Fairly nice… except that having just a different animation is a bit too subtle for this drastic influx of power, isn’t it? Time to have some fun with VFX!

Adding some nice VFX!

Ok so – for this tutorial, I’ll be using a FX from this really cool pack of free Cartoon FX by Jean Mareno for my power strike. I’ve picked the one called “Hit C White” from the first set that looks like that:

Now, to actually show it when I use the skill, I’m going to first add a reference to this prefab in my SkillData class:

Then, when I throw the super-powered punch, I’ll need to instantiate this prefab (that will automatically play the particle system animation) at the moment I send my hit event, and then destroy the prefab when it’s done.

Note: you could optimise this by having some object pool for the VFX, as we discussed in a previous tutorials, but here I’ll keep things simple and do a normal instantiate/destroy method since this does not happen in a critical path or at too large a scale 🙂

The cool thing with this lib is that it comes ready with a script to auto-destroy the prefab when it’s finished executing – so we won’t even have to worry about that: as long as we have this CFX_AutoDestructShuriken script, our prefab will get cleaned up when we don’t need it anymore.

Now, to instantiate our FX prefab at the right time, we’ll have to give a bit more info to our SkillEffect callback function. For now, it doesn’t receive anything – we’re going to pass it the SkillData object so that it can check for an FX prefab and instantiate if need be. While we’re at it, we can also extract the damage value in our object. All in all, this gives us the following changes:

And here we are! Our special skill now has a little FX that clearly distinguishes it from the normal punches 🙂

Alright, next, to keep improving our system, we have to add all that to our UI so that players can easily trigger the skills and get enough feedback after using them!

Showing the skill in our UI

Now that we’ve gotten the logic working, let’s add some visual info that tells the player about this skill. We’ll want to put a button in the UI to make it easier for the players to remember which skills they currently have, but don’t forget we’ll also have to link the casting of the skill to some input keys to keep our game cross-platform 🙂

And we’ll need to have some visual indicator of whether the skill is currently in cooldown mode or not, too.

Ok so – for this tutorial, I’ll be using an icon from this really cool free icon packs by Blink for my power strike (the one called “Brawler1” from the set):

Let’s add a Sprite variable in our SkillData to reference this image:

We can now use it in our UI to populate a skill bar somewhere on our screen. But first, let’s prepare a prefab for our skill button. I’ll use the same background and border images as I did for my inventory panel and add two images as children:

  • the “Icon”, which shows the image that is linked to this skill (the one I just picked from the free icons set, for example) – this element also has a Button component so we can click on it
  • the “Cooldown”, which is another image that is just a lightly transparent black overlay that will mark the skill as currently unusable

For the “Cooldown” image, you’ll notice that it’s in “Filled” mode so that I can easily change its height just by setting the fill value in the [0, 1] range. Also, for the “Border” image, don’t forget to turn of the “Raycast Target” option so it doesn’t block the mouse clicks.

But of course, by default, the icon is not set – we want to set it automatically in our script when we add the skill to our skills bar. So we’ll actually disable our icon in the prefab, and set the cooldown overlay to a fill of 0:

Now, we have to populate our scene with this prefab to prepare our skills bar. I’ll put it in the bottom-left corner of the screen in a Horizontal Layout Group:

At this point, our UI is ready but totally empty. The next step is to create some global UI manager to setup and update the skills bar. I’ll make a new class called UIManager with the following code:

Here, I just have a reference to my skills bar container object, and in the Awake() method I use this reference to get direct references to each slot in the skills bar.

Note: don’t forget to add this script to our “MANAGER” game object in the scene and to fill the various slots with the proper references 😉

Then, let’s add a function to set a specific slot with the data from a specific skill, and add some singleton reference to our script to access it more easily (as usual, there’s not much risk with using this global accessor since we have just this one instance of the script in the scene):

Note that we have to store our skills here two so that we can get the right reference when we click the button in our interface.

In our PlayerManager, we’ll remove the test OnGUI() function and, when the game first starts, we’ll use our manually-inputted test data and loop through it to update our UI properly:

We’ll also have to link these buttons to some callbacks – I’ll have them dynamically cast the right skill depending on the slot:

And finally, let’s add a basic cooldown animation so that the “Cooldown” overlay reduces as time goes by and eventually disappears when the cooldown has elapsed. We’ll also have to toggle the “Raycast Target” property of the overlay on and off to allow the mouse to click on the button underneath:

If you try to re-run the game, you’ll see that you can now click on the button to trigger the skill! To properly stop the left-click from triggering our normal chain of punches if we’re hovering the UI, we’ll also need to add a few lines in our PlayerController:

To wrap up this skills system, we can add some inputs to let players trigger skills both on desktop and console!

Setting up a few inputs

For this last part, there’s nothing very new: I’ll just add some inputs to our Input Actions object for my six power slots. On gamepad controllers, I’ll use both the four primary buttons and the bumpers so that the overall input scheme stays constant between normal and menu modes.

To avoid the conflicts with the loot mode, I’ve also moved our previous loot-related inputs to their own map and updated the code to probably toggle it on or off:

The inputs can then be assigned in our UIManager:

You can then restart the game, and you’ll see that you’re now able to trigger the skill with these new shortcuts 😉

Conclusion

And that’s it: we’ve finished our last system today, and our hero can now throw over-powered Power Strikes to one-shot the enemy and get all the interesting stuff in their pockets 🙂

Next week, we’ll have a final conclusion to this series on how to make a Hack’n’slash in Unity/C#.

I hope you liked it and as usual, thanks for your support and for reading! 🙂

Leave a Reply

Your email address will not be published.