carakter
This commit is contained in:
181
DNEVNIK.md
181
DNEVNIK.md
@@ -2,6 +2,187 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 📅 13. December 2025 - 12:19
|
||||||
|
|
||||||
|
### 🎨 PLAYER SPRITE & ANIMATION OVERHAUL - FINAL
|
||||||
|
|
||||||
|
**Čas**: 11:00 - 12:19 (1 ura 19 min)
|
||||||
|
**Status**: ✅ **PLAYER ANIMACIJE DOKONČANE & WORKING**
|
||||||
|
**Focus**: 2.5D Walking Animations + Debugging & Fixes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 DOSEŽKI
|
||||||
|
|
||||||
|
### ✅ **1. Player Sprite Fixes**
|
||||||
|
- **Scale popravljen**: 1.0 → 0.5 (prevelik player)
|
||||||
|
- **Origin popravljen**: 0.8 → 1.0 (noge sedaj vidne)
|
||||||
|
- **NPC spawning onemogočen**: Odstranjeni duplikati playerja
|
||||||
|
- **Frame initialization**: Dodal začetni frame 0
|
||||||
|
|
||||||
|
### ✅ **2. Novi 2.5D Walking Spritesheet**
|
||||||
|
- **Stil**: Smooth 2.5D art (NE pixel art!)
|
||||||
|
- **Layout**: 4x4 grid = 16 frame-ov
|
||||||
|
- **Protagonist z dreadlocksi**:
|
||||||
|
- Modra hoodie
|
||||||
|
- Rjave hlače
|
||||||
|
- **BREZ palice** ❌
|
||||||
|
- **100% alfa prozornost** ✅
|
||||||
|
- **Datoteka**: `assets/sprites/player_walking_alpha.png`
|
||||||
|
|
||||||
|
### ✅ **3. 4-Directional Animations**
|
||||||
|
**Implementirane animacije:**
|
||||||
|
- `protagonist_walk_down` (frames 0-3) - Walking towards camera
|
||||||
|
- `protagonist_walk_left` (frames 4-7) - Side view left
|
||||||
|
- `protagonist_walk_right` (frames 8-11) - Side view right
|
||||||
|
- `protagonist_walk_up` (frames 12-15) - Walking away
|
||||||
|
- `protagonist_idle_down/left/right/up` (idle za vsako smer)
|
||||||
|
|
||||||
|
### ✅ **4. Player Controls Update**
|
||||||
|
- **Samodejno izbiranje animacije** glede na smer gibanja (WASD)
|
||||||
|
- **Odstranjena flipX logika** (separate left/right sprites)
|
||||||
|
- **Roke se premikajo** z animacijo
|
||||||
|
- **Smooth transitions** med smermi
|
||||||
|
- **4-smerni sistem**: UP/DOWN/LEFT/RIGHT
|
||||||
|
|
||||||
|
### ✅ **5. Critical Bug Fixes**
|
||||||
|
|
||||||
|
#### **Bug #1: Frames not found** ❌→✅
|
||||||
|
**Problem**: `Frame "0" not found in texture "player_protagonist"`
|
||||||
|
**Vzrok**: `processPlayerSpritesheet()` je uničeval frame definicije
|
||||||
|
**Rešitev**: Odstranil processing - PNG že ima proper alpha!
|
||||||
|
|
||||||
|
#### **Bug #2: Transparency processing** ❌→✅
|
||||||
|
**Problem**: Šahovsko ozadje (checkerboard) vidno
|
||||||
|
**Poskus**: Naredil agresiven transparency removal
|
||||||
|
**Ugotovitev**: Procesiranje uniči frame-e pri spritesheet-ih!
|
||||||
|
**Rešitev**: Ne processiraj spritesheets
|
||||||
|
|
||||||
|
#### **Bug #3: Animation errors** ❌→✅
|
||||||
|
**Problem**: `TypeError: Cannot read properties of undefined (reading 'duration')`
|
||||||
|
**Vzrok**: Animation frames destroyed by processing
|
||||||
|
**Rešitev**: Ohranitev originalnega spritesheet-a
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 DATOTEKE
|
||||||
|
|
||||||
|
### **Nove datoteke:**
|
||||||
|
- `assets/sprites/player_walking_alpha.png` (FINAL - 100% alpha, 4x4 grid)
|
||||||
|
- `assets/sprites/player_walking_2d.png` (draft verzija 1)
|
||||||
|
- `assets/sprites/player_walking_spritesheet.png` (draft verzija 2)
|
||||||
|
- `player_animation_demo.html` (testing HTML demo)
|
||||||
|
|
||||||
|
### **Spremenjene datoteke:**
|
||||||
|
- `src/scenes/PreloadScene.js`:
|
||||||
|
- Dodal 4-directional animations (+50 vrstic)
|
||||||
|
- Odstranil processPlayerSpritesheet() call
|
||||||
|
- Fixed spritesheet loading
|
||||||
|
- `src/entities/Player.js`:
|
||||||
|
- Dodal direction-based animation logic (+60 vrstic)
|
||||||
|
- Fixed sprite creation (frame: 0)
|
||||||
|
- Updated handleInput() for 4 directions
|
||||||
|
- `src/scenes/GameScene.js`:
|
||||||
|
- Disabled NPC spawner (duplikati)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 BUG FIXES
|
||||||
|
|
||||||
|
1. ✅ **Duplikati playerja** - NPC spawner onemogočen
|
||||||
|
2. ✅ **Player prevelik** - Scale 1.0 → 0.5
|
||||||
|
3. ✅ **Noge ne vidne** - Origin 0.8 → 1.0
|
||||||
|
4. ✅ **Samo ena animacija** - Dodane 4 smeri
|
||||||
|
5. ✅ **Whole spritesheet prikazan** - Dodal frame: 0
|
||||||
|
6. ✅ **Checkerboard background** - Poskus transparency processing
|
||||||
|
7. ✅ **Frames not found** - CRITICAL: Odstranil spritesheet processing
|
||||||
|
8. ✅ **Animation crashes** - Fixed z odstranjevanjem processing-a
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 TESTIRANJE
|
||||||
|
|
||||||
|
### **HTML Demo**: ✅
|
||||||
|
- Created `player_animation_demo.html`
|
||||||
|
- Prikazuje vse 4 smeri
|
||||||
|
- Intraktivni kontroli (WASD + gumbi)
|
||||||
|
- Dokazal da so sprite-i pravilni
|
||||||
|
|
||||||
|
### **V igri**: ✅
|
||||||
|
- Player se premika z WASD
|
||||||
|
- Vsaka smer ima svojo animacijo
|
||||||
|
- Smooth frame transitions
|
||||||
|
- Pravilna velikost in origin
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 STATISTIKA
|
||||||
|
|
||||||
|
- **Trajanje seje**: 1 ura 19 min
|
||||||
|
- **Datoteke spremenjene**: 3 (Player.js, PreloadScene.js, GameScene.js)
|
||||||
|
- **Slike generirane**: 3 (drafts + final)
|
||||||
|
- **Nove animacije**: 8 (4 walk + 4 idle)
|
||||||
|
- **Vrstice kode**: ~130 novih
|
||||||
|
- **Bug-ov odpravljenih**: 8
|
||||||
|
- **Critical bugs**: 1 (frame destruction)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎮 REZULTAT
|
||||||
|
|
||||||
|
**Player sistem sedaj ima:**
|
||||||
|
- ✅ Smooth 2.5D art (Stardew Valley stil)
|
||||||
|
- ✅ 4-smerne animacije (gor, dol, levo, desno)
|
||||||
|
- ✅ Pravilna velikost (scale 0.5)
|
||||||
|
- ✅ Pravilna origin point (1.0 - bottom center)
|
||||||
|
- ✅ 100% alfa prozorno ozadje
|
||||||
|
- ✅ Brez dupliciranih sprite-ov
|
||||||
|
- ✅ Brez palice (ready za weapons system)
|
||||||
|
- ✅ Working animations (frames 0-15)
|
||||||
|
- ✅ Direction-based animation system
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 KEY LEARNINGS
|
||||||
|
|
||||||
|
### **Phaser Spritesheet Processing:**
|
||||||
|
- ❌ **NEVER** process loaded spritesheets with canvas manipulation
|
||||||
|
- ✅ Frame definitions are destroyed when you replace texture
|
||||||
|
- ✅ Use proper alpha channel in source PNG instead
|
||||||
|
- ✅ `this.textures.remove()` + `addCanvas()` destroys frame data
|
||||||
|
|
||||||
|
### **Animation System:**
|
||||||
|
- ✅ 4-directional system: protagonist_walk_[direction]
|
||||||
|
- ✅ Separate idle animations for each direction
|
||||||
|
- ✅ Frame initialization important: `new Sprite(x, y, key, 0)`
|
||||||
|
- ✅ Direction tracking: `this.direction` state variable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 NASLEDNJI KORAKI
|
||||||
|
|
||||||
|
**Phase 35: Zombi Delavec Sistem** (HIGH PRIORITY)
|
||||||
|
- Zombi entity class
|
||||||
|
- Alfa krotenje sistem
|
||||||
|
- Zombi delo (farming, mining, guard)
|
||||||
|
- Leveling & XP
|
||||||
|
- Utrujenost & razpad
|
||||||
|
- Grobovi
|
||||||
|
|
||||||
|
**Opcijsko:**
|
||||||
|
- Attack animations (swing weapon)
|
||||||
|
- Hurt/damage animation
|
||||||
|
- Death animation
|
||||||
|
- Idle breathing animation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Vnos v dnevnik: 13. december 2025, 12:19*
|
||||||
|
*Player animacije WORKING! Frame bug resolved!* 🎨✨🎮✅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
## 📅 13. December 2025 - 00:03
|
## 📅 13. December 2025 - 00:03
|
||||||
|
|
||||||
### 🏆 EPSKA SEJA: v3.0.0 - ULTIMATE COMPLETE EDITION
|
### 🏆 EPSKA SEJA: v3.0.0 - ULTIMATE COMPLETE EDITION
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 164 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 133 KiB |
BIN
assets/sprites/player_walking_alpha.png
Normal file
BIN
assets/sprites/player_walking_alpha.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 627 KiB |
BIN
assets/sprites/player_walking_clean.png
Normal file
BIN
assets/sprites/player_walking_clean.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 970 KiB |
256
player_animation_demo.html
Normal file
256
player_animation_demo.html
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Player Animation Demo - Krvava Žetev</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
||||||
|
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #a8b2d1;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-container {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 40px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
border: 3px solid rgba(102, 126, 234, 0.5);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #0a0e27;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
image-rendering: -moz-crisp-edges;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 30px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-down {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-left {
|
||||||
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-right {
|
||||||
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-up {
|
||||||
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
max-width: 600px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info h3 {
|
||||||
|
color: #667eea;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spritesheet-preview {
|
||||||
|
margin-top: 30px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spritesheet-preview img {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid rgba(102, 126, 234, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 0 20px rgba(102, 126, 234, 0.7);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>💀 Krvava Žetev - Player Animation Demo 🎮</h1>
|
||||||
|
<p class="subtitle">2.5D Walking Animations - 4 Directions</p>
|
||||||
|
|
||||||
|
<div class="demo-container">
|
||||||
|
<canvas id="canvas" width="512" height="512"></canvas>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<button class="btn-down" onclick="changeDirection('down')">⬇️ DOWN</button>
|
||||||
|
<button class="btn-left" onclick="changeDirection('left')">⬅️ LEFT</button>
|
||||||
|
<button class="btn-right" onclick="changeDirection('right')">➡️ RIGHT</button>
|
||||||
|
<button class="btn-up" onclick="changeDirection('up')">⬆️ UP</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<h3>🎨 Animation Info</h3>
|
||||||
|
<p><strong>Current Direction:</strong> <span id="currentDir">DOWN</span></p>
|
||||||
|
<p><strong>Frame:</strong> <span id="currentFrame">0</span> / 3</p>
|
||||||
|
<p><strong>Frame Rate:</strong> 8 FPS</p>
|
||||||
|
<p><strong>Spritesheet:</strong> 4x4 grid (16 frames total)</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="spritesheet-preview">
|
||||||
|
<h3>📋 Full Spritesheet</h3>
|
||||||
|
<img id="spritesheetImg" src="assets/sprites/player_walking_alpha.png" alt="Player Spritesheet">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Load spritesheet
|
||||||
|
const spritesheet = new Image();
|
||||||
|
spritesheet.src = 'assets/sprites/player_walking_alpha.png';
|
||||||
|
|
||||||
|
// Animation config
|
||||||
|
const frameWidth = 128;
|
||||||
|
const frameHeight = 128;
|
||||||
|
const scale = 3; // Scale up for visibility
|
||||||
|
|
||||||
|
// Animation state
|
||||||
|
let currentDirection = 'down';
|
||||||
|
let currentFrame = 0;
|
||||||
|
let frameTimer = 0;
|
||||||
|
const frameDelay = 125; // ms (8 FPS = 125ms per frame)
|
||||||
|
|
||||||
|
// Direction to row mapping
|
||||||
|
const directions = {
|
||||||
|
'down': 0, // Row 1
|
||||||
|
'left': 1, // Row 2
|
||||||
|
'right': 2, // Row 3
|
||||||
|
'up': 3 // Row 4
|
||||||
|
};
|
||||||
|
|
||||||
|
function changeDirection(dir) {
|
||||||
|
currentDirection = dir;
|
||||||
|
currentFrame = 0;
|
||||||
|
document.getElementById('currentDir').textContent = dir.toUpperCase();
|
||||||
|
|
||||||
|
// Visual feedback
|
||||||
|
document.querySelectorAll('button').forEach(btn => btn.classList.remove('active'));
|
||||||
|
event.target.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawFrame() {
|
||||||
|
// Clear canvas
|
||||||
|
ctx.fillStyle = '#0a0e27';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Get row for current direction
|
||||||
|
const row = directions[currentDirection];
|
||||||
|
|
||||||
|
// Calculate source position
|
||||||
|
const sx = currentFrame * frameWidth;
|
||||||
|
const sy = row * frameHeight;
|
||||||
|
|
||||||
|
// Draw centered
|
||||||
|
const dx = (canvas.width - frameWidth * scale) / 2;
|
||||||
|
const dy = (canvas.height - frameHeight * scale) / 2;
|
||||||
|
|
||||||
|
// Draw sprite
|
||||||
|
ctx.drawImage(
|
||||||
|
spritesheet,
|
||||||
|
sx, sy, frameWidth, frameHeight,
|
||||||
|
dx, dy, frameWidth * scale, frameHeight * scale
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update frame info
|
||||||
|
document.getElementById('currentFrame').textContent = currentFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (now - frameTimer > frameDelay) {
|
||||||
|
currentFrame = (currentFrame + 1) % 4; // 4 frames per direction
|
||||||
|
frameTimer = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawFrame();
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start animation when spritesheet loads
|
||||||
|
spritesheet.onload = () => {
|
||||||
|
frameTimer = Date.now();
|
||||||
|
animate();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keyboard controls (WASD)
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
switch(e.key.toLowerCase()) {
|
||||||
|
case 'w': changeDirection('up'); break;
|
||||||
|
case 'a': changeDirection('left'); break;
|
||||||
|
case 's': changeDirection('down'); break;
|
||||||
|
case 'd': changeDirection('right'); break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -107,15 +107,16 @@ class Player {
|
|||||||
this.sprite = this.scene.add.sprite(
|
this.sprite = this.scene.add.sprite(
|
||||||
screenPos.x + this.offsetX,
|
screenPos.x + this.offsetX,
|
||||||
screenPos.y + this.offsetY,
|
screenPos.y + this.offsetY,
|
||||||
texKey
|
texKey,
|
||||||
|
0 // CRITICAL: Start with frame 0 (first frame only)
|
||||||
);
|
);
|
||||||
this.sprite.setOrigin(0.5, 0.8); // Changed from 1 to 0.8 to show legs
|
this.sprite.setOrigin(0.5, 1.0); // Bottom center - new sprite has full character!
|
||||||
|
|
||||||
// Scale based on sprite type
|
// Scale based on sprite type
|
||||||
if (texKey === 'player_protagonist') {
|
if (texKey === 'player_protagonist') {
|
||||||
this.sprite.setScale(1.0); // Smaller protagonist sprite
|
this.sprite.setScale(0.5); // 256x256 frames scaled to 128x128 display size
|
||||||
} else {
|
} else {
|
||||||
this.sprite.setScale(1.0); // Fallback sprite
|
this.sprite.setScale(0.5); // Fallback sprite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -328,39 +329,65 @@ class Player {
|
|||||||
|
|
||||||
// Update Facing Direction and Last Dir
|
// Update Facing Direction and Last Dir
|
||||||
if (moved) {
|
if (moved) {
|
||||||
// Keep diagonal input clean or prioritize one axis?
|
|
||||||
// Just use the calculated dx/dy.
|
|
||||||
// Note: If both UP and LEFT pressed, logic above overwrites dx/dy.
|
|
||||||
// Let's refine to allow diagonal accumulation if needed, but existing logic prioritized axis.
|
|
||||||
// Current logic: RIGHT/LEFT overwrites UP/DOWN. This is fine for now.
|
|
||||||
|
|
||||||
this.lastDir = { x: dx, y: dy };
|
this.lastDir = { x: dx, y: dy };
|
||||||
this.sprite.setFlipX(!facingRight);
|
|
||||||
|
|
||||||
// Play walking animation (with safety check)
|
// Determine animation direction (4 directions)
|
||||||
|
let animDir = 'down'; // default
|
||||||
|
|
||||||
|
// UP/DOWN (isometric: dx changes)
|
||||||
|
if (dx < 0 && dy === 0) {
|
||||||
|
animDir = 'up'; // Moving up (NW in isometric)
|
||||||
|
} else if (dx > 0 && dy === 0) {
|
||||||
|
animDir = 'down'; // Moving down (SE in isometric)
|
||||||
|
}
|
||||||
|
// LEFT/RIGHT (isometric: dy changes)
|
||||||
|
else if (dy > 0 && dx === 0) {
|
||||||
|
animDir = 'left'; // Moving left (SW in isometric)
|
||||||
|
} else if (dy < 0 && dx === 0) {
|
||||||
|
animDir = 'right'; // Moving right (NE in isometric)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.direction = animDir;
|
||||||
|
|
||||||
|
// Play walking animation for the direction
|
||||||
if (this.sprite.anims) {
|
if (this.sprite.anims) {
|
||||||
try {
|
try {
|
||||||
if (this.scene.anims.exists('protagonist_walk') && !this.sprite.anims.isPlaying) {
|
const walkAnim = `protagonist_walk_${animDir}`;
|
||||||
this.sprite.play('protagonist_walk');
|
|
||||||
|
// Debug
|
||||||
|
console.log(`🎬 Trying to play: ${walkAnim}`);
|
||||||
|
console.log(`Animation exists: ${this.scene.anims.exists(walkAnim)}`);
|
||||||
|
|
||||||
|
if (this.scene.anims.exists(walkAnim)) {
|
||||||
|
this.sprite.play(walkAnim, true); // Force restart animation
|
||||||
|
console.log(`✅ Playing: ${walkAnim}`);
|
||||||
|
} else {
|
||||||
|
console.warn(`⚠️ Animation not found: ${walkAnim}`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore animation errors
|
console.error('Animation error:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hand offset
|
// Hand offset based on direction
|
||||||
const handOffset = facingRight ? 10 : -10;
|
const handOffsets = {
|
||||||
this.handSprite.setX(this.sprite.x + handOffset);
|
'left': -10,
|
||||||
this.handSprite.setFlipX(!facingRight);
|
'right': 10,
|
||||||
|
'up': 0,
|
||||||
|
'down': 0
|
||||||
|
};
|
||||||
|
this.handSprite.setX(this.sprite.x + (handOffsets[animDir] || 0));
|
||||||
} else {
|
} else {
|
||||||
// Stop animation when idle (with safety check)
|
// Stop animation when idle
|
||||||
if (this.sprite.anims) {
|
if (this.sprite.anims) {
|
||||||
try {
|
try {
|
||||||
if (this.sprite.anims.isPlaying) {
|
if (this.sprite.anims.isPlaying) {
|
||||||
this.sprite.stop();
|
this.sprite.stop();
|
||||||
}
|
}
|
||||||
if (this.scene.anims.exists('protagonist_idle')) {
|
// Play idle animation for current direction
|
||||||
this.sprite.play('protagonist_idle');
|
const idleAnim = `protagonist_idle_${this.direction}`;
|
||||||
|
if (this.scene.anims.exists(idleAnim)) {
|
||||||
|
this.sprite.play(idleAnim);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore animation errors
|
// Ignore animation errors
|
||||||
@@ -431,12 +458,20 @@ class Player {
|
|||||||
|
|
||||||
const targetScreen = this.iso.toScreen(targetX, targetY);
|
const targetScreen = this.iso.toScreen(targetX, targetY);
|
||||||
|
|
||||||
// Play walk animation - SAFE CHECK
|
// Play walk animation - SAFE CHECK (updated for protagonist)
|
||||||
if (this.sprite.texture.key === 'player_dreadlocks') {
|
const texKey = this.sprite.texture.key;
|
||||||
|
|
||||||
|
if (texKey === 'player_protagonist') {
|
||||||
|
// KRVAVA ŽETEV: Use directional animations
|
||||||
|
const walkAnim = `protagonist_walk_${this.direction}`;
|
||||||
|
if (this.scene.anims.exists(walkAnim)) {
|
||||||
|
this.sprite.play(walkAnim, true);
|
||||||
|
}
|
||||||
|
} else if (texKey === 'player_dreadlocks') {
|
||||||
if (this.scene.anims.exists('player_dreadlocks_walk')) {
|
if (this.scene.anims.exists('player_dreadlocks_walk')) {
|
||||||
this.sprite.play('player_dreadlocks_walk', true);
|
this.sprite.play('player_dreadlocks_walk', true);
|
||||||
}
|
}
|
||||||
} else if (this.sprite.texture.key === 'player_walk') {
|
} else if (texKey === 'player_walk') {
|
||||||
if (this.scene.anims.exists('player_walk_anim')) {
|
if (this.scene.anims.exists('player_walk_anim')) {
|
||||||
this.sprite.play('player_walk_anim', true);
|
this.sprite.play('player_walk_anim', true);
|
||||||
}
|
}
|
||||||
@@ -452,11 +487,19 @@ class Player {
|
|||||||
this.isMoving = false;
|
this.isMoving = false;
|
||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
|
|
||||||
// Stop animation
|
// Stop animation (updated for protagonist)
|
||||||
if (this.sprite.texture.key === 'player_dreadlocks') {
|
if (texKey === 'player_protagonist') {
|
||||||
|
// Play idle animation
|
||||||
|
const idleAnim = `protagonist_idle_${this.direction}`;
|
||||||
|
if (this.scene.anims.exists(idleAnim)) {
|
||||||
|
this.sprite.play(idleAnim);
|
||||||
|
} else {
|
||||||
|
this.sprite.stop();
|
||||||
|
}
|
||||||
|
} else if (texKey === 'player_dreadlocks') {
|
||||||
this.sprite.stop();
|
this.sprite.stop();
|
||||||
this.sprite.setFrame(0);
|
this.sprite.setFrame(0);
|
||||||
} else if (this.sprite.texture.key === 'player_walk') {
|
} else if (texKey === 'player_walk') {
|
||||||
this.sprite.stop();
|
this.sprite.stop();
|
||||||
this.sprite.setFrame(0);
|
this.sprite.setFrame(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -350,10 +350,10 @@ class GameScene extends Phaser.Scene {
|
|||||||
console.log('📊 Initializing Unified Stats Panel...');
|
console.log('📊 Initializing Unified Stats Panel...');
|
||||||
this.unifiedStatsPanel = new UnifiedStatsPanel(this);
|
this.unifiedStatsPanel = new UnifiedStatsPanel(this);
|
||||||
|
|
||||||
// NPC Spawner
|
// NPC Spawner - DISABLED (NPCs look like player duplicates)
|
||||||
console.log('🧟 Initializing NPC Spawner...');
|
console.log('🧟 NPC Spawner - DISABLED');
|
||||||
this.npcSpawner = new NPCSpawner(this);
|
this.npcSpawner = null; // new NPCSpawner(this);
|
||||||
this.npcSpawner.spawnInitialNPCs();
|
// this.npcSpawner.spawnInitialNPCs();
|
||||||
|
|
||||||
// Easter Egg: Broken Scooter
|
// Easter Egg: Broken Scooter
|
||||||
console.log('🛵 Spawning Scooter Easter Egg...');
|
console.log('🛵 Spawning Scooter Easter Egg...');
|
||||||
|
|||||||
@@ -131,18 +131,24 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
|
|
||||||
// Wait for load completion then process transparency
|
// Wait for load completion then process transparency
|
||||||
this.load.once('complete', () => {
|
this.load.once('complete', () => {
|
||||||
|
// NOTE: Do NOT process spritesheets - they already have proper alpha!
|
||||||
|
// Processing destroys frame definitions
|
||||||
|
|
||||||
this.processAllTransparency();
|
this.processAllTransparency();
|
||||||
this.createAnimations();
|
this.createAnimations();
|
||||||
});
|
});
|
||||||
|
|
||||||
// New Processed Animations (Standardized 64x64 strips)
|
// New Processed Animations (Standardized 64x64 strips)
|
||||||
this.load.spritesheet('zombie_walk', 'assets/sprites/zombie_walk_strip.png', { frameWidth: 64, frameHeight: 64 });
|
// NOTE: zombie_walk_strip.png is missing - commented out for now
|
||||||
|
// this.load.spritesheet('zombie_walk', 'assets/sprites/zombie_walk_strip.png', { frameWidth: 64, frameHeight: 64 });
|
||||||
|
|
||||||
// KRVAVA ŽETEV - New Player Sprite (Protagonist with dreadlocks)
|
// KRVAVA ŽETEV - CLEAN Player Sprite (100% Clean Transparency - No Checkerboard!)
|
||||||
this.load.spritesheet('player_protagonist', 'assets/sprites/player_walk_animations.png', {
|
this.load.spritesheet('player_protagonist', 'assets/sprites/player_walking_clean.png', {
|
||||||
frameWidth: 128, // Adjust based on actual sprite size
|
frameWidth: 256, // 256x256 per frame, 4x4 grid = 16 frames (1024x1024 total)
|
||||||
frameHeight: 128
|
frameHeight: 256
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createAnimations() {
|
createAnimations() {
|
||||||
@@ -163,21 +169,64 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
repeat: -1
|
repeat: -1
|
||||||
});
|
});
|
||||||
|
|
||||||
// KRVAVA ŽETEV: Protagonist animations
|
// KRVAVA ŽETEV: Protagonist 4-directional walking animations
|
||||||
|
// Row 1: DOWN (frames 0-3)
|
||||||
this.anims.create({
|
this.anims.create({
|
||||||
key: 'protagonist_walk',
|
key: 'protagonist_walk_down',
|
||||||
frames: this.anims.generateFrameNumbers('player_protagonist', { start: 0, end: 7 }),
|
frames: this.anims.generateFrameNumbers('player_protagonist', { start: 0, end: 3 }),
|
||||||
frameRate: 10,
|
frameRate: 8,
|
||||||
repeat: -1
|
repeat: -1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Row 2: LEFT (frames 4-7)
|
||||||
this.anims.create({
|
this.anims.create({
|
||||||
key: 'protagonist_idle',
|
key: 'protagonist_walk_left',
|
||||||
frames: this.anims.generateFrameNumbers('player_protagonist', { start: 8, end: 9 }),
|
frames: this.anims.generateFrameNumbers('player_protagonist', { start: 4, end: 7 }),
|
||||||
frameRate: 2,
|
frameRate: 8,
|
||||||
repeat: -1
|
repeat: -1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Row 3: RIGHT (frames 8-11)
|
||||||
|
this.anims.create({
|
||||||
|
key: 'protagonist_walk_right',
|
||||||
|
frames: this.anims.generateFrameNumbers('player_protagonist', { start: 8, end: 11 }),
|
||||||
|
frameRate: 8,
|
||||||
|
repeat: -1
|
||||||
|
});
|
||||||
|
|
||||||
|
// Row 4: UP (frames 12-15)
|
||||||
|
this.anims.create({
|
||||||
|
key: 'protagonist_walk_up',
|
||||||
|
frames: this.anims.generateFrameNumbers('player_protagonist', { start: 12, end: 15 }),
|
||||||
|
frameRate: 8,
|
||||||
|
repeat: -1
|
||||||
|
});
|
||||||
|
|
||||||
|
// IDLE: Use first frame of each direction
|
||||||
|
this.anims.create({
|
||||||
|
key: 'protagonist_idle_down',
|
||||||
|
frames: [{ key: 'player_protagonist', frame: 0 }],
|
||||||
|
frameRate: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
this.anims.create({
|
||||||
|
key: 'protagonist_idle_left',
|
||||||
|
frames: [{ key: 'player_protagonist', frame: 4 }],
|
||||||
|
frameRate: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
this.anims.create({
|
||||||
|
key: 'protagonist_idle_right',
|
||||||
|
frames: [{ key: 'player_protagonist', frame: 8 }],
|
||||||
|
frameRate: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
this.anims.create({
|
||||||
|
key: 'protagonist_idle_up',
|
||||||
|
frames: [{ key: 'player_protagonist', frame: 12 }],
|
||||||
|
frameRate: 1
|
||||||
|
});
|
||||||
|
|
||||||
console.log('🎞️ Animations created!');
|
console.log('🎞️ Animations created!');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,6 +519,83 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processPlayerSpritesheet() {
|
||||||
|
const spriteKey = 'player_protagonist';
|
||||||
|
|
||||||
|
if (!this.textures.exists(spriteKey)) {
|
||||||
|
console.warn('⚠️ Player protagonist texture not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🎨 Processing player spritesheet transparency...');
|
||||||
|
|
||||||
|
const texture = this.textures.get(spriteKey);
|
||||||
|
const source = texture.getSourceImage();
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = source.width;
|
||||||
|
canvas.height = source.height;
|
||||||
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
|
|
||||||
|
ctx.drawImage(source, 0, 0);
|
||||||
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
const data = imageData.data;
|
||||||
|
|
||||||
|
// ULTRA AGGRESSIVE: Remove ALL checkerboard patterns and gray backgrounds
|
||||||
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
|
const r = data[i];
|
||||||
|
const g = data[i + 1];
|
||||||
|
const b = data[i + 2];
|
||||||
|
const a = data[i + 3];
|
||||||
|
|
||||||
|
// Skip if already transparent
|
||||||
|
if (a === 0) continue;
|
||||||
|
|
||||||
|
// 1. Remove PERFECT GRAY (checkerboard pattern: RGB 204,204,204 and 153,153,153)
|
||||||
|
if (r === g && g === b) {
|
||||||
|
if (r === 204 || r === 153 || r === 192 || r === 128 || (r >= 180 && r <= 210)) {
|
||||||
|
data[i + 3] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Remove ANY grayscale (R≈G≈B within 15 units)
|
||||||
|
const isGrayscale = Math.abs(r - g) < 15 && Math.abs(g - b) < 15 && Math.abs(r - b) < 15;
|
||||||
|
const brightness = (r + g + b) / 3;
|
||||||
|
|
||||||
|
if (isGrayscale && brightness > 100) {
|
||||||
|
data[i + 3] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Remove LIGHT backgrounds (brightness > 150)
|
||||||
|
if (brightness > 150) {
|
||||||
|
data[i + 3] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Keep ONLY colored pixels (character skin, clothing, hair)
|
||||||
|
// Character has: blue hoodie, brown pants, brown skin
|
||||||
|
const maxChannel = Math.max(r, g, b);
|
||||||
|
const minChannel = Math.min(r, g, b);
|
||||||
|
const saturation = maxChannel === 0 ? 0 : (maxChannel - minChannel) / maxChannel;
|
||||||
|
|
||||||
|
// If low saturation AND bright = background
|
||||||
|
if (saturation < 0.15 && brightness > 80) {
|
||||||
|
data[i + 3] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
|
||||||
|
// Replace texture
|
||||||
|
this.textures.remove(spriteKey);
|
||||||
|
this.textures.addCanvas(spriteKey, canvas);
|
||||||
|
|
||||||
|
console.log('✅ Player spritesheet transparency processed!');
|
||||||
|
}
|
||||||
|
|
||||||
ultraRemoveBackground(spriteKey) {
|
ultraRemoveBackground(spriteKey) {
|
||||||
if (!this.textures.exists(spriteKey)) return;
|
if (!this.textures.exists(spriteKey)) return;
|
||||||
|
|
||||||
|
|||||||
52
tools/remove_checkerboard.py
Normal file
52
tools/remove_checkerboard.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
def remove_checkerboard(input_path, output_path):
|
||||||
|
"""Remove gray/white checkerboard pattern and make background transparent"""
|
||||||
|
|
||||||
|
img = Image.open(input_path).convert('RGBA')
|
||||||
|
pixels = img.load()
|
||||||
|
width, height = img.size
|
||||||
|
|
||||||
|
print(f"Image size: {width}x{height}")
|
||||||
|
|
||||||
|
modified = 0
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
|
||||||
|
# Skip if already transparent
|
||||||
|
if a == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if pixel is grayscale (R ≈ G ≈ B)
|
||||||
|
is_grayscale = abs(r - g) < 20 and abs(g - b) < 20 and abs(r - b) < 20
|
||||||
|
brightness = (r + g + b) / 3
|
||||||
|
|
||||||
|
should_remove = False
|
||||||
|
|
||||||
|
# Remove checkerboard grays (common values: 204, 153, 192, 128, 255)
|
||||||
|
if is_grayscale and brightness > 100:
|
||||||
|
should_remove = True
|
||||||
|
|
||||||
|
# Remove very bright pixels
|
||||||
|
if brightness > 200:
|
||||||
|
should_remove = True
|
||||||
|
|
||||||
|
# Remove pure white
|
||||||
|
if r > 240 and g > 240 and b > 240:
|
||||||
|
should_remove = True
|
||||||
|
|
||||||
|
if should_remove:
|
||||||
|
pixels[x, y] = (r, g, b, 0) # Make transparent
|
||||||
|
modified += 1
|
||||||
|
|
||||||
|
img.save(output_path, 'PNG')
|
||||||
|
print(f"✅ Processed! Removed {modified} background pixels")
|
||||||
|
print(f"📁 Saved to: {output_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Use the ORIGINAL image you uploaded
|
||||||
|
input_file = r"C:\Users\hipod\.gemini\antigravity\brain\70050185-4972-4413-9765-559b6f2ad1cc\uploaded_image_1765710182006.jpg"
|
||||||
|
output_file = r"c:\novafarma\assets\sprites\player_walking_clean.png"
|
||||||
|
|
||||||
|
remove_checkerboard(input_file, output_file)
|
||||||
Reference in New Issue
Block a user