I need some assistance with the math on this one.
I am creating a ball that the player can control, and currently I am using a VectorForce to move the ball. So far it works, however I am attempting to tune it to make it feel more responsive, less sluggish.
I’d like to be able to create a variable to define a top speed for the ball, and to adjust the force relative to the ball’s current speed so that it doesn’t go faster than the top speed.
Any ideas on how to accomplish this?
my apologies if it’s simple math, my brain isn’t thinking straight atm
4 Likes
show us your current code so we can build on it
Here is the code that sets the force.
force.Force = char.Humanoid.MoveDirection * Vector3.new(25000,0,25000)
The force
variable is the VectorForce. char
is the character.
1 Like
By setting the force to some constant multiplied by the move direction, you’re effectively setting the acceleration to something with a constant magnitude. You want the velocity to have a constant magnitude, so it’s not quite right. Instead you should set the acceleration to something that scales with the difference between the target velocity and the current velocity.
E.g.:
local rollSpeed = 10
local P = 1
function unitOr0(vector)
return vector.Magnitude > 0 and vector.Unit or Vector3.new()
end
function updateRollForce(dt)
local targetVelocity = unitOr0(inputRollDir) * rollSpeed
local differenceVelocity = targetVelocity - ball.Velocity
rollForce.Force = differenceVelocity * ball:GetMass() * P
end
where inputRollDir
returns a vector representing the direction the player wants to move (e.g. humanoid:GetMoveDirection
and P
is some number that makes the ball match the rollSpeed
more or less “aggressively”. E.g. a P
of 1 makes the ball get to a speed of 10 in like 5 seconds, but a P
of 100 makes it reach it in just a few frames.
The unitOr0
function is just to deal with cases where inputRollDir
is (0, 0, 0) so inputRollDir.Unit
would be (nan, nan, nan)
and break stuff. We need the unit vector of the movement vector because otherwise diagonal movement would be faster than movement in 1 direction (W + A = (-1, 0, -1)
).
EDIT:
This system has some limitations and caveats:
- Setting a high
rollSpeed
or P
, or even just having the system respond to the ball moving at very high velocities and changing direction, can cause the force to be so large that the ball “spazzes out” and flings into low earth orbit. This can be prevented by putting some upper limit to the magnitude of the force e.g. by comparing the some maxForceMagnitude
variable. This reduces responsiveness at very high speed differences but shouldn’t be a problem in most cases.
- As the velocity gets closer and closer to the target velocity, the force gets smaller and smaller. This means the target
rollSpeed
might never be reached, especially for small values of P
. Slightly increase the rollSpeed
to compensate for this, and check the actual velocity like so: print(string.format("%.2f", ball.Velocity.Magnitude))
. Alternatively, use a “PI controller” instead (this is just a “P” controller), but that’s a much more advanced topic and a bit harder to implement.
- This doesn’t respect when the ball has speed from something other than the player trying to control it. E.g. if the ball is rolling downhill at 20 studs/s or a jump pad shoots the ball at 50 studs/s, the control script will try to stop it in a way that makes it seem pretty unnatural. Temporarily disable the force being applied for a few seconds, or implement a more complicated movement system that can deal with this properly (e.g. the “Source engine” movement physics).
6 Likes
I’m looking for a way to be able to define a variable as the max speed. Is there a way to modify your functions to accomplish this?
1 Like
You just set the rollSpeed
variable to something at the top of your script
E.g.
local rollSpeed = 10
local P = 1
This should work in theory
local maxVelocity = 100
Part:GetPropertyChangedSignal('Velocity'):Connect(function()
local X,Y,Z
if math.abs(Part.Velocity.X)>maxVelocity then
X = (Part.Velocity.X/math.abs(Part.Velocity.X))*maxVelocity
else
X = Part.Velocity.X
end
if math.abs(Part.Velocity.Y)>maxVelocity then
Y = (Part.Velocity.Y/math.abs(Part.Velocity.Y))*maxVelocity
else
Y = Part.Velocity.Y
end
if math.abs(Part.Velocity.Z)>maxVelocity then
Z = (Part.Velocity.Z/math.abs(Part.Velocity.Z))*maxVelocity
else
Z = Part.Velocity.Z
end
Part.Velocity = Vector3.new(X,Y,Z)
end)
I think
try it out
The issue is that your code doesn’t seem to provide much acceleration at all. How should I modify it to increase the acceleration?
It’s probably simple, my brain isn’t working atm
Nevermind, figured it out. Completely missed the P variable!
1 Like