Initial observations
First of all, according to Roblox documentation CFrame
is a "coordinate frame" which "describes a 3D position and orientation". That is a Transform
in Godot 3 or a Transform3D
in Godot 4. Note that Basis
does not have position.
Second, I can't find what the function r
does.
Third, I believe WalkingOffset
was a CFrame
because the code interpolates it with CFrame
s. And thus it should be a Transform
in Godot 3 or Transform3D
in Godot 4.
And of course, please stop the cargo cult.
Translating
The method CFrame.Angles
is documented to create a CFrame
from Euler angles in radians, with the rotations applied in Z, Y, X order.
Thus, the arguments here are Euler angles in radians:
CFrame.Angles(
.045*abs(sin(tick*totalspeed*.6)),
-.02*sin(tick*totalspeed*.6),
clamp(r(pos.X*15),-.08,.08)
)
Now, I don't know what is the axis orientation and handiness in Roblox, and I don't know if it matters. I also don't know if you are using Godot 3 or Godot 4, so I'll start with a flexible solution for this:
Godot 3 or Godot 4
Basis.IDENTITY.rotated(
Vector3.FORWARD,
.045*abs(sin(tick*totalspeed*.6))
).rotated(
Vector3.UP,
-.02*sin(tick*totalspeed*.6)
).rotated(
Vector3.RIGHT,
clamp(r(pos.X*15),-.08,.08)
)
Note: I don't know what r
is, I'm leaving it as is.
There you can change the order or whatever you need to make it match what you want. The point is not changing the order of the angles, but the order in which their rotations are applied.
You, of course, would make it part of a Transform
in Godot 3 or a Transform3D
in Godot 4. For example:
Godot 3
Transform(
Basis(...),
Vector3.ZERO
)
Godot 4
Transform3D(
Basis(...),
Vector3.ZERO
)
Where:
Basis(...)
is the basis constructed as shown above... Or as shown below, because I'll give you some alternatives.
Vector3.ZERO
is the position. I'll get to that.
These are version specific alternatives:
Godot 3 (with rotations applied in Y, X, Z order)
Transform(
Basis.IDENTITY.rotated(
Vector3(
.045*abs(sin(tick*totalspeed*.6)),
-.02*sin(tick*totalspeed*.6),
clamp(r(pos.X*15),-.08,.08)
)
),
Vector3.ZERO
)
Godot 4 (with rotations applied in Y, X, Z order)
Transform3D(
Basis.from_euler(
Vector3(
.045*abs(sin(tick*totalspeed*.6)),
-.02*sin(tick*totalspeed*.6),
clamp(r(pos.X*15),-.08,.08)
)
),
Vector3.ZERO
)
Furthermore, Godot 4 lets you specify the order:
Godot 4 (with rotations applied in Z, Y, X order)
Transform3D(
Basis.from_euler(
Vector3(
.045*abs(sin(tick*totalspeed*.6)),
-.02*sin(tick*totalspeed*.6),
clamp(r(pos.X*15),-.08,.08)
),
EULER_ORDER_ZYX
),
Vector3.ZERO
)
No, it is not the same as changing the order of the arguments. Changing the order of the arguments would change to which axis each angle is applied, not in which order they are applied.
Next, you multiply the first transformation by another one that looks like this:
*CFrame.new(
035*abs(sin(tick*totalspeed*.55)),
.01*sin(tick*totalspeed*.6),
0
)
That is a Transform
with only a position. The multiplication of transformations composes them. So we could just write the position directly in that Vector3.ZERO
we had in Godot:
Godot 3
Transform(
Basis (...),
Vector3(
035*abs(sin(tick*totalspeed*.55)),
.01*sin(tick*totalspeed*.6),
0
)
)
Godot 4
Transform3D(
Basis (...),
Vector3(
035*abs(sin(tick*totalspeed*.55)),
.01*sin(tick*totalspeed*.6),
0
)
)
And now you want to lerp a transformation. Godot does not have a method to do that directly. The only reason it is common is because people keep copying this kind of code. I know I'm enabling you to copy, so I hope at least you get some understanding from this.
Instead, we will decompose the transform it into rotation and position (which is all we have in this case).
- Rotation: We created a
Basis
from Euler angles. But that does not interpolate well, instead we are going to start by getting a quaternion from it.
- Position: We have a position vector. We can interpolate directly.
So, we are going to do this:
Godot 3 or Godot 4
var a_basis := WalkingOffset.basis
var a_origin := WalkingOffset.origin
var b_basis := Basis(...)
var b_origin := Vector3(...)
Where Basis(...)
is the Basis
created as before, and Vector3(...)
is the position vector as before.
Then we can get quaternions from the Basis
:
Godot 3
var a_quat := a_basis.get_rotation_quat().normalized()
var b_quat := b_basis.get_rotation_quat().normalized()
Godot 4
var a_quat := a_basis.get_rotation_quaternion().normalized()
var b_quat := b_basis.get_rotation_quaternion().normalized()
Then we can interpolate:
Godot 3 or 4
var new_quat := a_quat.slerp(b_quat, 0.3)
var new_basis := Basis(new_quat)
var new_origin := a_origin.lerp(b_origin, 0.3)
And with that we build the interpolated transform:
Godot 3
var new_transform := Transform(new_basis, new_origin)
Godot 4
WalkingOffset = Transform3D(new_basis, new_origin)
On the other branch of the code we find CFrame.new()
which should be an identity transform, which is interpolated. So you can directly use Quat.IDENTITY
(Godot 3) or Quaternion.IDENTITY
(Godot 4) and Vector3.ZERO
.
By the way, are you sure you want to apply the transformation to the position? If you only change the position of the weapon it can only possibly move the weapon. Not rotate it. If that is what you want, I believe we could have made something much simpler, avoiding all the rotation bushiness.
Other notes
I want to point out that you could create a quaternion directly instead of dealing with all the business of Euler angles to Basis to quaternion. I do not know what is the intended rotation, but I assure it can be expressed as axis-angle:
var b_axis := Vector3(0.0, 0.0, 1.0)
var b_angle := 0.0
From which you can build a quaternion:
Godot 3
var b_quat := Quat(b_axis, b_angle)
Godot 4
var b_quat := Quaternion(b_axis, b_angle)
I don't know if the axis should change with time (tick
), but the angle should. Something like this: wrapf(tick*my_speed*some_other_factor, -PI, PI)
.
You could experiment with that and try to find something that gives you a desirable result.
I also want to point out that you could compute the quaternion between the ones you have:
Godot 3 or Godot 4
var diff_quat := a_quat.inverse() * b_quat
convert that to axis angle:
Godot 3 or Godot 4
# make sure the rotation angle is the short way around
if diff_quat.w < 0:
diff_quat = -diff_quat
diff_quat = diff_quat.normalized()
var diff_axis := Vector3(diff_quat.x, diff_quat.y, diff_quat.z).normalized()
var diff_angle := 2 * acos(diff_quat.w)
And then scale the angle. Which enables another way to interpolate quaternions:
Godot 3
var new_quat := a_quat * Quat(diff_axis, diff_angle * 0.3)
Godot 4
var new_quat := a_quat * Quatternion(diff_axis, diff_angle * 0.3)
Here 0.3
is the interpolation weight.
Just in case, know that this is what lerp does:
func lerp(a, b, t:float):
return (1 - t) * a + t * b;
When t
is zero, it should return a
, when t
is one it should return b
. But your t
is always 0.3
. So as far as the math is concerned, it will never get to b
(in practice it will, because eventually the difference is too small to be represented in floating point numbers).
Also, since it is 0.3
every frame, and the time between frame can vary, this is frame rate dependent.