Files
novafarma/backup_faza11_extracted/backup_faza11_2025-12-11/SPRITE_SHEET_GUIDE.md
2025-12-11 11:34:23 +01:00

12 KiB
Raw Blame History

🎨 SPRITE SHEET SYSTEM

Novafarma - Sprite Atlas & Animation Guide

Date: 10.12.2025
Status: In Development


📊 Current Sprite Sheets

1. Player Walk Animation

// Loaded in PreloadScene.js line 94
this.load.spritesheet('player_walk', 'assets/sprites/player_walk_strip.png', { 
    frameWidth: 64, 
    frameHeight: 64 
});

// Animation created (line 101-106)
this.anims.create({
    key: 'player_walk_anim',
    frames: this.anims.generateFrameNumbers('player_walk', { start: 0, end: 5 }),
    frameRate: 12,
    repeat: -1
});

Specs:

  • File: assets/sprites/player_walk_strip.png
  • Frame Size: 64x64 pixels
  • Frames: 6 (0-5)
  • Frame Rate: 12 FPS
  • Total Width: 384px (6 frames × 64px)
  • Total Height: 64px

2. Zombie Walk Animation

// Loaded in PreloadScene.js line 95
this.load.spritesheet('zombie_walk', 'assets/sprites/zombie_walk_strip.png', { 
    frameWidth: 64, 
    frameHeight: 64 
});

// Animation created (line 108-113)
this.anims.create({
    key: 'zombie_walk_anim',
    frames: this.anims.generateFrameNumbers('zombie_walk', { start: 0, end: 5 }),
    frameRate: 8,
    repeat: -1
});

Specs:

  • File: assets/sprites/zombie_walk_strip.png
  • Frame Size: 64x64 pixels
  • Frames: 6 (0-5)
  • Frame Rate: 8 FPS (slower than player)
  • Total Width: 384px
  • Total Height: 64px

📦 Sprite Sheet Format

Horizontal Strip (Current)

[Frame 0][Frame 1][Frame 2][Frame 3][Frame 4][Frame 5]
  64px     64px     64px     64px     64px     64px

Advantages:

  • Simple to create
  • Easy to visualize
  • Small file for few frames

Disadvantages:

  • Wide files for many frames
  • No multi-direction support

[Idle 1][Idle 2][Idle 3][Idle 4]
[Walk 1][Walk 2][Walk 3][Walk 4]
[Run  1][Run  2][Run  3][Run  4]
[Attack1][Attack2][Attack3][Attack4]

Advantages:

  • Organized by animation type
  • Square-ish texture (better for GPU)
  • Easy to add new animations

Directional Grid (Best for Isometric)

        [DOWN]     [LEFT]     [RIGHT]    [UP]
[Frame0][Frame1] [Frame0][Frame1] [Frame0][Frame1] [Frame0][Frame1]

Example for Player (4 directions × 4 frames = 16 total):
  64px    64px     64px    64px     64px    64px     64px    64px
┌───────┬───────┐┌───────┬───────┐┌───────┬───────┐┌───────┬───────┐
│Down 0 │Down 1 ││Left 0 │Left 1 ││Right0 │Right1 ││Up 0   │Up 1   │
├───────┼───────┤├───────┼───────┤├───────┼───────┤├───────┼───────┤
│Down 2 │Down 3 ││Left 2 │Left 3 ││Right2 │Right3 ││Up 2   │Up 3   │
└───────┴───────┘└───────┴───────┘└───────┴───────┘└───────┴───────┘

Total Size: 512px × 128px (8 columns × 2 rows)

Priority 1: Player Complete (64x64)

// player_complete.png: 512x256 (8 cols × 4 rows)
Rows:
0: Walk Down    (4 frames)
1: Walk Left    (4 frames)
2: Walk Right   (4 frames)
3: Walk Up      (4 frames)

// Total: 32 frames (8×4)
// File size: ~50-100 KB

Loading Code:

this.load.spritesheet('player_complete', 'assets/sprites/player_complete.png', {
    frameWidth: 64,
    frameHeight: 64
});

Animation Code:

// Walk Down
this.anims.create({
    key: 'player_walk_down',
    frames: this.anims.generateFrameNumbers('player_complete', { start: 0, end: 3 }),
    frameRate: 10,
    repeat: -1
});

// Walk Left
this.anims.create({
    key: 'player_walk_left',
    frames: this.anims.generateFrameNumbers('player_complete', { start: 8, end: 11 }),
    frameRate: 10,
    repeat: -1
});

// Walk Right
this.anims.create({
    key: 'player_walk_right',
    frames: this.anims.generateFrameNumbers('player_complete', { start: 16, end: 19 }),
    frameRate: 10,
    repeat: -1
});

// Walk Up
this.anims.create({
    key: 'player_walk_up',
    frames: this.anims.generateFrameNumbers('player_complete', { start: 24, end: 27 }),
    frameRate: 10,
    repeat: -1
});

Priority 2: Player Actions (64x64)

// player_actions.png: 256x128 (4 cols × 2 rows)
Row 0: Idle animation     (4 frames)
Row 1: Attack animation   (4 frames)

// Total: 8 frames

Animations:

// Idle
this.anims.create({
    key: 'player_idle',
    frames: this.anims.generateFrameNumbers('player_actions', { start: 0, end: 3 }),
    frameRate: 6,
    repeat: -1
});

// Attack
this.anims.create({
    key: 'player_attack',
    frames: this.anims.generateFrameNumbers('player_actions', { start: 4, end: 7 }),
    frameRate: 15,
    repeat: 0 // Play once
});

Priority 3: Zombie Complete (64x64)

// zombie_complete.png: 512x128 (8 cols × 2 rows)
Row 0: Walk cycle    (8 frames)
Row 1: Attack cycle  (8 frames)

Priority 4: Items Atlas (32x32)

// items_atlas.png: 512x512 (16 cols × 16 rows = 256 items)

Items organized by category:
Rows 0-2:   Tools (axe, pickaxe, hoe, sword, etc.)
Rows 3-5:   Resources (wood, stone, iron_ore, gold, etc.)
Rows 6-8:   Seeds & Crops (wheat, carrot, potato, etc.)
Rows 9-11:  Food (bread, stew, apple, etc.)
Rows 12-14: Buildings (fence, chest, furnace, etc.)
Rows 15:    Special items (key, blueprint, etc.)

Loading:

this.load.spritesheet('items_atlas', 'assets/sprites/items_atlas.png', {
    frameWidth: 32,
    frameHeight: 32
});

Usage:

// Get specific item frame
const axeFrame = scene.textures.getFrame('items_atlas', 0); // First item
const pickaxeFrame = scene.textures.getFrame('items_atlas', 1); // Second item

// Create sprite from atlas
const axeIcon = scene.add.sprite(x, y, 'items_atlas', 0);

🛠️ Creating Sprite Sheets

Method 1: Piskel (Free Online)

  1. Go to piskelapp.com
  2. Create 64x64 canvas
  3. Draw each frame
  4. Export as sprite sheet
  5. Configure:
    • Format: PNG
    • Layout: Horizontal strip or Grid
    • Spritesheet: Yes
    • Frame size: 64x64

Method 2: Aseprite (Paid, Best)

  1. File → New → Sprite
    • Width: 64px
    • Height: 64px
  2. Draw animation frames
  3. File → Export Sprite Sheet
    • Sheet Type: By rows/columns
    • Columns: 8 (or as needed)
    • Padding: 0
    • Format: PNG

Method 3: Python Script (Automated)

# combine_sprites.py
from PIL import Image
import os

def create_sprite_sheet(images_folder, output_file, frame_width=64, frame_height=64, columns=8):
    """Combine individual PNG files into sprite sheet"""
    
    # Get all PNG files
    files = sorted([f for f in os.listdir(images_folder) if f.endswith('.png')])
    
    if not files:
        print("No PNG files found!")
        return
    
    # Calculate sheet dimensions
    num_frames = len(files)
    rows = (num_frames + columns - 1) // columns
    
    sheet_width = columns * frame_width
    sheet_height = rows * frame_height
    
    # Create blank sheet
    sheet = Image.new('RGBA', (sheet_width, sheet_height), (0, 0, 0, 0))
    
    # Paste each frame
    for idx, filename in enumerate(files):
        img = Image.open(os.path.join(images_folder, filename))
        img = img.resize((frame_width, frame_height), Image.NEAREST) # Pixel art scaling
        
        col = idx % columns
        row = idx // columns
        
        x = col * frame_width
        y = row * frame_height
        
        sheet.paste(img, (x, y))
        print(f"Added {filename} at ({x}, {y})")
    
    sheet.save(output_file)
    print(f"✅ Sprite sheet saved: {output_file} ({sheet_width}x{sheet_height})")
    print(f"   Frames: {num_frames}, Columns: {columns}, Rows: {rows}")

# Usage
create_sprite_sheet('frames/player_walk/', 'player_walk_strip.png', columns=6)
create_sprite_sheet('frames/zombie_walk/', 'zombie_walk_strip.png', columns=6)
create_sprite_sheet('frames/items/', 'items_atlas.png', frame_width=32, frame_height=32, columns=16)

📐 Sprite Sheet Calculator

/**
 * Calculate sprite sheet dimensions
 */
function calculateSheetSize(numFrames, frameSize, columns) {
    const rows = Math.ceil(numFrames / columns);
    const width = columns * frameSize;
    const height = rows * frameSize;
    
    console.log(`Frames: ${numFrames}`);
    console.log(`Layout: ${columns} cols × ${rows} rows`);
    console.log(`Size: ${width}×${height}px`);
    console.log(`File: ~${Math.ceil(width * height * 4 / 1024)}KB`);
    
    return { width, height, rows };
}

// Examples:
calculateSheetSize(6, 64, 6);   // Player walk: 384×64
calculateSheetSize(32, 64, 8);  // Player full: 512×256
calculateSheetSize(256, 32, 16); // Items atlas: 512×512

🎨 Frame Naming Convention

For Individual Frames (Before Combining):

player_walk_down_0.png
player_walk_down_1.png
player_walk_down_2.png
player_walk_down_3.png
player_walk_left_0.png
...

For Sprite Sheets:

player_complete.png        (all animations)
player_walk.png           (only walk cycle)
player_actions.png        (idle, attack, etc.)
zombie_complete.png       (all zombie animations)
items_atlas.png           (all items)
terrain_tiles.png         (all terrain)

🔄 Animation State Machine

class AnimationController {
    constructor(sprite) {
        this.sprite = sprite;
        this.currentAnim = null;
    }
    
    playWalk(direction) {
        const animKey = `player_walk_${direction}`;
        if (this.currentAnim !== animKey) {
            this.sprite.anims.play(animKey);
            this.currentAnim = animKey;
        }
    }
    
    playIdle() {
        if (this.currentAnim !== 'player_idle') {
            this.sprite.anims.play('player_idle');
            this.currentAnim = 'player_idle';
        }
    }
    
    playAttack() {
        this.sprite.anims.play('player_attack');
        this.currentAnim = 'player_attack';
        
        // Return to idle when attack finishes
        this.sprite.once('animationcomplete', () => {
            this.playIdle();
        });
    }
}

📋 TODO: Sprite Sheets Needed

🔴 High Priority:

  • player_complete.png (512×256, 32 frames) - 4 directions
  • zombie_complete.png (512×128, 16 frames) - walk + attack
  • items_atlas.png (512×512, 256 items) - all game items

🟡 Medium Priority:

  • npc_merchant.png (256×64, 4 frames) - idle animation
  • animals.png (512×128, 16 frames) - cow, chicken animations
  • effects.png (256×256, 64 frames) - hit, explosion, sparkles

🟢 Low Priority:

  • ui_elements.png (512×512) - buttons, icons, borders
  • terrain_tiles.png (512×512) - all terrain variations
  • crops.png (256×128) - growth stages for all crops

💾 File Size Optimization

PNG Optimization Tools:

# NPM package
npm install -g pngquant

# Optimize sprite sheet
pngquant --quality=65-80 player_complete.png -o player_complete_opt.png

# Batch optimization
pngquant --quality=65-80 assets/sprites/*.png --ext .png --force

Expected Sizes:

  • 64x64 × 32 frames (Player): ~80-120 KB
  • 32x32 × 256 items (Items): ~200-300 KB
  • Total assets: ~1-2 MB (very reasonable!)

🎯 Next Steps

  1. Create Priority Sheets:

    • Use Piskel/Aseprite to create player_complete.png
    • Generate items_atlas.png with Python script
  2. Update PreloadScene:

    • Add new sprite sheet loads
    • Create animations for all directions
  3. Update Player.js:

    • Use AnimationController
    • Switch animations based on movement
  4. Performance Test:

    • Measure FPS with full animations
    • Optimize if needed

Status: 📋 Planning Complete - Ready for Implementation
Tools: Piskel (free) or Aseprite ($20)
Python Script: Ready for batch processing