Animaties
Sprite animaties, tweens, en visuele effecten.
Overzicht
┌─────────────────────────────────────────────────────────────────┐
│ Animatie Types │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Sprite Animaties Tween Animaties │
│ ───────────────── ──────────────── │
│ • Frame-by-frame • Property interpolation │
│ • Sprite sheets • Easing curves │
│ • AnimatedSprite2D • Code-driven │
│ │
│ Particle Effects Shader Animaties │
│ ───────────────── ────────────────── │
│ • GPUParticles2D • Material animations │
│ • Emitters • Dissolve/fade effects │
│ • Physics-based • Outline glow │
│ │
└─────────────────────────────────────────────────────────────────┘
Sprite Sheet Formaat
Layout
┌───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ ← Frames horizontaal
├───┼───┼───┼───┤
│ 4 │ 5 │ 6 │ 7 │ ← Tweede rij (optioneel)
└───┴───┴───┴───┘
Frame Size: 256x256 px (voor karakters)
Frame Count: 4-8 per animatie
FPS: 10-12 voor walk, 8 voor idle
Animatie Definities
# character_animations.gd
extends AnimatedSprite2D
func _ready() -> void:
# Setup animaties vanuit sprite sheet
sprite_frames = SpriteFrames.new()
_add_animation("idle", 0, 4, 8.0, true)
_add_animation("walk", 4, 4, 12.0, true)
_add_animation("action", 8, 4, 10.0, false)
func _add_animation(name: String, start_frame: int, frame_count: int, fps: float, loop: bool) -> void:
sprite_frames.add_animation(name)
sprite_frames.set_animation_speed(name, fps)
sprite_frames.set_animation_loop(name, loop)
var texture = preload("res://assets/characters/milena/spritesheet.png")
var frame_size = Vector2(256, 256)
for i in range(frame_count):
var atlas = AtlasTexture.new()
atlas.atlas = texture
atlas.region = Rect2(
(start_frame + i) * frame_size.x,
0,
frame_size.x,
frame_size.y
)
sprite_frames.add_frame(name, atlas)
Karakter Animaties
Walk Cycle
# 4-frame walk cycle met bobbing
const WALK_FRAMES = 4
const WALK_FPS = 12.0
const BOB_AMPLITUDE = 4.0
var walk_time: float = 0.0
func _process(delta: float) -> void:
if is_moving:
walk_time += delta * WALK_FPS
animated_sprite.play("walk")
# Bobbing effect
var bob = sin(walk_time * PI) * BOB_AMPLITUDE
animated_sprite.position.y = base_y + bob
else:
animated_sprite.play("idle")
animated_sprite.position.y = base_y
Idle Breathing
# Subtle idle animation
func _setup_idle_breathing() -> void:
var tween = create_tween()
tween.set_loops()
tween.tween_property(body_sprite, "scale", Vector2(1.0, 1.02), 1.5)
tween.tween_property(body_sprite, "scale", Vector2(1.0, 1.0), 1.5)
Action Animations
signal action_completed()
func play_action(action_name: String) -> void:
animated_sprite.play(action_name)
# Wait for animation to finish
await animated_sprite.animation_finished
# Return to idle
animated_sprite.play("idle")
action_completed.emit()
# Planten animatie
func plant_seed() -> void:
# Squat down
var tween = create_tween()
tween.tween_property(self, "position:y", position.y + 20, 0.3)
await play_action("plant")
# Stand back up
tween.tween_property(self, "position:y", position.y, 0.3)
UI Animaties
Button Hover
# button_animated.gd
extends Button
@onready var base_scale: Vector2 = scale
func _ready() -> void:
mouse_entered.connect(_on_hover_start)
mouse_exited.connect(_on_hover_end)
pressed.connect(_on_pressed)
func _on_hover_start() -> void:
var tween = create_tween()
tween.tween_property(self, "scale", base_scale * 1.05, 0.1)
tween.set_ease(Tween.EASE_OUT)
func _on_hover_end() -> void:
var tween = create_tween()
tween.tween_property(self, "scale", base_scale, 0.1)
func _on_pressed() -> void:
var tween = create_tween()
tween.tween_property(self, "scale", base_scale * 0.95, 0.05)
tween.tween_property(self, "scale", base_scale, 0.1)
Panel Slide In
func show_panel() -> void:
visible = true
# Start off-screen
position.x = get_viewport_rect().size.x
# Slide in
var tween = create_tween()
tween.tween_property(self, "position:x", target_x, 0.3)
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_BACK)
func hide_panel() -> void:
var tween = create_tween()
tween.tween_property(self, "position:x", get_viewport_rect().size.x, 0.2)
tween.set_ease(Tween.EASE_IN)
tween.tween_callback(func(): visible = false)
Notification Pop
func animate_in() -> void:
modulate.a = 0
scale = Vector2(0.5, 0.5)
var tween = create_tween()
tween.set_parallel(true)
tween.tween_property(self, "modulate:a", 1.0, 0.2)
tween.tween_property(self, "scale", Vector2(1.0, 1.0), 0.3)
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_BACK)
func animate_out() -> void:
var tween = create_tween()
tween.set_parallel(true)
tween.tween_property(self, "modulate:a", 0.0, 0.2)
tween.tween_property(self, "position:y", position.y - 20, 0.2)
tween.tween_callback(queue_free)
Particle Effects
Harvest Sparkles
# harvest_particles.tscn
extends GPUParticles2D
func _ready() -> void:
emitting = true
one_shot = true
# Auto cleanup
await get_tree().create_timer(lifetime + 0.5).timeout
queue_free()
# ParticleProcessMaterial settings:
# - Direction: Up (0, -1)
# - Spread: 45 degrees
# - Initial Velocity: 50-100
# - Gravity: (0, 98)
# - Scale: 0.5 -> 0.0 over lifetime
# - Color: Gold (#FFD700) -> Transparent
Water Drops
func create_water_particles() -> GPUParticles2D:
var particles = GPUParticles2D.new()
particles.amount = 20
particles.lifetime = 0.8
particles.one_shot = true
var material = ParticleProcessMaterial.new()
material.direction = Vector3(0, 1, 0)
material.spread = 30.0
material.initial_velocity_min = 80
material.initial_velocity_max = 120
material.gravity = Vector3(0, 200, 0)
particles.process_material = material
# Blue water texture
particles.texture = preload("res://assets/effects/water_drop.png")
return particles
Growth Effect
# Plant growth sparkle
func play_growth_effect(plant_node: Node2D) -> void:
var particles = preload("res://scenes/effects/growth_sparkle.tscn").instantiate()
particles.global_position = plant_node.global_position
get_parent().add_child(particles)
# Also scale the plant slightly
var tween = create_tween()
tween.tween_property(plant_node, "scale", plant_node.scale * 1.1, 0.1)
tween.tween_property(plant_node, "scale", plant_node.scale, 0.2)
Shader Animaties
Outline Glow
// outline_glow.gdshader
shader_type canvas_item;
uniform vec4 outline_color : source_color = vec4(1.0, 1.0, 0.0, 1.0);
uniform float outline_width : hint_range(0.0, 10.0) = 2.0;
uniform float glow_speed : hint_range(0.0, 5.0) = 2.0;
void fragment() {
vec4 col = texture(TEXTURE, UV);
// Sample neighbors for outline
float outline = 0.0;
vec2 size = TEXTURE_PIXEL_SIZE * outline_width;
outline += texture(TEXTURE, UV + vec2(-size.x, 0)).a;
outline += texture(TEXTURE, UV + vec2(size.x, 0)).a;
outline += texture(TEXTURE, UV + vec2(0, -size.y)).a;
outline += texture(TEXTURE, UV + vec2(0, size.y)).a;
outline = min(outline, 1.0);
// Pulsing glow
float glow = (sin(TIME * glow_speed) + 1.0) / 2.0;
vec4 final_outline = outline_color * glow;
// Combine
if (col.a < 0.1 && outline > 0.0) {
COLOR = final_outline;
} else {
COLOR = col;
}
}
Dissolve Effect
// dissolve.gdshader
shader_type canvas_item;
uniform sampler2D noise_texture;
uniform float dissolve_amount : hint_range(0.0, 1.0) = 0.0;
uniform vec4 edge_color : source_color = vec4(1.0, 0.5, 0.0, 1.0);
uniform float edge_width : hint_range(0.0, 0.2) = 0.05;
void fragment() {
vec4 col = texture(TEXTURE, UV);
float noise = texture(noise_texture, UV).r;
if (noise < dissolve_amount) {
discard;
} else if (noise < dissolve_amount + edge_width) {
COLOR = edge_color;
} else {
COLOR = col;
}
}
Animation Library
Centrale Animatie Functions
# animation_utils.gd (autoload)
extends Node
func pop_in(node: Node2D, duration: float = 0.3) -> Tween:
node.scale = Vector2.ZERO
var tween = node.create_tween()
tween.tween_property(node, "scale", Vector2.ONE, duration)
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_BACK)
return tween
func fade_in(node: CanvasItem, duration: float = 0.2) -> Tween:
node.modulate.a = 0.0
var tween = node.create_tween()
tween.tween_property(node, "modulate:a", 1.0, duration)
return tween
func shake(node: Node2D, intensity: float = 5.0, duration: float = 0.3) -> Tween:
var original_pos = node.position
var tween = node.create_tween()
var shake_count = int(duration / 0.05)
for i in range(shake_count):
var offset = Vector2(randf_range(-intensity, intensity), randf_range(-intensity, intensity))
tween.tween_property(node, "position", original_pos + offset, 0.05)
tween.tween_property(node, "position", original_pos, 0.05)
return tween
func bounce(node: Node2D, height: float = 20.0, duration: float = 0.5) -> Tween:
var original_y = node.position.y
var tween = node.create_tween()
tween.tween_property(node, "position:y", original_y - height, duration / 2)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(node, "position:y", original_y, duration / 2)
tween.set_ease(Tween.EASE_IN)
return tween