Double speed?

The following code is called a number of time throughout my game, it makes my game object walk, the problem is everytime its called, it doubles the speed of the game object, how can I get it to move at a constant speed?

function Walk(){
	

	 // newtimer2 =0.0;
	animation.CrossFade("walk");
	transform.rotation = Quaternion.identity;
	
   while(shouldMoveAir){
   	
   	//transform.position = transform.position;
   	
  				transform.position += transform.TransformDirection(Vector3.forward) * Time.deltaTime; 
   	


         yield;
        
}

Well, you don’t appear to have any way of ending the loop currently. As such, every time you call the function it starts another loop that runs alongside the first loop.

In order to avoid this, you should have an if statement in front that checks to see if you’re walking already.

when i want to change from walk to another movement I set:
shouldMoveAir = false;

and then it sets it back to

shouldMoveAir = true;

to start the coroutine again, the problem is, the code runs too quick and shouldMoveAir = false never is set. Any ideas?

yield WaitForEndOfFrame(); ?

Where should I put that?

Well, I don’t think my suggestion was meaningful.

I think a basic yeild; like you have already pauses it until the next frame, I think WaitForEndOfFrame just specifies where in the calling thread you want to resume, but otherwise has the same duration as a regular yield;

Charles

It sounds like maybe you have started the Coroutine more than once?

the coroutine is started more than once, my script decides what to do:

function Decide() {//Decide
	
newtimer = 0.0;
    
    
     
     	animation.Stop();
     	
     			 var toDo = Random.Range(2,2);
     			 Debug.Log(toDo);
     			if (toDo == 1){
     			
     			Idle();

    			 }
     
    			 if (toDo == 2){
     			
     			Walk();
    			 }
     
    			 if (toDo == 3){
     			
     				TakeOff();
 
   }
   

   
   }

then it does the function, in a corutine:

function Walk(){
	

	 // newtimer2 =0.0;
	animation.CrossFade("walk");
	transform.rotation = Quaternion.identity;
	//shouldMoveAir = true;
	
	 shouldMoveAir = true;
   while(shouldMoveAir){
   	
   	
  				transform.position += transform.TransformDirection(Vector3.forward) * Time.deltaTime; 



         yield;
        
}

Then every six seconds (will be random interval in future) it decides what to do again:

function Update(){
	
	//every 6 seconds decide what to do

	 newtimer += Time.deltaTime;
  if (newtimer > 6){

shouldMoveAir = false;

Decide();
}

The problem Im having is that the code runs too quickly and shouldMoveAir never gets set to false, when I delay the reloading of the delay function, it works:

if (newtimer > 6.1){
Decide();
}

But this causes a slight pause with my game object and isnt ideal…

So any ideas how to solve this?

Well, I think you might want to re-evaluate the structure of your program. Perhaps something like this;

Sorry, this is in C# but porting to JavaScript should not be hard (let me know if it turns into a chore and I’ll rewrite it)

using UnityEngine;
using System.Collections;

public class RandomBehavoir : MonoBehaviour 
{
    private int currentAction = 1;
    public int Speed = 10;

    void Start()
    {
        InvokeRepeating("MakeDecision", 0, 6.0F);
    }

    void MakeDecision()
    {
        currentAction = Random.Range(1, 2);
    }

    void Update()
    {
        switch (currentAction)
        {
            case 1:
                Walk();
                break;
            case 2:
                Idle();
                break;
            default:
                Idle();
                break;
        }
    }

    void Walk()
    {
        animation.CrossFade("Walk");
        transform.position += transform.TransformDirection(Vector3.forward * Speed * Time.deltaTime);
    }

    void Idle()
    {
        animation.CrossFade("Idle");
    }

}

Ooops, sorry had to edit it. I had a random 1/6 chance being rolled every seconds as I had originally forgot to just use InvokeRepeating for a reliable single random roll every 6 seconds.

(JavaScript version, just in case)

public var Speed = 10;

var currentAction = 1;

function Start()
{
	// Make a new decision in 0 seconds, and then every 6 seconds afterwards
	InvokeRepeating("MakeDecision", 0, 6);
	// Only need to call this one when we start up.  Crossfading will take it from there.
	animation.Stop();
}

function MakeDecision()
{
	// pick a random action
	currentAction = Random.Range(1,2);
}

function Update () 
{
	// execute ONE frame of whatever the current action is
	switch (currentAction)
	{
		case 1:
		Idle();
		break;
		
		case 2:
		Walk();
		break;
		
		default:
		Idle();
		
	}
	
}

function Idle()
{
	animation.CrossFade("Idle");
}

function Walk()
{
	animation.CrossFade("Walk");
	// Speed is an exposed (public) variable so you can "tweak" it from the component.
	transform.position += transform.TransformDirection(Vector3.forward * Speed * Time.deltaTime);	
}

cheers, that was great, im now having problems with rotating, when I walk, I want it to move 45 degrees upwards, ive tried but I just get jerky movements…

im using:

transform.rotation = Quaternion.Euler (0, 50, 0);

Do you mean you are rotating the object to match a 45 degree incline, and then just moving it with TransformDirection(Vector3.forward)?

Make sure your object does not have a rigidbody or a charactercollider that will want to naturally simulate gravity and such.

Or maybe post more of what you are trying to do, as there are usually more than one way to get the same effect.

Charles

my object has a charcter controller, as its needed for the game, at the minute is level with the ground, im trying to smoothly rotate it to face 45 degrees towards the sky but am having problems…

This rotates it towards the sky:

transform.rotation = Quaternion.Euler (360, 0, 0);

but it does it instantly, how can I get it to smoothly rotate to it from its current rotation, in the code given above?

Heh… you’ve been very busy in the forums. :smile:

Actually, AngryAnt gave you the answer earlier, now it’s just time to put it all together.

  1. Calculate where you WANT to face. Save that rotation as a variable.
  2. Smoothly rotate to match that direction using Update() and Slerp.

example:

Update()
{
// Step 1 - where do I WANT to face?
var desiredRotation = Quaternion.Euler (360, 0, 0);

// Step 2 - where am I facing NOW?
var currentRotation = transform.rotation;

// Step 3 - DON’T DO THIS
// This instantly rotates you in one frame.
// This will look jerky and bad.
transform.rotation = desiredRotation;

// Step 3 - THIS is better.
transform.rotation = Quaternion.Slerp(
currentRotation,
desiredRotation,
Time.deltaTime * RotationSpeed);

This will move you from one rotation to the next at a rate determined by your RotationSpeed as applied by each frame.

Remember, the reason you multiply changes by Time.deltaTime is that you are effectively saying,
if an entire second has passed since the last time a frame was rendered (i.e. Time.deltaTime = 1 second) then make the ENTIRE rotation now. That would look like crap, but of course you are only getting 1 Frame Per Second so you are screwed anyways.
But assuming a normal 60 frames per second, the Slerp function would then rotate 1/60 of the way each frame, so in the span of 1 second (and 60 frames) your object would smoothly rotate to match the desiredRotation.

“RotationSpeed” is just so you can tweak it, if needed.

also, please forgive me but I’m at work so this is just from memory, not guaranteed to compile verbatim.

Charles

Maybe this helps.

You might want to check other Lerps.

Cheers!

cheers!

Just wondering in the original script, how can I make the following happen at random time periods? Surely if I use random range it will only be called on start, thus making only one random number?

function Start()
{
// Make a new decision in 0 seconds, and then every 6 seconds afterwards
InvokeRepeating(“MakeDecision”, 0, 6);
// Only need to call this one when we start up. Crossfading will take it from there.
animation.Stop();
}

Option 1

function MakeDecision(int lastDecision)
{
// Stop the call back
CancelInvoke(“MakeDecision”);
// ReStart the call back with a new Random.Range
InvokeRepeating(“MakeDecision”, Random.Range(1,6);

// return another random decision or one
// guaranteed to be different?

// Random (might be the same decision again)
return Random.Range(1,2);

OR

// guaranteed to be different.
// coded as a case statement in the event
// you have many states and want to do other stuff
switch (lastDecision)
{
case 1:
return 2;
case 2:
return 1;

}

OR

// always step to the next decision
// and roll over when at the end

return (++LastDecision % 3);
// this would generate 0, 1, 2, 0, 1, 2

return (++LastDecision % 3) + 1;
// this would generate 1, 2, 3, 1, 2, 3

}

Note, I haven’t tried call back from inside a call back but I don’t see why it shouldn’t work.

Option 2

Lower the original timer to 1 second intervals.

function Start()
{
InvokeRepeating(“MakeDecision”, 0, 1);
}

Then…

function MakeDecision(int PreviiousDecision)
{
// 50% to keep on trucking
if (Random.Range(1,2) == 2)
return PreviousDecision;

// roll the dice again
return Random.Range(1,2);

}

Ultimately a lot depends on how erratic or how smooth you want your behavior to be.

Charles

One quick note; if you do choose to just start a new call back every time, with a newly random wait time, I don’t think Invoke"Repeating" is necessary (or correct) since you will just cancel the Callback everytime you get it, I just don’t have the docs near me but I assume there is a simple, single-call one-shot invoke where you can seed call back time, service it and then fire off another call back (without having to cancelinvoke on the Invoke"Repeating".

Charles