Need help on a Spring Physics Camera module

I found this open sourced module which creates a spring effect on the camera, everything works fine until I have few concerns:

You might want to check out the file with the module that I’ve modified for a clearer picture: SpringPhysicsCamera.rbxl (19.4 KB)

Or copy the code and create this structure if you don’t want to download files:

Code

image
Local Script:

local SpringCam = require(script.Parent:WaitForChild("CameraHandler"))
wait(5)
SpringCam:accelerate(0.25, 20, math.rad(5), 0, math.rad(130)) -- Damper, Speed, X,Y,Z	

CameraHandler:

local physics = require(script.PhysicsModule)

local cam = {}
	
cam.current = workspace.CurrentCamera
cam.recoil = {}
cam.recoil.x = nil
cam.recoil.y = nil
cam.recoil.z = nil
	
function cam:accelerate(damper,speed,x,y,z)
	if x then
		cam.recoil.x = physics.new{d=damper;s=speed;}
		cam.recoil.x.impulse(x)
	end
	if y then
		cam.recoil.y = physics.new{d=damper;s=speed;}
		cam.recoil.y.impulse(y)
	end
	if z then
		cam.recoil.z = physics.new{d=damper;s=speed;}
		cam.recoil.z.impulse(z)
	end
end
	
local function updatecam()	
	if cam.recoil.x and cam.recoil.y and cam.recoil.z then	
		cam.current.CoordinateFrame = cam.current.CoordinateFrame*CFrame.Angles(cam.recoil.x.p(),cam.recoil.y.p(),cam.recoil.z.p())
	end
end

game:GetService("RunService"):BindToRenderStep("RecoilCam",2000,function()
    updatecam()
end)

return cam

Physics Module:

local physics = {}

	local tick = tick
	local setmt	= setmetatable
	local sin = math.sin
	local cos = math.cos
	local atan2	= math.atan2
	local tan = math.tan
	local e = 2.718281828459045
	
function physics.new(data)
		local c0,c1
		local t0
		local d,s
		local target
		local cd
		if data then
			c0,c1=0,0
			t0=tick()
			d,s=data.d,data.s
			target=0
			cd=0
		else
			c0,c1=0,0
			t0=tick()
			d,s=1,1
			target=0
			cd=0
		end
		
		local function position()
			local t=tick()
			local sx=s*(t-t0)
			if d==1 then
				return (c0+c1*sx)/e^sx+target
			else
				return (c0*cos(cd*sx)+c1*sin(cd*sx))/e^(d*sx)+target
			end
		end
		
		local function velocity()
			local t=tick()
			local sx=s*(t-t0)
			if d==1 then
				return (c1*(s-sx)-c0)/e^sx
			else
				return s*((cd*c1-c0*d)*cos(cd*sx)-(cd*c0+c1*d)*sin(cd*sx))/e^(d*sx)
			end
		end
		
		return {

			impulse=function(v)
				local p0,v0=position(),(velocity()+v)/s
				t0=tick()
				c0=p0-target
				if d==1 then
					c1=v0+c0
				else
					cd=(1-d*d)^0.5
					c1=(v0+c0*d)/cd
				end
			end;
			p=position;
		}
	end
return physics

Play it in studio and the camera will shake after 5 seconds

It includes 3 scripts:

  • Local Script > The one who trigger the camera module
  • CameraHandler Module Script > Receives the signal from Local script and trigger the physics module script for calculations which returns a function and it will be stored in a table cam.recoil. Then call the returned function will return the actual camera angle. A render step will check is the angle presents and set the camera’s cframe.
  • PhysicsModule Module Script > For all the mathematical calculations (I didn’t modify it much)

My concern is how do I detect when the shake is done? It’s a complicated strucutre which doesn’t yield or return anything when the shake is called (unlike TweenService with Tween.Completed).
I tried methods like comparing values but it doesn’t work. Maybe there’s a way to get the shake total time and apply wait(time) to it but I can’t really figure it out.

Basicially I want the function updatecam() (which set the camera’s cframe) to stop running after the shake, any ideas?

6 Likes

You can check if the magnitude between the new CFrame and the old CFrame is zero

1 Like

1 Like