- Home /
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!
Answer by Statement · Dec 15, 2010 at 12:15 PM
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.
I tried this and it works, BUT, if my terrain goes negative (walking down a hill), then it doesn't work because its following the 0. I've tried making it a negative number, such as -20, but then the enemy walks slower due to the 'pulling mass'.
It sounds like your character just have to apply gravity?
I have gravity applied - what do you suggest for a new approach then? All my enemy do is to follow one waypoint after another in sequence.
How about you give proper pathfinding a shot? http://www.arongranberg.com/unity/a-pathfinding/
I know I'm far to late, but I felt like I just had to say this: Use moveDirection.y = transform.position.y
. That way it will really ignore the y, placing it on the level as the enemy. Gravity and all the other physics will still work as normal.
Answer by Herman-Tulleken · Dec 16, 2010 at 08:13 AM
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; };
Looks good - I'm trying to convert it into JavaScript, but I keep getting errors. Could you give me a hand? Cheers :)
Answer by fireDude67 · Dec 15, 2010 at 04:30 AM
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
I keep getting errors like: 'position is not a member of Unity transform[]'
Answer by Novodantis 1 · Dec 15, 2010 at 10:31 AM
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.
Answer by Statement · Dec 16, 2010 at 03:16 PM
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.
I had a look at this - but its not really for my game - $$anonymous$$es more of a tower defence type - so the enemy follow a specific path.
Then simply do this. Ignore the rigidbody all together. Use the velocity to find the next position. (nextPosition = position + velocity * Time.deltaTime). Then sample the Y coordinate of your terrain and replace nextPosition.Y with that sample. Finally position your character at nextPosition. Done.
I like the idea of this - Could you please collaborate some more? $$anonymous$$aybe give an example of code in your post? Thanks