Smooth ball movement

a simple way to get smooth SBB-style rolling without LinearVelocity explosions: drive the ball with AngularVelocity (torque-like) and cap the linear speed. High-ish friction + low elasticity keeps wall hits sane.

here is a tut to do it

  • Make a Ball part (Shape = Ball, Anchored = false).
  • Inside it, add an Attachment and an AngularVelocity constraint.
  • Drop this LocalScript in StarterPlayerScripts (it finds/creates the pieces, rolls relative to camera, and clamps speed).
-- LocalScript (StarterPlayerScripts)
local Players = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local cam = workspace.CurrentCamera

local ball = workspace:WaitForChild("Ball")
local att = ball:FindFirstChildOfClass("Attachment") or Instance.new("Attachment", ball)
local av = ball:FindFirstChildOfClass("AngularVelocity") or Instance.new("AngularVelocity", ball)
av.Attachment0 = att
av.RelativeTo = Enum.ActuatorRelativeTo.World
av.MaxTorque = 20000
av.ReactionTorqueEnabled = true

-- grippy, non-bouncy
pcall(function()
	ball.CustomPhysicalProperties = PhysicalProperties.new(1, 0.8, 0, 0, 1)
end)

local move = Vector3.zero
local function setKey(k, down)
	local delta = down and 1 or -1
	if k == Enum.KeyCode.W then move += Vector3.new(0,0,-1)*delta end
	if k == Enum.KeyCode.S then move += Vector3.new(0,0, 1)*delta end
	if k == Enum.KeyCode.A then move += Vector3.new(-1,0,0)*delta end
	if k == Enum.KeyCode.D then move += Vector3.new( 1,0,0)*delta end
end

UIS.InputBegan:Connect(function(i,g) if not g then setKey(i.KeyCode, true) end end)
UIS.InputEnded:Connect(function(i,g) setKey(i.KeyCode, false) end)

local radius = ball.Size.X*0.5
local targetSpeed = 40   -- how fast it tries to roll (studs/s)
local maxSpeed = 60      -- hard cap

RunService.RenderStepped:Connect(function()
	-- movement dir relative to camera on XZ
	local dir = Vector3.new(move.X,0,move.Z)
	if dir.Magnitude > 0 then
		dir = cam.CFrame:VectorToWorldSpace(dir)
		dir = Vector3.new(dir.X,0,dir.Z).Unit
		-- roll: ω = (up × dir) * (speed / r)
		av.AngularVelocity = Vector3.new(0,1,0):Cross(dir) * (targetSpeed / math.max(0.01, radius))
	else
		av.AngularVelocity = Vector3.zero
	end

	-- clamp crazy speeds after wall hits
	local v = ball.AssemblyLinearVelocity
	local s = v.Magnitude
	if s > maxSpeed then
		ball.AssemblyLinearVelocity = v.Unit * maxSpeed
	end
end)

I know it’s a little copy and paste’y. but if you are a new dev and don’t understand some of it just try breaking it up and learning what parts do. You can give it to chat gpt or ask me questions about it. it is client so other players will not see it but you can use remote events to connect the client to server

1 Like