How to Optimize Entity System Even More For Tower Defense?

Hello everyone, as of recently I’ve created a new entity system that uses tables and lerping. It was very simple to make and it’s pretty good recv-wise. Though there’s some things that I probably optimize.

Here’s the things that I want to optimize:

A remote event firing everytime an entity spawns.
I want to rework this to only fire one remote event for every quantity that spawn. I tried doing this but I couldn’t get the spawn delay to working. So I just abandoned it completely.

zzssas

Another thing I’d like to optimize is replacing heartbeats with something else in the client. I can heard this can cause memory leaks and I don’t wanna take any chances. Here is the code for this aswell.

Thanks and I hope to find any solutions and replacements to these problems. Sorry for my bad english…

1 Like

Your heartbeat code is good, memory leaks only happen when you forget to disconnect events or forget to remove certain elements.

Could you show me the client code? (please copy and paste it, it’s a pain to rewrite it based off a screenshot)

It would also help if you pasted the other code that you showed in your screenshot.

Hello! First of all good morning. Second of all yeah, I can show you the client code.

RunService.Heartbeat:Connect(function(DeltaTime)
		VisualEnemy:PivotTo(Path:CalculateUniformCFrame((workspace:GetServerTimeNow() - Enemy.StartTime) / (Path:GetPathLength() / 3))+Enemy.Offset)
end)

Here’s the server code aswell if needed.

Mob.New = function(Quantity: number, TimeBetweenSpawns: number)
	local Enemies = {}
	
	for i=1, Quantity do
		local Table = {
			Name = "Test";
			Health = 5;
			Offset = Vector3.new(math.random(-2, 2), 0, 0);
			StartTime = workspace:GetServerTimeNow();
		}
		
		table.insert(Enemies, Table)
		LoadEnemy:Fire(BridgeNet2.AllPlayers(), Table)
		task.wait(TimeBetweenSpawns)
	end
end

Forgot to mention, the path module that I use here is BezierPath.

I need the code that receives LoadEnemy.

LoadEnemy:Connect(function(Enemy)
	local VisualEnemy = EnemyModels[Enemy.Name]:Clone()
	local Info = EnemyInfo[Enemy.Name]

	local Waypoints = {}

	for i=1,#workspace:WaitForChild("Waypoints"):GetChildren() do
		local Part = workspace:WaitForChild("Waypoints")[i]
		table.insert(Waypoints, Part.Position)
	end

	local Path = BezierPath.new(Waypoints, 6)

	local Lerp: RBXScriptConnection

	VisualEnemy.Parent = workspace:WaitForChild("Enemies")

	local Animation = Instance.new("Animation")
	Animation.AnimationId = "rbxassetid://"..Info.Animation

	local Animator = VisualEnemy.AnimationController.Animator
	Animator:LoadAnimation(Animation):Play()

	Lerp = RunService.Heartbeat:Connect(function(DeltaTime)
		local Alpha = (workspace:GetServerTimeNow() - Enemy.StartTime) / (Path:GetPathLength() / Info.Speed)

		VisualEnemy:PivotTo(Path:CalculateUniformCFrame(Alpha)*CFrame.new(Enemy.Offset, 0, 0))

		if Alpha >= 1 then
			print("Finished!")
			VisualEnemy:Destroy()
			Lerp:Disconnect()
		end
	end)
end)

--[[LoadTroop:Connect(function(Troop)
	local Visual = TroopModels[Troop.Name]:Clone()
	Visual:PivotTo(Troop.CFrame)
end)]]

blud creation a new lerp for each enemies, ik this is old tho

also didn’t say but hi zephrus

1 Like

Sorry for the extremely late reply, I just saw this now.

To fix it, I would use one RunService loop to change every enemy’s position. You should also be caching the BezierPath to avoid wasting resources.

Code:

local Enemies = workspace:WaitForChild("Enemies")
local Waypoints = {}

for i, part in workspace:WaitForChild("Waypoints"):GetChildren() do
	table.insert(Waypoints, part.Position)
end

local Path = BezierPath.new(Waypoints, 6)

local enemies = {}

LoadEnemy:Connect(function(Enemy)
	local Info = EnemyInfo[Enemy.Name]
	
	local VisualEnemy = EnemyModels[Enemy.Name]:Clone()
	VisualEnemy.Parent = Enemies

	local Animation = Instance.new("Animation")
	Animation.AnimationId = "rbxassetid://"..Info.Animation

	local Animator = VisualEnemy.AnimationController.Animator
	Animator:LoadAnimation(Animation):Play()

	enemies[VisualEnemy] = {
		Enemy.StartTime,
		Enemy.Offset
	}
end)

RunService.Heartbeat:Connect(function(DeltaTime)
	for enemy, enemyData in enemies do
		local Info = EnemyInfo[enemy.Name]
		local Alpha = (workspace:GetServerTimeNow() - enemyData[1]) / (Path:GetPathLength() / Info.Speed)

		enemy:PivotTo(Path:CalculateUniformCFrame(Alpha) * CFrame.new(enemyData[2], 0, 0))

		if Alpha >= 1 then
			enemies[enemy] = nil
			enemy:Destroy()
		end
	end
end)
1 Like

Thanks, but I already optimized my entity system xD, it’s really good as well, I’ve made it have no desync at all, one remote per quantity and one heartbeat for every enemy (using bulkmoveto)

1 Like

hi, blockbuster!
Posts must be at least 30

BezierPath module already caches itself or stuff, also i’m pretty sure it’s better to put pivotto after the if statement so you don’t need to update the cframe for no reasons

No, it doesn’t cache the actual path. It caches the CFrames generated though.

This is true, but the difference in performance will be so extremely minuscule that it doesn’t matter.

oh i see you meant in that way ok
yeah it doesn’t cache the actual path but i don’t really see the purpose of caching the path if you need to generate a path like only once in the entire game

Look at their code. They generated a new path for every single enemy. That is quite literally the only reason I’m precreating the path.

didn’t see that lol, but i know zephrus and since then he fixed those stuff so

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.