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