Screen shake is a visual effect where the camera shakes, sometimes violently, to create a feeling of impact or chaos. Unless your game is intended to be relaxing, it would probably benefit from some precisely-tuned screen shake.
Screen shake is one of the core pillars of "game juice". "Game juice" refers to all the little details in a game that make it feel fun to play.
If you are interested in learning more about the concept of "game juice", we recommend The Art of Screenshake by Jan Willem Nijman.
Camera Shake in Godot 3.2
This method of implementing camera shake in Godot was created by the YouTuber "Game Endeavor". Watch his video here.
This method is the best because it is modular. It contains all the screen shake logic in a single node, which you can attach to any Camera2D node. Not only that, but is easily customizable, allowing you to create different intensities for different events in your game. And if multiple screen shake events happen in quick succession, this method will ensure the more important events are not overwritten. Read on to learn more.
This solution is designed to be a direct child of a camera node. Add a child to the camera of type Node.
Rename the node to "ScreenShake" and save it as it’s own scene.
Open the ScreenShake scene by clicking the movie clapper icon.
Add a node of type Tween and rename it to "ShakeTween".
A tween node, short for "in-between", smoothly animates a node’s properties over time. We will use the tween node to change the camera’s position to create the screen shake effect. Shaking the camera can be a jarring effect, but the tween will smoothly transition the camera from shaking to still or to shaking at a different intensity.
Next, add two Timer nodes as children of the ScreenShake node. Name one timer "Frequency" and other timer "Duration".
Attach a script to the ScreenShake node.
extends Node
const TRANS = Tween.TRANS_SINE
const EASE = Tween.EASE_IN_OUT
var amplitude = 0
var priority = 0
onready var camera = get_parent()
func start(duration = 0.2, frequency = 15, amplitude = 16, priority = 0):
if (priority >= self.priority):
self.priority = priority
self.amplitude = amplitude
$Duration.wait_time = duration
$Frequency.wait_time = 1 / float(frequency)
$Duration.start()
$Frequency.start()
_new_shake()
func _new_shake():
var rand = Vector2()
rand.x = rand_range(-amplitude, amplitude)
rand.y = rand_range(-amplitude, amplitude)
$ShakeTween.interpolate_property(camera, "offset", camera.offset, rand, $Frequency.wait_time, TRANS, EASE)
$ShakeTween.start()
func _reset():
$ShakeTween.interpolate_property(camera, "offset", camera.offset, Vector2(), $Frequency.wait_time, TRANS, EASE)
$ShakeTween.start()
priority = 0
To start a screen shake, you will want to call the "start" function. The "duration" is how long the effect will last in seconds. The "frequency" is how many camera times per second the camera will change direction. The "amplitude" is far from center the camera will move. You can also set the "priority" of the effect. A higher priority effect will not be overwritten by a low priority effect. Big explosions will continue uninterupted by smaller explosions.
Now let’s connect the two timers to this script.
Select the Frequency node. Select the "Node" tab on the Inspector window. Make sure you are on the "Signals" section. Double-click the "timeout()" signal. Connect it to the ScreenShake script.
func _on_Frequency_timeout() -> void:
_new_shake()
Now let’s to the same for the Duration timer. Double-click it’s timeout signal and connect it to the ScreenShake node.
func _on_Duration_timeout() -> void:
_reset()
$Frequency.stop()
Now that ScreenShake node is set up, you can attach a script to the Camera. You can define functions to start the screen shake effect with preset values.
extends Camera2D
func small_shake -> void:
$ScreenShake.start(0.1, 15, 4, 0)
June 27, 2020
Hi Diego,
I hope you’re well bud. I’m having an issue with the Screenshake code. on line 17 $Frequency.wait_time = 1 / float(frequency) The error I’m getting is Invalid set index “wait_time” (on base: “null instance”) with value of type “float”. I’m also using Godot 3.2.1 so not sure what the problem is or what the error even means, is this a Godot 3.2.1 issue? as I think you used 3.1 whilst making the tutorials. any help on this matter would be greatly appreciated. love the tutorials hope you continue to make many more of them. Take care, stay safe and thanks again. Gary
June 27, 2020
Hey Gary,
My first instinct with that error is that “$Frequency” is not defined or is defined in the wrong location. The dollar sign syntax is shorthand for referencing a direct child node of the node you’re writing the script for.
Did you define the two timer nodes named “Frequency” and “Duration”? Are they both direct children of the “ScreenShake” node?
June 28, 2020
Diego,
I got throught the series in the end. I realised I had a spelling mistake on the Frequency timer node, I typed in Fequency and of course the code was referring Frequency. I really have to watch out for typos as its a Game killer. I’m making a platformer and there are certain features I still want to impliment (enemy fire and the likes) so these videos have been invaluable to me, so Thanks so much for the series and look forward to watching whatever Godot tutorials you do next.
Gary
June 28, 2020
I’m glad you were able to figure it out.
And thanks for the kind words. Good luck with your platformer. I hope you are able to make something you’re proud of.
April 15, 2021
This is fantastic and so simple!
I added the effect to my space shooter RTS when ships explode and looks amazing!
Thanks!