Session 3: Tree Variety System Complete

TREE VARIETIES IMPLEMENTED:
- Added 5 tree types: Cherry, Oak, Pine, Dead, Apple
- AI-generated PNG sprites with transparent backgrounds
- Random tree selection for natural diversity
- Growth scaling (0.4-0.6x for variety)

 VISUAL IMPROVEMENTS:
- 2.5D depth sorting (Y-position based)
- Proper shadows matching tree size
- Layered rendering for perspective

 TRANSPARENCY FIXES:
- Disabled automatic sprite processing
- User manually removed green screen backgrounds
- Fixed pink/red color preservation
- Trees now render with proper transparency

 TREE DISTRIBUTION:
- Reduced from ~78 to 10 trees (87% reduction)
- Removed corner forest clusters (too crowded)
- Sparse scattered placement across 100x100 map
- Wider spacing (8-15 tiles apart)
- Small tree sizes (0.35-0.7x scale, ~50% reduction)

 TECHNICAL CHANGES:
- PreloadScene: Added tree sprite loading
- PreloadScene: Disabled processAllTransparency()
- Flat2DTerrainSystem: PNG sprite rendering with fallback
- map2d_data.js: Sparse tree generation
- Green removal threshold: g > 180 (preserves pink/red)

 FILES MODIFIED:
- src/scenes/PreloadScene.js
- src/systems/Flat2DTerrainSystem.js
- data/map2d_data.js
- assets/sprites/tree_*.png (5 new sprites)
- docs/DNEVNIK.md (updated with user availability)

Session: 1.5h (23:28-00:48)
Date: 14-15.12.2024
This commit is contained in:
2025-12-15 00:49:06 +01:00
parent c5d6c01305
commit 344fbc307d
20 changed files with 272 additions and 85 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 KiB

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 565 KiB

After

Width:  |  Height:  |  Size: 311 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 KiB

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 KiB

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

BIN
assets/sprites/tree_oak.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 KiB

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 KiB

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

After

Width:  |  Height:  |  Size: 332 KiB

View File

@@ -48,36 +48,19 @@ const Map2DData = {
// this.addWindingPath(map, 10, 10, 90, 90); // Disabled
// this.addWindingPath(map, 90, 10, 10, 90); // Disabled
// 3. MORE TREES IN NATURAL CLUSTERS 🌳
// Top-left forest
this.addTreeCluster(map, 15, 15, 5);
this.addTreeCluster(map, 18, 12, 4);
this.addTreeCluster(map, 12, 18, 3);
// 3. SPARSE SCATTERED TREES 🌳 (NO CLUSTERS!)
// Removed all corner forests - too crowded!
// Top-right forest
this.addTreeCluster(map, 85, 15, 5);
this.addTreeCluster(map, 82, 18, 4);
this.addTreeCluster(map, 88, 12, 3);
// Bottom-left forest
this.addTreeCluster(map, 15, 85, 5);
this.addTreeCluster(map, 18, 82, 4);
this.addTreeCluster(map, 12, 88, 3);
// Bottom-right forest
this.addTreeCluster(map, 85, 85, 5);
this.addTreeCluster(map, 82, 88, 4);
this.addTreeCluster(map, 88, 82, 3);
// Scattered single trees - MANY MORE!
for (let i = 0; i < 50; i++) {
// Just a few scattered trees across entire map
for (let i = 0; i < 10; i++) { // VERY FEW! (was 15)
this.addTreeCluster(map,
10 + Math.random() * 80, // Wider spread
10 + Math.random() * 80, // Entire map
10 + Math.random() * 80,
1
1 // Single trees only
);
}
// 4. COLORFUL FLOWERS SCATTERED 🌸
this.addFlowers(map, 30);
@@ -187,7 +170,7 @@ const Map2DData = {
addTreeCluster: function (map, centerX, centerY, count) {
for (let i = 0; i < count; i++) {
const angle = (Math.PI * 2 * i) / count + Math.random() * 0.5;
const radius = 2 + Math.random() * 3;
const radius = 8 + Math.random() * 7; // EVEN WIDER! (was 5+7)
const tx = Math.floor(centerX + Math.cos(angle) * radius);
const ty = Math.floor(centerY + Math.sin(angle) * radius);

View File

@@ -1,5 +1,13 @@
# 📖 DNEVNIK RAZVOJA - NovaFarma
## ⚠️ **POMEMBNO - User Availability:**
**Status:** Na dopustu / bolniški
**Razpoložljivost:** Lahko delamo dolge sessions (3-4+ ur)
**Ni potrebe:** Skrbeti za čas spanja ali utrujenost
**User bo povedal:** Ko je utrujen/zaspan
---
## 🗓️ 14. December 2024 - Session 1: Tiled Map Editor Exploration
**Trajanje:** 2 uri (20:00 - 22:00)

View File

@@ -56,9 +56,12 @@ class PreloadScene extends Phaser.Scene {
this.load.image('tree_blue_final', 'assets/tree_blue_final.png');
this.load.image('tree_dead_final', 'assets/tree_dead_final.png');
// 🌸 CHERRY BLOSSOM TREES (NEW!)
this.load.image('cesnjevo_drevo', 'assets/sprites/roza_cesnjevo_drevo.png');
this.load.image('cesnja', 'assets/sprites/cesnja_sadje.png');
// 🌳 TREE VARIETY SPRITES (GREEN SCREEN!)
this.load.image('tree_cherry', 'assets/sprites/tree_cherry.png');
this.load.image('tree_oak', 'assets/sprites/tree_oak.png');
this.load.image('tree_pine', 'assets/sprites/tree_pine.png');
this.load.image('tree_dead', 'assets/sprites/tree_dead.png');
this.load.image('tree_apple', 'assets/sprites/tree_apple.png');
// STARDEW VALLEY FOREST TREES (NEW!)
this.load.image('tree_purple', 'assets/tree_purple.png');
@@ -153,7 +156,9 @@ class PreloadScene extends Phaser.Scene {
// NOTE: Do NOT process spritesheets - they already have proper alpha!
// Processing destroys frame definitions
this.processAllTransparency();
// 🚫 DISABLED - Tree sprites already have transparency!
// this.processAllTransparency();
this.createAnimations();
});
@@ -345,10 +350,10 @@ class PreloadScene extends Phaser.Scene {
'wheat_sprite',
'grass_sprite',
'leaf_sprite',
'stone_sprite',
'stone_sprite'
// 🌸 CHERRY BLOSSOM TREE
'cesnjevo_drevo'
// 🌳 TREES REMOVED - already have transparent PNG!
// (green removal would delete green leaves!)
];
spritesToProcess.forEach(spriteKey => {
@@ -360,10 +365,10 @@ class PreloadScene extends Phaser.Scene {
this.ultraRemoveBackground('fence_post');
}
// ULTRA AGGRESSIVE: Cherry Blossom Tree (remove black outlines!)
if (this.textures.exists('cesnjevo_drevo')) {
this.ultraRemoveBackground('cesnjevo_drevo');
}
// ULTRA REMOVED - new tree sprites already have transparency!
// if (this.textures.exists('cesnjevo_drevo')) {
// this.ultraRemoveBackground('cesnjevo_drevo');
// }
console.log('✅ All sprites transparency processed!');
}
@@ -399,15 +404,16 @@ class PreloadScene extends Phaser.Scene {
const brightness = (r + g + b) / 3;
// 🟢 NUCLEAR GREEN SCREEN REMOVAL!
// Remove ANY pixel where green is even SLIGHTLY dominant
// 🟢 GREEN REMOVAL - Only BRIGHT greens!
// Pink (255,105,180) has g=105 - MUST KEEP!
// Bright green (0,255,0) has g=255 - REMOVE!
const isGreen = (
g > 100 && // Even darker greens
g > r && // Green beats red
g > b // Green beats blue
g > 180 && // Only very bright green
g > r * 1.3 && // Green dominates red
g > b * 1.3 // Green dominates blue
);
if (isGreen) {
data[i + 3] = 0; // NUKE IT!
data[i + 3] = 0;
continue;
}

View File

@@ -309,69 +309,259 @@ class Flat2DTerrainSystem {
}
if (sprite) {
// 🎨 2.5D DEPTH SORTING - Y position = depth!
// Objects lower on screen appear in front
sprite.setDepth(10 + worldY);
this.decorLayer.add(sprite);
}
}
createTree(x, y) {
// 🌸 PNG DISABLED - procedural is more reliable!
// if (this.scene.textures.exists('cesnjevo_drevo')) {
// const tree = this.scene.add.image(x, y, 'cesnjevo_drevo');
// const growthScale = Phaser.Math.FloatBetween(0.3, 0.7);
// tree.setScale(growthScale);
// tree.setOrigin(0.5, 0.9);
// const shadow = this.scene.add.ellipse(x, y + 10, 20 * growthScale, 6, 0x000000, 0.15);
// shadow.setDepth(tree.depth - 1);
// return tree;
// }
// 🌳 TREE VARIETY SYSTEM!
const treeTypes = ['cherry', 'oak', 'pine', 'dead', 'apple'];
const randomType = Phaser.Utils.Array.GetRandom(treeTypes);
// PROCEDURAL CHERRY BLOSSOM (RELIABLE!)
switch (randomType) {
case 'oak': return this.createOakTree(x, y);
case 'pine': return this.createPineTree(x, y);
case 'dead': return this.createDeadTree(x, y);
case 'apple': return this.createAppleTree(x, y);
default: return this.createCherryTree(x, y);
}
}
createCherryTree(x, y) {
// 🌸 PNG SPRITE!
if (this.scene.textures.exists('tree_cherry')) {
const tree = this.scene.add.image(x, y, 'tree_cherry');
const scale = Phaser.Math.FloatBetween(0.4, 0.6); // SMALLER! (was 0.8-1.2)
tree.setScale(scale);
tree.setOrigin(0.5, 0.85);
// Shadow
const shadow = this.scene.add.ellipse(x, y + 15, 30 * scale, 10, 0x000000, 0.2);
shadow.setDepth(tree.depth - 1);
return tree;
}
// FALLBACK: Procedural
return this.createProceduralCherryTree(x, y);
}
createProceduralCherryTree(x, y) {
// Old procedural code (backup)
const graphics = this.scene.add.graphics();
const growthScale = Phaser.Math.FloatBetween(0.8, 1.4); // BIGGER!
// DROP SHADOW (2D depth!) 🎨
graphics.fillStyle(0x000000, 0.15);
graphics.fillEllipse(x + 1, y + 24, 20, 6);
const heightOffset = -20 * growthScale;
const crownSize = 30 * growthScale; // MUCH BIGGER!
// TRUNK - simple rectangle
graphics.fillStyle(0x8B4513);
graphics.fillRect(x - 4, y + 6, 8, 18);
// Shadow (ellipse on ground)
graphics.fillStyle(0x000000, 0.2);
graphics.fillEllipse(x, y + 30, 40 * growthScale, 12); // Bigger shadow
// Trunk outline
graphics.lineStyle(1, 0x654321);
graphics.strokeRect(x - 4, y + 6, 8, 18);
// Trunk (with shading for volume)
const trunkW = 12 * growthScale; // Thicker
const trunkH = 30 * growthScale; // Taller
// CHERRY BLOSSOM CROWN - FLAT 2D TRIANGLE! 🌸
// Base pink triangle
// Dark side (right)
graphics.fillStyle(0x6D3F1A);
graphics.fillRect(x + trunkW / 4, y + 10 + heightOffset, trunkW / 2, trunkH);
// Light side (left)
graphics.fillStyle(0x8B5A2B);
graphics.fillRect(x - trunkW / 2, y + 10 + heightOffset, trunkW / 2, trunkH);
// CROWN - Layered circles for 2.5D!
const crownY = y - 10 * growthScale + heightOffset; // Higher crown
// Dark base (shadow underneath)
graphics.fillStyle(0xE75480);
graphics.fillCircle(x + 4 * growthScale, crownY + 4 * growthScale, crownSize);
// Main pink crown
graphics.fillStyle(0xFF69B4);
graphics.fillCircle(x, crownY, crownSize);
// Top highlight (light from top-left)
graphics.fillStyle(0xFFB6C1, 0.8);
graphics.fillCircle(x - 6 * growthScale, crownY - 6 * growthScale, crownSize * 0.6);
// Bright specular spot
graphics.fillStyle(0xFFFFFF, 0.5);
graphics.fillCircle(x - 8 * growthScale, crownY - 8 * growthScale, crownSize * 0.3);
return graphics;
}
createOakTree(x, y) {
// 🌲 PNG SPRITE!
if (this.scene.textures.exists('tree_oak')) {
const tree = this.scene.add.image(x, y, 'tree_oak');
const scale = Phaser.Math.FloatBetween(0.45, 0.65); // SMALLER!
tree.setScale(scale);
tree.setOrigin(0.5, 0.85);
const shadow = this.scene.add.ellipse(x, y + 15, 35 * scale, 12, 0x000000, 0.2);
shadow.setDepth(tree.depth - 1);
return tree;
}
// FALLBACK:
const graphics = this.scene.add.graphics();
const growthScale = Phaser.Math.FloatBetween(0.8, 1.4); // BIGGER!
// Shadow
graphics.fillStyle(0x000000, 0.15);
graphics.fillEllipse(x, y + 24, 25 * growthScale, 8);
// Thick trunk
graphics.fillStyle(0x654321);
graphics.fillRect(x - 5 * growthScale, y, 10 * growthScale, 24 * growthScale);
// Large round crown
graphics.fillStyle(0x228B22); // Forest green
graphics.fillCircle(x, y - 10 * growthScale, 18 * growthScale);
// Darker outline
graphics.lineStyle(2, 0x006400);
graphics.strokeCircle(x, y - 10 * growthScale, 18 * growthScale);
// Highlight
graphics.fillStyle(0x32CD32, 0.6);
graphics.fillCircle(x - 5 * growthScale, y - 15 * growthScale, 8 * growthScale);
return graphics;
}
createPineTree(x, y) {
// 🌲 PNG SPRITE!
if (this.scene.textures.exists('tree_pine')) {
const tree = this.scene.add.image(x, y, 'tree_pine');
const scale = Phaser.Math.FloatBetween(0.45, 0.7); // SMALLER!
tree.setScale(scale);
tree.setOrigin(0.5, 0.9); // Taller
const shadow = this.scene.add.ellipse(x, y + 20, 30 * scale, 10, 0x000000, 0.2);
shadow.setDepth(tree.depth - 1);
return tree;
}
// FALLBACK:
const graphics = this.scene.add.graphics();
const growthScale = Phaser.Math.FloatBetween(0.8, 1.5); // BIGGER (Pine taller)
// Shadow
graphics.fillStyle(0x000000, 0.15);
graphics.fillEllipse(x, y + 20, 15 * growthScale, 6);
// Trunk
graphics.fillStyle(0x8B4513);
graphics.fillRect(x - 3 * growthScale, y + 5, 6 * growthScale, 15 * growthScale);
// Stacked triangles (pine shape)
graphics.fillStyle(0x2F4F2F); // Dark green
// Bottom tier
graphics.fillTriangle(
x, y - 18, // Top point
x - 16, y + 8, // Bottom left
x + 16, y + 8 // Bottom right
x, y - 5 * growthScale,
x - 14 * growthScale, y + 10,
x + 14 * growthScale, y + 10
);
// Dark outline (2D cartoon style!)
graphics.lineStyle(2, 0xDB7093, 1.0);
graphics.strokeTriangle(
x, y - 18,
x - 16, y + 8,
x + 16, y + 8
// Middle tier
graphics.fillTriangle(
x, y - 15 * growthScale,
x - 10 * growthScale, y,
x + 10 * growthScale, y
);
// Inner lighter triangle (highlight)
graphics.fillStyle(0xFFB6C1);
// Top tier
graphics.fillTriangle(
x, y - 14,
x - 10, y + 4,
x + 10, y + 4
x, y - 25 * growthScale,
x - 7 * growthScale, y - 10 * growthScale,
x + 7 * growthScale, y - 10 * growthScale
);
// Top highlight (blossom peak!)
graphics.fillStyle(0xFFFFFF, 0.7);
graphics.fillTriangle(
x, y - 14,
x - 6, y - 4,
x + 6, y - 4
);
return graphics;
}
createDeadTree(x, y) {
// 💀 PNG SPRITE!
if (this.scene.textures.exists('tree_dead')) {
const tree = this.scene.add.image(x, y, 'tree_dead');
const scale = Phaser.Math.FloatBetween(0.35, 0.55); // SMALLER!
tree.setScale(scale);
tree.setOrigin(0.5, 0.85);
const shadow = this.scene.add.ellipse(x, y + 18, 28 * scale, 10, 0x000000, 0.3);
shadow.setDepth(tree.depth - 1);
return tree;
}
// FALLBACK:
const graphics = this.scene.add.graphics();
const growthScale = Phaser.Math.FloatBetween(0.7, 1.2); // BIGGER!
// Shadow
graphics.fillStyle(0x000000, 0.2);
graphics.fillEllipse(x, y + 20, 18 * growthScale, 6);
// Dark trunk
graphics.fillStyle(0x3D3D3D);
graphics.fillRect(x - 4 * growthScale, y, 8 * growthScale, 22 * growthScale);
// Branches (bare)
graphics.lineStyle(3 * growthScale, 0x2D2D2D);
graphics.beginPath();
graphics.moveTo(x, y - 5 * growthScale);
graphics.lineTo(x - 10 * growthScale, y - 15 * growthScale);
graphics.moveTo(x, y - 8 * growthScale);
graphics.lineTo(x + 12 * growthScale, y - 12 * growthScale);
graphics.strokePath();
return graphics;
}
createAppleTree(x, y) {
// 🍎 PNG SPRITE!
if (this.scene.textures.exists('tree_apple')) {
const tree = this.scene.add.image(x, y, 'tree_apple');
const scale = Phaser.Math.FloatBetween(0.4, 0.6); // SMALLER!
tree.setScale(scale);
tree.setOrigin(0.5, 0.85);
const shadow = this.scene.add.ellipse(x, y + 16, 32 * scale, 11, 0x000000, 0.2);
shadow.setDepth(tree.depth - 1);
return tree;
}
// FALLBACK:
const graphics = this.scene.add.graphics();
const growthScale = Phaser.Math.FloatBetween(0.8, 1.3); // BIGGER!
// Shadow
graphics.fillStyle(0x000000, 0.15);
graphics.fillEllipse(x, y + 22, 22 * growthScale, 7);
// Trunk
graphics.fillStyle(0x8B4513);
graphics.fillRect(x - 4 * growthScale, y + 2, 8 * growthScale, 20 * growthScale);
// Green crown
graphics.fillStyle(0x3CB371); // Medium sea green
graphics.fillCircle(x, y - 8 * growthScale, 16 * growthScale);
// Apples (red circles)
graphics.fillStyle(0xFF0000);
const applePositions = [
{ x: -8, y: -12 }, { x: 5, y: -10 }, { x: -3, y: -5 },
{ x: 8, y: -8 }, { x: 0, y: -15 }
];
applePositions.forEach(pos => {
graphics.fillCircle(
x + pos.x * growthScale,
y + pos.y * growthScale,
3 * growthScale
);
});
return graphics;
}