|
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: How can I solve this problem? Thanks!
(comments are locked)
|
|
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.
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
}
}
}
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:
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)
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)
|

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?
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.