Sunday 31 January 2016

Animation Events and Animator Override Controller

Animation evens are really useful when we want something to happen that needs to be triggered at a particular point during the playing of an animation.

In this scene, for example, our character will perform a "shooting fireball like" animation. To make it look good, we need the fireball to be spawn at a specific moment of the animation.
For this little project I used the same character model we used last time found here , the KY Magic Effect Free found here and the Magic Pack animations found here.

I also reused the animator controller we created in the previous post. I will now show you how we can reuse the same controller but with different animations or with new animations added.

Let's drag in the model and do not forget to change the rig to humanoid. Let's rename it Player.

Now we create the animation controller. Since or character will move in the exact same way as the character of our last post, we are going to reuse the same controller. But we want to add one animation to it, so we are going to use an Animator Override Controller. To create one, right click in the Project folder then choose Create -> Animation Override Controller and let's call it PlayerOverrideController. Do not forget to check the Apply Root Motion box.

If we select our controller, we can see that there is one parameter empty that expects a Controller. Here we can place the animator we created in the previous post, which I believe is called Player. We can now see the all the animations we set up last time, the parameters and the blend tree Motion are available for our new controller.(Fig 1).

Fig 1

This allows for a quick reuse of the same state machine, without having to recreate a whole new one to achieve the same thing. This also lets us modify a state machine without touching the original controller.

We can now drag in our controller a new animation. Let's get it from the folder Mixamo then MagicPack -> Animations and it's called Standing_1H_Magic_Attack_03. We can simply drag it and drop it in our animator controller window and that will create a new state. Rename it "Shoot". As we are still in the animator window, let's create a new parameter of type Trigger and call it Shoot.

Too create a transition, right click on the Motion state and select MakeTransition. To pint the arrow to the shooting state, simply click on it. Let's do the same thing but in revers, from the shooting state to our Motion state. (Fig 2).

Fig 2
We want to translate to the shooting animation whenever the parameter Shoot is triggered, and we want to go back to motion when the shooting animation is finished. To set this up, first click on the transition arrow from Motion to Shoot. Uncheck the HasExitTime parameter and, in the Conditions tab below, click the + sign to add our new trigger parameter Shoot. (Fig 3).

Fig 3
Now let's click on the other arrow. Here, we'll leave the HasExitTime box checked and we can leave the default settings as they are for now. However, I tweaked the blending points I little in order to get a better transition (for my opinion). I did that by dragging around the 2 little blue arrows you see in the graph below. (Fig 4).

Fig 4

Before we set up the animation event, we need to create the script that will handle the shooting and a script for the actual "Magic ball" to move an die out. Create a new C# script called Shooting and one called MagicBall.

Let's also set up the MagicBall object: in the KY_Effects folder, go to MagicEffectsPartFree -> prefab and choose ErekiBall2. You can of course pick any one you like. Let's add a RigidBody component, uncheck the UseGravity box. Then add  SphereCollider, adjust it if you will and check isTrigger. We don't really need colliders and rigidbody for this example, but I is good practice to give these components to moving objects. Finally, let's add the MagicBall script and that's it. We no longer need this object in the scene, so we can drag It into the project folder so a prefab will be created, and then we can delete it from the scene.

Here is the MagicBall C# script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class MagicBall : MonoBehaviour {

 Vector3 direction;
 float speed = 10;

 Rigidbody rb;


 // Use this for initialization
 void Start () {

  rb = GetComponent<Rigidbody> ();
  StartCoroutine (die ());
 
 }

 IEnumerator die()
 {
  yield return new WaitForSeconds (5);
  Destroy (this.gameObject);
 }
 
 // Update is called once per frame
 void Update () {
 
 }

 void FixedUpdate()
 {
  rb.MovePosition (rb.transform.position + direction * speed * Time.deltaTime);


 }

 public void setDirection(Vector3 dir)
 {
  direction = dir;
  
 }
}

The direction variable is going to be set later by the Shooting script via the method setDirection(...) (Line 35).  In the fixed update function we simply move the rigidbody of the object accordingly. We also start the corotine die() as soon as the object is created (Line 13), so our Magic Ball will destroy itself after 5 seconds. Now, the magic ball is complete.

Let's create a new empty game object called ShootingPoint. This is going to be an empty game object that will handle the spawning of our ball. Place it as a child of our Player object, and set its transform to X: 0.744,Y: 0.62,Z: 0.511. (Fig 5). This will ensure that the ball will spawn right where the hand of the character model is.

Fig 5
Now let's add to the ShootinPoint object the C# script Shooting. Below is the script itself:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Shooting : MonoBehaviour {

 public GameObject prefab;

 // Use this for initialization
 void Start () {
 
 }
 
 // Update is called once per frame
 void Update () {
 
 }

 public void ShootBall(Vector3 v)
 {
  GameObject ball = Instantiate (prefab, transform.position, Quaternion.identity) as GameObject;

  ball.GetComponent<MagicBall> ().setDirection (v);
  
 }
}

The public game object variable is going to be our MagicBall prefab we created earlier, so DO NOT forget to add the prefab to the public variable in the inspector. (Fig 6)

Fig 6

Then, when the ShootBall method is called, we instantiate a Magic Ball in the scene (line 17), grab the MagicBall script and call the method setDirection and pass it the direction vector (line 19).

Finally, let's create a last C# script called PlayerMovement, which is going to be identical to our last script from the previous post and will only have one additional method called Shoot.




 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class PlayerMovement : MonoBehaviour {

 public float dampSpeed;
 public float dampRotation;


 Animator anim;
 Vector3 originalPos;
 Shooting shooting;

 // Use this for initialization
 void Start () {

  anim = GetComponent<Animator> ();
  shooting = GetComponentInChildren<Shooting> ();
  originalPos = transform.position;

 }

 // Update is called once per frame
 void Update () {

  float speed = Input.GetAxis ("Vertical")/2;
  float rot = Input.GetAxis ("Horizontal");

  if (Input.GetKey (KeyCode.Space))
   speed *= 2;

  if (Input.GetKey (KeyCode.R))
   transform.position = originalPos;

  if (Input.GetMouseButtonDown (0))
   anim.SetTrigger ("Shoot");

 

  anim.SetFloat ("Speed", speed,dampSpeed,Time.deltaTime);
  anim.SetFloat ("Rotation", rot,dampRotation,Time.deltaTime);



 }

 public void Shoot()
 {
  shooting.ShootBall (this.transform.forward);
 }
}

We get a reference of the Shooting coming from the child object ShootingPoint (line 9 and 15) an the method Shoot() simply call the ShootBall method of the Shooting script, passing it the transform.forward of the player model object so the magic ball will move towards the direction our character is facing. Also, we detect if there's a mouse click and, if so, the variable Shoot in the animator will be set, triggering the shooting animation (line 32,33).

At this point, we can go on and create the animation event that will call the aforementioned Shoot() method. Select the Player object and go to Window -> Animation. Once on this window, open the animation list on the top left and select Standing_1H_Magic_Attack_Free. by keeping an eye on our character in the editor window, drag the red line forward at around 0.22, as that is the exact moment of the animation at which we want the magic ball to spawn. (Fig 7).

Fig 7
To add an animation event, click on the "stick like" icon on the top left. hat icon will appear exactly where your red line is placed on the time line. By double clicking on that, we can select the function to be called. (Fig 8).

Fig 8
All done! Now everything is set up and our character should move around and shoot magic balls at every click.

One thing to remember: when adding animation events, we can only call void types functions which accept primitive parameters and are part of a script attached to the same game object which is playing the current animation.

Example scene of the final result here.

No comments:

Post a Comment