Rotate Tank Turret slowly

I am attempted to get a tank turret to rotate to where the mouse is pointing but at a set speed. Getting the turret to point to the mouse is the easy part, but I’m not sure how I would go along getting it to rotate at a set speed (I don’t want it to turn instantly towards the mouse but slowly rotate towards it)

I was thinking I might have to use Slerp/Lerp however at this point I’m not even sure about that anymore as Lerp uses a % rotation between steps rather than distance between steps (Lerp(a,b, 0.5 ) finds a value halfway between a & b meaning larger differences result in larger steps)

edit:
I’m not sure what my setup will be just yet but for now I just want to figure this out as if the turret was anchored to the ground and I was using CFrames to move it.

edit 2: This is what I poorly tried to explain:

1 Like

What is your setup? Is the turrent part anchored? Are you using BodyMovers? Are you using hinges/Motor6Ds?

edit: Is it just rotating on one axis? Is it a full 3D transform?

Use Motor6Ds.

It’s going to rotate 3D transformation. I plan to extend this to controlling planes however I figured I’d go a bit simple first and do tank turrets as that’s one less axis I need to worry about.

here is a great video in unity of what I’m trying to achieve.

While I’ll try to transform as much of the code over to lua as I can. Quaternians are a new topic for me and so I doubt I’ll get that bit right, or at least not efficient.

Hey tycolt3!

Would something like this help?

function rotateTankTurret(turretPart, degreesToRotate)

    local TweenService = game:GetService("TweenService")

    local tweenInfo = TweenInfo.new(
    	10, -- Increase to make it slower
    	Enum.EasingStyle.Linear, -- EasingStyle
    	Enum.EasingDirection.In, -- EasingDirection
    	0, -- RepeatCount (when less than zero the tween will loop indefinitely)
    	false, -- Reverses (tween will reverse once reaching it's goal)
    	0 -- DelayTime
    )

    local tween = TweenService:Create(turretPart, tweenInfo, {CFrame = turretPart.CFrame * CFrame.Angles(0,math.rad(degreesToRotate),0)})

    tween:Play()

end

One thing I’m not sure about is if it always runs at the same speed or if it is a different speed based on distance moved.

Of course, this doesn’t solve the problem of telling it where to move to :wink: But perhaps you could do that using the technique shown here (where he gets the head to look at the bar) and then send the value into the rotateTankTurret as a CFrame or Vector3 (rather than degrees):

IDK if a tween would be best as the endpoint would be changing rapidly, and yes that also rotates faster or slower depending on the distance as the first parameter is time (the 10 value) meaning I control time not speed. If I could figure out how to measure the distance between the two points (I’m thinking using the dot product of the current orientation and the target orientation?) I could plug that value into the Speed formula (Speed = Distance/Time) to solve for the correct time.

Essentially, Tween service lets us set time as a constant. However, in this case, we want Speed as a constant (or at least give it a max value). So if we set Speed to a constant and we can figure out what distance is, then we can rearange the formula to (Time = Distance/Speed)

I guess it’s 2 problems:

  1. Work out what the new angle should be set to
  2. Put the new angle into an animation queue

For #1 I think you can do what Alvinblox does in the video I posted above and for #2 maybe you can just use a simple for while loop changing the CFrame from the current angle to the target angle?

Hope I’m not confusing the issue!

Ya thats what I plan to do, I’m just trying to figure out the Quaternion manipulation stuff. I don’t think there is an API just for quaternions so I’m struggling with that bit.

(C# Code for reference)

Quaternion rotTarget = Quaternion.LookRotation(FollowObject.position - this.transform.position);

this.transform.rotation = Quaternion.RotateTowards(this.transform.rotation, rotTarget, Speed * Time.deltaTime);

Ooo, that looks very cool. Can’t wait to hear your final solution!

Part way through trying to figure out quaternions I realized I was making this a lot harder than it needed to be, CFrame.Lerp already does slerp (despite the name) so I just needed to figure out what the distance between the two angles were so I could fix the step.

This is what I got in the end:

while true do wait()
	A = Turret.CFrame
	B = Mouse.Origin
	Step = math.min(0.1,math.acos(A.LookVector:Dot(B.LookVector)))
	Turret.CFrame = A:Lerp(B-B.p+A.p,Step)
end

It’s not done yet however this is a big step in the right direction. I limit the max step it can take to 0.1

math.acos(A.LookVector:Dot(B.LookVector))
gives me the angle between the two Vectors (I think this is the angle found on a shared plane?) I still need to tweak it a bit but it’s a good start

1 Like

So far it works fairly well, it just doesn’t want to work when I move my mouse. I think it has to do with the resetting StartTime but it’s a bit hard for me to explain why.

local EstTime,StartTime = 0,os.clock() --EstimatedTime
local Speed = math.rad(270) -- Higher this number the slower it turns

Mouse.Move:Connect(function()
    Update()
    StartTime = os.clock()
    OldPos = Turret.CFrame
    NewPos = Mouse.Origin - Mouse.Origin.p + OldPos.p
    local Dis = math.acos(OldPos.LookVector:Dot(NewPos.LookVector))
    EstTime = Speed/Dis
end)

function Update()
    local NewFrame = OldPos:Lerp(NewPos,math.min((os.clock() - StartTime)/EstTime,1))
    Turret.CFrame = NewFrame
end

while true do wait()
    Update()
end

Outside of that it works fairly well

Edit: Found a quick fix for it. If I call Update() right after I debounce the mouse it will get a step in. It works well enough now

4 Likes