Search code examples
c#interpolationgame-developmentgodotlinear-interpolation

GODOT Interpolation: Interpolation is not behaving how I expect visually


I am programming in the Godot game engine, and I have recently been working with interpolation to smooth out the turning (rotation) of one of my mob instances. I am new to interpolation in general, so I have done a deep dive into it, and using it in godot specifically. I am using Slerp interpolation, but before I show the code, I would like to step through what I am doing.

LOGICAL INTERPOLATION STEPS:

  1. I create a Quaterion instance, storing the current rotation of my mob instance.
  2. I use Godot's built in LookAt(Vector3 target, Vector3 up) instance function (from the Spatial Class) to then rotate my mob instance to look at the "target final rotation".

NOTE: Using the LookAt(Vector3 target, Vector3 up) instance function alone would cause a snapping or jumping turn to the desired rotation.

  1. I store this "target final rotation" as a new Quaterion instance.
  2. I set my mob instance's Rotation instance variable to the result of using the Quat.Slerp(Quat to, float weight) instance function. The Slerp instance function returns the result of the spherical linear interpolation between the invoking Quaterion instance ("current rotation") and the to Quaterion instance ("target final rotation") by a specified amount using the weight argument.

NOTE: I also use a GetEuler() instance function, to turn the Quaterion instance returned from the Slerp instance function into a Vector3 instance (which the Rotation instance variable uses).

  1. This repeats in a loop, until the Slerp instance function returns a "rotation" (Quaterion instance) that is the same as the "target final rotation".

CODE BLOCK BELOW:

GD.Print("1: " + pivoteNode.Rotation);
Quat qRotation = new Quat(pivoteNode.Rotation);
pivoteNode.LookAt(Translation + targetVelocity, Vector3.Up);
GD.Print("2: " + pivoteNode.Rotation);
Quat qTargetRot = new Quat(pivoteNode.Rotation);
GD.Print("3: " + pivoteNode.Rotation);
pivoteNode.Rotation = qRotation.Slerp(qTargetRot, 0.2f).GetEuler();
GD.Print("4: " + pivoteNode.Rotation);

Now my question is, how come the resulting "turning" motion of the mob instance looks so smooth? From the innerworkings of the code, it seems that my mob instance should start at a "starting rotation", snap all the way to the "target final rotation", then go back to the new "slerp calculated rotation" (with a weight of 0.2 means it is only a little past the "starting rotation"). It does not seem to do this, instead it is a nice smooth transition from the "starting rotation" to the "target final rotation". Is this because it all happens so unbelieveably fast that my eyes cannot percieve it the snapping to the end "target final rotation" repeatedly until the "slerp calculated rotation" finally equals the "target final rotation"? Or is it something completely different? Because I would think that if it is running this "snapping" LookAt() instance function repeatedly, then it would look choppy.

I am attaching a screenshot below of the output I got from the code above, it shows how the rotation is snapping around (do note that this is a small snippit from the thousands of outputs it actually gives):

enter image description here

This is all done inside the _PhysicsProcess(float delta) portion of the code. If you have any question about the code, or want more info, let me know! Ill answer right away.


Solution

  • All of this happens in a single frame:

    GD.Print("1: " + pivoteNode.Rotation);
    Quat qRotation = new Quat(pivoteNode.Rotation);
    pivoteNode.LookAt(Translation + targetVelocity, Vector3.Up);
    GD.Print("2: " + pivoteNode.Rotation);
    Quat qTargetRot = new Quat(pivoteNode.Rotation);
    GD.Print("3: " + pivoteNode.Rotation);
    pivoteNode.Rotation = qRotation.Slerp(qTargetRot, 0.2f).GetEuler();
    GD.Print("4: " + pivoteNode.Rotation);
    

    You might think that line number 3 would cause the mob to snap immediately, but that is not the case, rendering is only updated once per frame, at the end of the frame: only the final value set will actually be read by the rendering engine, in your case, that would be line number 7.

    To sum it up: changing the value of pivoteNode.Rotation will not update the rendered graphic immediately. That will be done later, outside your code, at the end of the frame.