3

I would like to rotate an object back and forth between 90,-90 on the Y axis. The problem is I can set the object in the editor at -90, but when I run the project -90 suddenly becomes 270. Anyway here is the code that I'm using:

void Update()
{
    if (transform.eulerAngles.y >= 270)
    {
        transform.Rotate(Vector3.up * speed * Time.deltaTime);
    }
    else if (transform.eulerAngles.y <= 90)
    {
        transform.Rotate(Vector3.up * -speed * Time.deltaTime);

    }
}

It always gets stuck in the middle around 360 degrees. Help?

Programmer
  • 121,791
  • 22
  • 236
  • 328
Abdou023
  • 1,654
  • 2
  • 24
  • 45
  • 1
    In theory it should be stuck between **360 and 0**, because when the rotation reaches 360 your code suggests: `>= 270` so continue rotating, making the rotation 0, then your code says: now its `<= 90` so rotate backwards, making it 360 again, etc.. – Hristo Apr 26 '17 at 08:26

2 Answers2

6

Just like moving GameObject back and forth, you can rotate GameObject back and forth with Mathf.PingPong. That's what it is used for. It will return value between 0 and 1. You can pass that value to Vector3.Lerp and generate the eulerAngle required to perform the rotation.

This can also be done with a coroutine but Mathf.PingPong should be used if you don't need to know when you have reached the destination. Coroutine should be used if you want to know when you reach the rotation destination.

public float speed = 0.36f;

Vector3 pointA;
Vector3 pointB;


void Start()
{
    //Get current position then add 90 to its Y axis
    pointA = transform.eulerAngles + new Vector3(0f, 90f, 0f);

    //Get current position then substract -90 to its Y axis
    pointB = transform.eulerAngles + new Vector3(0f, -90f, 0f);
}

void Update()
{
    //PingPong between 0 and 1
    float time = Mathf.PingPong(Time.time * speed, 1);
    transform.eulerAngles = Vector3.Lerp(pointA, pointB, time);
}

EDIT:

With the coroutine method that you can use to determine the end of each rotation.

public GameObject objectToRotate;
public float speed = 0.36f;

Vector3 pointA;
Vector3 pointB;


void Start()
{
    //Get current position then add 90 to its Y axis
    pointA = transform.eulerAngles + new Vector3(0f, 90f, 0f);

    //Get current position then substract -90 to its Y axis
    pointB = transform.eulerAngles + new Vector3(0f, -90f, 0f);

    objectToRotate = this.gameObject;
    StartCoroutine(rotate());
}

IEnumerator rotate()
{
    while (true)
    {
        //Rotate 90
        yield return rotateObject(objectToRotate, pointA, 3f);
        //Rotate -90
        yield return rotateObject(objectToRotate, pointB, 3f);

        //Wait?
        //yield return new WaitForSeconds(3);
    }
}

bool rotating = false;
IEnumerator rotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
    if (rotating)
    {
        yield break;
    }
    rotating = true;

    Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;

    Vector3 currentRot = gameObjectToMove.transform.eulerAngles;

    float counter = 0;
    while (counter < duration)
    {
        counter += Time.deltaTime;
        gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
        yield return null;
    }
    rotating = false;
}
Community
  • 1
  • 1
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • I'm familiar with "Mathf.PingPong" but I prefer not to use it because I would like to add a delay between each turn, later on, I might move the entire logic into a coroutine. I would prefer to achieve my goal using if statements to know exactly when I reach a specific degree. – Abdou023 Apr 26 '17 at 10:19
  • I know and I mentioned all these in my answer. Your question **only** says that you want to move back and forth. Nothing else. Even your code had a back and forth script. It would be good to add more details in your question next time. Check my updated answer for the coroutine method. – Programmer Apr 26 '17 at 11:19
  • Did you see the modified answer? – Programmer Apr 26 '17 at 23:18
  • Yes, I took some principles from it and applied them to my code. It did not work at first as I was expecting. Thanks. – Abdou023 Apr 27 '17 at 02:59
1

First of all you should always ensure that the angle stays between 0 and 359, meaning that 0 == 360 :

float angle = transform.eulerAngles.y % 360.00f;

After that you can check if it's between your minimum and maximum value :

if ( angle >= 270.00f &&  angle <= 90.00f )
{
    // do your logic here ...
}

Another issue with your code is that it will stuck somewhere between 350 and 10 degrees. To explain this in more details let's assume your speed is 10, Time.deltaTime will be 1 as well and starting angle is 300, now frame steps :

 Frame | Angle | Condition 
 ----- | ----- | :-----------:
    1  | 300   | angle >= 270  
    2  | 310   | angle >= 270  
    3  | 320   | angle >= 270  
    6  | 350   | angle >= 270  
    7  | 360   | angle >= 270  
    8  |  10   | angle <= 90   
    9  |   0   | angle <= 90   
   10  | 350   | angle >= 270  

... and this will go forever.

To deal with this you have to make some condition based on user input or if you want your camera to "bounce" between these two angle then you can try something like this :

// make private field in that object :
float currentlyRotated = 0;
bool shouldAdd = true;

void Update()
{
    var d = Vector3.up * (shouldAdd ? speed : -speed) * Time.deltaTime;
    var angle = transform.eulerAngles.y + (shouldAdd ? speed : -speed);
    angle %= 360.00f;

    transform.Rotate(d);

    if ( angle > 90 && angle < 270 )
    {
        shouldAdd = !shouldAdd;
    }
}
mrogal.ski
  • 5,828
  • 1
  • 21
  • 30