Why does this random movement script break..?

edit: wow… transform.Translate(Vector3.forward * spd * Time.deltaTime); was all I needed

Pretty simple stuff… works great and then, seemingly randomly, it’ll just break and go off very far away. It’ll keep generating random target positions as expected (within the correct bounds), but it seems to stop trying to get to them.

var damp : float = 1.0;
var targetPos : Vector3 = Vector3(0,0,0);
private var randomCountdown : float = 0.0;
 
var maxXTilt : float = 3.5;
var maxYTilt : float = 4.0;
 
var spd : float = 3.0;
 
var interval : float = 4.0;
 
function Update(){
        //turn toward target
        var lookPos = targetPos - transform.position;
    var desiredRotation = Quaternion.LookRotation(lookPos);
        transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, Time.deltaTime * damp);
       
        //move forward
        transform.Translate(transform.forward * spd * Time.deltaTime);
       
        //if enough time has passed, randomize target location again
        randomCountdown += Time.deltaTime;
        if(randomCountdown > interval){
                randomCountdown = 0.0; 
                targetPos = Vector3(Random.Range(-maxXTilt, maxXTilt), Random.Range(-maxYTilt, maxYTilt), 0);
        }
}

Note: maxXTilt (and Y variant) are named poorly – they simply dictate the area the random target position can be in.

So anything stand out? Is my method of looking toward certain coordinates flawed somehow?

Wierd.

Try it in a empty project.

Here I made a test scene for you to see. You can ramp up the simulation speed (time scale) to see it happen more often. It’ll be fine, sticking to the correct area… then all of the sudden shoot off like an animal breaking out of its cage :stuck_out_tongue:

http://upload.dfyb.net/uploaded/movingRandomly.unitypackage

edit: interestingly, it always seems to come back (only noticed this when observing at like time scale 100)… when it gets back, it behaves normally for a bit then shoots off again. It’s as if it behaves differently depending on where it happens to be… but I don’t see how my code would cause that.

edit2: increasing the area (maxXTilt, maxYTilt) seems to make breakage less likely, but it still happens.

When it breaks, it seems to shoot off along positive Z, make a perfect circle (as far as I can tell) out along X and ends up right back at the origin. Reminds me of how magnetic fields look, haha.

Any alternatives maybe? I really just need something that moves in 2D space somewhat naturally and randomly.

I put your script on a box, and left it running for about 5 mins, and it never exhibited the behavior your mentioning. Acts kind of weird as it is trying to move to the new coordinates and re-rotate to get there, but it does work.

Increase timescale – it doesn’t happen that often. Check out the unity package linked above.

The only thing I can think of is if the random position was ever 180 degrees behind the object, it may have trouble calculating the rotation. Other than that your position is always based off of the current rotation and a translation froward. So position is never dictated by the target position, but on a rotation based off of where that target position is in relation to the object.

The chances of that are incredibly slim though. If you wanted to set it up where it did not do that… simply add a Vector3.Dot check, if the number is less than -0.95 then pick another location.

if(Vector3.Dot(targetPos, transform.position) < -0.95){
    	print("reposition");
    	newTargetPos();
    	return;
	}

Added this – sometimes it is triggered, but it doesn’t stop it from breaking =/

I though that might be the problem, too, but I’m not sure how that problem would affect it for more than a very short period of time. As I understand it, the object travels at a constant speed and can rotate by a set amount every frame – so I have no idea how, for a temporary but extended amount of time, the turning radius is much worse than it normally is.

If it is way far out, shouldn’t it just make a sharp turn and go back toward the target? Instead it just continues on what seems to be a perfect circle until it gets back to near the center (the target pos can change several times without affecting the trajectory in the slightest, even though it should always be rotating toward that position), then it starts behaving normally again. What locks it into that behavior…?

Not exactly. First, the two vectors need to be in relation to each other. First, we are looking to get the targetPos and it’s relation to the current object. Then we compare it to the forward vector of the object. Now the forward vector once we get the targetPos aligned to it, is really just Vector3.forward.

Next, we don’t just check once, we keep checking until we get it right. So we invert the check to continue until our value is greater than 0.95. This should of course be 97.5 percent of the time.

Here is some updated code. I added in some start stuff so that we wouldnt start off errored out.

var damp : float = 1.0;
var targetPos : Vector3 = Vector3(0,0,0);
private var randomCountdown : float = 0.0;
 
var maxXTilt : float = 3.5;
var maxYTilt : float = 4.0;
 
var spd : float = 3.0;
 
var interval : float = 4.0;

function Start(){
	Time.timeScale=30.0;
	transform.position = Vector3(Random.Range(-maxXTilt, maxXTilt), Random.Range(-maxYTilt, maxYTilt), 0);
}
 
function Update(){
	//turn toward target
	var lookPos = targetPos - transform.position;
	var desiredRotation = Quaternion.LookRotation(lookPos);
	transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, Time.deltaTime * damp);
	   
	//move forward
	transform.Translate(transform.forward * spd * Time.deltaTime);
	   
	//if enough time has passed, randomize target location again
	randomCountdown += Time.deltaTime;
	if(randomCountdown > interval){
		randomCountdown = 0.0;
		var found=false;
		while(!found){
			targetPos = Vector3(Random.Range(-maxXTilt, maxXTilt), Random.Range(-maxYTilt, maxYTilt), 0);
			print(Vector3.Dot(Vector3.forward, transform.InverseTransformPoint(targetPos).normalized));
			found=Vector3.Dot(Vector3.forward, transform.InverseTransformPoint(targetPos).normalized)>-0.80;
		}
	}
}

Still behaves the same as far as I can tell

Unity Web Player | randomMovement web player at 99 timescale

OK, eventually that one causes a crash, so don’t use it.

The problem is that you are picking a point behind the object and not turning enough to make the new point. The fault is in the concept of picking a point in a bound then telling the unit it has to move forward to get to the new point. The more behind the unit it is, the more that unit will move away from it because it can’t make the rotational turn.

I had a similar problem that disappeared when I stopped incrementing by time.DeltaTime.

Based on your original code I would change this:

randomCountdown += Time.deltaTime;
if(randomCountdown > interval) {
    randomCountdown = 0.0;

to this (I have used some different var names to make it clearer what I am doing):

if (Time.time > nextChange) {
    nextChange = Time.time + interval;

You will need to set nextChange to nextChange = Time.time + interval in function Awake if you want a standard gap before the first change.

You do lose the fraction of the frame time that Time.time is greater than nextChange at each change. But I have found that is not really noticeable.

Yeah, that’s what I thought at first but it doesn’t make complete sense to me. If that’s the only problem, I have questions

-if that was the case, wouldn’t it just be stuck going straight?
-why does it only happen going into positive Z? Shouldn’t it be able to happen at any direction?
-why does it always result in circles in the x/z axis? What is stopping it from going into the Y axis when it does one of these offshoots?


This is what one of the offshoots can look like – but at this point, the target location is not behind the object… it should be able to find out the turn rotation, right? But instead of correcting course and turning toward the target, it completes the full circle. The target location can change but the object maintains the trajectory around the circle until it’s done.

But that snippet only handles picking new target locations… which works fine. I guess I could try it, but I don’t see how it’d help the object actually get to the target locations.

Well now I’m really confused…

transform.LookAt(targetPos);
transform.Translate(transform.forward * spd * Time.deltaTime);

shouldn’t this make it go directly toward the target? No curvy trajectories just direct angular movement, right? Instead it acts, as far as I can tell, just like my original script…

edit: wow… transform.Translate(Vector3.forward * spd * Time.deltaTime); was all I needed. Everything works as expected now