In this chapter, we'll start looking at specific gameplay elements rather than game genres. This project, unlike the previous three, will not be a game with a clear win and lose condition but will be a functional prototype that highlights a range of important coding techniques and ideas prevalent in the game development.
In the complete project, we'll have a Non-Player Character (NPC) that will patrol the level searching for the player using Unity's pathfinding tools, and when it finds the player, it will chase the player down until it is within range and mercilessly attack the player. Its behavior will be controlled using a Finite State Machine (FSM).
In this chapter, we'll cover the following:
To achieve our goals, we'll build on packages that we import from the Unity Asset Store. The UAS is an excellent resource that enables us to hit the ground running. By importing a terrain package, we don't need to construct the environment from scratch. Instead, we can change an existing terrain to fit our needs; and we'll do the same for the NPC by building on an existing package.
Once we've created the terrain, added an animated NPC, and created a waypoint system, we'll add the player to the world. In the previous chapters, we've relied on the first-person character included with the standard assets. However, by creating a customized version in this chapter, we will gain an understanding of what goes into creating a character controller and have a solid foundation that we can extend in future projects.
We'll start by examining the complete project before importing the required assets for the UAS.
In this chapter, you will start a new project that is not a direct continuation of previous projects. However, as with previous chapters, this chapter assumes that you have not only completed the previous projects but also have a good and basic knowledge of C# scripting generally, though not necessarily in Unity.
You can download the example code files for this book from GitHub at https://github.com/PacktPublishing/Unity-2020-By-Example. Once downloaded, you can find the AI project in the Chapter08/End folder.
This chapter uses two assets from the Unity Asset Store, which, along with their author details, can be found at the following links:
In this chapter, we'll start by working on a first-person game in which the player character can explore an environment. The terrain features hills, valleys, and varied terrain elements.
The player will not be alone in the world. An NPC in the form of a baby chicken, that is, a chick, will also move around the environment. The Artificial Intelligence (AI) for the NPC will operate using an FSM with three states: Patrol, Chase, and Attack. The chick will wander around (Patrol mode) searching for the player. If the player is sighted, the NPC will chase and pursue the player (Chase mode). If, during the chase, the enemy loses sight of the player, they will return to patrolling. On the other hand, if the enemy approaches the player during the chase, the enemy will attack the player (Attack mode).
See Figure 8.1 for a sneak peek of the completed project:
Now that we have an idea of what we want to achieve, we can take the first step toward the goal by importing external assets to jump-start our project.
As with all of the previous projects, there will be some assets we don't create ourselves. For this project, we will import packages for the terrain and character models as creating them is not crucial for understanding AI. We'll import those assets into an empty project:
We also need a terrifying enemy that will patrol and chase the player—striking fear into anyone that enters its territory. Once again, we'll use a premade asset for this, although you could create your own by using the 3D modeling software.
It may not look so scary now, but wait until this chick is chasing you down in-game!
We have now imported all of the external packages that we require for now. It's time to start molding them to make them our own. We'll start by creating the terrain that will be our NPC's home.
Now that we've imported all of the assets we require, we can start preparing the scene for our AI agent (also known as the terrifying chick for reasons that will become apparent):
Next, we want to increase the size of the terrain to provide more space for the terrifying chick and the player to walk around.
You should now have a scene that looks similar to Figure 8.8:
By creating empty parent objects, I've also arranged the objects into the following categories in the Hierarchy:
This arrangement will make it easier to define which objects should block the NPC movement (trees, rocks, and stumps) and which objects the terrifying chick should be able to walk through (grass, mushrooms, bushes, and branches).
You may wish to continue to experiment with the terrain tool or look on the UAS for assets to improve the level aesthetically. However, the world terrain is now fit for our purposes, and we can now start implementing features that will help us to achieve the main aim of this project. As a reminder, our goal is to create an enemy character that can wander freely around the terrain and will chase and attack the player whenever the player enters their field of view.
Now that we've created the level, we get one step closer to that goal by configuring the level for pathfinding using a navigation mesh.
The terrain is bumpy and features several hills, mountains, dips, and inclines. For an NPC character to navigate this terrain successfully, there are many complexities involved. For example, an NPC cannot necessarily travel in a straight line from point A to point B because doing so would cause the NPC to pass through solid objects and over terrain that should be impassable. The NPC needs to maneuver intelligently around, under, and over appropriate parts of the terrain. This illusion of intelligence is essential to create believable characters that inhabit a world. The computational processes involved in calculating a suitable path for an NPC is called pathfinding, and the method of making the character travel the calculated route is called navigation. Unity comes with built-in pathfinding and navigation features, making it easy for NPCs to calculate and travel paths.
To prepare our characters for navigation, a Navigation Mesh must be generated. This mesh is a special asset included in the scene, which uses non-rendered geometry to approximate the total walkable surface of a scene. This geometry, rather than the one visible to the player, is then used to calculate possible routes.
Important note
The navigation mesh is usually a much-simplified version of the actual geometry to reduce the complexity of the pathfinding calculations. The trade-off of this approach is that occasionally the navigation and terrain meshes can diverge to the extent that a character can walk a path that doesn't exist or won't take a route that they should. For this reason, it is vital that if the terrain is changed, the navigation mesh is regenerated.
For this process to work effectively, all non-movable floor meshes in the scene should be marked as Navigation Static. To do this, follow these steps:
By ticking the Static checkbox, you're enabling several options. If you click the arrow next to the Static label, it will list the options and allow you to enable and disable them individually. For our needs, we only need the Navigation Static option selected. However, as the object will not be moving at all in the scene, enabling all of the options provides several benefits, including a possible reduced rendering cost and the ability to precalculate lighting data for improved runtime performance. For these reasons, it is a good practice to mark all of the non-moving objects as static.
Tip
For more information on the individual static options, see the online documentation at https://docs.unity3d.com/Manual/StaticObjects.html.
Now that we've marked our terrain as static, we can now generate a navigation mesh by doing the following:
The Bake tab contains a number of settings related to generating the navigation mesh:
Important note
The Generated Off Mesh Links settings can safely be ignored as they are used to define how an agent can move between two disconnected meshes, and our terrain meshes are connected.
To get started, let's generate an initial Navigation Mesh to see how the default settings look. We can easily erase and regenerate the mesh under new settings if needed.
The blue area is the navigable terrain. You'll probably notice straight away that we have one problem: the navigation mesh doesn't take into account any of our other objects such as the rocks and trees. To fix this, we also need to mark them as Static:
The NavMesh asset itself is stored in a folder matching the scene name. When selected from the Project panel, you can preview various read-only properties describing the Navigation Mesh, such as the Height and Walkable Radius settings:
Next, we'll adjust the agent specific settings as our agent is relatively short and has a small radius. The Agent Radius setting is particularly important as it controls how large an average agent (NPC) is, and it affects how close the Navigation Mesh can expand toward the surrounding mesh floor and its edges. Lower settings allow the mesh to encroach nearer to the mesh edges, resulting in an expanded navigation mesh:
Tip
There are many tools available that extend or improve the NavMesh system. A popular one is NavMeshComponents. It's simple to use and is released and maintained by Unity. With NavMeshComponents, you can have multiple agent profiles, create dynamic navigation meshes that can be updated during play, and generate meshes during runtime for procedurally generated levels. For more information, see the GitHub page at https://github.com/Unity-Technologies/NavMeshComponents.
Congratulations! You have now constructed a Navigation Mesh for the level that our NPC will eventually use to traverse the environment either following waypoints or chasing the player down. Before the NPC can do this, we first need to add it to the scene, which we will do now.
With the foundation complete, we can now add the character that will eventually patrol the area and chase the player. As previously mentioned, we have picked an asset that will strike terror into the heart of the player as they are stalked mercilessly by the Toon Chick:
We'll refine this prefab by adding our own pathfinding logic.
By default, the Navigation Mesh assigns a Cylinder Collision volume to the Agent—the object that will navigate and move around. This volume is not a collider that acts with the physics system, but a pseudo-collider used to determine when the character nears the edge of a navigation mesh and how they collide with other agents in the scene. We don't have any other agents yet, but we could always add more in the future. Let's configure that collider now, on the Nav Mesh Agent component:
We should also update the settings related to how quickly the chick moves through the environment:
These settings (shown in Figure 8.18) better approximate the chick's small stature:
Great! We have everything that we need for the NPC to move through the environment apart from a destination. Let's fix that now.
To test that our navigation mesh and settings are working correctly, we'll create an object for the chick to seek in the environment, using pathfinding and steering to reach the goal. To implement this behavior, we'll also need to write a new script:
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class FollowDestination : MonoBehaviour
{
public Transform Destination = null;
private NavMeshAgent ThisAgent = null;
void Awake()
{
ThisAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
ThisAgent.SetDestination(Destination.position);
}
}
The following points summarize the code sample:
For the script to work, a NavMeshAgent component is required, without which it would throw a runtime error. The RequireComponent attribute is useful to prevent these types of errors from happening. Because we've added the RequireComponent attribute, if the object does not already have a NavMeshAgent component, Unity will add one for us.
Important note
The RequireComponent attribute does not work retroactively. For example, if this script (minus the attribute) was already attached to an object without a NavMesh component, then we added the RequireComponent attribute, Unity would not attempt to add the missing component to the existing object.
The Destination variable maintains the destination object.
In every frame SetDestination is called on the NavMeshAgent component. This function will update the agent's destination and trigger the calculation of a new path, which can take a few frames to complete. While we're not doing it here for the sake of simplicity, we should first check whether the destination has moved before calling the SetDestination function to reduce unnecessary path calculations.
During gameplay, move the destination object around via the Scene tab and see how the chick responds. The NPC should continually chase the Destination object. In addition, if you play the game with the Navigation window open and the NPC selected in the Hierarchy panel, the Scene view will display diagnostic information and gizmos, allowing you to preview and visualize the route calculated by the chick:
You may have noticed that the environment in which the chick moves is not animated. No matter what speed the chick travels, its feet remain stationary. We'll rectify this now.
At the moment, the chick floats around the scene without a walking or running animation. We'll change that now by animating the chick as it walks around the scene. The chick will transition between a walking and running animation depending on its movement speed. Luckily for us, the package we imported comes with several premade animations, so we don't need to start from scratch. Instead, we'll focus on how to apply these animations to the chick, in two different ways. First, we will create separate states to represent each animation, switching between a walking and running animation based on the chick's movement speed. Then, we will look at a method of seamlessly transitioning between these two animations using a Blend Tree.
Unity's animation system (also known as Mecanim) has two main panels to help us to develop animations, that is, the Animator and Animation panel. In this section, we'll look at the Animator panel, which controls how and when animations play.
Let's start by examining the animations currently associated with the chick object:
The Animator window has two main sections: The State Layout area and the Parameters or Layers view. Mecanim uses an FSM to manage its animation states, and this screen is a visual representation of that.
Important note
We'll go into more detail on FSMs in the next chapter when we write a custom FSM for the chick's behavioral states, that is, the Patrol, Chase, and Attack states. For now, it's enough to know that a state machine provides several distinct states and a method of transitioning between them based on specific conditions. It's a versatile pattern used for many different applications.
The rectangles in the state layout area represent a distinct animation state for a particular object (the object doesn't necessarily have to be a character, as we'll see later in this chapter). They are building blocks we can use to create complex animations. The white arrows between the states are user-defined transitions. One state can only transition to another if we define a conditional transition; there will be more on this very shortly when we create our own transitions.
The parameters in the Parameters list can be of the float, int, bool, or trigger type and are used as conditions for the transitions between animations. Based on whether a parameter is set to true or false (bool) or is greater or less than a specified amount (int and float), a transition can occur. As mentioned, you can also transition to an animation based on a trigger. You can think of a trigger as being similar to a Boolean, except that when we set it to true (that is, triggered), it is automatically reverted to false and stays in that state until triggered again. We'll discuss these parameters in more detail later in this chapter when we write a script that will set a parameter based on the chick's movement speed.
You can't delete the states provided by Unity, that is, Any State, Entry, and Exit. They provide unique functionality:
Now that we have the minimum number of states and parameters that we require, we can start modifying the functionality to meet our needs. Depending on the order in which the states were deleted, Unity may have changed the default state. The default state is the first state entered and is highlighted in orange. As shown in Figure 8.23, the default state is Jump W Root. As we don't want our chick to jump as soon as the game starts, let's change the initial state to something more suitable:
The chick will start the game moving toward the Destination object, so having the walking animation also playing at the start of the game suits our needs perfectly:
You will most likely have noticed that we were setting this state as the default state for this layer. Unity allows you to create different state machines for different layers. This functionality could be used, for example, if we had a complex character with different animations for different body parts. Layers are a big topic in themselves and one that is not required for this or any other project in this book. For more information on layers, see the Further reading section, where links to additional resources on this topic are included.
Tip
You can rearrange the states by clicking and dragging. This functionality comes in very handy as you add more states.
If you test the game now, you will see that the walking animation does indeed play as the chick moves around the environment, but you'll also notice that the run animation never plays. Depending on the speed of the chick, the running animation will be more appropriate, so we need to create a transition from the walking to the running animation:
We've now created a two-way transition. By default, a transition will happen whenever the animation has played a specified amount. Therefore, as our two-way transition now stands, the chick would walk a bit before running and then go back to walking with the cycle repeating indefinitely. We'll want to control this transition through code, but before we can do that, we will need to configure the conditions:
We can then use the Inspector panel to configure the settings for this transition. The window (shown in Figure 8.28) has several settings related to transitions, which include the following:
Has Exit Time: If checked, the transition can only happen after a specified time. If not selected, a transition condition must be specified.
Exit Time: This value specifies the normalized time at which the transition should occur. If there are conditions specified, they must also be met before the transition can happen.
Fixed Duration: If checked, the Transition Duration value is interpreted in seconds. If not checked, the Transition Duration value represents a fraction of the normalized time of the animation, for example, 0.25 equals 25 percent.
Transition Duration: This is the length of the transition in either seconds or as a normalized percentage.
Transition Offset: This is the normalized start time in the target state. For example, if you set a value of 0.5 when the transition occurs, the new animation will start playing halfway through.
Conditions: You can set conditions based on parameters specified in the Parameters list as shown in Figure 8.24. We'll see how to do this shortly.
The list of possible conditions here matches the parameter list. As we only have the Run parameter, this will be the only option. Configuring the condition as true here signifies that we need to set this parameter to true in our code for this transition to take effect. We'll see how to do that shortly.
Tip
We could have used a float parameter to store the chick's velocity. We could have then transitioned from walking to a running animation if this velocity parameter was greater than a specified amount (and transitioned back to walking when it becomes less than that amount).
We need to repeat the process but for the transition from Run In Place to Walk In Place:
We now have two animations and two transitions. The walk animation will play first, and when the parameter Run is set to true, then the run animation will play. Once in the run state, if the Run parameter is then set to false, the run state will transition back to the walking state. Setting this parameter is the last thing we need to do to set up our animation system. We'll do this now using a script:
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent), typeof(Animator))]
public class AnimationController : MonoBehaviour
{
public float RunVelocity = 0.1f;
public string AnimationRunParamName = "Run";
private NavMeshAgent ThisNavMeshAgent = null;
private Animator ThisAnimator = null;
void Awake()
{
ThisNavMeshAgent = GetComponent<NavMeshAgent>();
ThisAnimator = GetComponent<Animator>();
}
void Update()
{
ThisAnimator.SetBool(AnimationRunParamName, ThisNavMeshAgent.velocity.magnitude > RunVelocity);
}
}
The following points summarize the code sample:
The script requires access to the NavMeshAgent and Animator components, so the RequireComponent attribute is added before the class declaration. As you can see, you do not need separate attribute calls for each component.
The RunVelocity variable controls the running speed cut off. If the velocity of the NPC is above this value, then the Run animation parameter is set to true.
The object's velocity is checked in the Update function by retrieving the magnitude of the NavMeshAgents velocity and comparing that to RunVelocity. For more information on the magnitude of a vector, see the online documentation at https://docs.unity3d.com/ScriptReference/Vector3-magnitude.html.
Calling SetBool on the animation component and passing in AnimationRunParamName sets the state of the Run parameter. If the agent's velocity is greater than the predefined RunVelocity variable, then the Run parameter is set to true, and the run animation is played. Otherwise, it is set to false, and the walk animation is played.
Great! Now the chick will walk rather than float around the environment. To properly test that our animation changes are working as intended and to extend the chick's behavioral repertoire, we will soon add the ability to patrol the environment, leaving no stone unturned in its search for the player. However, before we move away from discussion animations, we will briefly go over how to use Blend Trees, a useful tool for creating natural-looking animation transitions.
In the next chapter, we'll be setting the animation state based on which AI state we are in, for example, if we're in a chase state, we'll tell the animator to play the run animation. Having distinct animation states, such as the ones we created in the previous section, works perfectly for us. However, there is also another way of transitioning between animations that is worth covering. You have probably guessed from the title of this section, but I'm talking about Blend Trees. Using a Blend Tree, we can blend two (or more) animations to create a more natural transition. For the chick, we will create a Blend Tree that combines its walk and run animations according to its current speed. As the chick gains momentum, they will move from playing the walking animation to playing a mix of the walking and running animations, until finally just playing the running animation.
Important note
If you are eager to move on, then feel free to skip this section as the rest of this book will rely on the states we created in the previous section. The steps here are to help you to understand the subject, but for this project, they do not need to be completed.
To create a Blend Tree, follow these steps:
To edit the newly created Blend Tree, follow these steps:
Tip
The breadcrumb at the upper-left corner shows that we're working on the Blend Tree. You can click on Base Layer to exit the Blend Tree.
Each motion field has three properties:
The threshold value represents the value of the Speed parameter required for that particular animation to have full influence.
Time Scale is the speed at which the animation plays.
Mirrored defines whether the animation is mirrored left to right.
After adding the motion fields, you'll notice that a diagram appears above the list, as shown in Figure 8.33. This diagram is a visual representation of where the different animations lie on the scale of the Speed parameter— lowest on the left, highest on the right. You can drag the red scrubber, shown in Figure 8.34, to preview the animation for various values. The preview is shown at the bottom of the Inspector panel:
If you untick Automate Thresholds in the Inspector, it will give you an additional option: Compute Thresholds. Compute Thresholds will calculate and set values for each of your motion fields. It will do this based on a property of root motion that you select; the options include the Speed parameter that we created earlier and other pre-built options including Velocity (on all three axes) and Angular speed (in radians and degrees).
Tip
When adding the motions, they were also added to the Blend Tree panel. You can click on the animations in that panel to see read-only information about the animation, including length and whether the animation is looped.
Underneath the Automate Thresholds option, there is the option to Adjust Time Scale. Using this option, you can make the speed for each animation Homogeneous, which means that each of the animations will result in the same speed of root motion. This option is only available if all of the motions are animations, not other Blend Trees.
Tip
2D Blend Trees work in a very similar way to 1D trees but blend on two dimensions.
Next, we need to set the newly created Blend Tree as the default state:
The last step is to set the Speed parameter based on the chick's movement speed:
public class AnimationController : MonoBehaviour
{
...
public string AnimationSpeedParamName = "Speed";
private float MaxSpeed;
void Awake()
{
...
MaxSpeed = ThisNavMeshAgent.speed;
}
void Update()
{
...
ThisAnimator.SetFloat(AnimationSpeedParamName, ThisNavMeshAgent.velocity.magnitude / MaxSpeed);
}
}
This code is very similar to what we wrote in the previous section to update the Run animation parameter, with the small change that we're no longer updating a Boolean but a float value instead. We divide the agent's velocity magnitude by MaxSpeed (obtained from the NavMeshAgent component) to get a normalized value between 0 and 1.
As this script is already attached to the Toon Chick object, there are no further steps required. Press the Play button to see the new blended animation in action.
That's it for the whistle-stop tour through Blend Trees. Remember that the rest of this chapter will use the animation states we created in the previous section. If you would like to do the same, you can reset the animations by doing the following:
We've now looked at two methods of adding animations to the chick. In the next section, we'll take a look at how animations can be used for a different purpose, namely, creating a waypoint system for the chick to follow because there's no point in having created walking and running animations if the chick doesn't move anywhere!
We now have an animated NPC that follows a destination object using Unity's built-in pathfinding and steering behavior. This functionality is a solid foundation for creating the next component in our terrifying chicks' behavioral repertoire: the ability to patrol a specified area.
If you break down a patrol, it's just moving from one destination to another in a sequence. We have most of that functionality already. We only need to implement a sequence of destinations. Multiple approaches could be taken to achieve this. One method is through scripting. We could write a script that maintains an array of different waypoint objects and allows the chick to iterate through them on a loop so that when the NPC reaches one destination, they'll move on to the next one. This approach can be very efficient and is easily customizable, but there's also another method we could use. Instead of using a script, we can create an animation to move a single destination object to different waypoint locations over time. As the NPC continually follows the destination wherever it moves, it will continuously patrol.
We'll take the second approach here for several reasons, one of the most crucial being that while we've recently examined Unity's Animator flow, we haven't yet created custom animations using Unity's Animation system:
Important note
A keyframe marks a transitional point. As you will see shortly, the intermediate points between keyframes are interpolated between these points to create a smooth animation.
Adding a property will automatically create starting and ending keyframes in the timeline, which are identical and hold the object's current position:
This completes the patrol animation with three patrol points. We leave the start and end of the animation with the same position to create a loop.
You can scrub through the timeline or press Play in the Animation panel to watch the Destination object move in the Scene view to test your patrol route. The animation will most likely playback too fast as we've only set 20 seconds between each waypoint. Don't worry, that will be easy to fix as we'll see shortly. However, you may have also noticed that the destination object position has been tweened between keyframes. The Unity animation interpolates between the keyframes in the timeline causing the destination object to move smoothly between waypoints. For an animation like this, however, we want the destination to snap between the waypoints immediately without any transition. To achieve this, we need to adjust the interpolation mode of the animation curves:
Important note
A point has a left and right tangent. Editing the left tangent changes the incoming curve to the left of the position, and changing the right tangent changes the outgoing curve to the right of the point.
The flat shape shown in Figure 8.43 signifies that all frames retain their values until the next keyframe, removing inter-frame interpolation:
Test whether the change has taken effect by pressing Play on the toolbar. When you do this, the Destination object should jump between waypoints as the animation progresses, and the NPC will try to move toward the destination. Due to the default speed of the animation, the NPC may seem a bit confused as they can never quite reach the rapidly changing destinations. To fix this, follow these steps:
In the Animator window, the default node is highlighted in orange. As a reminder, the default node defines which animation (if any) will play when the object is added to the scene, which for our Destination object is on level startup. As you can see from Figure 8.44, the default node for our object is the DestPatrol node, which plays the DestPatrol animation.
The chick should now move between destinations at a believable speed, moving from one waypoint to the next. If the NPC moves too fast or too slow between waypoints, increase or decrease the animation speed to get the result you need. You may also need to change NPC's steering speed on its NavMeshAgent component:
Congratulations! You now have a complete, animated waypoint system. The chick will happily patrol between points. At the moment, it can be quite challenging to get a close up of the chick in action as you need to catch them in the Scene view. We'll change this now by adding a controllable player to the scene.
In previous projects, we've relied on a first-person controller included with the Unity standard assets package. In this section, instead of relying on that package, we'll write our own functionality to accomplish the same task. We'll write a script to control the player's movement through the environment and another to control where the player looks. However, before we write the scripts, we'll create the required player object:
That's all the setup required for the GameObject. Next, we will script the movement:
public class PlayerController : MonoBehaviour
{
public float MovementSpeed = 3f;
void Update()
{
float horiz = Input.GetAxis("Horizontal");
float vert = Input.GetAxis("Vertical");
Vector3 translation = new Vector3(horiz, 0, vert);
translation *= MovementSpeed * Time.deltaTime;
transform.Translate(translation);
}
}
The following points summarize the code:
You'll quickly notice that while we can move around, we still can't rotate the camera to look around the environment. We'll fix this now by creating another script:
public class CameraLook : MonoBehaviour
{
public float LookSpeed = 3f;
public Transform Body;
public Camera LookCamera;
private Vector2 Rotation = Vector2.zero;
void Update()
{
Rotation.y += Input.GetAxis("Mouse X");
Rotation.x -= Input.GetAxis("Mouse Y");
Vector2 RotThisStep = Rotation * LookSpeed;
Body.eulerAngles = new Vector2(0, RotThisStep.y);
LookCamera.transform.localRotation = Quaternion.Euler(RotThisStep.x, 0, 0);
}
}
The following points summarize the code sample:
Rotation values are retrieved by calling Input.GetAxis in the Update function.
The Mouse Y axis is subtracted from the current X rotation as the X axis controls horizontal rotation.
The Mouse X axis is added to the Y rotation, which controls vertical rotation.
The Body transform is rotated left and right, and the LookCamera is rotated up and down based on mouse movement.
The Body transform is rotated left and right instead of the camera to replicate traditional first-person controls so that the camera is always facing the forward direction. If we rotated the camera instead of the body, for example, we would end up not facing in the forward direction, so pressing forward could move you backward.
Important note
A new script was created instead of extending the functionality of the PlayerController script to help create modular, re-usable, logical units. Separating the functionality in this manner will enable us to re-use either script without having to use the other. For example, we may have a stationary turret that we want the player to control, so we add the MouseLook script without the PlayerController script.
Once again, test the game and you will now be able to move freely through the environment. See if you can find the chick, as shown in Figure 8.50:
While this is only the basic moving functionality, it is adequate for our needs and can be extended for future projects.
Great work! We've now completed the first part of the AI project: constructing a terrain, generating a navigation mesh, and creating a basic waypoint system that an animated chick follows. We hit the ground running by importing 3D models for the terrain and NPC but also extended ourselves by writing a custom first-person controller instead of relying on the controller from the previous chapters.
This is an excellent beginning to simulating intelligence. However, as the project currently stands, there is no way for the chick to decide which action to take at any given moment; it is stuck patrolling in perpetuity. We'll fix this in the next chapter by constructing an FSM with distinct Patrol, Chase, and Attack states. We'll also look at how we can test to see whether the player is in the chick's line of sight using Raycasting. This check will act as a condition to transition between specific states, just as we used the Run parameter as a condition to switch between animation states in this chapter.
Q1. You can generate a walkable surface of the level for AI by using...
A. Pathfinding
B. A Collision Box
C. Navigation Mesh
D. A Path Tree
Q2. The Animator window of Mechanim is useful for...
A. Controlling when and how animations play
B. Creating loopable animations
C. Editing characters
D. Applying inverse kinematics
Q3. To walk on a Navigation Mesh, an object needs...
A. Pathfinding
B. A Collision Box
C. A NavMesh agent component
D. A Collider component
Q4. You can edit animation interpolation by changing...
A. High poly meshes
B. Keyframe curves
C. Box Colliders
D. Mesh renderer components
Q5. The blue local axis arrow of an object is known as...
A. Forward vector
B. Right vector
C. Up vector
D. Pitch
Check out the following links for more information on animations and pathfinding: