Tower defence enemies lagging behind

I’m going to add an attribute called CFrame on the model which represents the actual CFrame so your other scripts can reference it.

Server code:

--!native
--!strict
local module = {}
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local BezierPath = require(ReplicatedStorage.Packages.BezierPath)
local paths = {}

local Enemies = ReplicatedStorage.Enemies
local refresh_rate = 0.5

module.createnavpath = function(nodefolder, index)
	local nodePositions = {nodefolder.enemyspawn.Position}

	for i = 1, #nodefolder:GetChildren() - 2 do
		table.insert(nodePositions, nodefolder[i].Position)
	end

	table.insert(nodePositions, nodefolder.exit.Position)
	paths[index] = BezierPath.new(nodePositions, 3)
end

module.spawn = function(name:string, nodefolderindex:number, exit, spawns:CFrame, boss:BoolValue)
	local newPath = paths[nodefolderindex]
	local offset = math.random(-1,1)
	
	local actualModel = Enemies[name]
	
	local model = Instance.new("Part")
	model.Name = name
	model.CFrame = spawns
	model.Size = Vector3.one
	model.Anchored = true
	model.Transparency = 1
	model.CanCollide = false
	
	for i, child in actualModel:GetChildren() do
		if child:IsA("ValueBase") then
			child:Clone().Parent = model
		end
	end
	
	print("spawningenemy")

	if boss then
		ReplicatedStorage.AddHealthBar:FireAllClients(name,model.Health.Value,model.Health.Value)
	end

	model.Parent = workspace[ReplicatedStorage.Map.Value].enemies

	local modelTask = task.spawn(function()
		local speed:IntValue = model.speed

		local _, size = actualModel:GetBoundingBox()
		local PreviousTime = os.clock()

		local t = 0

		while t < 1 do
			t += (os.clock() - PreviousTime) * speed.Value / newPath:GetPathLength()
			PreviousTime = os.clock()

			model:SetAttribute("CFrame", newPath:CalculateUniformCFrame(t) * CFrame.new(offset, size.Y / 2, 0))

			task.wait(refresh_rate)
		end

		ReplicatedStorage.BaseHealth.Value -= model.Health.Value
		model:Destroy()
	end)

	local health:IntValue = model.Health
	health.Changed:Connect(function(newHealth)
		if newHealth <= 0 then
			task.cancel(modelTask)
			model:Destroy()
		end
	end)
end

return module

Client code:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")

local Map = ReplicatedStorage:WaitForChild("Map")
local Enemies = ReplicatedStorage:WaitForChild("Enemies")

local Connections = {}

local function OnEnemyAdded(enemy)	
	local model = Enemies[enemy.Name]:Clone()
	model.Name = "Model"
	model:PivotTo(enemy.CFrame)
	model.Parent = enemy

	local weldConstraint = Instance.new("WeldConstraint")
	weldConstraint.Part0 = model.PrimaryPart
	weldConstraint.Part1 = enemy
	weldConstraint.Parent = enemy

	model.PrimaryPart.Anchored = false
end

local function OnMapAdded(mapName)
	for i, connection in Connections do
		connection:Disconnect()
	end

	table.clear(Connections)

	local map = workspace:FindFirstChild(mapName)

	if not map then
		return
	end

	local enemyFolder = map:WaitForChild("enemies")

	local enemyData = {}

	table.insert(Connections, enemyFolder.ChildAdded:Connect(function(child)	
		OnEnemyAdded(child)

		child:GetAttributeChangedSignal("CFrame"):Connect(function()
			enemyData[child] = child:GetAttribute("CFrame")
		end)
	end))

	table.insert(Connections, enemyFolder.ChildRemoved:Connect(function(child)
		enemyData[child] = nil
	end))

	table.insert(Connections, RunService.Heartbeat:Connect(function(deltaTime)
		local i = 1

		local parts = {}
		local cframes = {}

		for part, goalCFrame in enemyData do
			parts[i] = part
			cframes[i] = part.CFrame:Lerp(goalCFrame, deltaTime * 3)
			
			i += 1
		end

		workspace:BulkMoveTo(parts, cframes, Enum.BulkMoveMode.FireCFrameChanged)
	end))
end

Map.Changed:Connect(OnMapAdded)
OnMapAdded(Map.Value)

um. It just teleports around the way points. Its way too fast

1 Like

Sorry, I accidentally changed the 10000 to 100. Try increasing the divisor to something like 1000. My edited code should work too.

uhm, I was using this equation because it was smoother and the speed wasn’t scaled

Try using my code’s formula, because that one doesn’t account for a change in speed.Value, which is why I didn’t use it in the first place.

Its really laggy and is stuttering a lot.

Edit: Also it scales the speed according to the distance

I’ll adjust mine to the formula you gave, I misread a part of it and assumed it didn’t account for speed. Check my edited code.

I’ve also changed my LocalScript, make sure to put it in StarterPlayerScripts.

@Katrist It dosent really work? I dont know how to describe this so here is a video. I am using your original code.

What do the errors say? I can’t read them in your video.

Also, just edited my client and server code, please try using it, I forgot to use :PivotTo. I’ve also gotten rid of the RemoteEvent since we don’t need it. I also added a new refresh_rate variable, which determines how often to update the goal position.

Lower the refresh_rate and it will appear more accurate to the track, but it will cost more performance. You can also multiply the deltaTime and the mobs will match more to what the server sees, but it may be more choppy.

This does look promising though.

It says "“Attempt to index nil with CFrame”. The new code doesn’t error.

I cant really seem to find a good refresh rate, as it is either really choppy, or extremely inaccurate to the path.
@Katrist

Edit: also found out the enemies were floating

The most accurate refresh_rate is 0, so keep it that.

Try multiplying the deltaTime in that client code by something like 3 or higher, should look more accurate to the path.

You can edit what CFrame the client sees here:

1 Like

still kinda didnt work, it still cuts corners

What have you multiplied it by? You can try higher values than three by the way.

i multiplied it by 3. Also, the enemies cut corners at the start, and then they dont cut corners after.

Try making that value higher.

1 Like

I set it to 5, then 10. It just cuts corners faster

So it works correctly near the end but doesn’t work at the start?

1 Like

yes, that is correct

300000characters

Alright, multiply the deltaTime by three, and try changing the refresh_rate to 0.1.

1 Like

still cuts corners at the start.