Moving a Nav-mesh Agent Between Points and to a Mouse Click

For this project, I added movement to my character game object from the previous project, and I also introduced enemy game objects that patrol between different waypoints. This project serves to demonstrate how a player game object can move through space. Also, this project demonstrates the first steps towards building an enemy artificial intelligence (AI) by thinking about, during a patrol state, an enemy will move between different waypoints in the game.

Player: Look at Mouse and Raycasting

Before controlling the character, the player has to see where the character will be going. I found that the best way to do this was to have the camera and the player game object rotate and follow the mouse. I placed the camera as a child of the player game object so its transform is always relative to its parent. I placed the script below to rotate the player game object, as well. The player game object’s transform.rotation is affected by the look at mouse script below. The script uses a raycast, which creates an invisible ray from a point in a specific direction. Raycasting can be used for projects that involve, for example, shooting a gun.

I also wanted the camera and player to only follow the mouse on a clickable area, which was the ground. If the mouse hovered off in the distance above the ground (in a non-clickable area), I did not want the camera to rotate. I found a script from the Unify Community Wikipedia page (http://wiki.unity3d.com/index.php/LookAtMouse).

The look at mouse script first generates a plane that intersects the transform position of the player and upwards. This is later used to check if the ray that will be cast is not parallel to the plane, and therefore not within the clickable area. To rotate the player game object (and the camera), a ray is generated from the cursor’s position, and then the script determines the point where the cursor ray intersects the plane. Raycasting gives a distance, so the distance is used to find the point along that ray that meets that distance. This is the point that the camera rotates towards. The location and rotation of the target point is calculated, and then the player game object is smoothly rotated towards the target point by calling Quaternion.Slerp(). Slerp is means that the object spherically interpolates between two vectors. A Lerp or Slerp takes three parameters: Start position (a vector 3) (point A), End position (another vector 3)(point B) and a float value. The float value parameter indicates the position between point A and point B. If the value is 0, then the method returns the position of point A. If the values is 1, this returns the position of point B. If the value is 0.5, this is the middle point between point A and point B. For this reason, many people use a value equivalent to Time.deltaTime divided by some number as this float value. This creates a smooth movement from point A to point B, over time. In the case of the look at mouse script, Time.deltaTime is multiplied by a public speed value set in Unity.

Look at Mouse Script:

using UnityEngine;
using System.Collections;

public class LookAtMouse : MonoBehaviour {

    public Camera camera;
    public float speed;
 
    void FixedUpdate () {
            Plane playerPlane = new Plane (Vector3.up, transform.position);
            Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
            float hitdist = 0.0f;
            
            if (playerPlane.Raycast (ray, out hitdist)) {         
                Vector3 targetPoint = ray.GetPoint (hitdist);   
                Quaternion targetRotation = Quaternion.LookRotation (targetPoint – 
                transform.position);
                transform.rotation = Quaternion.Slerp (transform.rotation, 
                targetRotation, speed * Time.deltaTime);
        }
    }

 }

Player: Move to Mouse Click

After looking in the direction of the mouse, the player game object needs to move to a position where the player clicks. To do this, the moving player script below changes the player game object’s transform by lerping. When the player clicks, the StartLerping() method is called. The target position is determined by casting a ray to the mouse position. Once the position is determined (and the player has clicked on the screen), the distance between the player and the point is calculated and the player begins to lerp. A timer counts up and this value is used to calculate a percentage of how long the lerping has occured. This percentage is then used to determine the 3rd parameter in the Vector3.lerp, and therefore is used to determine at what point the player game object is located (between the player’s original transform and the target position’s transform).
The player also appears to be running while it is lerping. The player’s animator (which was initialized in another section of the script) has a Boolean value labeled isRunning. As long as the player game object is lerping, this value in the animator is set to true, and so the player game object appears to be running.

I had originally set this script up to move the player to the target position that is determined by the raycast. For that script, the player would move to the destination, and while the player’s navmesh magnitude was larger than 0, the isRunning Boolean would be set to true. In other words, as long as the player was moving, the run animation would play. However, the reason that I am using a lerp as opposed to moving the player game object’s navmesh is because I had issues controlling the the look at mouse script while the player was moving to a new location. This is because the player game object’s navmesh is constantly moving towards a set vector 3 position (and in fact “facing” or “looking at” the destination). If the mouse cursor moved during the navmesh’s movement (when the player would look at a different space in the game world), the camera would move back to the target position. This is because the camera is a child of the player game object. For this reason, the screen would appear to shake as the player was moving. Using a Vector3.lerp() and physically changing the transform of the player game object allows the camera game object to move (as it would seem independently) while the player is moving.

Moving Player Script:

    private Vector3 currentPosition;
    private Vector3 targetPosition;
    private Vector3 targetPositionGrounded;
    public float timeTakenDuringLerp = 1f;     //Time taken to move- start to finish positions
    private bool isLerping; //whether we are interpolating or not
    private float timeStartedLerping; //Time.time value when we started interpolation 

    void Update () {
        currentPosition = player.transform.position;
        HandleInput();
    } 

    void HandleInput () {
        if (Input.GetMouseButtonDown (0) && !isLerping) {
            StartLerping ();
        }
    } 

   void StartLerping () {
        isLerping = true;
        timeStartedLerping = Time.time;
        Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast (ray, out hit)) {
            targetPosition = hit.point;
        }
    }

    void HandleLerping () {
        if (isLerping) {
            playerAnimator.SetBool ("isRunning", true);
            targetPositionGrounded = new Vector3 (targetPosition.x, 0, targetPosition.z);
            float timeSinceStarted = Time.time - timeStartedLerping;
            float percentageComplete = timeSinceStarted / timeTakenDuringLerp;
            player.transform.position = Vector3.lerp (currentPosition, targetPositionGrounded, 
            Time.deltaTime);

            if (percentageComplete >= 0.9f) {
                isLerping = false;
                playerAnimator.SetBool("isRunning", false);
            }

        }
    } 

Enemy Movement: Moving Nav Mesh Agent between waypoints (Patrol State)

For this project, I added enemies to the scene. The enemies will have 3 states: Idle state, attack state, and patrol state. To program moving nav mesh agents around the screen, I have decided that I would first create a patrol state. During the patrol state, the enemy game object moves towards waypoint destinations, which are set as transforms of empty game objects set in the scene. These transforms are stored in a list. While the enemy is in the patrol state, the navmesh agent will move towards these waypoints for a time, before switching to the next waypoint. While the enemies walk around, a walk animation is triggered by the speed float “Speed”.

    public Transform[] patrolWayPoints;
    public float timeToNextWaypoint;
    private float patrolTimer = 0;
    private int wayPointIndex;



   void PatrolState () {
        patrolTimer += Time.deltaTime;
        if (patrolTimer > timeToNextWaypoint) {
            if (wayPointIndex == patrolWayPoints.Length - 1) {
                wayPointIndex = 0;
            } else {
                wayPointIndex++;
            }
                patrolTimer = 0;
            }

        animator.SetFloat ("Speed", 0.2f);
        agent.destination = patrolWayPoints [wayPointIndex].position;
    }

Future Projects and Conclusion

For the next project, I will be expanding on the mouse controls of the player and add an attack state. The player will be mouse controllable. I will also be building the enemy AI by building more states, such as an idle state and attack state to go alongside the patrol state. As a bonus, and to finally make this project into a recognizable game, I plan on having the enemies spawn at the different waypoints. I will incorporate the animation states for when the player game object receives damage, and also when they are dead. I will also add an internal economy so that the player has health, can take damage, and also score by destroying enemies. I will also add a pickup system so that the player can interact with health packs and increase their health.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s