Roblox soccer ball keeps bugging out

Long story short, I’ve been trying to fix this bug with my roblox soccer ball/football for the past 2 hours or so, and I just cannot get it to work. It must be a very simple fix but I just can’t figure it out.

The issue I am facing is that the ball keeps teleporting back and then continues rolling. This only occurs after it has been kicked multiple times, and it sort of ‘builds up’. It gets worse every time.

Video:

Script:

local cooldown = 0.2
local lastShot = 0

local friction = 0.985
local spinFriction = 0.98

local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {workspace}

local function resetBall()
	local ball = script.Parent
	ball.CanCollide = true
	ball.Anchored = false
	ball.CanTouch = true
	ball.Massless = false
	ball.AssemblyLinearVelocity = Vector3.zero
	ball.AssemblyAngularVelocity = Vector3.zero
	ball.Velocity = Vector3.zero
	ball.RotVelocity = Vector3.zero
	ball.Position = ball.Position + Vector3.new(0, 0.01, 0)
	ball.Orientation = Vector3.zero
	task.wait(0.002)
end

local function applyFriction()
	while true do
		local ball = script.Parent
		local velocity = ball.AssemblyLinearVelocity
		local angularVelocity = ball.AssemblyAngularVelocity
		local touching = ball:GetTouchingParts()
		local onGround = false

		for _, part in ipairs(touching) do
			if part.Name == "Grass" then
				onGround = true
				break
			end
		end

		if onGround then
			ball.AssemblyLinearVelocity *= friction
			ball.AssemblyAngularVelocity *= spinFriction

			if velocity.Magnitude < 0.05 then
				ball.AssemblyLinearVelocity = Vector3.zero
			end
			if angularVelocity.Magnitude < 0.05 then
				ball.AssemblyAngularVelocity = Vector3.zero
			end
		end
		task.wait(0.03)
	end
end

task.spawn(applyFriction)

local function shoot(force, lift, player, rawPower)
	resetBall()
	task.wait(0.005)

	local ball = script.Parent
	local direction = player.HumanoidRootPart.CFrame.LookVector
	local finalForce = (direction * force) + Vector3.new(0, lift, 0)

	ball.AssemblyLinearVelocity = finalForce
	ball.AssemblyAngularVelocity = Vector3.zero

	if rawPower > 75 then
		ball.Shoot:Play()
	else
		ball.Kick:Play()
	end

	task.wait(1)
	ball.CurrentOwner.Value = nil
end

game:GetService("ReplicatedStorage").Events.Shoot.OnServerEvent:Connect(function(player, power, height)
	local character = player.Character
	if not character then return end

	local hitbox = character:FindFirstChild("FeetHitbox")
	if not hitbox then return end

	if tick() - lastShot < cooldown then
		print("shot blocked")
		return
	end

	lastShot = tick()

	script.Parent.CanTouch = true
	script.Parent.Massless = false

	local waitTime = 0.425
	local start = tick()
	local confirmed = false

	while tick() - start < waitTime do
		local ballPos = script.Parent.Position
		local hitboxPos = hitbox.Position
		local ballSize = script.Parent.Size / 2
		local hitboxSize = hitbox.Size / 2

		local touching =
			math.abs(ballPos.X - hitboxPos.X) <= (hitboxSize.X + ballSize.X) and
			math.abs(ballPos.Y - hitboxPos.Y) <= (hitboxSize.Y + ballSize.Y) and
			math.abs(ballPos.Z - hitboxPos.Z) <= (hitboxSize.Z + ballSize.Z)

		for _, part in ipairs(script.Parent:GetTouchingParts()) do
			if part:IsDescendantOf(hitbox.Parent) then
				touching = true
				break
			end
		end

		if touching then
			local clampedPower = math.clamp(power, 25, 100)
			local adjustedPower = math.clamp(clampedPower / 100 * 150, 20, 150)
			local adjustedHeight = (height / 100 * 60) * clampedPower / 100

			print("height: " .. tostring(height) .. " adjusted: " .. tostring(adjustedHeight))
			script.Parent.LastOwner.Value = player
			resetBall()
			shoot(adjustedPower, adjustedHeight, character, clampedPower)
			confirmed = true
			break
		end
		task.wait(0.05)
	end

	if not confirmed then
		print("no shot")
	end
end)
1 Like

I would recommend doing a raycast to check if the ball is on the ground. I’m not sure how reliable GetTouchingParts is for this (that’s usually used for intersecting parts). Per the docs: “Parts that are adjacent but not intersecting are not considered touching”.

For your main problem, I would check the network ownership of the ball. You can turn on highlights for that in the studio settings (under physics I believe). You probably want it to always be server owned though I’m not sure.

The teleporting is probably from the ownership switching between the server and the client.

The ball always has a grey box around it, which I think means the server is the network owner.

1 Like

You can check the ownership with:

ball:GetNetworkOwner() -- nil is the server

If it’s not changing though, that means the teleporting isn’t from the network owner switching.

1 Like

Wait, I believe I have found the issue.

After kicking the ball a few times, it starts to glitch out, and the box becomes red.
I made a function to print the network owner, and it printed my name. I think this is the issue, because when it becomes red, it glitches out.

I’ll try my best to fix it, thank you.

Managed to completely fix the issue.

Thank you so much, really saved me from wasting a lot more time trying to find dumb solutions.

2 Likes