- Home /
Wandering AI "ignores" speed
I´m trying to write a simple wandering AI for a 2D game. My code should generate a random position, move the object there and continue this. I know how it shoud be done, but my code is just not working.
Here is my code: using UnityEngine; using System.Collections;
public class ZombieAI : MonoBehaviour {
public float WalkSpeed;
public float WalkRadius = 10;
public bool Wandering;
// Update is called once per frame
void Update () {
if (Wandering == true) {
Vector2 WalkPoint = Random.insideUnitCircle * WalkRadius + new Vector2(transform.position.x,transform.position.y);
Debug.Log(WalkPoint);
while(new Vector2(transform.position.x, transform.position.y) != WalkPoint){
transform.position = Vector3.MoveTowards (transform.position, WalkPoint, WalkSpeed / Time.deltaTime);
}
}
}
When I run my game now, the Sprite realy fast from one point to another, like it´s teleporting. Even if i lower the speed value noting changes.
I think the proble is with the while-loop, but if I delete it the another problem shows up: The debug.Log returns normal coordinates, but the Sprite moves just very tiny very fast steps. It seems like the Sprite has not enough time to move to the target point ,instead it generates a new target every frame.
Please help me to figure out what´s wrong.
Thank you, TheSakuron
Answer by Dave-Carlile · Jul 28, 2015 at 09:05 PM
You're correct that the while loop is the issue. You're generating the random point and moving the transform all the way to the target point in a single frame. And when you remove the while loop, you're generating the random point, moving one step towards it in a single frame, and then the next frame you're generating a new random point and moving one step towards that.
This kind of scenario comes up all of the time in game programming. You need to be able to do work that spans multiple frames. There are a number of ways you could handle this, but Coroutines are a decent fit here.
Something like this:
void Start()
{
// start the coroutine to randomly move the zombie around
StartCoroutine(ZombieMove());
}
IEnumerator ZombieMove()
{
// keep on running - you could replace "true" with a variable that would let you control when it stops
while (true)
{
Vector2 WalkPoint = Random.insideUnitCircle * WalkRadius +
new Vector2(transform.position.x,transform.position.y);
Debug.Log(WalkPoint);
while(new Vector2(transform.position.x, transform.position.y) != WalkPoint)
{
transform.position = Vector3.MoveTowards (transform.position, WalkPoint, WalkSpeed / Time.deltaTime);
yield return null; // wait for the next frame
}
}
}
Oh, and I just noticed that you're comparing transform.position
with WalkPoint
. Since vectors use floating point, it's generally not a good idea to compare values for equality. Because of rounding errors and such it will be rare that they're exactly equal to another value. A good way to handle this is to compare distance from the target point. So replace the while (new Vector2...
code with this:
while (Vector2.Distance(new Vector2(transform.position.x, transform.position.y),
WalkPoint) > 0.1f)
{
}
This will keep looping until the transform moves within 0.1 units of the target. You may want a smaller values than 0.1, but that's something to play around with. Probably make it a public member variable so you can set it in the inspector.
Thank you for your fast answer and the hint with comparing vectors. I tried the code, but I´m afraid it´s not working. The sprite moves every frame to a new position again.
What does your new code look like?
Actually, the value you're passing to $$anonymous$$oveTowards
for maxDistanceDelta
is potentially fairly large. What value are you using for WalkSpeed
? If it's 1, dividing that by deltaTime
, which would be 0.0167 at 60fps, will give you a value of 60 units. This means the position will change by 60 units each frame, which will cover the entire distance to your WalkPoint
in a single frame.
You probably want WalkSpeed * Time.deltaTime
ins$$anonymous$$d, which is what the documentation shows in the example code.
Guys stay away from while in Update ...
if ((Vector2.Distance(new Vector2(transform.position.x, transform.position.y), WalkPoint) < 0.1f)
{
reached = true;
}
if (!reached)
{
transform.position = Vector3.$$anonymous$$oveTowards (transform.position, WalkPoint, WalkSpeed / Time.deltaTime);
}
else if (reached)
{
Vector2 WalkPoint = Random.insideUnitCircle * WalkRadius + new Vector2(transform.position.x,transform.position.y);
Debug.Log(WalkPoint);
reached = false;
}
^ Shove this in Update. Hope I helped ^^.
@Acex, that is another way to do it (and I often do things that way), but it splits up the logic. The Coroutine allows for more intuitive code in this case.
The Coroutine function is doing one task. It's randomly moving a zombie around, using pretty much the same code OP provided as an example. I don't think UA is really the place to $$anonymous$$ch coding philosophy. :)
Answer by Wolfdog · Jul 29, 2015 at 02:12 PM
Try
transform.position = Vector3.MoveTowards (transform.position, WalkPoint, WalkSpeed * Time.deltaTime);
instead.
Your answer
Follow this Question
Related Questions
How to Add Knockback Force Based on What Rotation it Came From 2 Answers
how to make 2D platformer AI for a skeleton 0 Answers
Do something every each multiple of 1000.. 2 Answers
NPC movement 1 Answer
Enemy AI Movement Decision Making 1 Answer