So I’m new to coroutines and I’m really hoping I’m just missing something simple but I can’t figure this problem out. I have an object I want to rotate but I don’t want it to rotate instantaneously, I want it to rotate over a certain amount of time. In another post I found a bit of code that is supposed to accomplish what I’m looking for through a coroutine but it doesn’t seem to be working. The object does not rotate at all. If I use the normal transform.rotate the object will rotate but does so instantly which is not what I want.
GameObject objectToRotate;
public float speed = 1f;
public Vector3 rotationAmount = new Vector3(0, 0, 180);
[SerializeField] private List<GameObject> platformsMoved;
private void Start()
{
objectToRotate = platformsMoved[0];
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Bullet"))
{
clickSource.PlayOneShot(switchClick);
StartCoroutine(MovePlatforms());
}
}
IEnumerator MovePlatforms()
{
float endTime = Time.time + speed;
float step = 1f/speed;
var fromAngle = objectToRotate.transform.eulerAngles;
var targetRotation = objectToRotate.transform.eulerAngles + rotationAmount;
float t = 0;
while(Time.time <= endTime)
{
t += step * Time.deltaTime;
objectToRotate.transform.eulerAngles = Vector3.Lerp(fromAngle, targetRotation, t);
yield return 0;
}
}
1 Answer
1
Well, first of all your coroutine is over complicated. While using eulerAngles is not recommended, this should generally work. Your coroutine could be just:
static IEnumerator RotatePlatform(Transform objectToRotate, Vector3 relativeRotation, float duration)
{
var fromAngle = objectToRotate.eulerAngles;
var toAngle = objectToRotate.eulerAngles + relativeRotation;
Debug.Log("Rotate platform from " + fromAngle + " to " + toAngle, objectToRotate);
for(float t = 0f; t < 1f; t += Time.deltaTime / duration)
{
objectToRotate.eulerAngles = Vector3.Lerp(fromAngle, toAngle, t);
yield return null;
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Bullet"))
{
clickSource.PlayOneShot(switchClick);
StartCoroutine(RotatePlatform(objectToRotate.transform, rotationAmount, speed));
}
}
There are many things we don’t know about your setup. Though keep in mind that StartCoroutine starts a coroutine on the monobehaviour here you run StartCoroutine on. So the coroutine is bound to that object. If that object is destroyed of deactivated your coroutine will be stopped / terminated.
Note that I added a Debug.Log statement for debugging to the coroutine. See if you can see this log in the console. If not, your trigger does not Fire at all.
Note I’ve refactored a few things. Your “speed” actually represents a “duration” which is pretty much the opposite of what a speed represents. A higher value means it takes longer. You should never yield the value “0”. This will produce garbage as an integer value returned as object need to be boxed. When you want to wait for one frame, just yield null. I also renamed the coroutine since it doesn’t move platforms but it rotates a single one ^^. Using class member fields to pass information to methods is bad design. By passing the object to rotate, the rotation amount and the duration as parameters the method is self-contained (so I made it static). This makes it way easier to reason about the code.
Finally keep in mind when this code is supposed to rotate a platform, this platform needs to have a kinematic rigidbody if it should interact with other rigidbodies.
ps: The debug log call has a second parameter which represents a context object. I passed the objectToRotate as context. When you click on the debug message in the console, Unity will ping / highlight that object in your project / hierarchy. Maybe you’re trying to rotate the wrong object?