I’m working on the enemies system for my game and there’s one problem which completely destroyed me. Basically what I want to do is I want to make smooth rotation of enemy when he rotates by 180 degress and goes backwards, and to make this I want to make smooth enemy rotation based on DeltaTime value.

local function SetEnemyCFrame(EnemyObject, DeltaTime, NewCFrame: CFrame)
--local RotSpeed = DeltaTime * EnemyObject.Speed
local X, Y, Z = EnemyObject.CFrame:ToObjectSpace(NewCFrame):ToOrientation()
-- X, Y, Z should be multiplied by RotSpeed, so they rotate smoothly
-- on all devices and this is based on enemy speed
print(math.deg(X), math.deg(Y), math.deg(Z))
EnemyObject.CFrame = CFrame.new(NewCFrame.Position) * CFrame.Angles(X, Y, Z)
end

I found this solution while searching a TON of topics but it still work incorrectly for me. Here’s what’s going on in the actual game: https://youtu.be/ltsb3SOi-qc

CFrame of new enemy position is calculated perfectly (with rotation) and there are no problems with it, but the problem is that rotating difference between current enemy CFrame and new enemy CFrame which I am trying to calculate turns out to be incorrect every 2nd time it calculates. So I cannot make smooth rotation because I cannot calculate difference between 2 CFrames rotations to then multiply it by RotSpeed value.

And yes, I looked through 10 pages on Google, trillion devforum topics, tried to open websites with advanced mathematics, spent a whole day solving this problem and still didn’t understand what is going wrong with the calculation. So I would appreciate any help!

I would try using the dot product between the two CFrame’s LookVectors. Combining that with inverse cosine would give you the angle in radians.

-- replace with your own cframes being used
local angularDifference = math.acos(cframe1.LookVector:Dot(cframe2.LookVector))

Reference if needed: Dot product - extreme oversimplification: gives a number that indicates how similar two vectors are Sine/cosine - trigonometry functions, using their inverses can help find angles

Also the turret controller linked in the above post is probably helpful for this situation, I personally have used it for a different purpose before.

I’m really sorry for late response. Thank you for your help, that’s exactly what I was needed and you saved a ton of my time, but I’ve got 1 question.

This is a code snippet of your TurretController module:

local adjustedLerpAlpha
if step and self.ConstantSpeed then
local angularDistance = VectorUtil.AngleBetween(currentRotation.LookVector,goalRotationCFrame.LookVector)
local estimatedTime = self.AngularSpeed/angularDistance
adjustedLerpAlpha = math.min(step*estimatedTime,1)
elseif step then
adjustedLerpAlpha = step
end
local newRotationCF = currentRotation:lerp(goalRotationCFrame, adjustedLerpAlpha or self.LerpAlpha)
self.JointMotor6D.C0 = CFrame.new(originalC0Position)*newRotationCF

You find estimatedTime by dividing self.AngularSpeed by angularDistance but usually time is distance divided by speed, so why is it necessary to do the opposite in this case? I tried to divide angularDistance by self.AngularSpeed but it didn’t seem like a very good idea.

The answer is my variable naming is probably incorrect or doesnt make sense.

The formula can be rearranged like this.

(AngularSpeed / distance)*step

Step*(AngularSpeed / Distance )

It finds the distance traveled in a frame speed * time then divides it by the total travel distance to get lerp alpha as lerp alpha is a fraction of distance.

In the end the units will cancel out the above is distance / distanceToGoal, with my naming convention its time/timeToReachGoal

I didnt really understand this formula before got it off a devforum post.

So in this case self.AngularSpeed means how much was travelled like speed * time, then it finds alpha using distance (which is self.AngularSpeed) / whole distance (which is angularDistance) and then it calculates new alpha by multiplying it all by step (which is deltaTime)?

So final result is value between 0 and 1 which represents lerping time, right?

It’s a little confusing because of the variables names, but makes sense.

I didn’t know that you can lerp between 2 orientation CFrames and get such behaviour. It’s quite simple but really smart.