Models smooth rotation

Greetings, fellow developers.
So, recently i’ve been working on a tower defense style game with the help of some friends, but we’ve ran into a problem while making the enemies movement:
All of the system works perfectly, except that for some reason, we cannot get the enemies to rotate smoothly after each waypoint. We’ve tried using CFrame.lookAt, BodyGyro, TweenService and others, but nothing seems to be working.

Here’s the Server side code:

local mob = {}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Events = ReplicatedStorage:WaitForChild("Events")
local LastUpdate = tick()
local UpdateDT = 0.12
local ClientInfo = {}
local Positions = {}

game:GetService("RunService").Heartbeat:Connect(function(DeltaTime)
	local map = workspace:WaitForChild("Map") 

	for number, data in pairs(Positions) do 

		local CurrentWaypoint = map.Waypoints:FindFirstChild(data.Waypoint)
		local OldWaypoint = map.Waypoints:FindFirstChild(data.Waypoint - 1) or map.Start

		if not CurrentWaypoint then 
			map.Base.Humanoid:TakeDamage(data.SpeedHealth.Y)
			ClientInfo[number] = {Dead = true}
			Events.UpdatePosition:FireAllClients(ClientInfo)
			Positions[number] = nil
			continue
		end

		local distance = (OldWaypoint.Position - CurrentWaypoint.Position).Magnitude

		data.Moved += DeltaTime * data.SpeedHealth.X / distance
		if data.Moved >= 1 then
			data.Waypoint += 1
			data.Moved = 0
		end

		ClientInfo[number] = Vector2int16.new(math.floor(CurrentWaypoint.Position.X*50), math.floor(CurrentWaypoint.Position.Z*50))
	end

	if tick() - LastUpdate >= UpdateDT then
		LastUpdate = tick()
		Events.UpdatePosition:FireAllClients(ClientInfo)
	end
end)


local num = 0 
shared["currentMobs"] = 0
function mob.Spawn(name, quantity, map)

	local Exists = ReplicatedStorage.Mobs:FindFirstChild(name)

	if Exists then 
		for i = 1, quantity do 
			num += 1
			if shared["currentMobs"] < 0 then
				shared["currentMobs"] = 0
			end
			shared["currentMobs"] += 1
			Positions[num] = {
				Waypoint = 1,
				SpeedHealth = Vector2int16.new(math.round(Exists.Config.Speed.Value), math.round(Exists.Config.Health.Value)),
				Moved = 0
			}
			Events.Render:FireAllClients(name, num)
			
			task.wait(0.25)	
		end
	end

end

return mob

And here’s the Client side code:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Events = ReplicatedStorage:WaitForChild("Events")

local LastUpdate = tick()
local ud = 0.01

local Enemies = {}
local positions = {}

local map = workspace:WaitForChild("Map")
local base = map:WaitForChild("Base")

local PS = game:GetService("PhysicsService")

local function animation(object, anim)
	Events.AnimateZombie:Fire(object, anim)
end

Events.UpdatePosition.OnClientEvent:Connect(function(info)
	positions = info
end)

game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
	if tick() - LastUpdate >= ud then 
		LastUpdate = tick()
		for Enemyobject, data in pairs(Enemies) do 
			if Enemyobject and positions[data] then 					

				local pos = positions[data]			

				if not pos.X or not pos.Y then 
					Enemies[Enemyobject] = nil
					Enemyobject:Destroy()
					continue
				end

				local Position = Vector3.new(pos.X/50, Enemyobject.PrimaryPart.Position.Y, pos.Y/50)
				local Distance = (Enemyobject.PrimaryPart.Position - Position).Magnitude

				local speed = Enemyobject.Config.Speed.Value

				local cframe = CFrame.new(pos.X/50, Enemyobject.PrimaryPart.Position.Y, pos.Y/50)

				Enemyobject.PrimaryPart.CFrame = CFrame.lookAt(Enemyobject.PrimaryPart.Position, Position)
				Enemyobject:PivotTo(Enemyobject.PrimaryPart.CFrame:Lerp(cframe, deltaTime*speed/Distance))		

			end
		end
	end	
end)

Events.Render.OnClientEvent:Connect(function(name, EnemyNumber)

	local map = workspace:WaitForChild("Map")
	local start = map:WaitForChild("Start")

	local enemy = ReplicatedStorage.Mobs:FindFirstChild(name):Clone()
	enemy.Parent = workspace.Mobs
	if not enemy.PrimaryPart then
		enemy.PrimaryPart = enemy.HumanoidRootPart
	end
	enemy.PrimaryPart.Anchored = true
	enemy.PrimaryPart.CanCollide = false

	for i, v in pairs(enemy:GetChildren()) do 
		if v:IsA("BasePart") then 
			PS:SetPartCollisionGroup(v, "Mob")
		end
	end

	

	enemy.PrimaryPart.CFrame = start.CFrame + Vector3.new(0, (enemy.PrimaryPart.Size.Y/2 - 1), 0)

	Enemies[enemy] = EnemyNumber 
	animation(enemy, "Walk")
end)

Does anyone know any possible solution? Thanks for reading!

2 Likes

I would create a primary part and weld all the parts in the model to that part. Use tweenservice to tween the primary part and the rest of the model would follow.

1 Like

Set the network ownership of an enemy to the client and see if it looks smooth. If it does, this means it’s a network communication problem. Best thing to do in this specific case is to animate the rotation. Costs less than tweening and CFraming, will not be ‘laggy’ like physics controlled from server replicated to the client.

1 Like