Aller au contenu principal

Animation Banks Reference

Animation banks are JSON files that define sprite animations, frame sequences, and playback properties. This reference covers the complete JSON format and best practices.

File Structure Overview

{
"id": "string",
"spritesheetPath": "string",
"frameWidth": number,
"frameHeight": number,
"columns": number,
"spacing": number,
"margin": number,
"animations": [
{
"name": "string",
"loop": boolean,
"speed": number,
"nextAnimation": "string",
"frameRange": { ... } | "frames": [ ... ]
}
]
}

Top-Level Properties

id (required)

Type: string
Purpose: Unique identifier for this animation bank

Used to reference the bank in:

  • VisualAnimation_data.bankId component field
  • AnimationManager.GetBank(id) API calls
  • Debug logs and error messages

Rules:

  • Must be unique across all loaded banks
  • Case-sensitive
  • Use alphanumeric characters and underscores
  • Should match the filename (convention)

Examples:

"id": "player"        // Good: Simple and clear
"id": "enemy_goblin" // Good: Descriptive with underscore
"id": "boss_dragon_phase1" // Good: Specific variant
"id": "Player 1" // Bad: Contains space
"id": "123" // Bad: Numeric only (use "enemy_123")

spritesheetPath (required)

Type: string
Purpose: Path to the PNG spritesheet file

Path is relative to the executable location, not the JSON file.

Best Practices:

  • Use forward slashes / (cross-platform compatible)
  • Organize spritesheets in Gamedata/Sprites/ directory
  • Use descriptive filenames: character_idle_walk.png
  • Keep spritesheets small (under 2048×2048 for compatibility)

Examples:

"spritesheetPath": "Gamedata/Sprites/Characters/hero.png"
"spritesheetPath": "Gamedata/Sprites/Enemies/zombie_animations.png"
"spritesheetPath": "../Assets/player_spritesheet.png" // Relative path

Common Errors:

"spritesheetPath": "C:\\Gamedata\\Sprites\\hero.png"  // Bad: Absolute path
"spritesheetPath": "Sprites/hero.png" // May fail if Gamedata is expected

frameWidth, frameHeight (required)

Type: number (pixels)
Purpose: Dimensions of each frame in the spritesheet

All frames in the spritesheet must have the same dimensions. If you need variable-sized frames, use multiple banks.

How to Measure:

  1. Open spritesheet in an image editor
  2. Measure a single frame's width and height
  3. Verify all frames are the same size

Examples:

"frameWidth": 64,
"frameHeight": 64 // 64×64 pixel frames (common for 2D games)

"frameWidth": 32,
"frameHeight": 48 // 32×48 pixel frames (tall characters)

"frameWidth": 128,
"frameHeight": 128 // 128×128 pixel frames (high-res)

columns (required)

Type: number
Purpose: Number of frames per row in the spritesheet

Calculation:

columns = floor(spritesheetWidth / frameWidth)

Or manually count the frames in the first row.

Examples:

Spritesheet: 512×256, Frame: 64×64
columns = 512 / 64 = 8
"columns": 8  // 8 frames per row

spacing (optional, default: 0)

Type: number (pixels)
Purpose: Gap between frames horizontally and vertically

Use spacing when your spritesheet has padding between frames.

Visualization:

No Spacing (spacing: 0)
┌──┬──┬──┐
│ 1│ 2│ 3│
├──┼──┼──┤
│ 4│ 5│ 6│
└──┴──┴──┘

With Spacing (spacing: 2)
┌──┐ ┌──┐ ┌──┐
│ 1│ │ 2│ │ 3│
└──┘ └──┘ └──┘
┌──┐ ┌──┐ ┌──┐
│ 4│ │ 5│ │ 6│
└──┘ └──┘ └──┘

Examples:

"spacing": 0    // Frames are tightly packed
"spacing": 2 // 2 pixels between frames
"spacing": 4 // 4 pixels between frames

margin (optional, default: 0)

Type: number (pixels)
Purpose: Border pixels around the entire grid

Use margin when your spritesheet has padding around the edges.

Visualization:

No Margin (margin: 0)
┌──┬──┬──┐
│ 1│ 2│ 3│
├──┼──┼──┤
│ 4│ 5│ 6│
└──┴──┴──┘

With Margin (margin: 4)
┌─────────┐
│ ┌──┬──┐ │
│ │ 1│ 2│ │
│ ├──┼──┤ │
│ │ 3│ 4│ │
│ └──┴──┘ │
└─────────┘

Examples:

"margin": 0     // No border
"margin": 4 // 4-pixel border around grid
"margin": 8 // 8-pixel border

Animation Properties

Each animation in the animations array has these properties:

name (required)

Type: string
Purpose: Unique identifier for this animation within the bank

Used to:

  • Play animations: AnimationSystem::PlayAnimation(entity, "walk")
  • Reference in animation graphs: "animation": "walk"
  • Set initial state: "currentAnimName": "idle"

Rules:

  • Must be unique within the bank
  • Case-sensitive
  • Use alphanumeric and underscores
  • Should be descriptive

Common Names:

"name": "idle"       // Standing still
"name": "walk" // Walking
"name": "run" // Running
"name": "jump" // Jumping
"name": "fall" // Falling
"name": "attack" // Generic attack
"name": "attack1" // First attack in combo
"name": "death" // Death animation
"name": "hit" // Taking damage
"name": "cast" // Casting spell

loop (required)

Type: boolean
Purpose: Whether the animation repeats

  • true: Animation loops forever (idle, walk, run)
  • false: Animation plays once and stops (attack, death, jump)

Examples:

"loop": true   // Looping animations
"loop": false // One-shot animations

Use Cases:

Animation TypeLoop ValueReason
idletrueCharacter constantly breathes/sways
walktrueWalking is continuous
attackfalseAttack completes then stops
deathfalseCharacter stays dead
jumpfalseJump has a start and end
runtrueRunning is continuous

speed (optional, default: 1.0)

Type: number
Purpose: Playback speed multiplier

  • 1.0: Normal speed
  • 0.5: Half speed (slow motion)
  • 2.0: Double speed (fast forward)
  • 0.0: Paused (not recommended, use PauseAnimation() instead)

Examples:

"speed": 1.0    // Normal playback
"speed": 1.5 // 50% faster (energetic run)
"speed": 0.8 // 20% slower (heavy character)
"speed": 2.0 // 2× speed (rapid attack)

Note: Speed can also be controlled at runtime with SetPlaybackSpeed(), which multiplies this value.

nextAnimation (optional)

Type: string
Purpose: Animation to auto-transition to when this one completes

Only applies to non-looping animations (loop: false).

Examples:

{
"name": "attack",
"loop": false,
"nextAnimation": "idle" // Return to idle after attack
}

{
"name": "jump",
"loop": false,
"nextAnimation": "fall" // Transition to fall after jump
}

{
"name": "cast",
"loop": false,
"nextAnimation": "cast_hold" // Hold pose after casting
}

Use Cases:

  • Attack → Idle
  • Jump → Fall
  • Death → (none, character stays on last frame)
  • Hurt → Idle

Validation:

  • nextAnimation must reference a valid animation name in the same bank
  • Circular chains are allowed: A → B → C → A
  • Missing references will log warnings

Frame Definition: frameRange vs frames

Each animation must define frames using one of two methods:

Use when frames are consecutive in the spritesheet.

Structure:

"frameRange": {
"start": number,
"end": number,
"frameDuration": number
}

Properties:

  • start: First frame index (inclusive)
  • end: Last frame index (inclusive)
  • frameDuration: Duration of each frame in seconds

Examples:

"frameRange": {
"start": 0,
"end": 7,
"frameDuration": 0.1
}
// Frames: [0, 1, 2, 3, 4, 5, 6, 7]
// Duration: 0.1s per frame (total 0.8s)

"frameRange": {
"start": 16,
"end": 23,
"frameDuration": 0.08
}
// Frames: [16, 17, 18, 19, 20, 21, 22, 23]
// Duration: 0.08s per frame (total 0.64s)

Frame Index Calculation:

frameIndex = row × columns + column

Example with 8 columns:

  • Row 0: Frames 0–7
  • Row 1: Frames 8–15
  • Row 2: Frames 16–23

Method 2: frames (Advanced)

Use when:

  • Frames are non-consecutive
  • You need per-frame durations
  • You want frame events (sounds, effects)
  • You need custom hot spots (pivot points)

Structure:

"frames": [
{
"srcRect": { "x": number, "y": number, "w": number, "h": number },
"duration": number,
"hotSpot": { "x": number, "y": number },
"events": ["string", ...]
}
]

Properties:

  • srcRect: Source rectangle in spritesheet (pixels)
  • duration: Frame duration in seconds
  • hotSpot: Pivot point offset from top-left
  • events: Array of event names to trigger

Examples:

Simple explicit frames:

"frames": [
{
"srcRect": { "x": 0, "y": 0, "w": 64, "h": 64 },
"duration": 0.1
},
{
"srcRect": { "x": 64, "y": 0, "w": 64, "h": 64 },
"duration": 0.1
},
{
"srcRect": { "x": 128, "y": 0, "w": 64, "h": 64 },
"duration": 0.1
}
]

Variable frame durations (attack animation):

"frames": [
{ "srcRect": { "x": 0, "y": 128, "w": 64, "h": 64 }, "duration": 0.2 }, // Wind-up
{ "srcRect": { "x": 64, "y": 128, "w": 64, "h": 64 }, "duration": 0.05 }, // Strike!
{ "srcRect": { "x": 128, "y": 128, "w": 64, "h": 64 }, "duration": 0.15 } // Recovery
]

Frame events (footsteps):

"frames": [
{ "srcRect": { "x": 0, "y": 64, "w": 64, "h": 64 }, "duration": 0.1, "events": [] },
{ "srcRect": { "x": 64, "y": 64, "w": 64, "h": 64 }, "duration": 0.1, "events": ["footstep_left"] },
{ "srcRect": { "x": 128, "y": 64, "w": 64, "h": 64 }, "duration": 0.1, "events": [] },
{ "srcRect": { "x": 192, "y": 64, "w": 64, "h": 64 }, "duration": 0.1, "events": ["footstep_right"] }
]

Custom hot spots (weapon pivot):

"frames": [
{
"srcRect": { "x": 0, "y": 192, "w": 96, "h": 96 },
"duration": 0.1,
"hotSpot": { "x": 48, "y": 80 } // Pivot at character's hand
}
]

Multi-Spritesheet Support

Different animations can use different spritesheets by overriding spritesheetPath per animation:

{
"id": "boss",
"spritesheetPath": "Gamedata/Sprites/Bosses/boss_phase1.png",
"frameWidth": 128,
"frameHeight": 128,
"columns": 6,
"animations": [
{
"name": "idle_phase1",
"loop": true,
"frameRange": { "start": 0, "end": 5, "frameDuration": 0.15 }
},
{
"name": "idle_phase2",
"loop": true,
"spritesheetPath": "Gamedata/Sprites/Bosses/boss_phase2.png",
"frameRange": { "start": 0, "end": 5, "frameDuration": 0.15 }
}
]
}

This allows one bank to manage animations from multiple PNG files.

Best Practices

Organizing Banks

Option 1: One bank per character

thesee_animations.json     (all Thésée animations)
goblin_animations.json (all goblin animations)
dragon_animations.json (all dragon animations)

Option 2: One bank per animation category

player_movement.json       (idle, walk, run, jump)
player_combat.json (attack1, attack2, block)
player_magic.json (cast, channel, summon)

Recommendation: One bank per character for simplicity.

Common Animation Patterns

Standard Character Set:

{
"id": "character",
"animations": [
{ "name": "idle", "loop": true, "frameRange": { ... } },
{ "name": "walk", "loop": true, "frameRange": { ... } },
{ "name": "run", "loop": true, "frameRange": { ... } },
{ "name": "jump", "loop": false, "nextAnimation": "fall", "frameRange": { ... } },
{ "name": "fall", "loop": true, "frameRange": { ... } },
{ "name": "land", "loop": false, "nextAnimation": "idle", "frameRange": { ... } },
{ "name": "attack", "loop": false, "nextAnimation": "idle", "frameRange": { ... } },
{ "name": "hurt", "loop": false, "nextAnimation": "idle", "frameRange": { ... } },
{ "name": "death", "loop": false, "frameRange": { ... } }
]
}

Enemy Set:

{
"id": "enemy",
"animations": [
{ "name": "idle", "loop": true, "frameRange": { ... } },
{ "name": "patrol", "loop": true, "frameRange": { ... } },
{ "name": "alert", "loop": false, "nextAnimation": "chase", "frameRange": { ... } },
{ "name": "chase", "loop": true, "frameRange": { ... } },
{ "name": "attack", "loop": false, "nextAnimation": "idle", "frameRange": { ... } },
{ "name": "hurt", "loop": false, "nextAnimation": "idle", "frameRange": { ... } },
{ "name": "death", "loop": false, "frameRange": { ... } }
]
}

Complete Example: Thésée Animations

{
"id": "thesee",
"spritesheetPath": "Gamedata/Sprites/Characters/thesee_spritesheet.png",
"frameWidth": 64,
"frameHeight": 64,
"columns": 8,
"spacing": 0,
"margin": 0,
"animations": [
{
"name": "idle",
"loop": true,
"speed": 1.0,
"frameRange": {
"start": 0,
"end": 7,
"frameDuration": 0.1
}
},
{
"name": "walk",
"loop": true,
"speed": 1.0,
"frameRange": {
"start": 8,
"end": 15,
"frameDuration": 0.08
}
},
{
"name": "run",
"loop": true,
"speed": 1.0,
"frameRange": {
"start": 16,
"end": 23,
"frameDuration": 0.06
}
},
{
"name": "hit",
"loop": false,
"speed": 1.0,
"nextAnimation": "idle",
"frameRange": {
"start": 24,
"end": 27,
"frameDuration": 0.1
}
}
]
}

Validation and Error Checking

The AnimationManager validates banks on load:

Common Errors

Error MessageCauseSolution
"Bank ID 'X' already loaded"Duplicate bank IDUse unique IDs
"Spritesheet not found: X"File doesn't existCheck path relative to executable
"Invalid frameRange: start > end"start=10, end=5Swap start and end values
"Animation 'X' has no frames"Missing frameRange/framesAdd frame definition
"nextAnimation 'X' not found"Invalid referenceCheck spelling, add animation
"columns must be > 0"columns=0 or missingSet correct column count

Debug Loading

Enable verbose logging:

AnimationManager::Get().SetVerbose(true);
AnimationManager::Get().LoadAnimationBank("...");

Output:

[AnimationManager] Loading bank from 'thesee_animations.json'
[AnimationManager] ID: thesee
[AnimationManager] Spritesheet: 512×256 (8 columns)
[AnimationManager] Animation 'idle': 8 frames (0.8s loop)
[AnimationManager] Animation 'walk': 8 frames (0.64s loop)
[AnimationManager] Animation 'run': 8 frames (0.48s loop)
[AnimationManager] Animation 'hit': 4 frames (0.4s once)
[AnimationManager] Loaded bank 'thesee' successfully

See Also