PRIORITY TRACKER: 100% COMPLETE (41 assets x 2 styles = 82 PNGs) P1 CRITICAL (46 PNGs): Kai walk south, actions, idle breathing, wheat cycle, tilled soil, zombie, tools P2 HIGH (12 PNGs): Inventory items, UI elements P3 MEDIUM (10 PNGs): Dialogue system, water effects P4 LOW (6 PNGs): Environment decorations BACKGROUND REMOVAL: 99/99 PNGs with perfect RGBA transparency! BACKUP: Original white-bg versions saved to demo_originals_with_white_bg/ DOCUMENTATION: - KICKSTARTER_DEMO_IMPLEMENTATION_GUIDE.md (complete 10hr roadmap) - TONIGHT_TILED_QUICKSTART.md (2hr map building guide) - DEMO_MAP_PLAN.md (8x8 layout spec) - DEV_JOURNAL updated with evening session API: Turtle Mode, 100% success rate, 0 errors, 120 minutes generation ASSETS: 99 total transparent PNGs ready for Phaser.js integration STATUS: READY FOR IMPLEMENTATION PHASE! Session: 2.5 hours | Project total: 128 hours | Cost: 0 EUR
1356 lines
30 KiB
Markdown
1356 lines
30 KiB
Markdown
# 🎮 KICKSTARTER DEMO - COMPLETE IMPLEMENTATION GUIDE
|
||
|
||
**Target:** First Playable Demo
|
||
**Timeline:** 3-5 days (6-8 work hours)
|
||
**Status:** Assets 100% ready! ✅
|
||
**Last Updated:** Dec 30, 2025 21:35
|
||
|
||
---
|
||
|
||
## 📋 **PREREQUISITES (ALREADY DONE!)**
|
||
|
||
✅ 99 PNG assets generated (dual style A + B)
|
||
✅ Background removal complete (transparent PNGs)
|
||
✅ DEMO_MAP_PLAN.md created (8×8 layout spec)
|
||
✅ PRIORITY_TRACKER.csv complete
|
||
✅ Git commit ready
|
||
|
||
**Current Progress:** 10% (assets ready, code not started)
|
||
|
||
---
|
||
|
||
## 🗓️ **3-DAY IMPLEMENTATION TIMELINE**
|
||
|
||
### **DAY 1: TILED MAP + ASSET LOADING** (3 hours)
|
||
- ⏳ Task 1: Build Tiled map (2 hours)
|
||
- ⏳ Task 2: Phaser asset loading (1 hour)
|
||
|
||
### **DAY 2: ANIMATIONS + MOVEMENT** (4 hours)
|
||
- ⏳ Task 3: Create animations (1.5 hours)
|
||
- ⏳ Task 4: Player controls (1.5 hours)
|
||
- ⏳ Task 5: Camera & collision (1 hour)
|
||
|
||
### **DAY 3: GAMEPLAY + POLISH** (3 hours)
|
||
- ⏳ Task 6: Farming mechanics (1.5 hours)
|
||
- ⏳ Task 7: UI implementation (1 hour)
|
||
- ⏳ Task 8: Demo script (30 min)
|
||
|
||
**TOTAL:** 10 hours → **FIRST PLAYABLE DEMO!** 🎉
|
||
|
||
---
|
||
|
||
# 📅 DAY 1: TILED MAP + ASSET LOADING
|
||
|
||
---
|
||
|
||
## 🗺️ **TASK 1: BUILD TILED MAP** (2 hours)
|
||
|
||
### **Step 1.1: Open Tiled Editor** (5 min)
|
||
|
||
**Check if Tiled is installed:**
|
||
```bash
|
||
which tiled
|
||
# If not found: brew install tiled
|
||
```
|
||
|
||
**Launch Tiled:**
|
||
```bash
|
||
tiled &
|
||
# OR use Applications → Tiled
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 1.2: Create New Map** (10 min)
|
||
|
||
**In Tiled:**
|
||
1. File → New → New Map
|
||
2. Settings:
|
||
- **Orientation:** Orthogonal
|
||
- **Tile layer format:** CSV
|
||
- **Tile render order:** Right Down
|
||
- **Map size:** 8 × 8 tiles
|
||
- **Tile size:** 64 × 64 pixels
|
||
3. Save as: `maps/demo_micro_farm.tmx`
|
||
|
||
**Result:** Empty 512×512px map
|
||
|
||
---
|
||
|
||
### **Step 1.3: Create Tilesets** (30 min)
|
||
|
||
**Tileset 1: Terrain** (`terrain_demo.tsx`)
|
||
|
||
1. Map → New Tileset
|
||
2. Name: `terrain_demo`
|
||
3. Type: Collection of Images
|
||
4. Add tiles:
|
||
- `assets/images/demo/terrain/grass_tile_styleA.png`
|
||
- `assets/images/demo/terrain/dirt_tile_styleA.png`
|
||
- `assets/images/demo/terrain/tilled_dry_styleA.png`
|
||
- `assets/images/demo/terrain/tilled_watered_styleA.png`
|
||
5. Save tileset: `maps/tilesets/terrain_demo.tsx`
|
||
|
||
**Tileset 2: Crops** (`crops_demo.tsx`)
|
||
|
||
1. Map → New Tileset
|
||
2. Name: `crops_demo`
|
||
3. Add tiles:
|
||
- `wheat_stage0_styleA.png` through `wheat_stage4_styleA.png`
|
||
5. Save: `maps/tilesets/crops_demo.tsx`
|
||
|
||
**Tileset 3: Objects** (`objects_demo.tsx`)
|
||
|
||
1. Map → New Tileset
|
||
2. Name: `objects_demo`
|
||
3. Add tiles:
|
||
- `tent_styleA.png`
|
||
- `campfire_styleA.png`
|
||
- `dead_tree_styleA.png`
|
||
- `rock_styleA.png`
|
||
4. Save: `maps/tilesets/objects_demo.tsx`
|
||
|
||
**NOTE:** Start with Style A only, can duplicate for B later!
|
||
|
||
---
|
||
|
||
### **Step 1.4: Create Layers** (10 min)
|
||
|
||
**In Layers panel, create (top to bottom):**
|
||
|
||
1. **"Collision"** (Object Layer)
|
||
2. **"Spawns"** (Object Layer)
|
||
3. **"Decorations"** (Object Layer)
|
||
4. **"Buildings"** (Object Layer)
|
||
5. **"Crops"** (Tile Layer)
|
||
6. **"Tilled Soil"** (Tile Layer)
|
||
7. **"Base Terrain"** (Tile Layer) ← Start here!
|
||
|
||
---
|
||
|
||
### **Step 1.5: Paint Base Terrain** (15 min)
|
||
|
||
**Select "Base Terrain" layer:**
|
||
|
||
1. Select `grass_tile_styleA` from terrain tileset
|
||
2. Use **Bucket Fill** tool (G)
|
||
3. Fill entire 8×8 map with grass
|
||
|
||
**Add dirt path (optional):**
|
||
1. Select `dirt_tile_styleA`
|
||
2. Use **Brush** tool (B)
|
||
3. Paint a few dirt tiles for variety
|
||
|
||
**Result:** Grassy map with optional dirt patches
|
||
|
||
---
|
||
|
||
### **Step 1.6: Paint Tilled Soil** (10 min)
|
||
|
||
**Select "Tilled Soil" layer:**
|
||
|
||
1. Select `tilled_dry_styleA`
|
||
2. Paint 2×2 patch at positions (1,1) to (2,2)
|
||
3. This is the farmable area
|
||
|
||
**Layout:**
|
||
```
|
||
0 1 2 3 4 5 6 7
|
||
1 [ ][🟫][🟫][ ][ ][ ][ ][ ]
|
||
2 [ ][🟫][🟫][ ][ ][ ][ ][ ]
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 1.7: Add Buildings & Decorations** (15 min)
|
||
|
||
**Select "Buildings" layer (Object Layer):**
|
||
|
||
1. Insert → Insert Tile (T)
|
||
2. Select `tent_styleA`
|
||
3. Place at position (6,1) - 384px, 64px
|
||
4. Right-click tent → Object Properties:
|
||
- Name: `tent`
|
||
- Type: `building`
|
||
- Custom property: `collision = true`
|
||
|
||
**Select "Decorations" layer:**
|
||
|
||
1. Place `campfire_styleA` at (6,5)
|
||
2. Place `dead_tree_styleA` at (0,0)
|
||
3. Place `rock_styleA` at (4,0)
|
||
4. Set Type: `decoration` for each
|
||
|
||
---
|
||
|
||
### **Step 1.8: Add Spawn Points** (10 min)
|
||
|
||
**Select "Spawns" layer (Object Layer):**
|
||
|
||
**Kai spawn:**
|
||
1. Insert → Insert Rectangle (R)
|
||
2. Draw 32×32 box at position (2,5) - 128px, 320px
|
||
3. Object Properties:
|
||
- Name: `kai_spawn`
|
||
- Type: `player`
|
||
- Custom: `facing = "south"`
|
||
|
||
**Zombie spawn:**
|
||
1. Insert Rectangle at (4,4) - 256px, 256px
|
||
2. Properties:
|
||
- Name: `zombie_1`
|
||
- Type: `npc_zombie`
|
||
- Custom: `ai = "idle_dig_loop"`
|
||
|
||
---
|
||
|
||
### **Step 1.9: Add Collision Objects** (10 min)
|
||
|
||
**Select "Collision" layer:**
|
||
|
||
**Draw collision rectangles around:**
|
||
1. Tent (64×64 box)
|
||
2. Dead tree (48×64 box)
|
||
3. Rock (40×32 box)
|
||
|
||
Set `Type: collision` for all
|
||
|
||
---
|
||
|
||
### **Step 1.10: Export Map** (5 min)
|
||
|
||
1. File → Export As → JSON
|
||
2. Save to: `maps/demo_micro_farm.json`
|
||
3. Verify file exists and opens in text editor
|
||
|
||
**TASK 1 COMPLETE!** ✅ Tiled map ready!
|
||
|
||
---
|
||
|
||
## 📦 **TASK 2: PHASER ASSET LOADING** (1 hour)
|
||
|
||
### **Step 2.1: Create Asset Manifest** (20 min)
|
||
|
||
**Create:** `src/demo/assets.js`
|
||
|
||
```javascript
|
||
// Asset manifest for demo
|
||
export const DemoAssets = {
|
||
|
||
// Tilemap
|
||
tilemap: {
|
||
key: 'demo_map',
|
||
url: 'maps/demo_micro_farm.json'
|
||
},
|
||
|
||
// Terrain tiles
|
||
terrain: [
|
||
{ key: 'grass_A', url: 'assets/images/demo/terrain/grass_tile_styleA.png' },
|
||
{ key: 'dirt_A', url: 'assets/images/demo/terrain/dirt_tile_styleA.png' },
|
||
{ key: 'tilled_dry_A', url: 'assets/images/demo/terrain/tilled_dry_styleA.png' },
|
||
{ key: 'tilled_wet_A', url: 'assets/images/demo/terrain/tilled_watered_styleA.png' }
|
||
],
|
||
|
||
// Kai frames
|
||
kai: {
|
||
idle_south: [
|
||
'assets/images/demo/characters/kai_idle_south_1_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_south_2_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_south_3_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_south_4_styleA.png'
|
||
],
|
||
walk_south: [
|
||
'assets/images/demo/characters/kai_walk_south_1_styleA.png',
|
||
'assets/images/demo/characters/kai_walk_south_2_styleA.png',
|
||
'assets/images/demo/characters/kai_walk_south_3_styleA.png',
|
||
'assets/images/demo/characters/kai_walk_south_4_styleA.png'
|
||
],
|
||
idle_north: [
|
||
'assets/images/demo/characters/kai_idle_north_1_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_north_2_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_north_3_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_north_4_styleA.png'
|
||
],
|
||
idle_east: [
|
||
'assets/images/demo/characters/kai_idle_east_1_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_east_4_styleA.png' // Just 2 for now
|
||
],
|
||
idle_west: [
|
||
'assets/images/demo/characters/kai_idle_west_1_styleB.png',
|
||
'assets/images/demo/characters/kai_idle_west_2_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_west_3_styleA.png',
|
||
'assets/images/demo/characters/kai_idle_west_4_styleA.png'
|
||
],
|
||
hoe: 'assets/images/demo/characters/kai_hoe_action_styleA.png',
|
||
watering: 'assets/images/demo/characters/kai_watering_styleA.png'
|
||
},
|
||
|
||
// Crops
|
||
wheat: [
|
||
'assets/images/demo/crops/wheat_stage0_styleA.png',
|
||
'assets/images/demo/crops/wheat_stage1_styleA.png',
|
||
'assets/images/demo/crops/wheat_stage2_styleA.png',
|
||
'assets/images/demo/crops/wheat_stage3_styleA.png',
|
||
'assets/images/demo/crops/wheat_stage4_styleA.png'
|
||
],
|
||
|
||
// NPCs
|
||
zombie: {
|
||
idle: 'assets/images/demo/characters/zombie_idle_1_styleA.png',
|
||
dig: 'assets/images/demo/characters/zombie_dig_1_styleA.png'
|
||
},
|
||
|
||
// Buildings
|
||
tent: 'assets/images/demo/buildings/tent_styleA.png',
|
||
|
||
// Environment
|
||
campfire: 'assets/images/demo/environment/campfire_styleA.png',
|
||
tree: 'assets/images/demo/environment/dead_tree_styleA.png',
|
||
rock: 'assets/images/demo/environment/rock_styleA.png',
|
||
|
||
// UI
|
||
ui: {
|
||
health_full: 'assets/images/demo/ui/health_bar_full_styleA.png',
|
||
health_half: 'assets/images/demo/ui/health_bar_half_styleA.png',
|
||
slot_empty: 'assets/images/demo/ui/inventory_slot_empty_styleA.png',
|
||
slot_selected: 'assets/images/demo/ui/inventory_slot_selected_styleA.png',
|
||
dialogue_box: 'assets/images/demo/ui/dialogue_box_styleA.png',
|
||
kai_portrait: 'assets/images/demo/ui/kai_portrait_styleA.png'
|
||
},
|
||
|
||
// Items
|
||
items: {
|
||
hoe: 'assets/images/demo/items/wooden_hoe_styleA.png',
|
||
watering_can: 'assets/images/demo/items/watering_can_styleA.png',
|
||
wheat_seeds: 'assets/images/demo/items/wheat_seeds_styleA.png',
|
||
wheat_bundle: 'assets/images/demo/items/wheat_bundle_styleA.png'
|
||
},
|
||
|
||
// Effects
|
||
effects: {
|
||
water_drops: 'assets/images/demo/effects/water_anim_1_styleA.png',
|
||
water_splash: 'assets/images/demo/effects/water_anim_2_styleA.png'
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 2.2: Create Demo Scene** (20 min)
|
||
|
||
**Create:** `src/scenes/DemoScene.js`
|
||
|
||
```javascript
|
||
import Phaser from 'phaser';
|
||
import { DemoAssets } from '../demo/assets.js';
|
||
|
||
export default class DemoScene extends Phaser.Scene {
|
||
|
||
constructor() {
|
||
super({ key: 'DemoScene' });
|
||
}
|
||
|
||
preload() {
|
||
console.log('Loading demo assets...');
|
||
|
||
// Load tilemap
|
||
this.load.tilemapTiledJSON(
|
||
DemoAssets.tilemap.key,
|
||
DemoAssets.tilemap.url
|
||
);
|
||
|
||
// Load Kai frames
|
||
this.loadKaiFrames();
|
||
|
||
// Load environment
|
||
this.load.image('tent', DemoAssets.tent);
|
||
this.load.image('campfire', DemoAssets.campfire);
|
||
this.load.image('tree', DemoAssets.tree);
|
||
this.load.image('rock', DemoAssets.rock);
|
||
|
||
// Load wheat stages
|
||
DemoAssets.wheat.forEach((url, i) => {
|
||
this.load.image(`wheat_${i}`, url);
|
||
});
|
||
|
||
// Load zombie
|
||
this.load.image('zombie_idle', DemoAssets.zombie.idle);
|
||
this.load.image('zombie_dig', DemoAssets.zombie.dig);
|
||
|
||
// Load UI
|
||
Object.keys(DemoAssets.ui).forEach(key => {
|
||
this.load.image(`ui_${key}`, DemoAssets.ui[key]);
|
||
});
|
||
|
||
// Progress bar (optional)
|
||
this.load.on('progress', (value) => {
|
||
console.log(`Loading: ${Math.floor(value * 100)}%`);
|
||
});
|
||
}
|
||
|
||
loadKaiFrames() {
|
||
// Idle south
|
||
DemoAssets.kai.idle_south.forEach((url, i) => {
|
||
this.load.image(`kai_idle_s_${i}`, url);
|
||
});
|
||
|
||
// Walk south
|
||
DemoAssets.kai.walk_south.forEach((url, i) => {
|
||
this.load.image(`kai_walk_s_${i}`, url);
|
||
});
|
||
|
||
// Idle north
|
||
DemoAssets.kai.idle_north.forEach((url, i) => {
|
||
this.load.image(`kai_idle_n_${i}`, url);
|
||
});
|
||
|
||
// Idle east
|
||
DemoAssets.kai.idle_east.forEach((url, i) => {
|
||
this.load.image(`kai_idle_e_${i}`, url);
|
||
});
|
||
|
||
// Idle west
|
||
DemoAssets.kai.idle_west.forEach((url, i) => {
|
||
this.load.image(`kai_idle_w_${i}`, url);
|
||
});
|
||
|
||
// Actions
|
||
this.load.image('kai_hoe', DemoAssets.kai.hoe);
|
||
this.load.image('kai_water', DemoAssets.kai.watering);
|
||
}
|
||
|
||
create() {
|
||
console.log('Demo scene created!');
|
||
|
||
// Create map
|
||
this.createMap();
|
||
|
||
// Test: show grass in center
|
||
this.add.image(256, 256, 'kai_idle_s_0');
|
||
this.add.text(10, 10, 'Demo Scene Loaded!', {
|
||
fontSize: '24px',
|
||
color: '#00ff00'
|
||
});
|
||
}
|
||
|
||
createMap() {
|
||
const map = this.make.tilemap({ key: 'demo_map' });
|
||
// We'll implement this fully in Day 2
|
||
console.log('Map loaded:', map);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 2.3: Update Main Config** (10 min)
|
||
|
||
**Edit:** `src/main.js` or `src/index.js`
|
||
|
||
```javascript
|
||
import Phaser from 'phaser';
|
||
import DemoScene from './scenes/DemoScene.js';
|
||
|
||
const config = {
|
||
type: Phaser.AUTO,
|
||
width: 800,
|
||
height: 600,
|
||
parent: 'game-container',
|
||
backgroundColor: '#2d2d2d',
|
||
physics: {
|
||
default: 'arcade',
|
||
arcade: {
|
||
gravity: { y: 0 },
|
||
debug: true // Turn off later
|
||
}
|
||
},
|
||
scene: [DemoScene]
|
||
};
|
||
|
||
const game = new Phaser.Game(config);
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 2.4: Test Loading** (10 min)
|
||
|
||
**Run dev server:**
|
||
```bash
|
||
npm run dev
|
||
# OR
|
||
npm start
|
||
```
|
||
|
||
**Open browser:** `http://localhost:8080`
|
||
|
||
**Expected result:**
|
||
- ✅ "Demo Scene Loaded!" text visible
|
||
- ✅ Kai idle frame shows in center
|
||
- ✅ Console shows "Loading: 100%"
|
||
- ✅ No errors in console
|
||
|
||
**If errors:**
|
||
- Check file paths in `assets.js`
|
||
- Verify PNGs exist in `/assets/images/demo/`
|
||
- Check Phaser version compatibility
|
||
|
||
**TASK 2 COMPLETE!** ✅ Assets loading!
|
||
|
||
---
|
||
|
||
# 📅 DAY 2: ANIMATIONS + MOVEMENT
|
||
|
||
---
|
||
|
||
## 🎬 **TASK 3: CREATE ANIMATIONS** (1.5 hours)
|
||
|
||
### **Step 3.1: Animation Setup** (30 min)
|
||
|
||
**Add to DemoScene.js `create()` method:**
|
||
|
||
```javascript
|
||
create() {
|
||
console.log('Creating animations...');
|
||
|
||
this.createMap();
|
||
this.createAnimations();
|
||
this.createPlayer();
|
||
this.createNPCs();
|
||
this.createCamera();
|
||
this.createControls();
|
||
}
|
||
|
||
createAnimations() {
|
||
// Kai idle south
|
||
this.anims.create({
|
||
key: 'kai_idle_south',
|
||
frames: [
|
||
{ key: 'kai_idle_s_0', duration: 500 },
|
||
{ key: 'kai_idle_s_1', duration: 500 },
|
||
{ key: 'kai_idle_s_2', duration: 500 },
|
||
{ key: 'kai_idle_s_3', duration: 500 }
|
||
],
|
||
frameRate: 2,
|
||
repeat: -1
|
||
});
|
||
|
||
// Kai walk south
|
||
this.anims.create({
|
||
key: 'kai_walk_south',
|
||
frames: [
|
||
{ key: 'kai_walk_s_0' },
|
||
{ key: 'kai_walk_s_1' },
|
||
{ key: 'kai_walk_s_2' },
|
||
{ key: 'kai_walk_s_3' }
|
||
],
|
||
frameRate: 8,
|
||
repeat: -1
|
||
});
|
||
|
||
// Kai idle north
|
||
this.anims.create({
|
||
key: 'kai_idle_north',
|
||
frames: [
|
||
{ key: 'kai_idle_n_0', duration: 500 },
|
||
{ key: 'kai_idle_n_1', duration: 500 },
|
||
{ key: 'kai_idle_n_2', duration: 500 },
|
||
{ key: 'kai_idle_n_3', duration: 500 }
|
||
],
|
||
frameRate: 2,
|
||
repeat: -1
|
||
});
|
||
|
||
// Kai idle east
|
||
this.anims.create({
|
||
key: 'kai_idle_east',
|
||
frames: [
|
||
{ key: 'kai_idle_e_0', duration: 700 },
|
||
{ key: 'kai_idle_e_1', duration: 700 }
|
||
],
|
||
frameRate: 1.5,
|
||
repeat: -1
|
||
});
|
||
|
||
// Kai idle west
|
||
this.anims.create({
|
||
key: 'kai_idle_west',
|
||
frames: [
|
||
{ key: 'kai_idle_w_0', duration: 500 },
|
||
{ key: 'kai_idle_w_1', duration: 500 },
|
||
{ key: 'kai_idle_w_2', duration: 500 },
|
||
{ key: 'kai_idle_w_3', duration: 500 }
|
||
],
|
||
frameRate: 2,
|
||
repeat: -1
|
||
});
|
||
|
||
// Kai hoe action
|
||
this.anims.create({
|
||
key: 'kai_hoe',
|
||
frames: [{ key: 'kai_hoe' }],
|
||
frameRate: 1,
|
||
repeat: 0
|
||
});
|
||
|
||
// Kai watering action
|
||
this.anims.create({
|
||
key: 'kai_water',
|
||
frames: [{ key: 'kai_water' }],
|
||
frameRate: 1,
|
||
repeat: 0
|
||
});
|
||
|
||
// Zombie animations
|
||
this.anims.create({
|
||
key: 'zombie_idle',
|
||
frames: [{ key: 'zombie_idle' }],
|
||
frameRate: 1,
|
||
repeat: -1
|
||
});
|
||
|
||
this.anims.create({
|
||
key: 'zombie_dig',
|
||
frames: [{ key: 'zombie_dig' }],
|
||
frameRate: 1,
|
||
repeat: 0
|
||
});
|
||
|
||
console.log('✅ Animations created!');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 3.2: Create Player Sprite** (30 min)
|
||
|
||
```javascript
|
||
createPlayer() {
|
||
// Find spawn point from tilemap
|
||
const map = this.make.tilemap({ key: 'demo_map' });
|
||
const spawnLayer = map.getObjectLayer('Spawns');
|
||
const kaiSpawn = spawnLayer.objects.find(obj => obj.name === 'kai_spawn');
|
||
|
||
// Create player sprite
|
||
this.player = this.physics.add.sprite(
|
||
kaiSpawn.x + 16, // Center in spawn box
|
||
kaiSpawn.y + 16,
|
||
'kai_idle_s_0'
|
||
);
|
||
|
||
// Set physics properties
|
||
this.player.setCollideWorldBounds(true);
|
||
this.player.body.setSize(32, 48); // Collision box
|
||
this.player.body.setOffset(16, 16); // Center collision
|
||
|
||
// Start idle animation
|
||
this.player.play('kai_idle_south');
|
||
|
||
// Player state
|
||
this.player.facing = 'south';
|
||
this.player.isMoving = false;
|
||
|
||
console.log('✅ Player created at:', kaiSpawn.x, kaiSpawn.y);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 3.3: Create NPCs** (30 min)
|
||
|
||
```javascript
|
||
createNPCs() {
|
||
const map = this.make.tilemap({ key: 'demo_map' });
|
||
const spawnLayer = map.getObjectLayer('Spawns');
|
||
const zombieSpawn = spawnLayer.objects.find(obj => obj.name === 'zombie_1');
|
||
|
||
if (zombieSpawn) {
|
||
this.zombie = this.physics.add.sprite(
|
||
zombieSpawn.x + 16,
|
||
zombieSpawn.y + 16,
|
||
'zombie_idle'
|
||
);
|
||
|
||
this.zombie.play('zombie_idle');
|
||
|
||
// Zombie AI loop
|
||
this.time.addEvent({
|
||
delay: 5000,
|
||
callback: () => {
|
||
this.zombie.play('zombie_dig');
|
||
this.time.delayedCall(1000, () => {
|
||
this.zombie.play('zombie_idle');
|
||
});
|
||
},
|
||
loop: true
|
||
});
|
||
|
||
console.log('✅ Zombie created!');
|
||
}
|
||
}
|
||
```
|
||
|
||
**TASK 3 COMPLETE!** ✅ Animations working!
|
||
|
||
---
|
||
|
||
## 🎮 **TASK 4: PLAYER CONTROLS** (1.5 hours)
|
||
|
||
### **Step 4.1: Setup Input** (20 min)
|
||
|
||
```javascript
|
||
createControls() {
|
||
// WASD keys
|
||
this.keys = {
|
||
W: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W),
|
||
A: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A),
|
||
S: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S),
|
||
D: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D),
|
||
E: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
|
||
H: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.H),
|
||
SPACE: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE)
|
||
};
|
||
|
||
// Cursor keys (arrows)
|
||
this.cursors = this.input.keyboard.createCursorKeys();
|
||
|
||
console.log('✅ Controls setup!');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 4.2: Movement Logic** (40 min)
|
||
|
||
```javascript
|
||
update() {
|
||
if (!this.player) return;
|
||
|
||
this.handleMovement();
|
||
}
|
||
|
||
handleMovement() {
|
||
const speed = 160;
|
||
let velocityX = 0;
|
||
let velocityY = 0;
|
||
let newFacing = this.player.facing;
|
||
|
||
// Check input
|
||
if (this.keys.W.isDown || this.cursors.up.isDown) {
|
||
velocityY = -speed;
|
||
newFacing = 'north';
|
||
} else if (this.keys.S.isDown || this.cursors.down.isDown) {
|
||
velocityY = speed;
|
||
newFacing = 'south';
|
||
}
|
||
|
||
if (this.keys.A.isDown || this.cursors.left.isDown) {
|
||
velocityX = -speed;
|
||
newFacing = 'west';
|
||
} else if (this.keys.D.isDown || this.cursors.right.isDown) {
|
||
velocityX = speed;
|
||
newFacing = 'east';
|
||
}
|
||
|
||
// Apply velocity
|
||
this.player.setVelocity(velocityX, velocityY);
|
||
|
||
// Normalize diagonal speed
|
||
this.player.body.velocity.normalize().scale(speed);
|
||
|
||
// Update animations
|
||
this.updatePlayerAnimation(velocityX, velocityY, newFacing);
|
||
}
|
||
|
||
updatePlayerAnimation(vx, vy, facing) {
|
||
const isMoving = vx !== 0 || vy !== 0;
|
||
|
||
// Update facing if direction changed
|
||
if (facing !== this.player.facing) {
|
||
this.player.facing = facing;
|
||
}
|
||
|
||
// Play appropriate animation
|
||
if (isMoving) {
|
||
// Only have walk south for now
|
||
if (facing === 'south') {
|
||
this.player.play('kai_walk_south', true);
|
||
} else {
|
||
// Use idle for other directions until we have walk anims
|
||
this.player.play(`kai_idle_${facing}`, true);
|
||
}
|
||
this.player.isMoving = true;
|
||
} else {
|
||
// Idle
|
||
this.player.play(`kai_idle_${facing}`, true);
|
||
this.player.isMoving = false;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 4.3: Test Movement** (30 min)
|
||
|
||
**Test checklist:**
|
||
- ✅ WASD moves Kai in 4 directions
|
||
- ✅ Arrow keys also work
|
||
- ✅ Kai stops when keys released
|
||
- ✅ Idle animation plays when stopped
|
||
- ✅ Walk south animation plays when moving down
|
||
- ✅ Facing direction updates correctly
|
||
|
||
**TASK 4 COMPLETE!** ✅ Player controls working!
|
||
|
||
---
|
||
|
||
## 📷 **TASK 5: CAMERA & COLLISION** (1 hour)
|
||
|
||
### **Step 5.1: Camera Follow** (20 min)
|
||
|
||
```javascript
|
||
createCamera() {
|
||
// Set world bounds
|
||
this.physics.world.setBounds(0, 0, 512, 512);
|
||
|
||
// Camera follows player
|
||
this.cameras.main.startFollow(this.player);
|
||
this.cameras.main.setBounds(0, 0, 512, 512);
|
||
this.cameras.main.setZoom(1.5); // Zoom in slightly
|
||
|
||
console.log('✅ Camera setup!');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 5.2: Collision Setup** (40 min)
|
||
|
||
```javascript
|
||
createMap() {
|
||
const map = this.make.tilemap({ key: 'demo_map' });
|
||
|
||
// NOTE: Tilesets must match names in Tiled!
|
||
const terrainTileset = map.addTilesetImage('terrain_demo');
|
||
|
||
// Create layers
|
||
this.terrainLayer = map.createLayer('Base Terrain', terrainTileset, 0, 0);
|
||
this.tilledLayer = map.createLayer('Tilled Soil', terrainTileset, 0, 0);
|
||
|
||
// Add collision objects from Collision layer
|
||
const collisionLayer = map.getObjectLayer('Collision');
|
||
this.collisionGroup = this.physics.add.staticGroup();
|
||
|
||
collisionLayer.objects.forEach(obj => {
|
||
const rect = this.add.rectangle(
|
||
obj.x + obj.width/2,
|
||
obj.y + obj.height/2,
|
||
obj.width,
|
||
obj.height,
|
||
0xff0000,
|
||
0 // Invisible
|
||
);
|
||
this.physics.add.existing(rect, true);
|
||
this.collisionGroup.add(rect);
|
||
});
|
||
|
||
console.log('✅ Map created with collision!');
|
||
}
|
||
|
||
// In create(), after createPlayer():
|
||
createCollision() {
|
||
// Player vs collision objects
|
||
this.physics.add.collider(this.player, this.collisionGroup);
|
||
|
||
console.log('✅ Collision setup!');
|
||
}
|
||
```
|
||
|
||
**TASK 5 COMPLETE!** ✅ Camera + collision working!
|
||
|
||
**DAY 2 COMPLETE!** ✅ Movement demo playable!
|
||
|
||
---
|
||
|
||
# 📅 DAY 3: GAMEPLAY + POLISH
|
||
|
||
---
|
||
|
||
## 🌾 **TASK 6: FARMING MECHANICS** (1.5 hours)
|
||
|
||
### **Step 6.1: Farmable Tiles System** (30 min)
|
||
|
||
```javascript
|
||
create() {
|
||
// ... existing code ...
|
||
|
||
this.createFarmSystem();
|
||
}
|
||
|
||
createFarmSystem() {
|
||
// Track farmable tiles
|
||
this.farmTiles = [];
|
||
|
||
const map = this.make.tilemap({ key: 'demo_map' });
|
||
|
||
// Find tilled soil tiles (positions 1,1 to 2,2)
|
||
for (let x = 1; x <= 2; x++) {
|
||
for (let y = 1; y <= 2; y++) {
|
||
this.farmTiles.push({
|
||
x: x,
|
||
y: y,
|
||
worldX: x * 64 + 32,
|
||
worldY: y * 64 + 32,
|
||
planted: false,
|
||
watered: false,
|
||
stage: -1,
|
||
crop: null
|
||
});
|
||
}
|
||
}
|
||
|
||
console.log('✅ Farm system ready! Tiles:', this.farmTiles.length);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 6.2: Plant Seeds** (30 min)
|
||
|
||
```javascript
|
||
update() {
|
||
if (!this.player) return;
|
||
|
||
this.handleMovement();
|
||
this.handleActions();
|
||
}
|
||
|
||
handleActions() {
|
||
// E key to plant
|
||
if (Phaser.Input.Keyboard.JustDown(this.keys.E)) {
|
||
this.attemptPlant();
|
||
}
|
||
|
||
// SPACE to water
|
||
if (Phaser.Input.Keyboard.JustDown(this.keys.SPACE)) {
|
||
this.attemptWater();
|
||
}
|
||
|
||
// H to harvest
|
||
if (Phaser.Input.Keyboard.JustDown(this.keys.H)) {
|
||
this.attemptHarvest();
|
||
}
|
||
}
|
||
|
||
attemptPlant() {
|
||
// Find nearby farmable tile
|
||
const nearbyTile = this.farmTiles.find(tile => {
|
||
const dist = Phaser.Math.Distance.Between(
|
||
this.player.x, this.player.y,
|
||
tile.worldX, tile.worldY
|
||
);
|
||
return dist < 64 && !tile.planted;
|
||
});
|
||
|
||
if (nearbyTile) {
|
||
// Play hoe animation
|
||
this.player.play('kai_hoe');
|
||
|
||
// Plant seed after animation
|
||
this.time.delayedCall(500, () => {
|
||
nearbyTile.planted = true;
|
||
nearbyTile.stage = 0;
|
||
|
||
// Create crop sprite
|
||
nearbyTile.crop = this.add.sprite(
|
||
nearbyTile.worldX,
|
||
nearbyTile.worldY,
|
||
'wheat_0'
|
||
);
|
||
|
||
console.log('🌱 Planted wheat!');
|
||
|
||
// Return to idle
|
||
this.player.play(`kai_idle_${this.player.facing}`);
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 6.3: Water & Grow** (30 min)
|
||
|
||
```javascript
|
||
attemptWater() {
|
||
const nearbyTile = this.farmTiles.find(tile => {
|
||
const dist = Phaser.Math.Distance.Between(
|
||
this.player.x, this.player.y,
|
||
tile.worldX, tile.worldY
|
||
);
|
||
return dist < 64 && tile.planted && !tile.watered;
|
||
});
|
||
|
||
if (nearbyTile) {
|
||
// Play watering animation
|
||
this.player.play('kai_water');
|
||
|
||
this.time.delayedCall(500, () => {
|
||
nearbyTile.watered = true;
|
||
|
||
// Start growth timer (10 seconds for demo)
|
||
this.growCrop(nearbyTile);
|
||
|
||
console.log('💧 Watered crop!');
|
||
this.player.play(`kai_idle_${this.player.facing}`);
|
||
});
|
||
}
|
||
}
|
||
|
||
growCrop(tile) {
|
||
// Grow through 5 stages over 10 seconds
|
||
const growthInterval = 2000; // 2 seconds per stage
|
||
|
||
const growTimer = this.time.addEvent({
|
||
delay: growthInterval,
|
||
callback: () => {
|
||
tile.stage++;
|
||
|
||
if (tile.stage <= 4) {
|
||
// Update sprite
|
||
tile.crop.setTexture(`wheat_${tile.stage}`);
|
||
console.log(`🌾 Wheat grew to stage ${tile.stage}`);
|
||
} else {
|
||
// Fully grown!
|
||
growTimer.remove();
|
||
console.log('✨ Wheat ready to harvest!');
|
||
}
|
||
},
|
||
repeat: 4 // Stages 1-4 (0 already set)
|
||
});
|
||
}
|
||
|
||
attemptHarvest() {
|
||
const nearbyTile = this.farmTiles.find(tile => {
|
||
const dist = Phaser.Math.Distance.Between(
|
||
this.player.x, this.player.y,
|
||
tile.worldX, tile.worldY
|
||
);
|
||
return dist < 64 && tile.stage === 4;
|
||
});
|
||
|
||
if (nearbyTile) {
|
||
// Remove crop
|
||
nearbyTile.crop.destroy();
|
||
|
||
// Reset tile
|
||
nearbyTile.planted = false;
|
||
nearbyTile.watered = false;
|
||
nearbyTile.stage = -1;
|
||
nearbyTile.crop = null;
|
||
|
||
// Add to inventory (we'll implement UI next)
|
||
console.log('✅ Harvested wheat!');
|
||
}
|
||
}
|
||
```
|
||
|
||
**TASK 6 COMPLETE!** ✅ Farming loop works!
|
||
|
||
---
|
||
|
||
## 🎨 **TASK 7: UI IMPLEMENTATION** (1 hour)
|
||
|
||
### **Step 7.1: Create UI Layer** (30 min)
|
||
|
||
```javascript
|
||
create() {
|
||
// ... existing code ...
|
||
|
||
this.createUI();
|
||
}
|
||
|
||
createUI() {
|
||
// Health bar
|
||
this.healthBar = this.add.image(20, 20, 'ui_health_full');
|
||
this.healthBar.setOrigin(0, 0);
|
||
this.healthBar.setScrollFactor(0); // Fixed to camera
|
||
|
||
// Inventory bar
|
||
const invY = this.cameras.main.height - 80;
|
||
this.inventorySlots = [];
|
||
|
||
for (let i = 0; i < 5; i++) {
|
||
const slot = this.add.image(
|
||
this.cameras.main.width/2 - 100 + i*50,
|
||
invY,
|
||
'ui_slot_empty'
|
||
);
|
||
slot.setScrollFactor(0);
|
||
this.inventorySlots.push(slot);
|
||
}
|
||
|
||
// Select first slot
|
||
this.selectedSlot = 0;
|
||
this.inventorySlots[0].setTexture('ui_slot_selected');
|
||
|
||
// Tutorial text
|
||
this.tutorialText = this.add.text(
|
||
this.cameras.main.width/2,
|
||
30,
|
||
'Use WASD to move!',
|
||
{
|
||
fontSize: '18px',
|
||
color: '#ffffff',
|
||
backgroundColor: '#000000',
|
||
padding: { x: 10, y: 5 }
|
||
}
|
||
);
|
||
this.tutorialText.setOrigin(0.5, 0);
|
||
this.tutorialText.setScrollFactor(0);
|
||
|
||
console.log('✅ UI created!');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Step 7.2: Update Tutorial Text** (30 min)
|
||
|
||
```javascript
|
||
showTutorial(message, duration = 3000) {
|
||
this.tutorialText.setText(message);
|
||
this.tutorialText.setAlpha(1);
|
||
|
||
// Fade out after duration
|
||
this.tweens.add({
|
||
targets: this.tutorialText,
|
||
alpha: 0,
|
||
duration: 500,
|
||
delay: duration
|
||
});
|
||
}
|
||
|
||
// Update attemptPlant():
|
||
attemptPlant() {
|
||
// ... existing code ...
|
||
|
||
if (nearbyTile) {
|
||
this.showTutorial('🌱 Wheat planted! Press SPACE to water!');
|
||
// ... rest of code
|
||
} else {
|
||
this.showTutorial('❌ No farmable tile nearby!', 1500);
|
||
}
|
||
}
|
||
|
||
// Similar for water and harvest...
|
||
```
|
||
|
||
**TASK 7 COMPLETE!** ✅ UI working!
|
||
|
||
---
|
||
|
||
## 🎬 **TASK 8: DEMO SCRIPT** (30 min)
|
||
|
||
### **Step 8.1: Auto-Demo Mode** (30 min)
|
||
|
||
```javascript
|
||
create() {
|
||
// ... existing code ...
|
||
|
||
// Start demo script after 3 seconds
|
||
this.time.delayedCall(3000, () => {
|
||
this.startDemoScript();
|
||
});
|
||
}
|
||
|
||
startDemoScript() {
|
||
// Tutorial sequence
|
||
const script = [
|
||
{ delay: 0, action: () => this.showTutorial('Use WASD to move around!', 3000) },
|
||
{ delay: 5000, action: () => this.panToZombie() },
|
||
{ delay: 8000, action: () => this.panToPlayer() },
|
||
{ delay: 10000, action: () => this.showTutorial('Press E near tilled soil to plant!', 3000) },
|
||
{ delay: 15000, action: () => this.showTutorial('This is just 1% of DolinaSmrti...', 3000) },
|
||
{ delay: 20000, action: () => this.showEnding() }
|
||
];
|
||
|
||
script.forEach(step => {
|
||
this.time.delayedCall(step.delay, step.action);
|
||
});
|
||
}
|
||
|
||
panToZombie() {
|
||
this.cameras.main.pan(
|
||
this.zombie.x,
|
||
this.zombie.y,
|
||
2000,
|
||
'Sine.easeInOut'
|
||
);
|
||
|
||
this.showTutorial('Workers till the fields...', 2000);
|
||
this.zombie.play('zombie_dig');
|
||
}
|
||
|
||
panToPlayer() {
|
||
this.cameras.main.pan(
|
||
this.player.x,
|
||
this.player.y,
|
||
2000,
|
||
'Sine.easeInOut'
|
||
);
|
||
}
|
||
|
||
showEnding() {
|
||
const endText = this.add.text(
|
||
this.cameras.main.width/2,
|
||
this.cameras.main.height/2,
|
||
'DOLINASMRTI\n\nComing 2025\n\nSupport us on Kickstarter!',
|
||
{
|
||
fontSize: '32px',
|
||
color: '#ffffff',
|
||
align: 'center',
|
||
backgroundColor: '#000000',
|
||
padding: { x: 30, y: 20 }
|
||
}
|
||
);
|
||
endText.setOrigin(0.5);
|
||
endText.setScrollFactor(0);
|
||
endText.setAlpha(0);
|
||
|
||
this.tweens.add({
|
||
targets: endText,
|
||
alpha: 1,
|
||
duration: 2000
|
||
});
|
||
}
|
||
```
|
||
|
||
**TASK 8 COMPLETE!** ✅ Demo script done!
|
||
|
||
**DAY 3 COMPLETE!** ✅✅✅
|
||
|
||
---
|
||
|
||
# 🎉 DEMO COMPLETE CHECKLIST
|
||
|
||
## ✅ **Assets** (Day 0)
|
||
- [x] 99 PNG files generated
|
||
- [x] Background removal complete
|
||
- [x] Git committed
|
||
|
||
## ✅ **Tiled Map** (Day 1)
|
||
- [ ] 8×8 map created
|
||
- [ ] Tilesets added
|
||
- [ ] Layers configured
|
||
- [ ] Spawn points set
|
||
- [ ] Collision objects placed
|
||
- [ ] Exported as JSON
|
||
|
||
## ✅ **Asset Loading** (Day 1)
|
||
- [ ] Asset manifest created
|
||
- [ ] DemoScene created
|
||
- [ ] All assets loading
|
||
- [ ] No console errors
|
||
|
||
## ✅ **Animations** (Day 2)
|
||
- [ ] Kai idles (4 directions)
|
||
- [ ] Kai walk south
|
||
- [ ] Kai actions (hoe, water)
|
||
- [ ] Zombie animations
|
||
|
||
## ✅ **Movement** (Day 2)
|
||
- [ ] WASD controls
|
||
- [ ] 4-directional movement
|
||
- [ ] Idle/walk transitions
|
||
- [ ] Facing direction
|
||
|
||
## ✅ **Camera** (Day 2)
|
||
- [ ] Follows player
|
||
- [ ] Bounded to map
|
||
- [ ] Smooth panning
|
||
|
||
## ✅ **Collision** (Day 2)
|
||
- [ ] World bounds
|
||
- [ ] Object collision
|
||
- [ ] Player collision box
|
||
|
||
## ✅ **Farming** (Day 3)
|
||
- [ ] Plant seeds (E key)
|
||
- [ ] Water crops (SPACE)
|
||
- [ ] Growth stages (10 sec)
|
||
- [ ] Harvest wheat (H key)
|
||
|
||
## ✅ **UI** (Day 3)
|
||
- [ ] Health bar displayed
|
||
- [ ] Inventory slots
|
||
- [ ] Tutorial text
|
||
- [ ] Item icons
|
||
|
||
## ✅ **Demo Script** (Day 3)
|
||
- [ ] Auto tutorial
|
||
- [ ] Camera pans
|
||
- [ ] Ending sequence
|
||
|
||
---
|
||
|
||
# 🚀 LAUNCH CHECKLIST
|
||
|
||
## **Before Recording Video:**
|
||
- [ ] Test full farming loop
|
||
- [ ] Test all controls
|
||
- [ ] Verify all animations
|
||
- [ ] Check for bugs
|
||
- [ ] Disable debug mode
|
||
- [ ] Clean console output
|
||
|
||
## **Video Recording:**
|
||
- [ ] 1920×1080 resolution
|
||
- [ ] 60 FPS
|
||
- [ ] 3-5 minute runtime
|
||
- [ ] Show both art styles
|
||
- [ ] Add voiceover
|
||
- [ ] Add music
|
||
- [ ] Export to MP4
|
||
|
||
## **Kickstarter Page:**
|
||
- [ ] Embed video
|
||
- [ ] Add screenshots
|
||
- [ ] Write story
|
||
- [ ] Set funding goals
|
||
- [ ] Create reward tiers
|
||
- [ ] Publish!
|
||
|
||
---
|
||
|
||
# 📞 TROUBLESHOOTING
|
||
|
||
## **Assets not loading?**
|
||
- Check file paths in `assets.js`
|
||
- Verify PNGs exist
|
||
- Check browser console errors
|
||
|
||
## **Animations not playing?**
|
||
- Verify animation keys match
|
||
- Check frame keys exist
|
||
- Enable Phaser debug mode
|
||
|
||
## **Player not moving?**
|
||
- Check physics enabled
|
||
- Verify velocity applied
|
||
- Test input detection
|
||
|
||
## **Collision not working?**
|
||
- Check static group setup
|
||
- Verify collision enabled
|
||
- Check collision bounds
|
||
|
||
---
|
||
|
||
**ESTIMATED TOTAL TIME:** 10 hours
|
||
**DIFFICULTY:** Medium
|
||
**REQUIRED SKILLS:** JavaScript, Phaser.js basics, Tiled
|
||
|
||
**YOU CAN DO THIS! 🎮✨**
|
||
|
||
**Let's build the demo and launch that Kickstarter! 🚀**
|