🎮 Dark-Chibi Noir Demo Scene v1.0 - Kaijev Svet
✨ Glavne Features: - Main Demo Scene (main_demo_scene.html) s Kaijem, campfire, trees, tent - Kai Camp Showcase (kai_camp_showcase.html) - isoliran showcase - Asset cleanup & transparency fix za vse PNG assete - Nova struktura: 'assets/slike/nova mapa faza 0-1/' - Glavne reference v 'assets/slike/glavna_referenca/' 🔧 Technical: - Python scripts: complete_visual_cleanup.py, remove_green_bg.py - Transparency fix za vse karakterje, trees, props (brez zelenih ozadij) - Dokumentacija: TENT_FOUND_REPORT, TRANSPARENCY_FIX_REPORT, VISUAL_CLEANUP_REPORT 🎨 Assets: - Kai z dreadi (standalone sprite) - Animated campfire (frame1 + frame2) - Dead trees (10 variants) - Tent, grass texture, wood logs - Full Dark-Chibi Noir aesthetic 🚀 Demo Ready: HTML5 Canvas 2D Scene with WASD movement controls
This commit is contained in:
529
main_demo_scene.html
Normal file
529
main_demo_scene.html
Normal file
@@ -0,0 +1,529 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="sl">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Kaijev Svet - Dark-Chibi Noir Demo</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(180deg, #0a0a0a 0%, #1a1a1a 50%, #2a2a2a 100%);
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #e0e0e0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-bottom: 3px solid #ff6b6b;
|
||||
width: 100%;
|
||||
box-shadow: 0 4px 20px rgba(255, 107, 107, 0.4);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
color: #ff6b6b;
|
||||
text-shadow: 0 0 15px rgba(255, 107, 107, 0.8),
|
||||
0 0 30px rgba(255, 107, 107, 0.5);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1em;
|
||||
color: #a0a0a0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#gameCanvas {
|
||||
border: 4px solid #ff6b6b;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 50px rgba(255, 107, 107, 0.5),
|
||||
inset 0 0 30px rgba(0, 0, 0, 0.5);
|
||||
margin-top: 120px;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: crisp-edges;
|
||||
background: #2a3a2a;
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
border: 2px solid #ff6b6b;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 0 30px rgba(255, 107, 107, 0.4);
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.controls button {
|
||||
background: #ff6b6b;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.95em;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.controls button:hover {
|
||||
background: #ff8787;
|
||||
box-shadow: 0 0 20px rgba(255, 107, 107, 0.7);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.controls button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.info {
|
||||
background: rgba(255, 107, 107, 0.1);
|
||||
padding: 8px 15px;
|
||||
border-radius: 5px;
|
||||
border-left: 4px solid #ff6b6b;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.loading {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(0, 0, 0, 0.95);
|
||||
border: 3px solid #ff6b6b;
|
||||
border-radius: 10px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
z-index: 2000;
|
||||
box-shadow: 0 0 50px rgba(255, 107, 107, 0.6);
|
||||
}
|
||||
|
||||
.loading h2 {
|
||||
color: #ff6b6b;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.loading-bar {
|
||||
width: 300px;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
border: 2px solid #ff6b6b;
|
||||
}
|
||||
|
||||
.loading-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #ff6b6b, #ff8787);
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
box-shadow: 0 0 10px rgba(255, 107, 107, 0.8);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>🏕️ KAIJEV SVET</h1>
|
||||
<p class="subtitle">Dark-Chibi Noir | Postapokaliptična Farma v Mrtvi Dolini</p>
|
||||
</div>
|
||||
|
||||
<div class="loading" id="loadingScreen">
|
||||
<h2>⚙️ Nalagam Demo Sceno...</h2>
|
||||
<div class="loading-bar">
|
||||
<div class="loading-fill" id="loadingFill"></div>
|
||||
</div>
|
||||
<p style="margin-top: 15px; color: #a0a0a0;" id="loadingText">Pripravljam assete...</p>
|
||||
</div>
|
||||
|
||||
<canvas id="gameCanvas" width="1280" height="720"></canvas>
|
||||
|
||||
<div class="controls">
|
||||
<div class="info">🎮 WASD = Gibanje</div>
|
||||
<button onclick="toggleFire()">🔥 Toggle Fire</button>
|
||||
<button onclick="resetScene()">🔄 Reset</button>
|
||||
<div class="info" id="fpsCounter">FPS: 60</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Canvas setup
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Game state
|
||||
const game = {
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
loaded: false,
|
||||
assetsToLoad: 0,
|
||||
assetsLoaded: 0,
|
||||
fireActive: true,
|
||||
campfireFrame: 0,
|
||||
campfireTimer: 0,
|
||||
keys: {},
|
||||
time: 0
|
||||
};
|
||||
|
||||
// Player (Kai)
|
||||
const player = {
|
||||
x: canvas.width / 2 - 50,
|
||||
y: canvas.height / 2 - 50,
|
||||
width: 120,
|
||||
height: 120,
|
||||
speed: 3,
|
||||
sprite: null
|
||||
};
|
||||
|
||||
// Assets
|
||||
const assets = {
|
||||
grass: null,
|
||||
kai: null,
|
||||
campfire: [],
|
||||
trees: [],
|
||||
tent: null,
|
||||
woodLog: null
|
||||
};
|
||||
|
||||
// Scene objects
|
||||
const sceneObjects = [];
|
||||
|
||||
// Load image helper
|
||||
function loadImage(src) {
|
||||
game.assetsToLoad++;
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
game.assetsLoaded++;
|
||||
updateLoadingBar();
|
||||
};
|
||||
img.onerror = () => {
|
||||
console.error(`Failed to load: ${src}`);
|
||||
game.assetsLoaded++;
|
||||
updateLoadingBar();
|
||||
};
|
||||
img.src = src;
|
||||
return img;
|
||||
}
|
||||
|
||||
// Update loading bar
|
||||
function updateLoadingBar() {
|
||||
const progress = (game.assetsLoaded / game.assetsToLoad) * 100;
|
||||
document.getElementById('loadingFill').style.width = progress + '%';
|
||||
document.getElementById('loadingText').textContent =
|
||||
`Nalagam... ${game.assetsLoaded}/${game.assetsToLoad} assetov`;
|
||||
|
||||
if (game.assetsLoaded >= game.assetsToLoad) {
|
||||
setTimeout(() => {
|
||||
document.getElementById('loadingScreen').classList.add('hidden');
|
||||
game.loaded = true;
|
||||
gameLoop();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Load all assets
|
||||
function loadAssets() {
|
||||
const basePath = 'assets/slike/nova mapa faza 0-1/';
|
||||
|
||||
// Grass texture
|
||||
assets.grass = loadImage(basePath + 'Environment/podlaga/MOJE_SLIKE_KONCNA_teren_grass.png');
|
||||
|
||||
// Kai character
|
||||
assets.kai = loadImage(basePath + 'Characters/kaj/Izdelek brez naslova (2).png');
|
||||
player.sprite = assets.kai;
|
||||
|
||||
// Campfire animation
|
||||
assets.campfire[0] = loadImage(basePath + 'Environment/props/MOJE_SLIKE_KONCNA_ostalo_campfire_frame1.png');
|
||||
assets.campfire[1] = loadImage(basePath + 'Environment/props/MOJE_SLIKE_KONCNA_ostalo_campfire_frame2.png');
|
||||
|
||||
// Dead trees (load 10 variants)
|
||||
for (let i = 0; i < 8; i++) {
|
||||
assets.trees.push(loadImage(basePath + `Environment/narava/unnamed-${i + 1}.png`));
|
||||
}
|
||||
assets.trees.push(loadImage(basePath + 'Environment/narava/unnamed.png'));
|
||||
assets.trees.push(loadImage(basePath + 'Environment/narava/unnamed Kopie.png'));
|
||||
|
||||
// Tent
|
||||
assets.tent = loadImage(basePath + 'Environment/stavbe/MOJE_SLIKE_KONCNA_ostalo_tent_basic_style32.png');
|
||||
|
||||
// Wood log
|
||||
assets.woodLog = loadImage(basePath + 'Environment/props/wood_log.png');
|
||||
}
|
||||
|
||||
// Initialize scene
|
||||
function initScene() {
|
||||
// Background trees (far layer)
|
||||
sceneObjects.push({
|
||||
type: 'tree',
|
||||
sprite: assets.trees[0],
|
||||
x: 100,
|
||||
y: 80,
|
||||
width: 200,
|
||||
height: 200,
|
||||
layer: 1
|
||||
});
|
||||
|
||||
sceneObjects.push({
|
||||
type: 'tree',
|
||||
sprite: assets.trees[1],
|
||||
x: 900,
|
||||
y: 100,
|
||||
width: 220,
|
||||
height: 220,
|
||||
layer: 1
|
||||
});
|
||||
|
||||
sceneObjects.push({
|
||||
type: 'tree',
|
||||
sprite: assets.trees[2],
|
||||
x: 1050,
|
||||
y: 150,
|
||||
width: 180,
|
||||
height: 180,
|
||||
layer: 1
|
||||
});
|
||||
|
||||
// Tent (mid layer)
|
||||
sceneObjects.push({
|
||||
type: 'tent',
|
||||
sprite: assets.tent,
|
||||
x: 200,
|
||||
y: 320,
|
||||
width: 180,
|
||||
height: 180,
|
||||
layer: 2
|
||||
});
|
||||
|
||||
// Campfire (center, mid-to-front layer)
|
||||
sceneObjects.push({
|
||||
type: 'campfire',
|
||||
sprite: assets.campfire[0],
|
||||
x: canvas.width / 2 + 80,
|
||||
y: canvas.height / 2 - 20,
|
||||
width: 120,
|
||||
height: 120,
|
||||
layer: 3
|
||||
});
|
||||
|
||||
// Wood log (foreground)
|
||||
sceneObjects.push({
|
||||
type: 'log',
|
||||
sprite: assets.woodLog,
|
||||
x: canvas.width / 2 - 100,
|
||||
y: canvas.height / 2 + 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
layer: 4
|
||||
});
|
||||
|
||||
// Foreground trees (close layer)
|
||||
sceneObjects.push({
|
||||
type: 'tree',
|
||||
sprite: assets.trees[3],
|
||||
x: 50,
|
||||
y: 450,
|
||||
width: 240,
|
||||
height: 240,
|
||||
layer: 5
|
||||
});
|
||||
|
||||
sceneObjects.push({
|
||||
type: 'tree',
|
||||
sprite: assets.trees[4],
|
||||
x: 950,
|
||||
y: 400,
|
||||
width: 260,
|
||||
height: 260,
|
||||
layer: 5
|
||||
});
|
||||
|
||||
// Sort objects by layer for proper rendering
|
||||
sceneObjects.sort((a, b) => a.layer - b.layer);
|
||||
}
|
||||
|
||||
// Draw grass background (tiled)
|
||||
function drawGrass() {
|
||||
if (!assets.grass || !assets.grass.complete) return;
|
||||
|
||||
const tileSize = 64; // Grass tile size
|
||||
for (let x = 0; x < canvas.width; x += tileSize) {
|
||||
for (let y = 0; y < canvas.height; y += tileSize) {
|
||||
ctx.drawImage(assets.grass, x, y, tileSize, tileSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw scene objects
|
||||
function drawSceneObjects() {
|
||||
sceneObjects.forEach(obj => {
|
||||
if (obj.type === 'campfire' && game.fireActive) {
|
||||
// Draw animated campfire
|
||||
const frameIndex = Math.floor(game.campfireFrame);
|
||||
const sprite = assets.campfire[frameIndex % 2];
|
||||
if (sprite && sprite.complete) {
|
||||
ctx.drawImage(sprite, obj.x, obj.y, obj.width, obj.height);
|
||||
}
|
||||
} else if (obj.sprite && obj.sprite.complete) {
|
||||
ctx.drawImage(obj.sprite, obj.x, obj.y, obj.width, obj.height);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Draw player (Kai)
|
||||
function drawPlayer() {
|
||||
if (player.sprite && player.sprite.complete) {
|
||||
ctx.drawImage(player.sprite, player.x, player.y, player.width, player.height);
|
||||
}
|
||||
}
|
||||
|
||||
// Update player movement
|
||||
function updatePlayer() {
|
||||
if (game.keys['w'] || game.keys['W']) player.y -= player.speed;
|
||||
if (game.keys['s'] || game.keys['S']) player.y += player.speed;
|
||||
if (game.keys['a'] || game.keys['A']) player.x -= player.speed;
|
||||
if (game.keys['d'] || game.keys['D']) player.x += player.speed;
|
||||
|
||||
// Keep player in bounds
|
||||
player.x = Math.max(0, Math.min(canvas.width - player.width, player.x));
|
||||
player.y = Math.max(0, Math.min(canvas.height - player.height, player.y));
|
||||
}
|
||||
|
||||
// Update game state
|
||||
function update(deltaTime) {
|
||||
game.time += deltaTime;
|
||||
|
||||
// Update player
|
||||
updatePlayer();
|
||||
|
||||
// Update campfire animation
|
||||
if (game.fireActive) {
|
||||
game.campfireTimer += deltaTime;
|
||||
if (game.campfireTimer > 0.5) {
|
||||
game.campfireFrame = (game.campfireFrame + 1) % 2;
|
||||
game.campfireTimer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render everything
|
||||
function render() {
|
||||
// Clear canvas
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Draw grass background
|
||||
drawGrass();
|
||||
|
||||
// Draw scene objects (background to mid-ground)
|
||||
sceneObjects.forEach(obj => {
|
||||
if (obj.layer < 4 || obj.y < player.y) {
|
||||
if (obj.type === 'campfire' && game.fireActive) {
|
||||
const frameIndex = Math.floor(game.campfireFrame);
|
||||
const sprite = assets.campfire[frameIndex % 2];
|
||||
if (sprite && sprite.complete) {
|
||||
ctx.drawImage(sprite, obj.x, obj.y, obj.width, obj.height);
|
||||
}
|
||||
} else if (obj.sprite && obj.sprite.complete) {
|
||||
ctx.drawImage(obj.sprite, obj.x, obj.y, obj.width, obj.height);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Draw player
|
||||
drawPlayer();
|
||||
|
||||
// Draw remaining scene objects (foreground, in front of player)
|
||||
sceneObjects.forEach(obj => {
|
||||
if (obj.layer >= 4 && obj.y >= player.y) {
|
||||
if (obj.sprite && obj.sprite.complete) {
|
||||
ctx.drawImage(obj.sprite, obj.x, obj.y, obj.width, obj.height);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Game loop
|
||||
let lastTime = 0;
|
||||
let fpsTime = 0;
|
||||
let frameCount = 0;
|
||||
|
||||
function gameLoop(timestamp = 0) {
|
||||
if (!game.loaded) return;
|
||||
|
||||
const deltaTime = (timestamp - lastTime) / 1000;
|
||||
lastTime = timestamp;
|
||||
|
||||
// Update
|
||||
update(deltaTime);
|
||||
|
||||
// Render
|
||||
render();
|
||||
|
||||
// FPS counter
|
||||
fpsTime += deltaTime;
|
||||
frameCount++;
|
||||
if (fpsTime >= 1) {
|
||||
document.getElementById('fpsCounter').textContent = `FPS: ${frameCount}`;
|
||||
frameCount = 0;
|
||||
fpsTime = 0;
|
||||
}
|
||||
|
||||
requestAnimationFrame(gameLoop);
|
||||
}
|
||||
|
||||
// Controls
|
||||
function toggleFire() {
|
||||
game.fireActive = !game.fireActive;
|
||||
}
|
||||
|
||||
function resetScene() {
|
||||
player.x = canvas.width / 2 - 50;
|
||||
player.y = canvas.height / 2 - 50;
|
||||
game.campfireFrame = 0;
|
||||
game.fireActive = true;
|
||||
}
|
||||
|
||||
// Keyboard input
|
||||
window.addEventListener('keydown', (e) => {
|
||||
game.keys[e.key] = true;
|
||||
});
|
||||
|
||||
window.addEventListener('keyup', (e) => {
|
||||
game.keys[e.key] = false;
|
||||
});
|
||||
|
||||
// Initialize
|
||||
loadAssets();
|
||||
initScene();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user