Shake
Create realistic shake effects, such as camera or object shakes.
Creating a shake is very simple with this module. For every shake,
simply create a shake instance by calling Shake.new()
. From
there, configure the shake however desired. Once configured,
call shake:Start()
and then bind a function to it with either
shake:OnSignal(...)
or shake:BindToRenderStep(...)
.
The shake will output its values to the connected function, and then automatically stop and clean up its connections once completed.
Shake instances can be reused indefinitely. However, only one shake
operation per instance can be running. If more than one is needed
of the same configuration, simply call shake:Clone()
to duplicate
it.
Example of a simple camera shake:
local priority = Enum.RenderPriority.Last.Value
local shake = Shake.new()
shake.FadeInTime = 0
shake.Frequency = 0.1
shake.Amplitude = 5
shake.RotationInfluence = Vector3.new(0.1, 0.1, 0.1)
shake:Start()
shake:BindToRenderStep(Shake.NextRenderName(), priority, function(pos, rot, isDone)
camera.CFrame *= CFrame.new(pos) * CFrame.Angles(rot.X, rot.Y, rot.Z)
end)
Shakes will automatically stop once the shake has been completed. Shakes can
also be used continuously if the Sustain
property is set to true
.
Fixing drift
If you are not controlling the initial CFrame of the camera (i.e. using Roblox's camera scripts), then you might run into odd behavior with the above example. This is because Roblox's camera scripts are influenced by the current CFrame value of the camera. Thus, the shake effect causes odd side-effects, especially noticeable at higher FPS.
The solution is to simply store the camera's CFrame before applying the shake CFrame, and then reapplying this stored CFrame value after rendering is complete (e.g. on Heartbeat).
local camCf: CFrame
shake:BindToRenderStep(Shake.NextRenderName(), priority, function(pos, rot, isDone)
-- Store the CFrame value that was set from Roblox's camera scripts:
camCf = camera.CFrame
camera.CFrame *= CFrame.new(pos) * CFrame.Angles(rot.X, rot.Y, rot.Z)
end)
RunService.Heartbeat:Connect(function()
-- Reapply the camera script CFrame before they run again.
-- Heartbeat runs AFTER Roblox has rendered, so it will not affect the camera this frame.
camera.CFrame = camCf
end)
Configuration
Here are some more helpful configuration examples:
local shake = Shake.new()
-- The magnitude of the shake. Larger numbers means larger shakes.
shake.Amplitude = 5
-- The speed of the shake. Smaller frequencies mean faster shakes.
shake.Frequency = 0.1
-- Fade-in time before max amplitude shake. Set to 0 for immediate shake.
shake.FadeInTime = 0
-- Fade-out time. Set to 0 for immediate cutoff.
shake.FadeOutTime = 0
-- How long the shake sustains full amplitude before fading out
shake.SustainTime = 1
-- Set to true to never end the shake. Call shake:StopSustain() to start the fade-out.
shake.Sustain = true
-- Multiplies against the shake vector to control the final amplitude of the position.
-- Can be seen internally as: position = shakeVector * fadeInOut * positionInfluence
shake.PositionInfluence = Vector3.one
-- Multiplies against the shake vector to control the final amplitude of the rotation.
-- Can be seen internally as: position = shakeVector * fadeInOut * rotationInfluence
shake.RotationInfluence = Vector3.new(0.1, 0.1, 0.1)
Types
UpdateCallbackFn
Properties
Amplitude
Shake.Amplitude:
number
Amplitude of the overall shake. For instance, an amplitude of 3
would mean the
peak magnitude for the outputted shake vectors would be about 3
.
Defaults to 1
.
Frequency
Shake.Frequency:
number
Frequency of the overall shake. This changes how slow or fast the shake occurs.
Defaults to 1
.
FadeInTime
Shake.FadeInTime:
number
How long it takes for the shake to fade in, measured in seconds.
Defaults to 1
.
FadeOutTime
Shake.FadeOutTime:
number
How long it takes for the shake to fade out, measured in seconds.
Defaults to 1
.
SustainTime
Shake.SustainTime:
number
How long it takes for the shake sustains itself after fading in and before fading out.
To sustain a shake indefinitely, set Sustain
to true
, and call the StopSustain()
method to stop the sustain
and fade out the shake effect.
Defaults to 0
.
Sustain
Shake.Sustain:
boolean
If true
, the shake will sustain itself indefinitely once it fades
in. If StopSustain()
is called, the sustain will end and the shake
will fade out based on the FadeOutTime
.
Defaults to false
.
PositionInfluence
Shake.PositionInfluence:
Vector3
This is similar to Amplitude
but multiplies against each axis
of the resultant shake vector, and only affects the position vector.
Defaults to Vector3.one
.
RotationInfluence
Shake.RotationInfluence:
Vector3
This is similar to Amplitude
but multiplies against each axis
of the resultant shake vector, and only affects the rotation vector.
Defaults to Vector3.one
.
TimeFunction
Shake.TimeFunction:
(
)
→
number
The function used to get the current time. This defaults to
time
during runtime, and os.clock
otherwise. Usually this
will not need to be set, but it can be optionally configured
if desired.
Functions
new
Construct a new Shake instance.
InverseSquare
Apply an inverse square intensity multiplier to the given vector based on the distance away from some source. This can be used to simulate shake intensity based on the distance the shake is occurring from some source.
For instance, if the shake is caused by an explosion in the game, the shake can be calculated as such:
local function Explosion(positionOfExplosion: Vector3)
local cam = workspace.CurrentCamera
local renderPriority = Enum.RenderPriority.Last.Value
local shake = Shake.new()
-- Set shake properties here
local function ExplosionShake(pos: Vector3, rot: Vector3)
local distance = (cam.CFrame.Position - positionOfExplosion).Magnitude
pos = Shake.InverseSquare(pos, distance)
rot = Shake.InverseSquare(rot, distance)
cam.CFrame *= CFrame.new(pos) * CFrame.Angles(rot.X, rot.Y, rot.Z)
end
shake:BindToRenderStep("ExplosionShake", renderPriority, ExplosionShake)
end
NextRenderName
Shake.
NextRenderName
(
) →
string
Returns a unique render name for every call, which can
be used with the BindToRenderStep
method optionally.
shake:BindToRenderStep(Shake.NextRenderName(), ...)
Start
Shake:
Start
(
) →
(
)
Start the shake effect.
NOTE
This must be called before calling Update
. As such, it should also be
called once before or after calling OnSignal
or BindToRenderStep
methods.
Stop
Shake:
Stop
(
) →
(
)
Stops the shake effect. If using OnSignal
or BindToRenderStep
, those bound
functions will be disconnected/unbound.
Stop
is automatically called when the shake effect is completed or when the
Destroy
method is called.
IsShaking
Shake:
IsShaking
(
) →
boolean
Returns true
if the shake instance is currently running,
otherwise returns false
.
StopSustain
Shake:
StopSustain
(
) →
(
)
Schedules a sustained shake to stop. This works by setting the
Sustain
field to false
and letting the shake effect fade out
based on the FadeOutTime
field.
Update
Calculates the current shake vector. This should be continuously
called inside a loop, such as RunService.Heartbeat
. Alternatively,
OnSignal
or BindToRenderStep
can be used to automatically call
this function.
Returns a tuple of three values:
position: Vector3
- Position shake offsetrotation: Vector3
- Rotation shake offsetcompleted: boolean
- Flag indicating if the shake is finished
local hb
hb = RunService.Heartbeat:Connect(function()
local offsetPosition, offsetRotation, isDone = shake:Update()
if isDone then
hb:Disconnect()
end
-- Use `offsetPosition` and `offsetRotation` here
end)
OnSignal
Bind the Update
method to a signal. For instance, this can be used
to connect to RunService.Heartbeat
.
All connections are cleaned up when the shake instance is stopped or destroyed.
local function SomeShake(pos: Vector3, rot: Vector3, completed: boolean)
-- Shake
end
shake:OnSignal(RunService.Heartbeat, SomeShake)
BindToRenderStep
Shake:
BindToRenderStep
(
name:
string
,
--
Name passed to RunService:BindToRenderStep
priority:
number
,
--
Priority passed to RunService:BindToRenderStep
callbackFn:
UpdateCallbackFn
) →
(
)
Bind the Update
method to RenderStep.
All bound functions are cleaned up when the shake instance is stopped or destroyed.
local renderPriority = Enum.RenderPriority.Camera.Value
local function SomeShake(pos: Vector3, rot: Vector3, completed: boolean)
-- Shake
end
shake:BindToRenderStep("SomeShake", renderPriority, SomeShake)
Clone
Creates a new shake with identical properties as this one. This does not clone over playing state, and thus the cloned instance will be in a stopped state.
A use-case for using Clone
would be to create a module
with a list of shake presets. These presets can be cloned
when desired for use. For instance, there might be presets
for explosions, recoil, or earthquakes.
--------------------------------------
-- Example preset module
local ShakePresets = {}
local explosion = Shake.new()
-- Configure `explosion` shake here
ShakePresets.Explosion = explosion
return ShakePresets
--------------------------------------
-- Use the module:
local ShakePresets = require(somewhere.ShakePresets)
local explosionShake = ShakePresets.Explosion:Clone()
Destroy
Shake:
Destroy
(
) →
(
)
Alias for Stop()
.