Godot's built-in Tween
doesn't meet my requirements:
- It transitions a property's value given a required duration. The tween I want is constant; i.e., it increases with a constant speed. The linear transition doesn't fulfill my requirement.
- It transitions from a rotation angle to another not always going the shortest route: either counterclockwise or clockwise, depending on how far the angles are.
So I implemented a simple tweener for spinning a Node2D
with a constant increment speed:
namespace Recoyx.CarKnockOnline.Util;
using Godot;
public class ConstantRotationTween
{
public float IncrementDegrees;
private Node2D _tweenNode = null;
private bool _running = false;
private float _incrementScale = 1;
private float _targetDegrees = 0;
public ConstantRotationTween(float incrementDegrees)
{
this.IncrementDegrees = incrementDegrees;
}
public bool IsRunning() => _running;
public void Tween(Node2D tweenNode, float targetDegrees)
{
this._tweenNode = tweenNode;
float a = 0;
// keep target rotation between 0-360
a = targetDegrees;
a = a < 0 ? 360 - (-a % 360) : a;
this._targetDegrees = Mathf.Round(a) % 360;
// keep node rotation between 0-360
this._lockTweenNodeRotation();
a = this._targetDegrees;
float b = this._tweenNode.RotationDegrees;
float ab = a - b, ba = b - a;
ab = ab < 0 ? ab + 360 : ab;
ba = ba < 0 ? ba + 360 : ba;
bool goClockwise = ab < ba;
this._incrementScale = goClockwise ? 1 : -1;
this._running = true;
}
public void Stop()
{
this._running = false;
this._tweenNode = null;
}
public void Process(double delta)
{
if (!this._running)
{
return;
}
this._lockTweenNodeRotation();
if (this._tweenNode.RotationDegrees == this._targetDegrees)
{
this._running = false;
return;
}
this._tweenNode.RotationDegrees += this.IncrementDegrees * this._incrementScale * ((float) delta);
}
/// <summary>Keep node rotation between 0-360.</summary>
private void _lockTweenNodeRotation()
{
float a = this._tweenNode.RotationDegrees;
a = a < 0 ? 360 - (-a % 360) : a;
this._tweenNode.RotationDegrees = Mathf.Round(a) % 360;
}
}
That's how you use it:
var tween = new ConstantRotationTween(150); // rotation speed = 150
tween.Tween(someNode, finalRotationDegrees); // starts a tween
tween.Stop(); // cancels the tween
tween.IsRunning();
// must be called in _Process
tween.Process(delta);
It almost works, just 2 issues:
- It may keep infinitely spinning the node or spinning it more than necessary to reach the final (target) rotation degree. This is due to the increment that is not equal 1.
- It has frame rate inaccuracy even when multiplying the increment by delta. I guess this is because I'm rounding the node's angle to an integer in
_lockTweenNodeRotation
.
It's almost two questions, but they're really simple and interconnected. So what can I do to solve this frame rate issue and the unnecessary spins?
Or better, is there something in Godot for achieving the same with fewer lines of code?
I improved the above tweener as you can see in my answer, but the frame rate issue remains. Very little slight abrupt jumps during the object spin.