Soccer Ball Latency

Im trying to make a smooth soccer ball kicking experience for my game Mini Cup. However, when the player touches the ball there is a delay. Also when other players touch the ball they look like they are a couple feet away from the ball.

How would I fix this issue?

Here is the code. There are two scripts.

SERVERSCIPT1 INSIDE BALL - SETS BALL ON SERVER

local ball = script.Parent
while true do
	task.wait(5)
ball:SetNetworkOwner(nil)
end

SERVERSCIPT2 INSIDE BALL - PERFORMS KICK

local Services = {
	Players = game:GetService("Players"),
	Debris = game:GetService("Debris")
}


local Settings = {
	Kick_Cooldown = 0, -- Ignore this, i am just too lazy to remove.
}
local Ball = script.Parent
local KickSound = Ball:WaitForChild("Kick")
local IgnoreTable = {}
debounce = false


Ball.Touched:Connect(function(Part)

	local Character = Part.Parent
	if not Character then
		return
	end

	local Player = Services.Players:GetPlayerFromCharacter(Character)
	local Humanoid = Character:FindFirstChildOfClass("Humanoid")
	local Root = Character:FindFirstChild("HumanoidRootPart")
	if not Player or not Humanoid or Humanoid.Health <= 0 or not Root or table.find(IgnoreTable, Player) then
		return
	end
	table.insert(IgnoreTable, Player)

	delay(Settings.Kick_Cooldown, function()
		if not Player then
			return
		end
		local Position = table.find(IgnoreTable, Player)
		if not Position then
			return
		end
		table.remove(IgnoreTable, Position)
	end)
	if debounce == false then
		debounce = true
	local Direction = Root.CFrame.LookVector
	--local Direction = CFrame.lookAt(Root.Position, Root.Position).LookVector
	local Kick_Force = Humanoid.WalkSpeed
	if Direction.Magnitude < 0.001 then
		return
	end
	if Ball:GetAttribute('LastTouch') ~= nil then
		if Ball:GetAttribute('LastTouch') ~= Player.Name then
			local st = Ball:GetAttribute('LastTouch')
			Ball:SetAttribute('LastTouch', Player.Name)
			Ball:SetAttribute('SecondTouch', st)
		else

		end
	else
		Ball:SetAttribute('LastTouch', Player.Name)
	end
	
		local TeamName = Player:GetAttribute('TeamName')
		
		if game.ServerStorage.GameFolder.Standings:FindFirstChild(TeamName) ~= nil and game.ServerStorage.GameFolder.Standings:FindFirstChild(TeamName):GetAttribute("Touches") ~= nil  then
			game.ServerStorage.GameFolder.Standings:FindFirstChild(TeamName):SetAttribute('Touches', game.ServerStorage.GameFolder.Standings:FindFirstChild(TeamName):GetAttribute('Touches') + 1)
		end
		
	local Velocity = Instance.new("BodyVelocity")
	
	Velocity.Parent = Ball
	Velocity.MaxForce = Vector3.new(1, 1, 1) * math.huge
	Velocity.Velocity = (Direction.Unit * Kick_Force * 1.7) + Vector3.new(0, Kick_Force * .9, 0)
	
	Services.Debris:AddItem(Velocity, 0.2)
		KickSound:Play()
		wait(1)
	debounce = false
end
	
end)

So as you can see, I have tried setting the balls network owner to the server (I dont know why I made it a loop…), but it still shows that latency.

What can I do to reduce or get rid of this latency?

https://streamable.com/vczfdh

Thanks!

This is normal “movement synchronization” latency on Roblox. Nothing you can do with it using default Roblox’s synchronization engine.
If you really wanna fix this issue, you’re forced to make your own synchronization. Ball position/velocity isn’t hard to synchronize smoothly since all the movement could be calculated by client if server sends RemoteEvent with needed data. However making your own humanoid movement sync engine is really hard work, this would probably require you to not use originally created humanoid on player join, but some kind of a “fake” humanoid which would behave as player, but would be completely controlled by the server.
I’ve realized that RemoteEvents have almost no latency if I compare it with movement sync.

Shortly said, you would have to reimplement all player controls and client->server->clients synchronization on your own using remote events.

3 Likes

So how exactly would I reimplement player controls like that?

Because I have currently done this much:

LOCAL SCRIPT IN STARTERCHARACTER SCRIPTS - DETECTS KICK

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local KickEvent = ReplicatedStorage.Events.Kick


function Kick(Part)
	if Part.Name == "Ball" then

	local Character = script.Parent
	if not Character then
		return
	end

	local Player = game.Players.LocalPlayer
	local Humanoid = Character:FindFirstChildOfClass("Humanoid")
	local Root = Character:FindFirstChild("HumanoidRootPart")
	if not Player or not Humanoid or Humanoid.Health <= 0 or not Root then
		return
	end

		local Direction = Root.CFrame.LookVector
		--local Direction = CFrame.lookAt(Root.Position, Root.Position).LookVector
		local Kick_Force = Humanoid.WalkSpeed
		if Direction.Magnitude < 0.001 then
			return
		end

	KickEvent:FireServer(Direction, Kick_Force)
	end
end

script.Parent["Head"].Touched:Connect(Kick)
script.Parent["Right Leg"].Touched:Connect(Kick)
script.Parent["Left Leg"].Touched:Connect(Kick)
script.Parent["Torso"].Touched:Connect(Kick)
script.Parent["Right Arm"].Touched:Connect(Kick)
script.Parent["Left Arm"].Touched:Connect(Kick)

SERVERSCRIPT INSIDE BALL INSIDE WORKSPACE - PERFORMS KICK

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local KickEvent = ReplicatedStorage.Events.Kick
debounce = false

local Services = {
	Players = game:GetService("Players"),
	Debris = game:GetService("Debris")
}

local Ball = script.Parent
local KickSound = Ball:WaitForChild("Kick")

local function KickBall(player, Direction, Kick_Force)
	local Character = player.Character
	local Player = Services.Players:GetPlayerFromCharacter(Character)
	local Humanoid = Character:FindFirstChildOfClass("Humanoid")
	local Root = Character:FindFirstChild("HumanoidRootPart")
	
	if debounce == false then
		debounce = true
		if Ball:GetAttribute('LastTouch') ~= nil then
			if Ball:GetAttribute('LastTouch') ~= Player.Name then
				local st = Ball:GetAttribute('LastTouch')
				Ball:SetAttribute('LastTouch', Player.Name)
				Ball:SetAttribute('SecondTouch', st)
			else

			end
		else
			Ball:SetAttribute('LastTouch', Player.Name)
		end

		local TeamName = Player:GetAttribute('TeamName')

		if game.ServerStorage.GameFolder.Standings:FindFirstChild(TeamName) ~= nil and game.ServerStorage.GameFolder.Standings:FindFirstChild(TeamName):GetAttribute("Touches") ~= nil  then
			game.ServerStorage.GameFolder.Standings:FindFirstChild(TeamName):SetAttribute('Touches', game.ServerStorage.GameFolder.Standings:FindFirstChild(TeamName):GetAttribute('Touches') + 1)
		end

		local Velocity = Instance.new("BodyVelocity")

		Velocity.Parent = Ball
		Velocity.MaxForce = Vector3.new(1, 1, 1) * math.huge
		
		Velocity.Velocity = (Direction.Unit * Kick_Force * 1.7) + Vector3.new(0, Kick_Force * .9, 0)

		Services.Debris:AddItem(Velocity, 0.2)
		KickSound:Play()
		task.wait(.8)
		debounce = false
	end
end

KickEvent.OnServerEvent:Connect(KickBall)

and of course the ANTILAG SERVER SCRIPT is in the ball as well:

local ball = script.Parent

task.wait(5)
ball:SetNetworkOwner(nil)

So now what do I do? I have made it so the ball kick is detected by the client and then sent to the server, now the ball gets kicked in the right direction, but there is still latency of when it is kicked.

1 Like

The problem here is that you’re setting the velocity only on server side, which will have noticeable lag since, the ball is synchronized with Roblox’s default synchronization system. After setting velocity, fire an event to all clients, that they should add a velocity to the ball as well. I’m not sure if it’s gonna fully work, but not much work needs to be done to test if it’s gonna work.

What am I trying to point out is, that if you tell all clients to calculate physics for the ball on their side as well, then the latency shouldn’t be so noticeable and you’d probably dodge synchronization errors (Ball is moving, sometimes stops in the air due to some kind of lag and then moves again)

If this solution won’t work, then there is more difficult solution to this. Every client creates an instance of ball for themselves alone, server will also create an instance of ball, but fully transparent (this ball will be used only to simulate its movement, so game can determine if it’s goal, block, or anything else).
The pro of having local instance of a ball for every client is that there is no server->client synchronization going, so you’d have to implement the whole synchronization yourself. This means, if player kicks the ball, server sends a remote event to all clients that the ball is kicked and every client will add a velocity to their own local ball instance. Server must add a velocity to its own ball as well for simulation purposes. Then check, if server ball’s path has been in any way interrupted (someone blocked the ball), and send a remote event to all clients changed velocity. Why this is important is because player movement synchronization is still under Roblox’s default synchronization which is very behind. So what could happen is that server’s ball has been blocked by a player, but for client’s POV, the players hasn’t been on that spot yet, so there would be a ball desynchronization, where client’s ball would still fly forward, but server’s ball would bounce of the player.

Hope this gives you some understanding on how the synchronization works or should work.

1 Like

Wouldn’t that make the ball travel twice as fast?

Because if I set it on the server it already replicates to clients.

So setting it on each client specifically would add another force on top of the one that was replicated from the server as well wouldn’t it?

Well, I wouldn’t say it’s automatically replicated, maybe the value, however the one who simulates physics is the owner of the object, which is server. Also, not add but rather set the Velocity, because sending and receiving an event is faster than position synchronization from server to all clients.
By what I said, we can implify that this solution will not work (sending an event and setting velocity manually), because even if you do set the velocity on clients manually, the physics simulation will still be handled by server and so the velocity wouldn’t make any sense. That’s why I was posting another solution, which is more difficult to implement, but should definitely work.

1 Like

I don’t really know how to do this honestly. No matter what I do it seems to get worse.

Are you open for commissions by any chance?

If so do you think you could do this?

I could try to write a tutorial with examples someday on how to bypass these synchronization things. One problem is that I mainly develop games in Typescript instead of Lua, so it’s not that easy for me to do any commissions since most games are mostly written in Lua.

1 Like

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