Tower defence enemies lagging behind

If you want the curve to be more exaggerated, you can move the nodes.

Also, could you show some code?

@Katrist
OH, i was using bezier path wrong. Wait lemme inplement it rq

@Katrist I tried implementing this, but it was super choppy, and it wouldn’t wait enough to delete its self. And on the 1st path, the enemies don’t damage the base at all.
Here’s the code:

--!native
--!strict
local module = {}
local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local PhysicsService = game:GetService("PhysicsService")
local moveremote = ReplicatedStorage:WaitForChild("tweenenemy")
local Players = game:GetService("Players")

module.spawn = function(name:string, waypointfolder:Folder, exit, spawns:CFrame, boss:BoolValue)
	local offset = math.random(-1,1)
	local tweenenemy = ReplicatedStorage.tweenenemy
	local model: Model = ServerStorage.Enemies[name]:Clone()
	model:PivotTo(spawns + Vector3.new(offset,0,0))

	if boss then
		ReplicatedStorage.AddHealthBar:FireAllClients(name,model.Health.Value,model.Health.Value)
	end
	--for _,parts:BasePart in pairs(model) do
	--	if parts:IsA("BasePart") then
	--		parts.CollisionGroup = "Enemies"
	--	end
	--end
	model.Parent = Workspace:FindFirstChild(ReplicatedStorage.Map.Value).enemies
	--local health = model.health
	--health:GetPropertyChangedSignal("Value"):Connect(function()
	--	if health.Value == 0 then
	--		model:Destroy()
	--	end
	--end)

	model.PrimaryPart:SetNetworkOwner(nil)
	
	local speed:IntValue = model:WaitForChild("speed")
	--model.speed:GetPropertyChangedSignl("Value"):Connect(function()
	--	enemySpeed = model.speed.Value
	--end)
	local _, size = model:GetBoundingBox()
	local nodepositions = {}
	local enemyspeed = speed.Value
	local nodeindex = 0
	print("spawningenemy")
	for _ = 1, #waypointfolder:GetChildren() - 2 do
		nodeindex += 1
		local node:Part = waypointfolder:FindFirstChild(tostring(nodeindex))
		table.insert(nodepositions,node.CFrame.Position)
	end
	table.insert(nodepositions,waypointfolder.exit.CFrame.Position)
	print("got position table")
	print(unpack(nodepositions))
	local totalwait = 0
	nodeindex = 0
	for _ = 1, #waypointfolder:GetChildren() - 2 do
		local success, err = pcall(function()
			nodeindex += 1
			local node:Part = waypointfolder:FindFirstChild(tostring(nodeindex))
			local distance = (node.CFrame.Position + Vector3.new(offset,size.Y / 2,0) - model.PrimaryPart.CFrame.Position).Magnitude
			totalwait += distance
		end)
		if err then
			warn(err)
		end
	end
	print("generated wait")
	tweenenemy:FireAllClients(model,nodepositions,Vector3.new(offset,size.Y / 2,0))
	task.wait(totalwait / enemyspeed)
	ReplicatedStorage.BaseHealth.Value -= model.Health.Value
	model:Destroy()
end

return module

heres the client code:

ReplicatedStorage.tweenenemy.OnClientEvent:Connect(function(model:Model,Position:table,offset:Vector3)
	print("tweening enemy")
	local modelpath = BezierPath.new(Position,3)
	for i = 0,1,1/100 do
		model.PrimaryPart.CFrame = modelpath:CalculateUniformCFrame(i) + offset
		task.wait(0.1)
	end
end)

here’s a vid:

@Katrist
thank you and have a happy new year!

Edit: wrong code, replaced code

I would do this all on the server, since BezierPath is fairly performant.

Code:

--!native
--!strict
local module = {}
local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local PhysicsService = game:GetService("PhysicsService")
local moveremote = ReplicatedStorage:WaitForChild("tweenenemy")
local Players = game:GetService("Players")

module.spawn = function(name:string, waypointfolder:Folder, exit, spawns:CFrame, boss:BoolValue)
	local offset = math.random(-1,1)
	local tweenenemy = ReplicatedStorage.tweenenemy
	local model: Model = ServerStorage.Enemies[name]:Clone()
	model:PivotTo(spawns + Vector3.new(offset,0,0))
	print("spawningenemy")

	if boss then
		ReplicatedStorage.AddHealthBar:FireAllClients(name,model.Health.Value,model.Health.Value)
	end
	--for _,parts:BasePart in pairs(model) do
	--	if parts:IsA("BasePart") then
	--		parts.CollisionGroup = "Enemies"
	--	end
	--end
	model.Parent = Workspace:FindFirstChild(ReplicatedStorage.Map.Value).enemies
	--local health = model.health
	--health:GetPropertyChangedSignal("Value"):Connect(function()
	--	if health.Value == 0 then
	--		model:Destroy()
	--	end
	--end)

	model.PrimaryPart:SetNetworkOwner(nil)

	local speed:IntValue = model:WaitForChild("speed")
	--model.speed:GetPropertyChangedSignl("Value"):Connect(function()
	--	enemySpeed = model.speed.Value
	--end)
	local _, size = model:GetBoundingBox()
	
	local nodePositions = {}
	
	for i = 1, #waypointfolder:GetChildren() - 2 do
		table.insert(nodePositions, waypointfolder[i].Position + Vector3.new(offset, size.Y / 2, 0))
	end
	
	table.insert(nodePositions, waypointfolder.exit.Position)
	
	local newPath = BezierPath.new(nodePositions)
	local t = 0
	
	while t < 1 do
		model:PivotTo(newPath:CalculateUniformCFrame(t))
		t += speed.Value / 100

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

return module

Make sure to add a variable for BezierPath, I didn’t add one.

If it’s too fast, make sure to change this value:

t += speed.Value / 100

Make the 100 a higher number for the speed to be lower, or make the 100 a lower number for the speed to be higher.

Have you anchored the PrimaryPart? You also won’t need :SetNetworkOwner(nil).

Happy new year to you too!

1 Like

I tried anchoring the primary part before, but that didn’t really work. I don’t know if calling set network ownership hanged the script, but il try it today. Thank you!

The script dosent work. It just teleports?

--!native
--!strict
local module = {}
local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local PhysicsService = game:GetService("PhysicsService")
local moveremote = ReplicatedStorage:WaitForChild("tweenenemy")
local Players = game:GetService("Players")
local BezierPath = require(ReplicatedStorage.Packages.BezierPath)

module.spawn = function(name:string, waypointfolder:Folder, exit, spawns:CFrame, boss:BoolValue)
	local offset = math.random(-1,1)
--	local tweenenemy = ReplicatedStorage.tweenenemy
	local model: Model = ServerStorage.Enemies[name]:Clone()
	model:PivotTo(spawns + Vector3.new(offset,0,0))
	print("spawningenemy")

	if boss then
		ReplicatedStorage.AddHealthBar:FireAllClients(name,model.Health.Value,model.Health.Value)
	end
	--for _,parts:BasePart in pairs(model) do
	--	if parts:IsA("BasePart") then
	--		parts.CollisionGroup = "Enemies"
	--	end
	--end
	model.Parent = Workspace:FindFirstChild(ReplicatedStorage.Map.Value).enemies
	--local health = model.health
	--health:GetPropertyChangedSignal("Value"):Connect(function()
	--	if health.Value == 0 then
	--		model:Destroy()
	--	end
	--end)

	model.PrimaryPart:SetNetworkOwner(nil)

	local speed:IntValue = model:WaitForChild("speed")
	--model.speed:GetPropertyChangedSignl("Value"):Connect(function()
	--	enemySpeed = model.speed.Value
	--end)
	local _, size = model:GetBoundingBox()
	
	local nodePositions = {}
	
	for i = 1, #waypointfolder:GetChildren() - 2 do
		table.insert(nodePositions, waypointfolder[i].Position + Vector3.new(offset, size.Y / 2, 0))
	end
	
	table.insert(nodePositions, waypointfolder.exit.Position)
	
	local newPath = BezierPath.new(nodePositions,3)
	local t = 0
	
	while t < 1 do
		model:PivotTo(newPath:CalculateUniformCFrame(t))
		t += speed.Value / 5

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

return module

Keep it 100 and see what happens, I’m assuming you set the speed to be far too fast. You should always test the code before editing values.

Also, make sure to anchor your PrimaryPart.

I set it to 5 and it just teleported to the end after a few seconds. Was I supposed to anchor the primary part?

Please read my post. I have said the same thing for the past two replies.

Oh okay sorry. But shouldn’t it still not be teleporting to the end?

I set it to 5… shouldn’t it be extremely slow?

Oh wait srry I’m stupid srry I didn’t read

You’re dividing a number.

100 / 5 = 20
100 / 100 = 1

Higher divisor = lower speed.

Please just use my code and test without modifications.

I will try this tomorrow sorry it’s like 5 am for me rn

Okay, I am using your original code. I just added a value for the curve size, and the path to Bezier. I also had to amp up 100 to a ridiculously high value for me to even see the enemies.

Here’s the code:

--!native
--!strict
local module = {}
local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local PhysicsService = game:GetService("PhysicsService")
local moveremote = ReplicatedStorage:WaitForChild("tweenenemy")
local Players = game:GetService("Players")
local BezierPath = require(ReplicatedStorage.Packages.BezierPath)

module.spawn = function(name:string, waypointfolder:Folder, exit, spawns:CFrame, boss:BoolValue)
	local offset = math.random(-1,1)
	local tweenenemy = ReplicatedStorage.tweenenemy
	local model: Model = ServerStorage.Enemies[name]:Clone()
	model:PivotTo(spawns + Vector3.new(offset,0,0))
	print("spawningenemy")

	if boss then
		ReplicatedStorage.AddHealthBar:FireAllClients(name,model.Health.Value,model.Health.Value)
	end
	--for _,parts:BasePart in pairs(model) do
	--	if parts:IsA("BasePart") then
	--		parts.CollisionGroup = "Enemies"
	--	end
	--end
	model.Parent = Workspace:FindFirstChild(ReplicatedStorage.Map.Value).enemies
	--local health = model.health
	--health:GetPropertyChangedSignal("Value"):Connect(function()
	--	if health.Value == 0 then
	--		model:Destroy()
	--	end
	--end)

	--model.PrimaryPart:SetNetworkOwner(nil)

	local speed:IntValue = model:WaitForChild("speed")
	--model.speed:GetPropertyChangedSignl("Value"):Connect(function()
	--	enemySpeed = model.speed.Value
	--end)
	local _, size = model:GetBoundingBox()
	
	local nodePositions = {}
	
	for i = 1, #waypointfolder:GetChildren() - 2 do
		table.insert(nodePositions, waypointfolder[i].Position + Vector3.new(offset, size.Y / 2, 0))
	end
	
	table.insert(nodePositions, waypointfolder.exit.Position)
	
	local newPath = BezierPath.new(nodePositions,2.5)
	local t = 0
	
	while t < 1 do
		model:PivotTo(newPath:CalculateUniformCFrame(t))
		t += speed.Value / 10000

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

return module

And the video:

You can see the enemys spawning right at the first waypoint. And the enemies are also extremely choppy.

You need to add the spawn position into the nodePositions table.

Okay i added the spawn position.

But now the enemys get pulled apart, and the enemys arms and feet move in different positons.

Heres the code:

--!native
--!strict
local module = {}
local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local PhysicsService = game:GetService("PhysicsService")
local moveremote = ReplicatedStorage:WaitForChild("tweenenemy")
local Players = game:GetService("Players")
local BezierPath = require(ReplicatedStorage.Packages.BezierPath)

module.spawn = function(name:string, waypointfolder:Folder, exit, spawns:CFrame, boss:BoolValue)
	local offset = math.random(-1,1)
	local tweenenemy = ReplicatedStorage.tweenenemy
	local model: Model = ServerStorage.Enemies[name]:Clone()
	model:PivotTo(spawns + Vector3.new(offset,0,0))
	print("spawningenemy")

	if boss then
		ReplicatedStorage.AddHealthBar:FireAllClients(name,model.Health.Value,model.Health.Value)
	end
	--for _,parts:BasePart in pairs(model) do
	--	if parts:IsA("BasePart") then
	--		parts.CollisionGroup = "Enemies"
	--	end
	--end
	model.Parent = Workspace:FindFirstChild(ReplicatedStorage.Map.Value).enemies
	--local health = model.health
	--health:GetPropertyChangedSignal("Value"):Connect(function()
	--	if health.Value == 0 then
	--		model:Destroy()
	--	end
	--end)

	--model.PrimaryPart:SetNetworkOwner(nil)

	local speed:IntValue = model:WaitForChild("speed")
	--model.speed:GetPropertyChangedSignl("Value"):Connect(function()
	--	enemySpeed = model.speed.Value
	--end)
	local _, size = model:GetBoundingBox()
	
	local nodePositions = {}
	table.insert(nodePositions,waypointfolder.enemyspawn.Position)
	for i = 1, #waypointfolder:GetChildren() - 2 do
		table.insert(nodePositions, waypointfolder[i].Position + Vector3.new(offset, size.Y / 2, 0))
	end

	table.insert(nodePositions, waypointfolder.exit.Position)
	
	local newPath = BezierPath.new(nodePositions,2.5)
	local t = 0
	
	while t < 1 do
		model:PivotTo(newPath:CalculateUniformCFrame(t))
		t += speed.Value / 10500

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

return module

Heres a vid:

Also its extremally laggy. Should i start sending the lerps back to client?

The reason I’m not telling you to do movement via the client is because it’s going to be hard to implement other systems like towers later. You can try though.

Also, are you sure you anchored ONLY the primary part?