x


Rotate a RigidObject 90 degrees every 2 seconds

Hi there! I need to have a rigidbody which rotates of 90 degrees (without animation, it just "snaps" in the new position) every n seconds. I tried to use yield to do this, assigning a status to my rigidbody. But there's a problem: my object starts rotating very fast, then it seems to stop, then it start rotating again. Here's my script:

private var objectstate:int;
function Start () {
    objectstate=0;
}

function Update () {
    switch (objectstate)
    {
       case 0:
       LookAround();
       break;

       case 1:
       rigidbody.transform.Rotate(0,90,0, Space.Self);
       objectstate=0;

    }
}

function LookAround(){
    yield WaitForSeconds(2);
    objectstate = 1;
}

How can I solve this problem? Thanks!

more ▼

asked May 15 '12 at 03:14 PM

kurai gravatar image

kurai
18 4 4 6

I might add something. Probably I got all in the wrong direction. Aldo's solution makes sense, but I need to add different states to my object.

This is the behavior I need: 1) the object rotates of 90 degrees every 2 seconds 2) if the player presses a button, the rotation stops and the object moves towards that direction.

Now, if I put the code in the start function I have no way to change the object states on input, am I right?

May 15 '12 at 04:32 PM kurai

I suppose it's possible: declare a member variable (outside any function) that shows the direction, and use it in the coroutine. You can have a regular Update function in your script that modifies this variable to control the direction (take a look at my edited answer). The same trick can be used to "pass" values from the coroutine to the main script: store the value in a member variable, and it can be read by other functions.

May 15 '12 at 11:18 PM aldonaletto
(comments are locked)
10|3000 characters needed characters left

1 answer: sort voted first

WaitForSeconds can only pause coroutines - when you call a coroutine, it returns almost immediately, but continues running in the background each frame. If you want to pause between rotations, do the whole job in a coroutine - like this:

function Start(){ // Start can be a coroutine
  while (true){
    yield WaitForSeconds(2);
    transform.Rotate(0,90,0, Space.Self);
  }
}

NOTE: Your original code is starting a new coroutine each Update - at 60 frames per second, there will be about 120 LookAround coroutines running at the same time!

NOTE 2: After some time, the object may be rotating around some weird axis - this happens because successive Rotate calls may accumulate errors. A better solution would be to set eulerAngles directly (the rigidbody follows the transform orientation):

function Start(){ // Start can be a coroutine
  var euler = transform.eulerAngles;
  while (true){
    yield WaitForSeconds(2);
    euler.y = (euler.y + 90)% 360; // rotate euler.y modulo 360
    transform.eulerAngles = euler;
  }
}

EDITED: If you want to control the direction, use a member variable:

var dir: int = 1; // member variable

function Start(){ // Start can be a coroutine
  var euler = transform.eulerAngles;
  while (true){
    yield WaitForSeconds(2);
    euler.y = (euler.y + dir * 90)% 360; // rotate euler.y according to dir
    transform.eulerAngles = euler;
  }
}

function Update(){
  if (Input.GetKeyDown("right")) dir = 1;
  if (Input.GetKeyDown("left")) dir = -1;
}

Remember that a coroutine returns almost immediately (basically, when it encounters the first yield) and is resumed automatically each frame, thus the Start coroutine will return despite the infinite loop.

EDITED 2: In this particular case, maybe a more traditional Update solution would be better: have a timeToRotate variable, which shows the next time to rotate; in Update, if the up arrow key is pressed, set rigidbody.velocity and reload timeToRotate to some interval (this will postpone the next rotation forever while the up key is pressed); if not pressed, stop the rigidbody and check timeToRotate: when it's reached, rotate and reload timeToRotate to time + desired interval.
This is a much more versatile solution: you can stop rotations and move while the key is pressed, and have different intervals for the first rotation after Start, the first rotation after releasing the key and for any rotation while key not pressed:

var dir: int = 1;
var moveSpeed: float = 10;
private var timeToRotate: float; // shows the time for next rotation
private var euler: Vector3; // save initial eulerAngles

// set the next rotation time to "interval" seconds from now:
function ReloadTime(interval: float){
  timeToRotate = Time.time + interval;
}

function Start(){
  euler = transform.eulerAngles;
  ReloadTime(4.5); // rotations will begin 4.5 seconds after Start
}

function Update(){
  if (Input.GetKey("up")){ // while up arrow pressed...
    rigidbody.velocity = transform.forward * moveSpeed; // move forward at moveSpeed...
    ReloadTime(1.2); // and postpone the new rotation time by 1.2 seconds
  } else { // up arrow not pressed:
    rigidbody.velocity = Vector3.zero; // stop the rigidbody...
    if (Time.time >= timeToRotate){ // and if timeToRotate has come...
      euler.y = (euler.y + dir * 90)% 360; // rotate euler.y according to dir...
      transform.eulerAngles = euler;
      ReloadTime(2.0); // and set next time to 2.0 seconds from now
    }
  }
}
more ▼

answered May 15 '12 at 03:35 PM

aldonaletto gravatar image

aldonaletto
41.2k 16 42 195

Uhm, maybe I wasn't clear on this one. It works brilliantly, but now I need this (and really I'm a newbie here, so I'm trying to grasp the concept of coroutines).

What I need is to made an object that rotates automatically and then moves on input to the direction it points. Let's say the object is pointing to the right, I need to press X and then stop rotating and move to the right.

I tried this:

var dir: int = 1; // member variable

function Start(){ // Start can be a coroutine var euler = transform.eulerAngles;

while (true){

yield WaitForSeconds(2);

euler.y = (euler.y + dir * 90)% 360; // rotate euler.y according to dir

transform.eulerAngles = euler;

}

}

function FixedUpdate(){

rigidbody.velocity = Vector3.zero;

if (Input.GetKey("right"))

{

rigidbody.velocity = transform.forward*10;

}

}

But it's not working, because the rotation keeps going even when I move. How can I solve that?

May 16 '12 at 08:40 AM kurai

You could stop the rotation just setting dir to zero, but the time would continue running - when you release the key, a rotation may occur anywhere in the next 2 seconds. Maybe a traditional Update solution could be more indicated: take a look at a new edition in my answer.

May 16 '12 at 12:01 PM aldonaletto

Thank you so much, I pretty much understand everything, though I have some doubts about euler.y. What this does represent exactly? Thanks again for your patience!

May 16 '12 at 01:26 PM kurai

euler is a Vector3 variable where the initial transform.eulerAngles is saved, and euler.y is the current rotation about the axis Y in degrees. When you modify euler.y and assign it back to transform.eulerAngles, the transform is rotated to the angle specified. eulerAngles are limited to the -360..+360 range, thus a modulo 360 is used to ensure the value never goes out of range (if it becomes 450, for instance, the modulo operation returns 90, which's the same thing in a circumference)
NOTE: I forgot to declare and initialize the variable euler in my last edition - now it's fixed.

May 16 '12 at 10:53 PM aldonaletto

Ok, that's it, I assumed it was a variable, but I wasn't unsure. Thank you for your time, it's really appreciated. I learned a lot from this one :)

May 17 '12 at 07:45 AM kurai
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x2155
x1780
x9

asked: May 15 '12 at 03:14 PM

Seen: 891 times

Last Updated: May 17 '12 at 07:45 AM