Monday, September 19, 2016

Tutorial: Programming with Animation

Sometimes you have assets that are not rigged and come with animations. The benefits of this is that these characters are best for static background filler to add atmosphere like background characters in a movie. They are also good for companion AI, basic NPC enemies.

Here is a free model that comes with animations already:
Once you have downloaded and imported Skeleton, navigate to it in the Project tab.

Click the arrow on the skeleton_animated to see the list of animation names, pay attention to the animation names their spelling particularly.
Before we begin programming we need to do two things:
1) drag the model into the game world.
2) select Skeleton and select Add Component in the Inspector tab, search or navigate for the Character Controller component.


**NOTE: If you notice the Animation component, dont mess with it, this is important and is default attached to the model, this allows us to use the animations that come with this model.

Now the character controller(the green capsule) might be too big, too small or sunken into the terrain. To fix it, change the Y Value under the Character Controller to 1, or play around if your circumstances are different.

Okay, now lets make a new C# script, call it: SkellyWalk.cs. If we look back on her animations we see that our model can run or walk. We will need a float variable for run and walk:

public float skellyWalkSpeed;
public float skellyRunSpeed;

Because our character can only move in two dimension we need to declare variables for moving horizontally and vertically:
public float horizontalX, verticalZ; 

Now we need to introduce two intrinsic classes to the Unity engine: Animation and Character Controller. The Animation class allows us to access the animations with in the model and the Character Controller is the one you have added to the model, this will allow us to move the model. As the character controller moves we will play the animation giving the illusion of real movement.

Animation skellyAnimations;
CharacterController amandaControler;

Before the script can access the above classes we must irt initialize their values. In the Start() method type the following:

skellyControler = GetComponentInChildren<CharacterController> ();
skellyAnimations = GetComponentInChildren<Animation> ();
skellyAnimations.SyncLayer (0);

The horizontal and vertical axes are controlled by the WASD keys, WS are vertical and AD are horizontal. These are built into Unity, so when the player presses AD or WS this is registered as a numerical value. If the value is greater than 0.0 the character controller will be in motion, if no key is pressed the value is 0.0 and the controller is idle.

In the Update() function we need to initialize the horizontalX and verticalZ value. To do so we have to link them to the value that Unity provides for our axes.
horizontalX = Input.GetAxis("Horizontal");
verticalY = Input.GetAxis("Vertical");

Now we have to do the transitions of animations based on keys pressed. If you map out the WASD keys on a Cartesian plane W has a positive value(> 0.0), S has a negative value(< 0.0), A has a negative value(< 0.0) and D has a positive value(> 0.0). This will be the basis for our if, else if, else structure. First we check for any movement:

if (verticalZ == 0 && horizontalX == 0)
skellyAnimations ["Idle"].wrapMode = WrapMode.Default;
skellyAnimations.CrossFade ("Idle");

Now we check if the player pressed the W key:

if (verticalZ >= 0.1)
{//walk forward
skellyAnimations ["Walk02"].wrapMode = WrapMode.Default;
skellyAnimations.CrossFade ("Walk02");
transform.Translate (Vector3.forward * Time.deltaTime * skellyWalkSpeed);

Now we check if the player pressed the S key:

else if (verticalZ < 0.0)
{//walk back
transform.Translate (Vector3.back * Time.deltaTime * amandaWalkSpeed);
amandaAnimations ["WalkBack"].wrapMode = WrapMode.Default;
amandaAnimations.CrossFade ("WalkBack");

Now we check if the player pressed the D key:

if(horizontalX >= 0.1)
transform.Translate(Vector3.right * Time.deltaTime * skellyWalkSpeed);
skellyAnimations["WalkRight"].wrapMode = WrapMode.Default;

Now we check if the player pressed the A key:

else if(horizontalX < 0.0)
transform.Translate(Vector3.left * Time.deltaTime * skellyWalkSpeed);
skellyAnimations["WalkLeft"].wrapMode = WrapMode.Default;

Just as a bonus, because this model comes with attack, below is the attack code, if the Key is Mouse0 or left click, play the animation "Swing normal"

if (Input.GetKey (KeyCode.Mouse0)) 
skellyAnimations["SwingNormal"].wrapMode = WrapMode.Default;

You may have notice three things:
1) .wrapMode = WrapMode.Default; This is another intrinsic feature in Unity. An animation is a series of frames showing movement. wrapMode has different settings to how to end the animation. This is all used to make the animation seem fluid and believable illusion of motion. In this instance we use Default which is set to Once. Meaning when the last frame of the animation plays, we will go back to the first frame in the animation.

2) .CrossFade(); All this does is allow us to fade into anther animation. For example you press W, skelyl moves forward, then you press D to move back. CrossFade lets us blend into walking back for a more natural transition.

3) transform.Translate(); This is an intrinsic class in unity, its purpose is to move the controller component. Its read like this, for example one of the above codes:
--transform.Translate(Vector3.left * Time.deltaTime * skellyWalkSpeed);
Move the character controller left the speed of the skelly walk variable times deltaTime. Delta time is a special form of time that calculates the time elapsed between two frames. If this is confusing (i dont blame you) especially delta time, i would suggest reading more about it, its a cornerstone of movement in programming.

Attach the script to the skeleton model, edit the float variables: