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:
Quaterion
instance, storing the current rotation of my mob
instance.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.
Quaterion
instance.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).
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):
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.
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.