mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
220 lines
5.9 KiB
Markdown
220 lines
5.9 KiB
Markdown
|
|
# PixelLab to Phaser.js Sprite Sheet Converter
|
||
|
|
|
||
|
|
This script converts PixelLab character animation directories into optimized sprite sheets with Phaser.js-compatible JSON atlases.
|
||
|
|
|
||
|
|
## Why Sprite Sheets?
|
||
|
|
|
||
|
|
For Phaser.js games, sprite sheets are essential for:
|
||
|
|
- **Performance**: Single HTTP request instead of dozens/hundreds
|
||
|
|
- **Memory**: More efficient GPU texture management
|
||
|
|
- **Rendering**: Faster frame switching during animations
|
||
|
|
- **Loading**: Quicker initial load times
|
||
|
|
|
||
|
|
## Installation
|
||
|
|
|
||
|
|
The script requires Python 3.6+ and Pillow (PIL):
|
||
|
|
|
||
|
|
```bash
|
||
|
|
pip install Pillow
|
||
|
|
```
|
||
|
|
|
||
|
|
Or if you have a requirements.txt:
|
||
|
|
```bash
|
||
|
|
pip install -r requirements.txt
|
||
|
|
```
|
||
|
|
|
||
|
|
## Usage
|
||
|
|
|
||
|
|
### Basic Usage
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python tools/convert_pixellab_to_spritesheet.py ~/Downloads/characters ./assets/sprites
|
||
|
|
```
|
||
|
|
|
||
|
|
### Arguments
|
||
|
|
|
||
|
|
- `input_dir`: Directory containing PixelLab character folders
|
||
|
|
- `output_dir`: Where to save the generated sprite sheets and atlases
|
||
|
|
- `--padding`: (Optional) Padding between frames in pixels (default: 2)
|
||
|
|
|
||
|
|
### Example
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Convert all characters from Downloads to game assets
|
||
|
|
python tools/convert_pixellab_to_spritesheet.py \
|
||
|
|
~/Downloads/characters \
|
||
|
|
./public/break_escape/assets/characters
|
||
|
|
```
|
||
|
|
|
||
|
|
## Input Directory Structure
|
||
|
|
|
||
|
|
The script expects this structure (as exported from PixelLab):
|
||
|
|
|
||
|
|
```
|
||
|
|
characters/
|
||
|
|
├── Female_woman._Hacker_in_a_hoodie._Hood_up_black_ob/
|
||
|
|
│ └── animations/
|
||
|
|
│ ├── breathing-idle/
|
||
|
|
│ │ ├── east/
|
||
|
|
│ │ │ ├── frame_000.png
|
||
|
|
│ │ │ ├── frame_001.png
|
||
|
|
│ │ │ └── ...
|
||
|
|
│ │ ├── north/
|
||
|
|
│ │ ├── north-east/
|
||
|
|
│ │ └── ...
|
||
|
|
│ ├── walk/
|
||
|
|
│ │ ├── east/
|
||
|
|
│ │ └── ...
|
||
|
|
│ └── ...
|
||
|
|
└── Other_Character/
|
||
|
|
└── animations/
|
||
|
|
└── ...
|
||
|
|
```
|
||
|
|
|
||
|
|
## Output Files
|
||
|
|
|
||
|
|
For each character, the script generates two files:
|
||
|
|
|
||
|
|
### 1. Sprite Sheet PNG (`character_name.png`)
|
||
|
|
- Single PNG combining all animation frames
|
||
|
|
- Optimized layout for efficient GPU usage
|
||
|
|
- Transparent background (RGBA)
|
||
|
|
|
||
|
|
### 2. Atlas JSON (`character_name.json`)
|
||
|
|
- Phaser.js-compatible JSON Hash format
|
||
|
|
- Contains frame positions and dimensions
|
||
|
|
- Includes animation metadata organized by type and direction
|
||
|
|
- Custom `animations` field for easy animation creation
|
||
|
|
|
||
|
|
## Using in Phaser.js
|
||
|
|
|
||
|
|
### 1. Loading the Sprite Sheet
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
function preload() {
|
||
|
|
this.load.atlas(
|
||
|
|
'female_hacker',
|
||
|
|
'break_escape/assets/characters/female_hacker.png',
|
||
|
|
'break_escape/assets/characters/female_hacker.json'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Creating Animations
|
||
|
|
|
||
|
|
#### Manual Method:
|
||
|
|
```javascript
|
||
|
|
function create() {
|
||
|
|
this.anims.create({
|
||
|
|
key: 'breathing-idle-east',
|
||
|
|
frames: this.anims.generateFrameNames('female_hacker', {
|
||
|
|
prefix: 'breathing-idle_east_frame_',
|
||
|
|
start: 0,
|
||
|
|
end: 3,
|
||
|
|
zeroPad: 3
|
||
|
|
}),
|
||
|
|
frameRate: 8,
|
||
|
|
repeat: -1
|
||
|
|
});
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Dynamic Method (Recommended):
|
||
|
|
```javascript
|
||
|
|
function create() {
|
||
|
|
// Load the atlas data
|
||
|
|
const atlasTexture = this.textures.get('female_hacker');
|
||
|
|
const atlasData = this.cache.json.get('female_hacker');
|
||
|
|
|
||
|
|
// Create all animations from metadata
|
||
|
|
if (atlasData.animations) {
|
||
|
|
for (const [animKey, frames] of Object.entries(atlasData.animations)) {
|
||
|
|
this.anims.create({
|
||
|
|
key: animKey,
|
||
|
|
frames: frames.map(frameName => ({
|
||
|
|
key: 'female_hacker',
|
||
|
|
frame: frameName
|
||
|
|
})),
|
||
|
|
frameRate: 8,
|
||
|
|
repeat: -1
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Use the animation
|
||
|
|
const sprite = this.add.sprite(400, 300, 'female_hacker');
|
||
|
|
sprite.play('breathing-idle_east');
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Switching Animations
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Change direction based on player input
|
||
|
|
if (cursors.right.isDown) {
|
||
|
|
sprite.play('walk_east', true);
|
||
|
|
} else if (cursors.up.isDown) {
|
||
|
|
sprite.play('walk_north', true);
|
||
|
|
} else {
|
||
|
|
sprite.play('breathing-idle_east', true);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Atlas JSON Structure
|
||
|
|
|
||
|
|
The generated JSON includes:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"frames": {
|
||
|
|
"breathing-idle_east_frame_000": {
|
||
|
|
"frame": {"x": 0, "y": 0, "w": 80, "h": 80},
|
||
|
|
"rotated": false,
|
||
|
|
"trimmed": false,
|
||
|
|
"spriteSourceSize": {"x": 0, "y": 0, "w": 80, "h": 80},
|
||
|
|
"sourceSize": {"w": 80, "h": 80}
|
||
|
|
},
|
||
|
|
...
|
||
|
|
},
|
||
|
|
"meta": {
|
||
|
|
"app": "PixelLab to Phaser Converter",
|
||
|
|
"image": "female_hacker.png",
|
||
|
|
"format": "RGBA8888",
|
||
|
|
"size": {"w": 1280, "h": 960},
|
||
|
|
"scale": "1"
|
||
|
|
},
|
||
|
|
"animations": {
|
||
|
|
"breathing-idle_east": ["breathing-idle_east_frame_000", "breathing-idle_east_frame_001", ...],
|
||
|
|
"breathing-idle_north": [...],
|
||
|
|
"walk_east": [...],
|
||
|
|
...
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Tips
|
||
|
|
|
||
|
|
1. **Frame Rate**: Adjust `frameRate` in your animations based on the visual speed you want (typically 8-12 for character animations)
|
||
|
|
|
||
|
|
2. **Preloading**: Load all sprite sheets in the preload phase to avoid lag during gameplay
|
||
|
|
|
||
|
|
3. **Multiple Characters**: Process all characters at once - the script handles multiple character directories automatically
|
||
|
|
|
||
|
|
4. **Direction Names**: The script preserves PixelLab's direction names (east, north, north-east, etc.)
|
||
|
|
|
||
|
|
5. **Animation Keys**: Animation keys are formatted as `{animation-type}_{direction}` (e.g., `walk_east`, `breathing-idle_north`)
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
**No animations found**: Ensure your character directory has an `animations` subdirectory
|
||
|
|
|
||
|
|
**Frame size mismatch**: All frames should be the same dimensions (e.g., 80x80 pixels)
|
||
|
|
|
||
|
|
**Large sprite sheets**: If your sprite sheet is too large, consider splitting characters into separate sheets or reducing frame counts
|
||
|
|
|
||
|
|
## Performance Notes
|
||
|
|
|
||
|
|
- Each sprite sheet is kept as a single texture for optimal GPU performance
|
||
|
|
- The JSON atlas allows Phaser to quickly locate frames without additional parsing
|
||
|
|
- The `animations` metadata enables dynamic animation creation without hardcoding frame names
|