How to start on client rendering for a tower defense

So I want to switch over to client rendering instead of having everything done on the server. Now I thought about how to do it by storing the CFrame on the server and having the actual enemy appear on the client. The only problem is actually how to store the CFrame on the server and then send it to the client every time it updates to move the enemy to that CFrame. So if anyone can help on this, I would really appreciate it!

4 Likes

um roblox automatically replicates and renders it on the client

2 Likes

I already know this but what im trying to achieve is to not have the actual enemy model on the server and only the client to not make that much lag. So what I would have to do is store the CFrame on the server and somehow update that on the client when it updates.

4 Likes

So you want the to get the cframe of the enemy from the server but not keep the enemy model on the server?

I feel like this is an unnecessary optimization (it might not be an optimization at all). But anyway if you want to send CFrame values to the client easily you can use the built in CFrameValue and put it in a replicated container and it will automatically replicate. Then you can listen to CFrameValue.Changed on the client.

3 Likes

Most tower defense games use some kind of client rendering where the actual enemy is on the client but not on the server and idk what to really do in terms if I have to do anything on the server. But I could try what you said and see if it helps.

3 Likes

Usually tower defense enemies are pretty determinstics like projectiles.

You only need to know the time it spawns and the current time to know the location of the enemy.

So you can do something like this

2 Likes

I will try to look into this and see what I can do with it.

1 Like

Ok I tried looking around in this post but I am stuck all because I need my enemies to be stopped or changing speed sometimes (mostly bosses). I didn’t put any of the code that was in there but this is my code. Lerping seems to be quite laggy on the server side but it perfectly stops and changes enemy speed. This is the module script if you couldn’t tell.

Code:

local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local events = ReplicatedStorage:WaitForChild("Events")

local mob = {}

function mob.LerpTo(newMob, target, XZOffset)
	local targetPos = Vector3.new(target.Position.X,target.Position.Y + newMob.Stats.YOffset.Value,target.Position.Z)
	local alpha = 0
	local startCFrame = newMob.HumanoidRootPart.Position
	local loop
	local loopBool = false
	local distance = (newMob.HumanoidRootPart.Position - targetPos).Magnitude
	
	loopBool = true
	loop = RunService.Heartbeat:Connect(function(deltaTime)
		if newMob and newMob:FindFirstChild("Stats") then
			local speed = newMob.Stats.Speed.Value
			local relativeSpeed = distance / speed
			local goalCFrame = startCFrame:Lerp(targetPos, alpha)
			newMob.HumanoidRootPart.Position = goalCFrame
			alpha += deltaTime / relativeSpeed
			if alpha >= 1 then
				loop:Disconnect()
				loopBool = false
			end
		else
			loop:Disconnect()
			loopBool = false
		end
	end)
	
	repeat task.wait()
	until loopBool == false
	newMob.HumanoidRootPart.CFrame = target.CFrame * CFrame.new(0,newMob.Stats.YOffset.Value,0)
end

function mob.Move(newMob, sb, pathNum, map, XZOffset)
	local humanoidRootPart = newMob:FindFirstChild("HumanoidRootPart")
	local waypoints = map:FindFirstChild("Waypoints"..pathNum)
	local newPos
	
	if sb == nil or game.Players:FindFirstChild(sb.Name) then
		for waypoint=2, #waypoints:GetChildren() do
			if newMob and newMob:FindFirstChild("MovingTo") then
				newMob.MovingTo.Value = waypoint
				newPos = waypoints[waypoint]
				mob.LerpTo(newMob, newPos, XZOffset)
			end
		end	
	end	
	
	if newMob:FindFirstChild("MovingTo") and newMob.MovingTo.Value ~= 0 then
		if map.Base:FindFirstChild("Humanoid") then
			map.Base.Humanoid:TakeDamage(newMob.Stats.Health)
		elseif map.Base:FindFirstChild("Health") and map.Base:FindFirstChild("MaxHealth") then
			map.Base.Health.Value -= newMob.Stats.Health.Value
		end	
	end
	
	newMob:Destroy()
end

function mob.Spawn(name, spawnloc, quantity, spawnedBy, map)
	local mobExists = ServerStorage.Mobs:FindFirstChild(name)
	
	if mobExists and workspace.Info.GameRunning.Value == true then
		for i=1, quantity do
			task.wait(0.5)
			local paths = map.Paths.Value
			if spawnedBy ~= nil and not game.Players:FindFirstChild(spawnedBy.Name) then
				paths = 1
			end
			for v=1, paths do
				local newMob = mobExists:Clone()
				local XZOffset = Random.new():NextNumber(-0.5,0.5)
				local YOffset = newMob.Stats.YOffset.Value
				
				if spawnedBy == nil then
					newMob.HumanoidRootPart.CFrame = map:FindFirstChild("Waypoints"..v)["1"].CFrame + Vector3.new(0,YOffset,0)
				else
					local mobName = newMob.Name
					newMob.Name = spawnedBy.Name .. "'s " .. mobName
					if spawnloc == nil then
						newMob.HumanoidRootPart.CFrame = map:FindFirstChild("Waypoints"..v)["1"].CFrame + Vector3.new(0,YOffset,0)
					else
						newMob.HumanoidRootPart.CFrame = spawnloc
					end	
				end	
				newMob.Parent = workspace.Mobs
				
				local movingTo = Instance.new("IntValue")
				movingTo.Name = "MovingTo"
				movingTo.Parent = newMob
				
				local pathValue = Instance.new("IntValue")
				pathValue.Name = "OnPath"
				if spawnedBy == nil or game.Players:FindFirstChild(spawnedBy.Name) then
					pathValue.Value = v
				end	
				pathValue.Parent = newMob
				
				if newMob:FindFirstChild("IsBoss") then
					events.NewBossBar:FireAllClients(newMob)
				end

				for p, object in ipairs(newMob:GetDescendants()) do
					if object:IsA("BasePart") then
						object.CollisionGroup = "Mob"
					end
				end

				newMob.Stats.Health:GetPropertyChangedSignal("Value"):Connect(function()
					if newMob.Stats.Health.Value <= 0 then
						task.wait(0.1)
						if not newMob:FindFirstChild("DieAnim") then
							newMob:Destroy()
						end	
					end	
				end)

				coroutine.wrap(mob.Move)(newMob, spawnedBy, v, map, XZOffset)
			end
		end	
	else
		
	end
end

events.SpawnEnemy.Event:Connect(function(enemyName, spawnln, amount, spawnedByEnemy, mapName)
	mob.Spawn(enemyName, spawnln, amount, spawnedByEnemy, mapName)
end)

return mob
1 Like