We used Animation Events sporadically in earlier recipes because they are so handy, and it was simply difficult to until to this moment. Most present games use Animation Events very extensively, especially for handling combat. Creating believable melee encounters would be a lot harder without this handy tool.
Getting ready
Before we start, we need to have a character with an attack animation. In the example files, we named the animation HumanAttack. We also need an enemy (we use a Spider character in this example) with Idle and Death animations. You can also download the provided example Unity project and go to the Chapter 06 Handling combat\Recipe 02 Using animation events to trigger script functions directory. You will find a scene called Example.unity there, with Humanoid and Spider characters. In the Rigs directory, you can find the Humanoid.fbx character with all required animations. When you play the game, the Humanoid character will attack the Spider several times and deal damage to it.
How to do it…
To use Animation Events to trigger script functions, follow these steps:
Import your character with the HumanAttack animation to Unity.
1.
Go to the Animation import settings tab in the Inspector window.
2.
Select the HumanAttack animation.
3.
Navigate down to the Events section and unfold it.
4.
Use the Preview window to scrub through the animation to the moment of the hit 5. in the attack.
Add an Animation Event by pressing the Add Event button, as shown in the 6. following screenshot:
Adding Animation Events
The Edit Animation Event window will appear. Type Attack in the Function 7. text field. To make the event work, we need to have a function with the same
name in a script assigned to our character.
We will create a simple fight scene, so we need a script for the enemy. Create a 8. new script and call it Enemy.cs. In this script, we have the public float
hitPoints variable to store current health of our enemy. We also have two public virtual void functions: Hit(float damage) and Die().
The Hit(float damage) function subtracts damage from our
current hitPoints and calls the Die() function when hitPoin ts drop to 0.
The Die() function plays the death animation by setting the bool Dead parameter in the controller. These functions are virtual because we want to derive from this script in later recipes to add new functionality:
public void Hit(float damage) {
hitPoints -= damage;
if (hitPoints <= 0) {
Die();
} }
public void Die() {
anim.SetBool("Dead", true);
}
Create one transition:
9.
In it, create the bool Dead parameter. Drag and drop the Idle and Death 10. animations to the controller. Make Idle the default state.
Create a new Animator Controller for our Spider character.
11. Idle | Death with the condition: Dead parameter set to true, Has Exit Time set to false, and Transition Duration set to 0.2 seconds.
Place the Spider character in the scene. Add a Rigidbody component, a Sphere Collider component, and our Enemy.cs script component to it. Set the Rigidbody component to Is Kinematic.
Assign the controller to Spider's Animator component.
12.
Create another Animator Controller for our Humanoid character.
13.
Place a looped HumanAttack animation in that controller and assign it to 14. the Humanoid character.
Place the Humanoid character near the Spider in the scene.
15.
Create a new script and call it MeleeAttack.cs. In this script, we use 16. our public void Attack() function. This is the function called from the
animation event. We have a list of all enemies in range. We iterate through that list and call the Hit() function on every enemy from that list. We also have a publicfloat damage variable to store the character's damage value and send it in the Hit() function:
Assign the script to the Humanoid character.
17.
We need one more script to get the targets in range. Create a new script and call 18. it TargetTrigger.cs. In the void OnTriggerEnter(Collider other)
function of this script, we check if the object entering the trigger has an Enemy.cs component. If so, we add it to the enemiesInRange in the MeleeAttack.cs component:
Enemy e = other.gameObject.GetComponent<Enemy>();
if (e != null) {
if (!attackScript.enemiesInRange.Contains(e))
attackScript.enemiesInRange.Add(e);
} }
In the void OnTriggerExit(Collider other) function, we remove the 19. enemy from the enemiesInRange list:
Enemy e = other.gameObject.GetComponent<Enemy>();
if (e != null) {
if (attackScript.enemiesInRange.Contains(e)) {
attackScript.enemiesInRange.Remove(e);
} }
In the preceding script, attackScript is the reference to the MeleeAttack.cs 20. script component.
Create an empty game object and name it TargetTrigger. Add a Box Collider 21. component to it and set the collider to Is Trigger. Assign
our TargetTrigger.cs script to it.
Make the TargetTrigger game object the child of our Humanoid character and 22. shape the Box Collider for checking melee hit range. See the following
screenshot:
Melee attack range trigger
Play the game to see the effect.
23.
How it works…
Animation Events call public functions from the scripts assigned to the game object playing the animation. To make it work, the Function field of the Animation Event needs to have the same name as the function we want to call from the script.
Events are extremely useful because they allow us to synchronize function calls with animations. The best moment to check if player hits the target is when we see a visual hit in the animation—a fist is in its extreme position in a punch, or a sword is in its extreme position in a swing. Without Animation Events, we would have to always manually delay function calls to achieve similar results.
There's more…
Animation Events can also have an int, float, string, or an object type parameter. To use these parameters, we have to implement them in our functions called by the event.
These parameters can be useful for playing special effects or sounds (you can find more information about it in the Using Animation Events to trigger sound and visual effects recipe in Chapter 7, Special Effects.