15 min read

(For more resources related to this topic, see here.)

After going through these principles, we will be completing the tasks to enhance the maze game and the gameplay. We will apply animations to characters and trigger these in particular situations. We will improve the gameplay by allowing NPCs to follow the player where he/she is nearby (behavior based on distance), and attack the user when he/she is within reach. All material required to complete this article is available for free download on the companion website: http://patrickfelicia.wordpress.com/publications/books/unity-outbreak/.

The pack for this article includes some great models and animations that were provided by the company Mixamo to enhance the quality of our final game. The characters were animated using Mixamo’s easy online sequences and animation building tools. For more information on Mixamo and its easy-to-use 3D character rigging and animation tools, you can visit http://www.mixamo.com.

Before we start creating our level, we will need to rename our scene and download the necessary assets from the companion website as follows:

  1. Duplicate the scene we have by saving the current scene (File Save | Scene), and then saving this scene as chapter5 (File | Save Scene As…).
  2. Open the link for the companion website: http://patrickfelicia.wordpress.com/publications/books/unity-outbreak/.
  3. Click on the link for the chapter5 pack to download this file.
  4. In Unity3D, create a new folder, chapter5, inside the Assets folder and select this folder (that is, chapter5).
  5. From Unity, select Assets | Import Package | Custom Package, and import the package you have just downloaded.
  6. This should create a folder, chapter5_pack, within the folder labeled chapter5.

Importing and configuring the 3D character

We will start by inserting and configuring the zombie character in the scene as shown in the following steps:

  1. Open the Unity Assets Store window (Window | Asset Store).
  2. In the Search field located in the top-right corner, type the text zombie.
  3. Click on the search result labeled Zombie Character Pack, and then click on the button labeled Import.
  4. In the new window entitled Importing package, uncheck the last box for the low-resolution zombie character and then click on Import.
  5. This will import the high-resolution zombie character inside our project and create a corresponding folder labeled ZombieCharacterPack inside the Assets folder.
  6. Locate the prefab zombie_hires by navigating to Assets | ZombieCharacterPack.
  7. Select this prefab and open the Inspector window, if it is not open yet.
  8. Click on the Rig tag, set the animation type to humanoid, and leave the other options as default.
  9. Click on the Apply button and then click on the Configure button; a pop-up window will appear: click on Save.
  10. In the new window, select: Mapping | Automap, as shown in the following screenshot:

  11. After this step, if we check the Hierarchy window, we should see a hierarchy of bones for this character. Select Pose | Enforce T-Pose as shown in the following screenshot:

  12. Click on the Muscles tab and then click on Apply in the new pop-up window.
  13. The Muscles tab makes it possible to apply constraints on our character.
  14. Check whether the mapping is correct by moving some of the sliders and ensuring that the character is represented properly. After this check, click on Done to go back to the previous window.

Animating the character for the game

Once we have applied these settings to the character, we will now use it for our scene.

  1. Drag-and-drop the prefab labeled zombie_hires by navigating to Assets | ZombieCharacterPack to the scene, change its position to (x=0, y =0, z=0), and add a collider to the character.
  2. Select: Component | Physics | Capsule Collider.
  3. Set the center position of this collider to (x=0, y=0.7, z=0), the radius to 0.5, the height to 2, and leave the other options as default, as illustrated in the following screenshot:

  4. Select: Assets | chapter5 | chapter5_pack; you will see that it includes several animations, including Zombie@idle, Zombie@walkForward, Zombie@attack, Zombie@hit, and Zombie@dead.

We will now create the necessary animation for our character.

Click once on the object zombie_hires in the Hierarchy window. We should see that it includes a component called Animator. This component is related to the animation of the character through Mecanim. You will also notice an empty slot for an Animator Controller. This controller will be created so that we can animate the character and control its different states, using a state machine.

Let’s create an Animator Controller that will be used for this character:

  1. From the project folder, select the chapter5 folder, then select Create | Animator Controller in the Project window. This should create a new Animator Controller labeled New Animator Controller in the folder chapter5.
  2. Rename this controller zombieController.
  3. Select the object labeled zombie_hires in the Hierarchy window.
  4. Locate the Animator Controller that we have just created by navigating to Assets | chapter5 (zombieController), drag-and-drop it to the empty slot to the right of the attribute controller in the Animator component of the zombie character, and check that the options Apply Root Motion and Animate Physics are selected. Our character is now ready to receive the animations.
  5. Open the Animator window (Window | Animator). This window is employed to display and manage the different states of our character. Since no animation is linked to the character, the default state is Any State.
  6. Select the object labeled zombie_hires in the Hierarchy window.
  7. Rearrange the windows in our project so that we can see both the state machine window and the character in the Scene view: we can drag the tab labeled Scene for the Scene view at the bottom of the Animator window, so that both windows can be seen simultaneously.

We will now apply our first animation to the character:

  1. Locate the prefab Zombie@idle by navigating to Assets | chapter5 | chapter5_pack.
  2. Click once on this prefab, and in the Inspector window, click the Rig tab.
  3. In the new window, select the option Humanoid for the attribute Animation Type and click on Apply.
  4. Click on the Animations tab, and then click on the label idle, this will provide information on the idle clip.
  5. Scroll down the window, check the box for the attribute Loop Pose, and click on Apply to apply this change (you will need to scroll down to locate this button).
  6. In the Project view, click on the arrow located to the left (or right, depending on how much we have zoomed-in within this window) of the prefab Zombie@idle; it will reveal items included in this prefab, including an animation called idle, symbolized by a gray box with a white triangle.
  7. Make sure that the Animator window is active and drag this animation (idle) to the Animator window.
  8. This will create an idle state, and this state will be colored in orange, which means that it is the default state for our character. Rename this state Idle (upper case I) using the Inspector.
  9. Play the scene and check that the character is in an idle state.
  10. Repeat steps 1-9 for the prefab Zombie@walkForward and create a state called WalkForward. To test the second animation, we can temporarily set the state walkForward to be the default state by right-clicking on the walkForward state in the Animator window, and selecting Set As Default. Once we have tested this animation, set the state Idle as the default state.

While the zombie is animated properly, you may notice that the camera on the First Person Controller might be too high. You will address this by changing the height of the camera so that it is at eye-level. In the Hierarchy view, select the object Main Camera that is located with the object First Person Controller and change its position to (x=0, y=0.5, z=0).

We now have two animations. At present, the character is in the Idle state, and we need to de fine triggers or conditions for the character to start or stop walking toward the player. In this game, we will have enemies with different degrees of intelligence. This first type will follow the user when it sees the user, is close to the user, or is being attacked by the user.

The Animator window will help to create animations and to apply transition conditions and blending between them so that transitions between each animation are smoother. To move around this window, we can hold the Alt key while simultaneously dragging-and-dropping the mouse. We can also select states by clicking on them or de fining a selection area (drag-and-drop the mouse to define the area). If needed, it is also possible to maximize this window using the icon located at its top-right corner.

Creating parameters and transitions

First, let’s create transitions. Open the Animator window, right-click on the state labeled Idle, and select the option Make Transition from the contextual menu. This will create an arrow that symbolizes the transition from this state to another state. While this arrow is visible, click on the state labeled WalkForward. This will create a transition between the states WalkForward and Idle as illustrated in the following screenshot:

Repeat the last step to create a transition between the state WalkForward and Idle: right-click on the state labeled WalkForward, select the option Make Transition from the contextual menu, and click on the state labeled Idle.

Now that these transitions have been defined, we will need to specify how the animations will change from one state to the other. This will be achieved using parameters. In the Animator window, click on the + button located at the bottom-right corner of the window, as indicated in the following screenshot:

Doing so will display a contextual menu, from which we can choose the type of the parameter. Select the option Bool to create a Boolean parameter. A new window should now appear with a default name for our new parameter as illustrated in the following screenshot: change the name of the parameter to walking.

Now that the parameter has been defined, we can start defining transitions based on this parameter. Let’s start with the first transition from the Idle state to the Walkforward state:

  1. Select the transition from the Idle state to the Walkforward state (that is, click on the corresponding transition in the Animator window).
  2. If we look at the Inspector window, we can see that this object has several components, including Transitions and Conditions. Let’s focus on the Conditions component for the time being. We can see that the condition for the transition is based on a parameter called ExitTime and that the value is 0.98. This means that the transition will occur when the current animation has reached 98 percent completion. However, we would like to use the parameter labeled walking instead.
  3. Click on the parameter ExitTime, this should display other parameters that we can use for this transition.
  4. Select walking from the contextual menu and make sure that the condition is set to true as shown in the following screenshot:

  5. The process will be similar for the other transition (that is, from WalkForward to Idle), except that the condition for the transition for the parameter walking will be false: select the second transition (WalkForward to Idle) and set the transition condition of walking to false.

To check that the transitions are working, we can do the following:

  1. Play the scene and look at the Scene view (not the Game view).
  2. In the Animator window, change the parameter walking to true by checking the corresponding box, as highlighted in the following screenshot:

  3. Check that the zombie character starts walking; click on this box again to set the variable walking to false, check that the zombie stops walking, and stop the Play mode (Ctrl + P).

Adding basic AI to enemies

We have managed to set transitions for the animations and the state of the zombie from Idle to walking. To add some challenge to the game, we will equip this enemy with some AI and create a script that changes the state of the enemy from Idle to WalkForward whenever it sees the player. First, let’s allocate the predefined-tag player to First Person Controller: select First Person Controller from the Hierarchy window, and in the Inspector window, click on the drop-down menu to the right of the label Tag and select the tag Player.

Then, we can start creating a script that will set the direction of the zombie toward the player. Create a folder labeled Scripts inside the folder Assets | chapter5, create a new script, rename it controlZombie, and add the following code to the start of the script:

public var walking:boolean = false;
public var anim:Animator;
public var currentBaseState:AnimatorStateInfo;
public var walkForwardState:int = Animator.StringToHash("Base
Layer.WalkForward");
public var idleState:int = Animator.StringToHash("Base
Layer.Idle");
private var playerTransform:Transform;
private var hit:RaycastHit;

  • In statement 1 of the previous code, a Boolean value is created. It is linked to the parameter used for the animation in the Animator window.
  • In statement 2 of the previous code, we define an Animator object that will be used to manage the animator component of the zombie character.
  • In statement 3 of the previous code, we create an AnimatorStateInfo variable that will be used to determine the current state of the animation (for example, Idle or WalkForward).
  • In statement 4 of the previous code, we create a variable, walkForwardState , that will represent the state WalkForward previously de fined in the Animator window. We use the method Animator.StringToHash to convert this state initially from a string to an integer that can then be used to monitor the active state.
  • In statement 5 of the previous code, similar to the previous comments, a variable is created for the state Idle.
  • In statement 6 of the previous code, we create a variable that will be used to detect the position of the player.
  • In statement 7 of the previous code, we create a ray that will be employed later on to detect the player.

Next, let’s add the following function to the script:

function Start ()
{
anim = GetComponent(Animator);
playerTransform = GameObject.FindWithTag("Player").transform;
}

In line 3 of the previous code, we initialize the variable anim with the Animator component linked to this GameObject.

We can then add the following lines of code:

function Update ()
{
currentBaseState = anim.GetCurrentAnimatorStateInfo(0);
gameObject.transform.LookAt(playerTransform);
}

  • In line 3 of the previous code, we determine the current state for our animation.
  • In line 4 of the previous code, the transform component of the current game object is oriented so that it is looking at the First Person Controller. Therefore, when the zombie is walking, it will follow the player.

Save this script, and drag-and-drop it to the character labeled zombie_hires in the Hierarchy window.

As we have seen previously, we will need to manage several states through our script, including the states Idle and WalkForward. Let’s add the following code in the Update function:

switch (currentBaseState.nameHash)
{
case idleState:
break;
case walkForwardState:

break;
default:
break;
}

  • In line 1 of the previous code, depending on the current state, we will switch to a different set of instructions
  • All code related to the state Idle will be included within lines 3-4 of the previous code
  • All code related to the state WalkForward will be included within lines 6-7

If we play the scene, we may notice that the zombie rotates around the x and z axes when near the player; its y position also changes over time. To correct this issue, let’s add the following code at the end of the function Update:

transform.position.y = -0.5;
transform.rotation.x = 0.0;
transform.rotation.z = 0.0;

We now need to detect whether the zombie can see the player, or detect its presence within a radius of two meters(that is, the zombie would hear the player if he/she is within two meters). This can be achieved using two techniques: by calculating the distance between the zombie and the player, and by casting a ray from the zombie and detecting whether the player is in front of the zombie. If this is the case, the zombie will start walking toward the player. We need to calculate the distance between the player and the zombie by adding the following code to the script controlZombie, at the start of the function Update, before the switch statement:

var distance:float = Vector3.Distance(transform.position,
playerTransform.position);

In the previous code, we create a variable labeled distance and initialize it with the distance between the player and the zombie. This is achieved using the built-in function Vector3.Distance.

Now that the distance is calculated (and updated in every frame), we can implement the code that will serve to detect whether the player is near or in front of the zombie.

Open the script entitled controlZombie, and add the following lines to the function Update within the block of instructions for the Idle state, so that it looks as follows:

case idleState:
if ((Physics.Raycast
(Vector3(transform.position.x,transform.position.y+.5,transform.po
sition.z), transform.forward, hit,40) &&
hit.collider.gameObject.tag == "Player") || distance <2.0f)
{
anim.SetBool("walking",true);
}

break;

In the previous lines of code, a ray or ray cast is created. It is casted forward from the zombie, 0.5 meters above the ground and over 40 meters. Thanks to the variable hit, we read the tag of the object that is colliding with our ray and check whether this object is the player. If this is the case, the parameter walking is set to true. Effectively, this should trigger a transition to the state walking, as we have defined previously, so that the zombie starts walking toward the player.

Initially, our code was written so that the zombie rotated around to face the player, even in the Idle state (using the built-in function LookAt). However, we need to modify this feature so that the zombie only turns around to face the player while it is following the player, otherwise, the player will always be in sight and the zombie will always see him/her, even in the Idle state. We can achieve this by deleting the code highlighted in the following code snippet (from the start of the function Update), and adding it to the code for the state WalkForward:

case walkForwardState:
transform.LookAt(playerTransform);
break;

In the previous lines, we checked whether the zombie is walking forward, and if this is the case, the zombie will rotate in order to look at and follow the player. Test our code by playing the scene and either moving within two meters of the zombie or in front of the zombie.

LEAVE A REPLY

Please enter your comment!
Please enter your name here