Up until now, characters have had free reign to instantly fire off abilities whenever they like. This is fine when you are dealing with nothing but floating geometry and just want to make sure the abilities work. But once you start adding in models and animations, this becomes problematic because the timing of your abilities taking effect won't match up to the actions of the character, making the combat lack impact and therefore feel unsatisfying. To rectify this issue before it comes up, I have added a cast time system into the game which causes characters to stop and "cast" for a set amount of time before an ability fires. This time will be filled later with an attack animation. If hit with a hard crowd control ability during a cast, a character will be interrupted, preventing the ability being used. This adds an element of counter play. To allow this interaction, I have implemented our first hard CC effect, a stun, which simply stops the character from doing anything for its duration.
The first step to implementing this system was to change the ability functions which apply the final effect (or spawn something) into coroutines so that they could be paused, allowing for a cast time. Now rather than just applying the effect to the target, the point and click coroutine below sets some new variables in the parent character, then waits before applying the effects and resetting those variables. The wait is initially just the ability's base cast time value but is reduced by the character's attack speed stat.
Back when things like these were functions, we could pass them variables like who to target, but you cant do that with a coroutine if you are starting it from another script. As you can see below, we sometimes need to start these coroutines from the parent character, like after the character has moved into range. This means the coroutines need to work without passing them any variables. To allow this, rather than passing them the target character or position, we now set the parent's target in the targeting function and then in the application/spawning function we take the parent's target and use that.
From player character update function:
Targeting function from the ability script:
Previously we used a "casting" bool to denote that the character was moving into range but that has now been repurposed to show that the character is in a casting wait period. As such, a new moving to range bool has been added to track if the character is running into position to fire off an ability. Ability script move into range functions:
To display ability cast times to the player, I have added a UI element under the health bar which displays a cast's current progress if casting. The function which informs the cast bar's fill is called in the player and enemy update functions like so:
The cast bar is identical to the health bar, just coloured white and lowered a little. It is also referenced in the same way. Updating it is very different though as how full it is changes over time. If the cast start time is 0, it means we are not yet casting, so we set it to be the current time, and set the time since the cast started to the difference between the current time and the start time, which right now is 0. If the cast bar is not active we activate it. This is only relevant for enemies as we deactivate their cast bar when they are not casting to reduce clutter. We then set the cast bar's slider value as the time since the cast started divided by the total cast time. We then update the time since start. If our time since start exceeds the full cast time, which it shouldn't really do, everything is reset. Here is the cast bar update function:
Now we have our cast times up and running, we need to add stuns to stop an opponent's cast. Stun is a new debuff type applied by effects. It is stored as a bool in the ability script and passed to the effect script when the ability applies the effect to a target. Then in the mod stats function of the effect script we set the target's new stunned bool to true and increment their new stuns int variable.
The movement and ability functions in the character scripts have "if not stunned" requirements on them now so that being stunned prevents them performing any actions. They also have "if not casting" requirements, forcing you to commit to casts, preventing you from interrupting yourself.
We have a stuns int value as well as the bool so that if the character is effected by more than one stun, and one of them wears off, they do not become unstunned as they still have a stunned int greater than 0. This is shown in the addition to the remove effect function.
As shown previously, applying a stun interrupts the target's cast. It can do this because when a cast starts we save the casting coroutine as a string in the character then when stunned we stop that coroutine and reset any related variables such as the target etc.
Here's a quick video of the casting and stuns in action:
Comments