5, 50, 4, 4 are the default values if no arguments are passed when creating a new spring object, i.e spring.new(). And I get this result when using the default spring values
But then when I do spring.new(5, 50, 4, 4), which is just the default values passed as arguments, I get a wildly different result. Whats going on?
-- Constants
local ITERATIONS = 8
-- Module
local SPRING = {}
-- Functions
function SPRING.new(self, mass, force, damping, speed)
local spring = {
Target = Vector3.new();
Position = Vector3.new();
Velocity = Vector3.new();
Mass = mass or 5;
Force = force or 50;
Damping = damping or 4;
Speed = speed or 4;
}
function spring.shove(self, force)
local x, y, z = force.X, force.Y, force.Z
if x ~= x or x == math.huge or x == -math.huge then
x = 0
end
if y ~= y or y == math.huge or y == -math.huge then
y = 0
end
if z ~= z or z == math.huge or z == -math.huge then
z = 0
end
self.Velocity = self.Velocity + Vector3.new(x, y, z)
end
function spring.update(self, dt)
local scaledDeltaTime = math.min(dt,1) * self.Speed / ITERATIONS
for i = 1, ITERATIONS do
local iterationForce= self.Target - self.Position
local acceleration = (iterationForce * self.Force) / self.Mass
acceleration = acceleration - self.Velocity * self.Damping
self.Velocity = self.Velocity + acceleration * scaledDeltaTime
self.Position = self.Position + self.Velocity * scaledDeltaTime
end
return self.Position
end
return spring
end
-- Return
return SPRING
the module include self which means it expects spring:new() instead spring.new().
Also the spring uses semi-implicit euler integration. Therefore expect errors at large timesteps.
Quentys spring module is better as it uses an exact solution.
comparing to wikipedia we can see why it uses math.exp a lot.
If GenerateRecoil() gets called too many times within a short period of time, the recoil gets progressively larger and larger, I’m guessing this is because spring velocity is stacking every time the function is called.
How can I solve this problem? I’ve tried setting the spring’s velocity back to 0 after every time fireShot() is called, but this doesn’t work.
EDIT: I modified the time and duration variables as well as the spring speed and damper. After some trial and error I managed to find values that got the springs to work correctly.
-- Local function to handle a single shot
local function fireShot()
-- Prevent firing until animation ends
canFire = false
-- Play animations and sounds
fireVMTrack:Play()
fireSound:Play()
GenerateRecoil()
task.wait(0.1)
canFire = true
end
function GenerateRecoil()
time = duration -- Reset the recoil timer
local X = settingsModule.getRecoilPattern(index)[1]
local Y = settingsModule.getRecoilPattern(index)[2]
local recoil = Vector3.new(X, Y, 0)
recoilSpring:Impulse((recoil) * deltaTime * 60)
index += 1
end
function Update(dt)
deltaTime = dt
local recoilCam = recoilSpring.Position
-- apply recoil
if time > 0 then
camera.CFrame *= CFrame.Angles(math.rad(recoilCam.X), math.rad(recoilCam.Y),0)
time -= dt
end
end