Ga naar hoofdinhoud

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

Volgende