Alright, so, I’m making a TDS game.
However, I wanted to handle spawning on the client to prevent server lag (event is fired on all clients);
function spawn_enemy(data)
--
local model = data.model:Clone()
local humanoid = model.Humanoid
local animator = humanoid.Animator
--
local attributes = model.attributes
local health_data = model.health_data
local resistances = model.resistances
local stats = model.stats
--
local map = _G.map
model.Parent = map
--
local thread
local walk_anim = animator:LoadAnimation(model.walk)
walk_anim:Play()
--
model.Parent = _G.map.enemies
_G.follow_path({
["model"] = model;
["nodes_folder"] = _G.map.nodes_folder
}) -- they follow the nodes
--
end
spawn_enemy_event.OnClientEvent:Connect(spawn_enemy)
An issue is latency.
For some clients, the enemy will spawn later/earlier than other clients, making it inconsistent.
And if someone is really laggy, an enemy might make it to their base before they even realize that it’s spawned yet.
How would I achieve this? How would I sync the enemy position with all clients?
I believe this would fall under “rollback” networking although I don’t actually understand the buzzword. To sync properly, you have the server notify each client to spawn the enemy along with a timestamp of when the enemy was created on the server. The client then receives this and jumps the enemy ahead to account for the delay.
E.g Server says to client “I spawned the boss troop at time t = 5s”
Client receives the remote event at time t = 7s to spawn the troop
Client sees the troop was spawned at t=5 as this is contained in the message
Client calculates a 2s delay and rather than spawning the troop from the start, spawn it 2s ahead from the starting point to account for the delay.
This reduces slight lag and ensures the troops are synced however “if someone is really laggy, an enemy might make it to their base before they even realize that it’s spawned yet” has no real solution as you cannot stop latency only account (to a certain extent) for it.
If your enemies are travelling at a linear speed (constant studs/s), this can be achieved pretty easily by using a time function.
How It Could Work
If you have enemies that travel at a constant linear speed between points, you can determine the
position of the enemy easily solely based on time. Here is an example of this with an array of positions (nodes):
function getPositionFromT(t: number, points: table, speed: number)
local baseT = 0
for i,point in ipairs(points) do
local previous = points[i-1]
if not previous then continue end
local distance = (point - previous).Magnitude
if t - baseT < distance/speed then
local delta = t - baseT
return previous + (point-previous).Unit*speed*delta
else
baseT += distance/speed
end
end
return points[#points]
end
Basically, this code goes through each point and determines if the point was already passed based on t. If it was passed, then continue to the next point. If it isn’t passed yet, the position is returned between the point and previous based on the relevant time and speed.
Now that you can get the position from any time, you can now determine the instantaneous position of an enemy based on when it was spawned by the server.
When a server spawns an enemy and tells the client, it can also tell the client the workspace:GetServerTimeNow() value when the enemy was spawned. Now, when the client updates the positions of the enemies (presumably every RenderStepped), t would equal to workspace:GetServerTimeNow() - spawnTime.
Since this system is time-reliant, no matter how laggy the client is, the position of the enemies when rendered will always be the same on every update.