Lesson 09
GlitchFall should feel corrupted and alive. We add cheap, high-impact effects: floating/tilted geometry, a subtle pickup pop, and a screen tint on hazard hits — all with tools you already know.
Take a few of the scenery meshes from Lesson 2 and make them hover and rotate. Create a tiny reusable script glitch_float.gd and attach it to any MeshInstance3D you want to animate:
extends MeshInstance3D
# Gentle bob + spin to make static geometry feel unstable/corrupted.
@export var bob_height: float = 0.4
@export var bob_speed: float = 2.0
@export var spin_speed: float = 0.6
var _base_y: float = 0.0
var _t: float = 0.0
func _ready() -> void:
_base_y = position.y
func _process(delta: float) -> void:
_t += delta
position.y = _base_y + sin(_t * bob_speed) * bob_height
rotate_y(spin_speed * delta)@export makes it tweakable
@export exposes a variable in the Inspector, so you can give each floating shape different bob height and speed without editing code. Reuse one script across many nodes, vary the numbers per instance.
glitch_float.gd to a few scenery meshes. Tilt some of them (set an odd rotation) before running so they float at broken angles.Give fragments a quick scale-up-and-vanish instead of an instant disappear. Update fragment.gd's collection to tween before freeing:
func _on_body_entered(body: Node3D) -> void:
if body.is_in_group("player"):
collected.emit(self)
_play_pickup_and_free()
func _play_pickup_and_free() -> void:
# Stop detecting further hits during the animation.
set_deferred("monitoring", false)
# Scale the fragment up quickly, then remove it.
var tw: Tween = create_tween()
tw.tween_property(self, "scale", scale * 1.8, 0.15)
tw.tween_callback(queue_free)Area3D has no modulate
Unlike 2D CanvasItem nodes, 3D nodes have no simple modulate alpha, so we can't fade them in one line. A scale pop is the clean, reliable cross-node effect. If you want a fade too, you'd tween the mesh material's albedo_color alpha instead — but the scale pop alone reads well.
Flash the screen red when the player hits a hazard. Add a full-screen ColorRect to the HUD and pulse it from Main.
HUD, add a ColorRect named DamageFlash. Set its anchors to Full Rect so it covers the screen, color to red, and its Modulate alpha to 0 (invisible).@onready var damage_flash: ColorRect = $HUD/DamageFlash
func _on_player_hit(_body: Node3D) -> void:
_respawn_player()
_flash_damage()
func _flash_damage() -> void:
damage_flash.modulate.a = 0.5
var tw: Tween = create_tween()
tw.tween_property(damage_flash, "modulate:a", 0.0, 0.4)Checkpoint — expected state
glitch_float.gd giving scenery an unstable bob and spin.