Waypoint To Ignore Height & Add Smoothness

Hello Unity Fans,

I've been using a simple AI waypoint system for sometime, and it has only just occurred to me that it causes my enemies to follow the same height as my waypoints too. In other words, if I position my waypoints 50 on the Y axis, then the enemies will be walking 50 above the floor.

What would I do to my script to ignore the Y axis of the waypoints?

Also, if you would be kind enough to make my enemies turn 'smoother' from one waypoint to another? Right now, the enemy walk to one waypoint, and then 'snap' to the other (Linier) - any way of making this smooth?

var waypoint : Transform[];
static var speed : float = 5;
private var currentWaypoint : int;
private var TurningSpeed : float = 15;

function Update () 
{
    if(currentWaypoint < waypoint.length)
    {
        var target : Vector3 = waypoint[currentWaypoint].position;
        var moveDirection : Vector3 = target - transform.position;
        var velocity = rigidbody.velocity;

        if(moveDirection.magnitude < 1)
        {
            currentWaypoint++;
        }
        else
        {
            velocity = moveDirection.normalized*speed;
        }
    }

    rigidbody.velocity = velocity;

    var lookAt = waypoint[currentWaypoint].position - transform.position;
    lookAt.y = 0;
    var rotation = Quaternion.LookRotation(lookAt);//target.position - transform.position
            transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * TurningSpeed);

}

Thanks folks!

What would I do to my script to ignore the Y axis of the waypoints?

// This line you already have.
var moveDirection : Vector3 = target - transform.position;

// Add this line to ignore y.
moveDirection.y = 0;

// This line is modified to have "simple (incorrect) gravity".
velocity = (moveDirection.normalized - (Vector3.up * 20.0f))  * speed;

Since your solution overrides the previous velocity of the rigid body, you might want to keep another variable updating the rate of fall, for gravity. The method I showed won't look pretty, but it won't slow down near the checkpoints due to the moveDirection is wrong.

I hate to hand it out to you, but I think you have a problem with your solution all together. Why did you use rigid bodies? They are hard to get right and is probably not what you need for most problems anyhow. Why don't you at least add forces instead of overwriting the velocity, so gravity and other forces are accounted for? Why not use the character controllers that are out there?

Anyhow, try the change with the last line of code to see if you get your desired effect.

For smooth rotation without physics, you can use this:

public static bool RotateTowards(Transform objectTransform, Vector3 targetLookAt, 
   float rotationSpeed)
{
  Vector3 targretDirection = 
    (targetLookAt - objectTransform.position).normalized;

  Vector3 newDirection = 
    Vector3.RotateTowards(objectTransform.forward, targretDirection, rotationSpeed, 1000f);

  objectTransform.LookAt(objectTransform.position + newDirection);

  return (newDirection - targretDirection).sqrMagnitude < 0.001f;
}

The function should be called in Update. It returns `true` when the object is in the correct rotation, that is, it is facing the point `targetLookAt`. The transform passed to the function is the transform you wish to rotate. The `rotationSpeed` controls how fast the object rotates (start with `0.1f`).

The magic number of 1000f you see there is one that works for me, but I don't really understand what it does :( The docs on Vector3.RotateTowards is not very clear.

The following is a JavaScript version (I am not a JavaScript native speaker, if anyone sees anything amiss, please let me know). It is not static, so you have to make put it in the same file as the thing you wish to rotate.

function RotateTowards(
    objectTransform : Transform, 
    targetLookAt : Vector3, 
    rotationSpeed : float )
{
   var targretDirection : Vector3 = 
     (targetLookAt - objectTransform.position).normalized;

   var newDirection : Vector3 = 
     Vector3.RotateTowards(objectTransform.forward, targretDirection, rotationSpeed, 1000f);

   objectTransform.LookAt(objectTransform.position + newDirection);

   return (newDirection - targretDirection).sqrMagnitude < 0.001f;
};

To make turning smoother, try `rigidbody.AddRelativeTorque()` and then stopping it from rotating when it's pointing towards the way point. See here for another way to do it from the Unity docs.

Here is some example code for looking at and then moving towards a waypoint called `waypoint`. Replace `waypoint` with some code to figure out the current waypoint.

var waypoint : Transform;
var speed : int = 3; // var something : int? That may be wrong. If it is, just remove it

function Update() {
   transform.LookAt(waypoint);
   transform.Rotate(transform.rotation.x, 0, transform.rotation.z);

   while (transform.position.x != waypoint.position.x && transform.position.z != waypoint.position.z) {
      rigidbody.AddRelativeForce(0, 0, speed);
   }
}

The way this works is that the gameObject this script is attached to looks at the gameObject `waypoint`, sets it y-axis rotation to zero, then keeps moving forward until it's `x` and `z` positions are the same as the waypoint's

You might also want to try using a dynamically-created Empty GameObject, instanced in the AI object's Awake(). Set the x&z position of this to where you want to go (say the waypoint's x&z), then you can set y to whatever you like (terrain height, etc). You can then move the AI Target to wherever you want when you want the target to change, without needing to touch its seeking code once you're happy with it. Also, using a Transform means you can treat your AI target like one with regard to handy functions like Translate(). It is however less optimised and streamlined than simply storing a target x and z number.

For smooth turning, AddRelativeTorque() does indeed work well, although Mathf.MoveTowardsAngle() and Mathf.SmoothDampAngle() can be useful too for this purpose, particularly if you aren't using rigidbodies.

I post another answer because this isn't related to my previous one.

How about you give proper pathfinding a shot? Then I think you have to worry less about all the different special cases such as slopes and colliders.