Flipping car animation with coroutine not functioning properly

I am creating a game in unity 3d that has to do with cars. If you double jump and you are holding the joystick in a certain direction then the car will flip that direction (360 degree flip). I am using a coroutine to run this asynchronously but for I am having a problem with smoothness.

Here is the code I use (the alterations of the positioning of the car is to make sure that the bumpers don't hit the ground during the flip):

public IEnumerator FlipCar(float xDir, float yDir)
{ // Going to flip 36 iterations at 10 degrees each so a full 360 is traveled
car.GetComponent<Rigidbody>().useGravity = false;
while (flipInc <= 36)
{
// rotate car toward xDir and yDir
if (yDir > .5f && (xDir > -.5f && xDir < .5f))
{
switch (flipInc)
{
case 1:
case 2:
case 3:
case 5:
case 6:
case 7:
case 8:
case 10:
case 20:
case 20:
car.transform.position = new Vector3(car.transform.position.x, car.transform.position.y + .25f, car.transform.position.z);
break;
case 4:
car.transform.position = new Vector3(car.transform.position.x, car.transform.position.y + .35f, car.transform.position.z);
break;
case 13:
case 29:
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
car.transform.position = new Vector3(car.transform.position.x, car.transform.position.y - .25f, car.transform.position.z);
break;
}
car.transform.Rotate(10, 0, 0);
}
else if (yDir < .5f && (xDir > -.5f && xDir < .5f))
car.transform.Rotate(-10, 0, 0);
else if (xDir > .5f && (yDir > -.5f && yDir < .5f))
car.transform.Rotate(0, 0, 10);
else if (xDir < .5f && (yDir > -.5f && yDir < .5f))
car.transform.Rotate(0, 0, -10);
// increment flipInc
flipInc++;

if (flipInc <= 36)
yield return new WaitForSeconds(0);
else
{
flipInc = 1;
car.GetComponent<Rigidbody>().useGravity = true;
yield break;
}
}
}


Basically my problem is that if I keep the yield return new WaitForSeconds(0); then the flipping is too slow and choppy...but if I remove it and just let the loop run until the flipInc is greater than 36, it runs too fast to even see. Is there a way to get this to run smoothly?

EDIT

Here is what I have changed it to but it doesn't function properly:

public IEnumerator FlipCar(float xDir, float yDir)
{
car.GetComponent<Rigidbody>().useGravity = false;
while (flipInc <= 360f)
{
//rotate car toward xDir and yDir
if (yDir > .5f && (xDir > -.5f && xDir < .5f))
{
currRot = 10 * Time.deltaTime;
flipInc += currRot;
car.transform.Rotate(currRot, 0, 0);
}
else if (yDir < .5f && (xDir > -.5f && xDir < .5f))
{
car.transform.Rotate(-10, 0, 0);
}
else if (xDir > .5f && (yDir > -.5f && yDir < .5f))
{
car.transform.Rotate(0, 0, 10);
}
else if (xDir < .5f && (yDir > -.5f && yDir < .5f))
{
car.transform.Rotate(0, 0, -10);
}

if (flipInc <= 360)
yield return new WaitForSeconds(0);
else
{
flipInc = 0;
car.GetComponent<Rigidbody>().useGravity = true;
yield break;
}
}
}

{
int rot = Mathf.RoundToInt(flipInc);
switch (rot)
{
case 10:
case 20:
case 30:
case 50:
case 60:
case 70:
case 80:
case 100:
case 200:
case 220:
if (lastRot != rot)
car.transform.position = new Vector3(car.transform.position.x, car.transform.position.y + .25f, car.transform.position.z);
break;
case 40:
if (lastRot != rot)
car.transform.position = new Vector3(car.transform.position.x, car.transform.position.y + .35f, car.transform.position.z);
break;
case 130:
case 290:
case 300:
case 310:
case 320:
case 330:
case 340:
case 350:
case 360:
if (lastRot != rot)
car.transform.position = new Vector3(car.transform.position.x, car.transform.position.y - .25f, car.transform.position.z);
break;
}
lastRot = rot;
}


Allen highlighted the issue but didn't come right out and say it.

If you get one "iteration" per frame, it should take 36 frames to spin 360 degrees.

At 60 FPS, that's 0.6 seconds (average deltaTime of 0.0166666).
At 120 FPS, it's only 0.3 seconds (average deltaTime of 0.00833333)

You are frame-rate dependent; you need to move X-degrees per second rather than X-degrees per frame because there are no guarantees regarding how often frames are rendered or how much time has elapsed since the last. One second is always one second though. That means replacing every occurence of 10 with 10 * Time.deltaTime. As Allen pointed out, it would be better to do rotationFactorPerSecond * Time.deltaTime to avoid forgetting to update one or more copies.

After modification, the car will rotate 10 degrees per second. At 60 FPS, the car will rotate, on average, 0.16666 degrees per frame. After one second, at 60 FPS, flipInc would want to be 60, well above your 36 cutoff. Flipping a full 360 degrees will take 36 seconds; flipInc would want to be approximately 2,160 (at 60 FPS average).

Allen has provided the solution for this as well. Basically, track the total amount of "flip" applied (add rotationFactorPerSecond * Time.deltaTime to a total each time through). When that total is 360.0, or greater, the car should finish the flip during this frame. At the same time, you can use that total angle to determine how much to push the car up and replace flipInc.

• Please check my edit. I have updated the post with new code but it is still not functioning properly. Is this code reflecting what you mean?
– sabo
Apr 16 '16 at 21:13
• @saboehnke, the purpose of flipInc appears to be to shift the car upward a bit as it flips because it is wider than it is tall. This was based on 10 degrees per flipInc. flipInc is now the actual angle, so you need to specify Y offsets for 90, 180, and 270. When flipInc is 90, the car should be on its' side, correct?
– Jon
Apr 16 '16 at 23:38
• No it should be on the front of the car. The current code should flip the car forward.
– sabo
Apr 17 '16 at 4:55
• The code in the edit flips it in the right direction. I am just trying to figure out if this is exactly what you were talking about because the car flips way too much. The y positioning code is just figuring where it needs to move the car so it doesn't touch the ground. For example if flip Inc converted to an int is equal to 1, I move the car up .25 so the front bumper doesn't touch the ground.
– sabo
Apr 17 '16 at 5:02
• You need to replace the switch and test flipInc as an angle from 0-360; do not round it or int it.
– Jon
Apr 17 '16 at 6:43

Your yield makes the coroutine execute once per frame. The problem is that the time between frames is variable, so your rotation is changing by a variable amount per second. Thankfully there is an easy solution! Multiple your change in rotation, the 10 in your example, by Time.deltaTime. (Also, you may need to increase the speed, because deltaTime is a small decimal unless your game has very low fps)

Here is a great video on the subject: https://unity3d.com/learn/tutorials/modules/beginner/scripting/delta-time

Here is a simple code demonstration. This rotates 360 degrees smoothly around the forward axis. You can easily modify it to be whatever axis you want.

public float rotationFactorPerSecond = 30;
public GameObject car;

public IEnumerator Flip360()
{
float flipAmount = 0;

car.GetComponent<Rigidbody>().useGravity = false;

while (flipAmount < 360)
{
car.transform.Rotate(transform.forward * rotationFactorPerSecond * Time.deltaTime);
flipAmount += rotationFactorPerSecond * Time.deltaTime;
yield return null;
}

car.GetComponent<Rigidbody>().useGravity = true;
}

• The .25 is just for the position of the car. It has nothing to do with the rotation itself. So you are saying if I multiply the actual rotation amount that I have (10 for every increment) by time.deltaTime then that would solve the issue?
– sabo
Apr 14 '16 at 2:29
• My apologies, I don't know how I mixed that up. Multiplying your rotation speed by Time.deltaTime will make it smooth. You may need to increase the speed to make it look how you want. That is in conjunction with the yield. Apr 14 '16 at 2:59
• I went ahead and changed it so that it multiplies the rotation amount by Time.deltaTime but since Time.deltaTime is so small, the car doesn't move at every flipInc increment. By increasing the "speed" do you mean the rotation? So I would just need to rotate by a bigger number? Because I have it set up so that it rotates 36 times at 10 degrees each so it does a full 360.
– sabo
Apr 14 '16 at 15:47
• Yes. I would make the 10 a variable and set it higher. Also, change flipInc to a float, and instead of flipInc++ do "flipInc += speed * Time.deltaTime". When the car has rotated 360, flipInc will also be equal to 360. (If you start with flipInc at 0 that is) Apr 14 '16 at 17:50
• Alright I'll give something like that a shot. I'll just have to figure out how to know when to reposition the car's y coordinate so it doesn't touch the ground while it is rotating.
– sabo
Apr 14 '16 at 20:58