Welcome to the fifth and final lesson of our series. The core mechanics and game loop are functional, but the game still feels like a prototype. Game "juice" is what separates a tutorial project from a finished product.
In this module, we will add audio feedback, a visceral screen shake when crashing, and a "Get Ready" state so the game doesn't instantly drop the bird upon opening.
1. Adding Audio Feedback#
Audio is crucial for player feedback. We need sounds for flapping, scoring, and crashing.
- In your
Mainscene, add three AudioStreamPlayer nodes (not 2D, as we want UI sounds to play at constant volume). Name themFlapSound,ScoreSound, andCrashSound. - Drag your respective
.wavor.oggfiles from theres://Assets/folder into the Stream property of each node.
Now, let's trigger them in Main.gd:
@onready var score_sound: AudioStreamPlayer = $ScoreSound
@onready var crash_sound: AudioStreamPlayer = $CrashSound
func add_score() -> void:
score += 1
score_label.text = str(score)
# Randomize pitch slightly for variety so the sound doesn't get annoying
score_sound.pitch_scale = randf_range(0.9, 1.1)
score_sound.play()
func _on_bird_crashed() -> void:
crash_sound.play()
# ... existing game over logic ...
(Remember to also trigger your FlapSound inside the Bird.gd script when the flap input is pressed!)
2. Screen Shake Component#
When the bird hits a pipe, we want the player to feel the impact. A quick camera shake is perfect for this.
- Add a Camera2D node to your
Mainscene and make it the Current camera. - Attach a new script to the
Camera2DnamedCameraShake.gd.
extends Camera2D
var shake_intensity: float = 0.0
var shake_fade: float = 5.0
func apply_shake(intensity: float = 10.0) -> void:
shake_intensity = intensity
func _process(delta: float) -> void:
if shake_intensity > 0:
# Apply random offset based on intensity
offset = Vector2(randf_range(-1, 1), randf_range(-1, 1)) * shake_intensity
# Fade out the shake over time
shake_intensity = lerpf(shake_intensity, 0.0, shake_fade * delta)
else:
offset = Vector2.ZERO
Now, in your Main.gd script's _on_bird_crashed() function, simply call $Camera2D.apply_shake(15.0).
3. Creating the "Get Ready" State#
Right now, gravity applies immediately. We want the bird to hover until the player makes their first flap.
- In
Bird.gd, add a boolean variable:var game_started: bool = false. - Wrap the gravity logic in an
ifstatement.
func _physics_process(delta: float) -> void:
# Only apply gravity if the game has started
if not is_on_floor() and game_started:
velocity.y += gravity * delta
if Input.is_action_just_pressed("flap") and is_alive:
game_started = true # First flap starts the game
velocity.y = jump_velocity
# ... play flap sound ...
- Back in
Main.gd, you will also want to wait to start yourSpawnTimerand the background shader until the bird emits a newstartedsignal.
Congratulations! You have built a complete, polished 2D game in Godot 4. You can now take these fundamentals—state management, physics overrides, and shader manipulation—and apply them to much larger projects.
.webp&w=1920&q=75)
