Stuttering physics2D framerate fixed by changing Fixed Timestep, but is there a better way?

(I think I fixed my issues on my own but if somebody could chime in with advice, I’d appreciate it.)

I had an issue with a choppy physics2D framerate (running via FixedUpdate) on a specific rigidbody2D (affecting only the rotation), and I was able to fix it by increase the rate of Fixed Timestep from (iirc) 0.02 to 0.005. It seems increasing the fixed timestep interval also increases the “power” of physics because the timestep is running at a greater/more frequent interval? I’m working on an “angry birds”-style game, and my sprintjoint2D that seems to have increased “throwing power” at a higher interval so I was curious. (Obviously I can compensate by changing the sprjoint2D settings, lowering the tension so the object doesn’t fly so far.)

I can only assume the rigidbody2D was choppy/stuttering because of the code I’m using (again, affecting only the character’s rotation). I’m treating the object like an arrow flying through air, and it wasn’t stuttering until I put in code to make the object turn in the direction it was flying.

Running the code via FixedUpdate it stutters; running the code via Update it seems to be smooth but sometimes the calculated rotation amount is set to zero which causes the object to rotate out of whack several times a second. (degrees: 125, 126, 127, 0, 128 …) (I can avoid the “0” amount but that just goes back to the FixedUpdate effect of being choppy because it’s skipping frames)

Should I just stick with the increased Fixed Timestep since that seems to work (though affects the physics “power”), or does anyone here have different suggestions?

//for physics calculations
void FixedUpdate()
{
    //constantly apply the right rotation to jonah
    if (ArrowController.canClick == false && !hitGround)
    {
        //change jonah direction/rotation
        RotationChangeWhileFlying();
    }
}

//change the angular velocity depending on jonah's speed in air
void RotationChangeWhileFlying()
{
    //get current position
    currPoint = transform.position;

    //get the direction (from previous pos to current pos)
    currDir = prevPoint - currPoint;

    //normalize the direction
    currDir.Normalize();
       
    //get angle whose tan = y/x, and convert from rads to degrees
    float rotationZ = Mathf.Atan2(currDir.y, currDir.x) * Mathf.Rad2Deg;

    //rotate z based on angle above + an offset (currently 90)
    transform.rotation = Quaternion.Euler(0, 0, rotationZ + rotateOffset);

    //store curr position as prev position for next frame
    prevPoint = currPoint;
}

Here are some reference gifs (Imgur converted them automatically to MP4s), though it may be hard to tell normal vs stuttering because the recording software affected the recorded framerate.

Working version, running in FixedUpdate, increased Fixed Timestep from 0.02 to 0.005:

Stuttering framerate, running in FixedUpdate, Fixed Timestep is normal at 0.02:
__Imgur: The magic of the Internet
Framerate seems kind of smooth but rotation goes “wacky”/sometimes goes back to 0, running in Update, [u]Fixed Timestep is normal at 0.02:[/u]
__[u]Imgur: The magic of the Internet

I suspect this has something to do with modifying the transform component directly in your RotationChangeWhileFlying method. Try replacing

currPoint = transform.position;
//with
currPoint = rigidBody2D.position;

and

transform.rotation = Quaternion.Euler(0, 0, rotationZ + rotateOffset);
//with
rigidBody2D.MoveRotation(Quaternion.Euler(0, 0, rotationZ + rotateOffset));
1 Like

Thanks, I think that was really useful! However I did have to use (and they both seem to avoid choppy movement I was having)

rb.rotation = rotationZ + rotateOffset;
//instead of
rb.MoveRotation(rotationZ + rotateOffset);

I think I can stick with rb.rotation fine (I think it works as smooth as MoveRotation), but if you have time, consider helping me understand the following and below: MoveRotation doesn’t seem to give control back to the physics engine right away when a collision happens. rb.rotation seems to allow for a lot more rotation changes right away, whereas (it seems) MoveRotation sort of sticks a rotation upon an initial collision. The difference is hard to explain and subtle.

I do have some gifs, but they may not provide much help: MoveRotation , rb.rotation
Maybe this image helps:

Like I said above I think rb.rotation will work fine, but I’m curious: if you sort of understand what I’m saying, is there a way to give back control to the “natural” physics engine instantaneously once a collision happens, when there’s no need for MoveRotation anymore? (the same effect as just using rb.rotation) Like,

//if there is a collision
if (hitGround)
{
//let player "bounce around" and rotate accordingly/let engine handle rotation again
}

Thanks again for the help.

There’s no reason you can’t set the rotation property directly, especially if it’s producing the desired behavior. MoveRotation allows the physics engine to smooth the rotation whereas setting the rotation property directly… well sets the rotation directly but the transform won’t be updated until the next update. I’m not sure why the behavior is different as subtle as it is. As for turning control over to the physics engine, you basically just need to stop setting the rotation directly when you’ve collided. Set a hasCollided bool to false in your OnCollisionEnter2D method and include it in your check before calling RotationChangeWhileFlying.

1 Like

I was already doing this with a “hitGround” bool in OnCollisionEnter2D, bypassing RotationChangeWhileFlying() when hitGround = true, but I don’t think OnCollisionEnter2D was updating quick enough or something (or maybe because OCE2D is called after Rotation, I may test that). I guess the Order of Execution for OCE2D plays a role here but I don’t 100% understand that.

In any case, if I call a method from Update and that method creates a ray that manages “hitGround”, MoveRotation seems to work the same as, or close to, rb.rotation, instantaneously turning physics control back to the engine. (Right now I just have one ray but I figure I can put 3 on each side.)

I may do that because the “safe side” seems to be managing rotation indirectly, and MoveRotation may be slightly smoother than rb.rotation. (update: after doing some testing I may stick with rb.rotation because MoveRotation still seems finicky even with a raycasting check managing it. We’ll see.) Thanks.