(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:
- Duplicate the scene we have by saving the current scene (File Save | Scene), and then saving this scene as chapter5 (File | Save Scene As…).
- Open the link for the companion website: http://patrickfelicia.wordpress.com/publications/books/unity-outbreak/.
- Click on the link for the chapter5 pack to download this file.
- In Unity3D, create a new folder, chapter5, inside the Assets folder and select this folder (that is, chapter5).
- From Unity, select Assets | Import Package | Custom Package, and import the package you have just downloaded.
- 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:
- Open the Unity Assets Store window (Window | Asset Store).
- In the Search field located in the top-right corner, type the text zombie.
- Click on the search result labeled Zombie Character Pack, and then click on the button labeled Import.
- In the new window entitled Importing package, uncheck the last box for the low-resolution zombie character and then click on Import.
- This will import the high-resolution zombie character inside our project and create a corresponding folder labeled ZombieCharacterPack inside the Assets folder.
- Locate the prefab zombie_hires by navigating to Assets | ZombieCharacterPack.
- Select this prefab and open the Inspector window, if it is not open yet.
- Click on the Rig tag, set the animation type to humanoid, and leave the other options as default.
- Click on the Apply button and then click on the Configure button; a pop-up window will appear: click on Save.
- In the new window, select: Mapping | Automap, as shown in the following screenshot:
- 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:
- Click on the Muscles tab and then click on Apply in the new pop-up window.
- The Muscles tab makes it possible to apply constraints on our character.
- 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.
- 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.
- Select: Component | Physics | Capsule Collider.
- 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:
- 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:
- 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.
- Rename this controller zombieController.
- Select the object labeled zombie_hires in the Hierarchy window.
- 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.
- 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.
- Select the object labeled zombie_hires in the Hierarchy window.
- 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:
- Locate the prefab Zombie@idle by navigating to Assets | chapter5 | chapter5_pack.
- Click once on this prefab, and in the Inspector window, click the Rig tab.
- In the new window, select the option Humanoid for the attribute Animation Type and click on Apply.
- Click on the Animations tab, and then click on the label idle, this will provide information on the idle clip.
- 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).
- 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.
- Make sure that the Animator window is active and drag this animation (idle) to the Animator window.
- 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.
- Play the scene and check that the character is in an idle state.
- 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:
- Select the transition from the Idle state to the Walkforward state (that is, click on the corresponding transition in the Animator window).
- 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.
- Click on the parameter ExitTime, this should display other parameters that we can use for this transition.
- Select walking from the contextual menu and make sure that the condition is set to true as shown in the following screenshot:
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:
- Play the scene and look at the Scene view (not the Game view).
- In the Animator window, change the parameter walking to true by checking the corresponding box, as highlighted in the following screenshot:
- 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
public var idleState:int = Animator.StringToHash("Base
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);
- 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:
- 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,
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:
sition.z), transform.forward, hit,40) &&
hit.collider.gameObject.tag == "Player") || distance <2.0f)
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:
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.
Sending messages to alert other close enemies
Now that we have created a relatively simple behavior for each enemy, we can add an additional AI feature. We will modify our code so that if there are several enemies in a similar location, those that have not detected the player will be alerted of its presence by other enemies, and also start walking toward the player. First, we will create a tag for each enemy. This will make it easier to identify any enemy within range:
- Select the object zombie_hires from the Hierarchy window; in the Inspector window, click the on the drop-down menu to the right of the label Tag and click on the option Add Tag.
- Select the line from the last tag element (for example, Element 7), and type the word zombie. This will create a new tag; once this is done, select our object (zombie_hires).
- In the Inspector window, click on the drop-down menu to the right of the label Tag.
- Select the label zombie that we have just created.
Next, we will create a function that will be accessible from other objects and that will change the value of the parameter walking for the zombie animation. By changing this parameter to true, we will be able to change the state of any zombie, and in our case, make them walk toward the player. Let's add the following code to the script controlZombie:
function setWalking(newWalkingValue: boolean)
In the previous code, we declare a function labeled setWalking. This function takes one parameter (newWalkingValue) that will be used to trigger a transition between the states Idle and WalkForward. We set the value of the parameter walking in the animation for the zombie using the built-in function anim.SetBool.
Then, we need to detect whether there are any zombies around the one that has detected the player. We will achieve this by adding the following code within the function Update in the script controlZombie, within the code dedicated to the state walkForward, as highlighted in the following code snippet:
var zombies:GameObject  = GameObject.FindGameObjectsWithTag("zomb
for (var zombie:GameObject in zombies)
if (Vector3.Distance(transform.position, zombie.transform.
position) < 8.0f)
In the previous code, we create an array of objects. This array will be populated with all zombies' objects in our game. We loop through this array, and assess the distance between each of these objects and the current zombie. Any zombie within a distance of 8 meters from the zombie that has detected the player will also start to walk toward the player. To test this behavior, we could do the following:
- Duplicate the zombie character twice to obtain a total of 3. Change the position of the two new zombies to (x=-2, y=-0.5, z=0) and (x=-4, y=-0.5, z=0), and rename them zombie_hires2 and zombie_hires3, respectively.
- Rotate the first two instances so that they look away from the player.
- Rotate the third instance so that it is facing the player.
- Play the scene and move the player so that it is in front of the third instance (that is, so that it can be seen). We should see that this zombie starts to follow the player as well as the other zombies, although these are not directly facing the player.
The script could be improved by also checking that the message is sent to an enemy only if it is not already walking toward the player.
Note that the method FindGameOBjectsWithTag can become computer intensive if our game includes many zombie characters. We may instead detect other objects using the built-in method Physics.OverlapSphere that makes it possible to detect colliders within a specific radius.
Once we have checked that this behavior works, we can delete or deactivate the two copies of the zombie.
Creating additional states
At this stage, we have created an interesting, yet simple, artificial behavior, whereby the enemies present in the maze will, if they see the character, or are within a specific radius, walk toward the player and alert all other enemies in this area. We will now include an additional state that we will call Attack, which will be triggered when these enemies are within reach of the player:
- Locate the prefab Zombie@attack by navigating to Assets | chapter5 | chapter5_pack.
- Click once on this prefab, and in the Inspector window, click on the Rig tab.
- In the new window, select the option Humanoid for the attribute Animation Type and click on Apply.
- Click on the Animations tab, and then click on the label attack, this will provide information on the attack clip.
- Scroll down the window, check the box for the attribute Loop Pose and click on Apply to apply this change.
- In the Project view, click on the arrow located to the left of the prefab Zombie@attack it will reveal items included in this prefab, including an animation called attack symbolized by gray box with a white triangle.
- Check that the Animator window is open and drag the animation attack to the Animator window.
- This will create an attack state; rename this state Attack (upper case A) using the Inspector.
- Check that the idle state is the default state
Once this is done, we will create transitions between these states:
- Create a transition from the state WalkForward to the state Attack.
- Create a transition from the state Idle to the state Attack.
- Create a new Boolean parameter called withinReach.
- Select the transition between the states WalkForward and Attack.
- In the Inspector window, set the condition for the transition to withinReach = true, and leave other attributes as default.
- Select the transition between the states Idle and Attack.
- In the Inspector window, set the condition for the transition to withinReach = true, and leave other attributes as default.
- Create a transition between the state Attack and the state WalkForward.
- Set the conditions for the transition to withinReach = false and ExitTime = 0.70 (to include an additional condition we can click on the + button in the same window).
The Animator window should now include three states and two transitions to the state Attack as highlighted in the following screenshot:
We have set up the animations in the Animator window by de fining states and transitions conditions; we now need to trigger these states within the script. Ideally, we would like the attack to be triggered when the enemies are within approximately 1.5 meter from the player. Let's modify the controlZombie script to implement this behavior:
Open the script controlZombie and add the following lines of code to the function Update just after the code that calculates the distance:
if (distance < 1.5f) anim.SetBool("withinReach",true);
The variable distance has already been defined in this script to detect the distance between the player and the enemies. In the previous code, we use this variable to determine whether the player is within reach, so that the attack can be perpetrated. If the distance is less than 1.5 meters, the parameter withinReach is set to true (and the state should change accordingly to Attack). If the distance is more than 1.5 meters,then the animator parameter withinReach is set to false, and the state should change accordingly to WalkForward.
Finally, we will create two additional states: a state when a bullet hits an enemy and a state when the enemy dies following significant injuries. Following the steps and instructions described previously, create a state called Hit, based on the prefab Zombie@hit. We don't need to loop this animation.
We then need to create transitions to the state Hit from the states WalkForward, Idle, or Attack:
- Create three transitions: from the state WalkForward to the state Hit, from the state Idle to the state Hit, and from the state Attack to the state Hit.
- Create a new Boolean parameter called hit, select the transition from the state Idle to the state Hit, and set the condition for the transition to hit = true, and leave the other attributes as default.
- Repeat the last step for the transition from the state WalkForward to Hit, and from the state Attack to Hit.
- Finally, create a transition between the state Hit and the state WalkForward, and set the transition condition to Exit Time = 0.9. In this case, when the enemy is hit, it will start walking toward the user, regardless of its previous state (that is, idle, walking, or attack).
The following figure highlights the new state and transitions that we have just created:
Finally, to be able to trigger these states, we will need to modify the script that fires bullets. If the bullet hits the enemy, then its state will change accordingly. Open the script shootBullet and add the following code within the function Update, inside the conditional statement that starts with if(Physics.Raycast (ray, hit, 100)).
if (hit.collider.gameObject.tag == "zombie")
- In statement 1 of the previous code, we check whether the bullet has collided with an enemy
- In statement 2 of the previous code, if this is the case, the Boolean parameter labeled hit is set to true for the corresponding animator.
We have created all necessary transitions; if we test our scene, we can see that the enemy, when hit by a bullet, transitions indefinitely between the states Hit and WalkForward. This is because the Boolean variable hit is set to true all the time, causing the transition from the state WalkForward to the state Hit to occur indefinitely. To fix this, we need to set the variable hit to false once the transition has occurred from the state Idle, Attack, or WalkForward to the state Hit. This can be achieved by setting the variable hit to false when the enemy is in the state Hit. We can do this through script by adding the following line at the start of the script controlZombie:
var HitState:int = Animator.StringToHash("Base Layer.Hit");
The previous line of code declares a state for the animator, so that we can monitor the state Hit. Let's add the following lines in the function Update, within the switch structure:
- In line 1 of the previous code, we test whether the enemy is in the state called Hit
- In line 3 of the previous code, if this is the case, we set the Boolean variable hit to false
Test the game, shoot at the zombie in the scene, and check that it behaves as expected (that is, following the player after being hit).
We now need to add a final state to our enemy, the state called Dead. The enemy will enter this state when it has sustained significant injuries. The zombie should not be able to transition to any other states from this state.
- Following the steps described in the previous pages, create a state called Dead, based on the prefab Zombie@dead. This animation does not need to loop.
- Create a new Boolean parameter called die.
- Create a transition from the state Hit to the state Dead.
- Set the condition for this transition to die = true.
The state machine should now look as illustrated in the following screenshot (the new transition and state have been highlighted with a circle).
public var damage:int;
public var DeadState:int = Animator.StringToHash("Base
- In statement 1 of the previous code, we declare a new integer variable called damage, that will be used to keep track of the damage inflicted to the enemy
- In statement 2 of the previous code, we declare a new state for the animator so that we can detect when the enemy is in the Dead state
Also, add the following line within the Start function to initialize the variable damage:
damage = 0;
Next, we need to apply damage to the zombie, every time it is being hit by a bullet. This can be done by adding the following code in the function Update within the switch structure:
if (anim.GetBool("hit")) damage++;
if (damage >=5) anim.SetBool("die",true);
- In line 3 of the previous code, if the enemy has just been hit, then the damage is increased
- In line 4 of the previous code, if this damage is 5 or more, then the Boolean parameter die for the animation is set to true
- In line 5 of the previous code, the parameter hit is set to false so that the damage is not increased continuously while the animation is played
Last but not least, we need to assess the damage caused by the enemy on the player every time it is attacking the player. Add the following code to the start of the script controlZombie:
var attackState:int = Animator.StringToHash("Base Layer.Attack");
In the previous line of code, we declared a new state for the animator, so that we can detect when the enemy is in the state Attack.
Now that we have managed to create the corresponding state, we need to decrease the player's health when it is attacked.
First, let's create a new function in the script healthBar to decrease the player's health:
function decreaseHealth (increment : int)
currHealth -= increment;
Then, let's create a new function in the script controlZombie:
Finally, we can add the following line of code in the script controlZombie in the function Update, within the switch structure:
- In line 1 of the previous code, we check whether the enemy is in the state called Attack.
- In line 2 of the previous code, if this is the case, we decrease the health of the player by 5. This is done by calling the function decreaseHealth that is within the script healthBar. This function, as we have seen previously, takes one parameter that is the amount by which the health should be decreased.
Test the scene and check that the health of the character decreases as enemies are attacking the player. As we test the scene, we will notice that the health of the player drops to 0 after the first attack, whereas it should decrease progressively by 5 after each attack. This is because the health is decreased constantly as the attack animation is being played. As a result, we need to create a Boolean variable that will help us to ensure that the energy is decreased only once per attack.
Add the following line at the start of the script controlZombie:
public var hasAttacked:boolean = false;
Add the following code at the start of the code dedicated to the state walkForwardState as highlighted in the following code snippet:
hasAttacked = false;
Modify the code related to the state Attack as highlighted in the following code snippet:
hasAttacked = true;
Finally, we need to destroy the zombie a few seconds after it has entered the state Dead. This can be done by adding the following code in the function Update within the switch structure:
- In line 1 of the previous code, we check whether the enemy has entered the Dead state
- In line 2 of the previous code, the zombie is destroyed after 3 seconds
Test the scene and check that the health of the player decreases progressively after each attack. Also, shoot at the zombie more than five times, and check that it disappears within three seconds.
- Select the folder Assets | chapter5 in the Project window.
- From the Project window, select Create | Prefab. This will create a new prefab.
- Drag-and-drop the object labeled zombie_hires from the Hierarchy window on this prefab.
- Rename this prefab staticEnemy.
Using waypoints to define a path
We will now create a new type of enemy that will patrol the maze. This character will navigate on a predefined path delimited by waypoints:
- Duplicate the animator labeled zombieController from the Assets | chapter5 by selecting this object and then navigating to Edit | Duplicate, and rename it zombiePatrolController.
- Double-click on this animator so that it opens in the Animator window.
- Rename the state Idle to Patrol.
- Locate the animation WalkForward by selecting Assets | chapter5 | chapter5_pack, within the prefab Zombie@walkForward (its icon is a white triangle within a gray box), or search it using the Search field in the Project window. Drag-and-drop it to the variable Motion for the state Patrol as highlighted in the following screenshot:
At this stage, we have created a new default state for our new type of enemy. This enemy will be moving based on waypoints. We will create four waypoints and determine, using scripting, which waypoint the zombie patroller should walk toward. Open the script controlZombie and add the next lines to the start of the script:
public var patrolState:int = Animator.StringToHash("Base
private var wayPointIndex:int = 1;
- In statement 1 of the previous code, we create a new variable to monitor the state Patrol
- In statement 2 of the previous code, we create an index that will be used to determine the next way point to walk forward
Next, let's add the following lines inside the function Update within the switch structure:
var distanceToWayPoint:float =
if ( distanceToWayPoint< 1.0f) wayPointIndex++;
if (wayPointIndex > 4) wayPointIndex = 1;
In the previous code, we check whether the state Patrol is active. The character looks in the direction of the next waypoint. We then calculate the distance between the zombie and the next waypoint; locate the corresponding waypoint, and if the next waypoint is close enough, a new waypoint is defined.
Last but not least, we need to create these waypoints, place them on the scene, and link them to the script as shown in the following steps:
- Create an empty object and rename it wayPoint1.
- Duplicate this object three times, and rename the copies wayPoint2, wayPoint3, and wayPoint4.
- Change the positions of these waypoints to (x=-22, y=0, z=7), (x=-21, y=0, z=22), (x=-7, y=0, z=22), and (x=-7, y=0, z=7).
- Duplicate the object zombie_hires and rename the duplicate patroller.
- Change the position of this object patroller to (x=-14, y=-0.5, z=7).
- Drag-and-drop the animator zombiePatrolController from Assets | chapter5 to the Animator component of the object patroller as highlighted in the following screenshot:
Test the scene and check that the object patroller walks on the path determined by our waypoints. Note that we could create more waypoints if necessary, and the process would be similar to the one already described.
Finally, we will create a prefab from this patroller object: select the folder Assets | chapter5; then navigate to Create | Prefab from the Project window, drag-and-drop the object labeled patroller on this prefab, and rename it zombie_patroller.
While this article has introduced basic AI principles, we can of course enhance our level by applying more complex behaviors and levels of intelligence. This can be done by using AI algorithms such as A* or other path- finding techniques, or by using dedicated libraries available from the assets store. Note that Unity3D includes a built-in path- finding feature referred as Mesh Navigation; however, this feature is only available and applicable in the Pro-version of Unity3D. Finally, while waypoints were employed to move the character along a path, you may find it useful to employ the library called iTween. This library, available for free in the assets store, is relatively easy to use and includes several interesting features that could definitely improve our game.
In this article, we have learned to apply animations to our character as well as some levels of artificial intelligence. We created different states and associated transitions using the Animator window, and we have also managed to monitor and trigger these states through scripting. Finally, we have used waypoints to define a path for one of the enemies.
Resources for Article:
- Unity Game Development: Welcome to the 3D world [Article]
- Introduction to Game Development Using Unity 3D [Article]
- Unity 3-0 Enter the Third Dimension [Article]