# Best way to clamp marble velocity?

I’m making a player-controlled marble thing, and I’m having a bit of trouble coming up with a solution to clamp the maximum force applied to the marble. I think I have something that works, however, controlling the marble becomes finnicky when it actually executes, it becomes hard to steer and starts veering off in inaccurate directions.

Not really sure what else I can try here. Thanks.

``````local function clampForce(forceToAdd) --//Prevents too much velocity from being applied
local wasClampingNeeded = false
local totalForce = marble.AssemblyLinearVelocity

--//Check each axis to determine if too much velocity is applied
local clampedAxes = {totalForce.X, totalForce.Y, totalForce.Z}

for i = 1, 3 do
local axis = (i == 1 and "X") or (i == 2 and "Y") or "Z"
local currentVel = math.abs(totalForce[axis])

if currentVel > MAX_VELOCITY then
local newVel = (totalForce.Unit*MAX_VELOCITY)[axis]

wasClampingNeeded = true
clampedAxes[i] = newVel
end
end

if wasClampingNeeded then
marble.AssemblyLinearVelocity = Vector3.new(unpack(clampedAxes))
end
end
``````

^ clamping code that executes every step

isn’t this easier?

``````local function clampVector(vector)
local dist = vector.Magnitude / MAX_VELOCITY
return vector.Unit * (dist < 1 and dist or 1)
end
``````

and faster. Unless you want a specific clamp for each axis

1 Like

Is there a particular reason why you’re clamping each axis independently? When you do it that way your marble will move faster diagonally than it will on a straight axis because the valid space your velocity can be is the shape of a cube instead of a sphere.

Usually when I do clamps like this I just set the vector to its unit * maxvel.

``````local MAX_VEL = 8
local function getClampedVel(curVel)
return curVel.Unit * math.min(MAX_VEL, curVel.Magnitude)
end
``````

I usually remove the Y axis because I don’t usually clamp gravity though. And putting Y in there can mess with your controls if you only have X,Z controls.

``````local MAX_VEL = 8
local function getClampedXZVel(curVel)
local yVel = Vector3.new(0,curVel.Y,0)
curVel*= Vector3.new(1,0.0000001,1) --Can't be zero so that .Unit will never have Vector3.new()
return curVel.Unit * math.min(MAX_VEL, curVel.Magnitude) + yVel
end
``````

It’s worth noting that when I tested your code in my little test place, my marble controlled just fine which might mean it is an issue elsewhere.

My marble code

I just put this in starterPlayerScripts in a blank baseplate. I have removed your code and just used my version of clamping, but your code worked just fine.

``````local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local INPUT_VEL_PER_SECOND = 60
local MAX_VELOCITY = 35

local character = game.Players.LocalPlayer.Character
while not character do wait() character = game.Players.LocalPlayer.Character end
character.Parent = game.Lighting

local marble = Instance.new("Part")
marble.Shape = Enum.PartType.Ball
marble.TopSurface = Enum.SurfaceType.Smooth
marble.BottomSurface = Enum.SurfaceType.Smooth
marble.Size = Vector3.new(4,4,4)
marble.Position = Vector3.new(0,5,0)
marble.Parent = game.Workspace

local control = function(dt)
local inputDirection = Vector3.new(
(UserInputService:IsKeyDown(Enum.KeyCode.A) and 1 or 0) + (UserInputService:IsKeyDown(Enum.KeyCode.D) and -1 or 0),
0,
(UserInputService:IsKeyDown(Enum.KeyCode.S) and -1 or 0) + (UserInputService:IsKeyDown(Enum.KeyCode.W) and 1 or 0)
)

local additionalForce = inputDirection * INPUT_VEL_PER_SECOND * dt

local projectedVelocity = marble.AssemblyLinearVelocity + additionalForce
marble.AssemblyLinearVelocity = projectedVelocity.Unit * Vector3.new(1,0,1) * math.min(MAX_VELOCITY, projectedVelocity.Magnitude) + Vector3.new(0, projectedVelocity.Y, 0)

local projectedVelocity = marble.AssemblyLinearVelocity + additionalForce
local yProjection = projectedVelocity.Y
projectedVelocity*=Vector3.new(1,-0.000001,1)
marble.AssemblyLinearVelocity = projectedVelocity.Unit * math.min(MAX_VELOCITY, projectedVelocity.Magnitude) + Vector3.new(0, yProjection, 0)
end

local camera = game.Workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable

local cam = function()
camera.CFrame = CFrame.new(marble.Position + Vector3.new(0,10,-25), marble.Position)
end

RunService:BindToRenderStep("control", Enum.RenderPriority.Character.Value, control)
RunService:BindToRenderStep("cam", Enum.RenderPriority.Camera.Value + 1, cam)
``````

Huh. I left my computer for a moment and came back and the marble disappeared because of a Nan error. I’m too lazy to find it right now, but that’s a thing that can happen apparently (despite the fact I thought I fixed it). Weirdly it also removed one of the ramps in my test place despite the fact my code doesn’t affect those.

1 Like