< Return to Video

Stealth game tutorial - 405 - Enemy Animation - Unity Official Tutorials

  • 0:00 - 0:02
    In this assignment we will write code
  • 0:02 - 0:05
    to control the movement of the enemy.
  • 0:05 - 0:07
    The enemy will be driven by the animator,
  • 0:07 - 0:09
    taking movement from the root motion of
  • 0:09 - 0:13
    the animation clips that we provided previously.
  • 0:14 - 0:16
    Animator parameters control which animation
  • 0:16 - 0:18
    states the animator should play
  • 0:18 - 0:20
    and therefore how the enemies move.
  • 0:21 - 0:23
    We will set these parameters using velocities
  • 0:23 - 0:25
    from the nav mesh agent
  • 0:25 - 0:27
    and we will modify the values with code
  • 0:27 - 0:29
    to correct certain behaviours.
  • 0:30 - 0:32
    When using the nav mesh the system gives
  • 0:32 - 0:35
    the agent a desired velocity.
  • 0:35 - 0:37
    This is not the actually velocity that the agent
  • 0:37 - 0:40
    is moving, but one that it should head towards.
  • 0:41 - 0:43
    Nav mesh agents such as our enemy
  • 0:43 - 0:45
    have a waypoint path to a destination,
  • 0:45 - 0:47
    shown here by a red line.
  • 0:48 - 0:50
    The agent turns to face the waypoint
  • 0:50 - 0:53
    whilst travelling towards it, and we say that
  • 0:53 - 0:56
    the velocity of the agent, shown by the black arrow
  • 0:56 - 0:58
    is heading towards the desired velocity,
  • 0:58 - 1:00
    shown with the green arrow.
  • 1:00 - 1:02
    This desired velocity will be the basis
  • 1:02 - 1:04
    of the values that we'll send to the enemy's
  • 1:04 - 1:08
    animator parameters Speed and Angular Speed.
  • 1:09 - 1:11
    Let's begin by creating the script to
  • 1:11 - 1:13
    control these parameters.
  • 1:13 - 1:15
    Add a new script to the Robot Guard called
  • 1:15 - 1:17
    EnemyAnimation.
  • 1:17 - 1:19
    Select char_robotGuard and choose
  • 1:19 - 1:22
    Add Component - New Script
  • 1:22 - 1:24
    and name it EnemyAnimation.
  • 1:24 - 1:28
    Launch the script for editing and prepare it.
  • 1:29 - 1:31
    First let's take a look at the class variables
  • 1:31 - 1:34
    that we will need. There is only one public variable
  • 1:34 - 1:36
    that we will need, which is a float
  • 1:36 - 1:37
    we will call deadZone.
  • 1:37 - 1:40
    The deadzone, similar to a deadzone in input terms,
  • 1:40 - 1:43
    is a value range which should be ignored
  • 1:43 - 1:45
    in the case of enemy navigation
  • 1:45 - 1:47
    we do not want the enemies to attempt to turn
  • 1:47 - 1:50
    when the difference between their forward direction
  • 1:50 - 1:52
    and their desired velocity is very small.
  • 1:52 - 1:54
    And we define this with the deadzone.
  • 1:55 - 1:57
    Without this we would see the enemies
  • 1:57 - 1:59
    oversteering and walking in wavering
  • 1:59 - 2:01
    lines towards each waypoint.
  • 2:01 - 2:03
    As shown in this example.
  • 2:14 - 2:16
    We will need 6 private variables.
  • 2:16 - 2:17
    The first of which is a reference
  • 2:17 - 2:19
    to the player's transform.
  • 2:19 - 2:21
    We will need this to set the direction that the enemy
  • 2:21 - 2:24
    faces to the player when they are in sight.
  • 2:26 - 2:28
    Next we will need a reference to the
  • 2:28 - 2:30
    EnemySight script so that we can change the
  • 2:30 - 2:32
    animation based on whether the player is in
  • 2:32 - 2:34
    sight or not.
  • 2:35 - 2:37
    Then we will need a reference to the nav mesh agent
  • 2:37 - 2:41
    component which will be guiding the enemies movement.
  • 2:41 - 2:43
    Next we will need a reference to the animator
  • 2:43 - 2:45
    component and to help reference it's
  • 2:45 - 2:47
    parameters we will need a reference to
  • 2:47 - 2:49
    the HashIDs script.
  • 2:51 - 2:53
    The last will be an instance of the helper class
  • 2:53 - 2:56
    that we made during the last assignment,
  • 2:56 - 2:58
    AnimatorSetup.
  • 2:59 - 3:01
    Next we will use the Awake function to
  • 3:01 - 3:03
    setup these references.
  • 3:25 - 3:27
    In addition to allocating the references
  • 3:27 - 3:29
    we will also need the awake function to perform
  • 3:29 - 3:31
    a number of other actions.
  • 3:31 - 3:33
    Firstly we must make sure that the rotation
  • 3:33 - 3:35
    of the enemy is set by the animator
  • 3:35 - 3:37
    and not by the nav mesh agent.
  • 3:37 - 3:40
    This will reduce the appearance of foot slipping
  • 3:40 - 3:42
    whilst turning corners.
  • 3:42 - 3:44
    We will also need to create an instance
  • 3:44 - 3:46
    of our helper function from the previous
  • 3:46 - 3:48
    assignment, and in doing so
  • 3:48 - 3:50
    call it's constructor.
  • 3:50 - 3:52
    We can parse in the animator component
  • 3:52 - 3:54
    and the HashIDs script that we already
  • 3:54 - 3:56
    have a reference to.
  • 3:56 - 3:58
    We need to set the layer weight of the different
  • 3:58 - 4:00
    layers of the animator.
  • 4:00 - 4:03
    The Weight value controls the balance of animation
  • 4:03 - 4:05
    from the layers on the animator.
  • 4:05 - 4:07
    We are going to make both the Shooting
  • 4:07 - 4:10
    and Gun layers have a weight of 1,
  • 4:10 - 4:12
    meaning that they will totally override the
  • 4:12 - 4:14
    layers beneath them, such as the base layer.
  • 4:14 - 4:16
    This means that the animation would be purely
  • 4:16 - 4:18
    based on those layers.
  • 4:18 - 4:20
    However we are using a mask for both the
  • 4:20 - 4:22
    Shooting and Gun layers so that the only
  • 4:22 - 4:24
    parts of the body that they can override
  • 4:24 - 4:27
    are the upper body and right hand respectively.
  • 4:27 - 4:30
    So we use the Set Layer Weight function
  • 4:30 - 4:32
    and use the integer of the layer.
  • 4:32 - 4:34
    For example the base layer should be referenced
  • 4:34 - 4:37
    with a 0, so because we're referencing Shooting and Gun
  • 4:37 - 4:40
    we will use 1 to represent Shooting
  • 4:40 - 4:42
    and 2 to represent Gun
  • 4:42 - 4:44
    and we set both of them to 1f.
  • 4:45 - 4:48
    The last thing that we need to do in the Awake function
  • 4:48 - 4:50
    is to convert the deadZone variable
  • 4:50 - 4:52
    from degrees to radians.
  • 4:52 - 4:54
    This is because the animator controller we made
  • 4:54 - 4:57
    earlier measures the parameter in radians.
  • 4:57 - 4:59
    Luckily the Mathf class has a constant
  • 4:59 - 5:02
    that we can use for this, deg2rad.
  • 5:02 - 5:04
    A constant is like a variable,
  • 5:04 - 5:07
    however once it has been set it cannot be changed.
  • 5:07 - 5:09
    In order to convert a number from degrees
  • 5:09 - 5:13
    to radians we simply multiply by this constant.
  • 5:13 - 5:15
    The majority of this script will be setting
  • 5:15 - 5:18
    up the animations based on the nav mesh agent.
  • 5:18 - 5:20
    To do this we will create a function that we
  • 5:20 - 5:22
    can call in the Update function.
  • 5:22 - 5:24
    We will call it NavAnimSetup.
  • 5:24 - 5:27
    The first thing we are going to do in this function
  • 5:27 - 5:29
    is to create two floats for the Speed
  • 5:29 - 5:31
    and Angle parameters that we will parse to
  • 5:31 - 5:33
    the helper class's Setup function.
  • 5:36 - 5:39
    Now we need to decide if the player is in sight.
  • 5:41 - 5:43
    If the player is in sight then we
  • 5:43 - 5:45
    want the enemy to stop, so we will set
  • 5:45 - 5:47
    the Speed parameter to 0.
  • 5:49 - 5:51
    Now we need to find the angle between the direction
  • 5:51 - 5:53
    the enemy should face and the direction
  • 5:53 - 5:55
    it is actually facing.
  • 5:55 - 5:58
    Negative to the left and positive to the right.
  • 5:59 - 6:01
    We will need to make another function in order
  • 6:01 - 6:03
    to do this for us.
  • 6:03 - 6:05
    It needs to return a float
  • 6:05 - 6:08
    and will need 3 vector3s as parameters.
  • 6:08 - 6:10
    A vector we are measuring from,
  • 6:10 - 6:12
    a vector we are measuring to,
  • 6:12 - 6:14
    and another vector to determine which
  • 6:14 - 6:17
    way is up. We will call this function
  • 6:17 - 6:19
    FindAngle.
  • 6:21 - 6:23
    The toVector we are going to parse in to
  • 6:23 - 6:27
    this is the nav mesh agent's desired velocity.
  • 6:27 - 6:29
    This will sometimes equal 0.
  • 6:29 - 6:31
    If this is the case then it might cause an error
  • 6:31 - 6:33
    so we will need to put in a way to check for this.
  • 6:33 - 6:35
    If the desired velocity is 0
  • 6:35 - 6:37
    then we want the direction to be 0.
  • 6:37 - 6:39
    If this is the case we can simply return
  • 6:39 - 6:42
    0 from the function, and if this isn't
  • 6:42 - 6:44
    the case then we will continue with
  • 6:44 - 6:46
    the rest of the code.
  • 6:46 - 6:48
    The next step is to find out the absolute
  • 6:48 - 6:50
    value of the angle.
  • 6:50 - 6:51
    We can do this very simply with the
  • 6:51 - 6:54
    vector3.angle function.
  • 6:55 - 6:57
    Now we need to determine whether this angle is
  • 6:57 - 6:59
    to the left or to the right of the
  • 6:59 - 7:02
    forward direction. To do this we can find the
  • 7:02 - 7:04
    cross product of the two vectors
  • 7:04 - 7:07
    and check the resultant normal vector.
  • 7:07 - 7:10
    Using the lefthand rule for the cross product
  • 7:10 - 7:12
    we know that if the toVector is to the right
  • 7:12 - 7:14
    of the fromVector their normal
  • 7:14 - 7:15
    will point upwards.
  • 7:15 - 7:18
    See the lesson on Vector Maths linked below
  • 7:18 - 7:20
    if you need reminding of this.
  • 7:33 - 7:35
    Now we can find the .product of this
  • 7:35 - 7:38
    Normal and the Up vector that we parsed in.
  • 7:38 - 7:40
    If the Normal and the Up vector are pointing
  • 7:40 - 7:42
    in the same direction the result will
  • 7:42 - 7:44
    be greater than 0, therefore we can multiply
  • 7:44 - 7:47
    the angle that we've already calculated
  • 7:47 - 7:50
    by the sign of the .product that we have found.
  • 7:50 - 7:52
    This will show whether the desired velocity
  • 7:52 - 7:55
    is to the left or the right of the Forward vector.
  • 7:56 - 7:59
    Remember that we need this angle to be in radians.
  • 7:59 - 8:01
    So we need to multiply it by the
  • 8:01 - 8:04
    Deg2Rad constant that we used earlier.
  • 8:04 - 8:07
    And finally we can return the angle.
  • 8:08 - 8:10
    Now that we have a function to find the angle
  • 8:10 - 8:12
    we can use it in our NavAnim
  • 8:12 - 8:14
    setup function.
  • 8:14 - 8:17
    The fromVector is the enemy's forward vector,
  • 8:17 - 8:19
    the toVector is the vector from the
  • 8:19 - 8:21
    enemy to the player and we will use the
  • 8:21 - 8:25
    enemy's upVector as the up vector.
  • 8:26 - 8:28
    Now we need to account for when the
  • 8:28 - 8:30
    player is not in sight.
  • 8:31 - 8:33
    Given that the player is not in sight
  • 8:33 - 8:35
    we want the speed to be based on the
  • 8:35 - 8:38
    nav mesh agent's desired velocity.
  • 8:38 - 8:40
    To achieve this we can use projection
  • 8:40 - 8:42
    in order to project the desired velocity
  • 8:42 - 8:46
    vector on to the enemy's forward vector.
  • 8:46 - 8:49
    Projection allows us to take 2 vectors
  • 8:49 - 8:51
    and find out how much of the first vector
  • 8:51 - 8:54
    is in the direction of the other vector.
  • 8:54 - 8:56
    A projection is found by drawing a line
  • 8:56 - 8:58
    from the tip of the first vector
  • 8:58 - 9:01
    towards the second vector.
  • 9:01 - 9:03
    It must be drawn so that this line is
  • 9:03 - 9:05
    perpendicular to the direction
  • 9:05 - 9:06
    of the second vector.
  • 9:07 - 9:09
    The point of the intersection of this line
  • 9:09 - 9:12
    defines the end of the projection vector.
  • 9:12 - 9:14
    Since we are projecting the nav mesh agent's
  • 9:14 - 9:18
    desired velocity on to the enemy's forward vector
  • 9:18 - 9:20
    the resultant vector will be small
  • 9:20 - 9:23
    if the enemy is not facing the same direction
  • 9:23 - 9:25
    as the desired velocity.
  • 9:25 - 9:27
    The speed variable will be set to the
  • 9:27 - 9:30
    magnitude of the projection vector.
  • 9:30 - 9:32
    Without using this projection the
  • 9:32 - 9:34
    enemy might have a high speed whilst
  • 9:34 - 9:36
    facing the wrong direction, and thus running a
  • 9:36 - 9:40
    wide arch in order to face the correct direction.
  • 9:41 - 9:43
    Next we need to determine the angle.
  • 9:43 - 9:46
    Again we will use our findAngle function
  • 9:46 - 9:48
    but this time the toVector is the
  • 9:48 - 9:51
    nav mesh agent's desired velocity.
  • 9:53 - 9:55
    Since we are using damping to set the animator
  • 9:55 - 9:57
    parameters when they are turning
  • 9:57 - 9:59
    they won't stop turning as soon as they face
  • 9:59 - 10:01
    their correct direction. Instead they will
  • 10:01 - 10:04
    continue to turn very slightly.
  • 10:04 - 10:07
    They will then need to turn back to compensate.
  • 10:07 - 10:09
    This will result in a snaking motion
  • 10:09 - 10:11
    as they finish turning corners.
  • 10:11 - 10:13
    In order to prevent this we need to put
  • 10:13 - 10:15
    a check in to our function.
  • 10:15 - 10:17
    This check will simply be whether the angle
  • 10:17 - 10:21
    is small, i.e. less than our deadZone variable.
  • 10:22 - 10:25
    If it is then we avoid using the animator controller
  • 10:25 - 10:27
    to set the direction that the enemy is facing
  • 10:27 - 10:30
    To do this we set the angle to 0
  • 10:30 - 10:32
    and set the enemy's transform to look at
  • 10:32 - 10:35
    the desired velocity from it's own position.
  • 10:39 - 10:41
    Now that we have calculated the speed and
  • 10:41 - 10:43
    angle we can parse them in to
  • 10:43 - 10:45
    the Setup function of our helper class
  • 10:45 - 10:47
    animator setup.
  • 10:47 - 10:49
    They can then have damping applied and be
  • 10:49 - 10:52
    parsed in to the animator controller.
  • 10:53 - 10:56
    Now that we have our completed NavAnimSetup function
  • 10:56 - 10:59
    we can call it in the Update function.
  • 11:02 - 11:04
    Note that whilst we have inserted this function
  • 11:04 - 11:06
    above ones that we have already written
  • 11:06 - 11:08
    this is not necessary.
  • 11:08 - 11:10
    We are doing this in order to maintain a flowing
  • 11:10 - 11:12
    structure to the script.
  • 11:13 - 11:15
    First come the class variables,
  • 11:15 - 11:17
    then the Awake function is called,
  • 11:17 - 11:20
    then every frame the Update function is called.
  • 11:22 - 11:24
    This in turn calls the NavAnimSetup function
  • 11:24 - 11:27
    which calls the FindAngle function.
  • 11:29 - 11:31
    The last thing we need to do with this script
  • 11:31 - 11:34
    is to effect the root motion of the enemy.
  • 11:34 - 11:36
    Normally we have the choice of either applying
  • 11:36 - 11:39
    root motion or not on the animator component.
  • 11:39 - 11:41
    However if we use the OnAnimatorMove
  • 11:41 - 11:45
    function, which is called after Update every frame,
  • 11:45 - 11:47
    then we can effect the root motion manually.
  • 11:48 - 11:51
    There are two things we need to control in this function.
  • 11:51 - 11:54
    The velocity of the enemy and it's rotation.
  • 11:54 - 11:56
    To control the velocity we must set the nav
  • 11:56 - 12:00
    mesh agent's velocity to the delta position
  • 12:00 - 12:02
    of the animation divided by the delta time.
  • 12:03 - 12:06
    This is the change in position per frame.
  • 12:07 - 12:09
    As for the rotation, we have already set the
  • 12:09 - 12:12
    nav mesh agent so that it does not control rotation.
  • 12:12 - 12:14
    This is so that we can effect the rotation
  • 12:14 - 12:18
    directly using the animation's root rotation.
  • 12:20 - 12:22
    And that's our script complete.
  • 12:22 - 12:24
    Now that the script is finished,
  • 12:24 - 12:27
    we can save it and return to the Editor.
  • 12:29 - 12:31
    Don't forget to tidy the script away by
  • 12:31 - 12:33
    putting it in to the Enemy folder.
  • 12:33 - 12:36
    Expand the Scripts folder and drop it from
  • 12:36 - 12:39
    the root of Assets in to the Enemy folder.
  • 12:40 - 12:43
    Now let's save the scene and save the project.
  • 12:47 - 12:49
    In the next assignment we will be making a
  • 12:49 - 12:51
    script to make the enemy shoot the player.
Title:
Stealth game tutorial - 405 - Enemy Animation - Unity Official Tutorials
Description:

more » « less
Duration:
12:52

English subtitles

Revisions