Initial commit

This commit is contained in:
Allen
2026-04-30 02:14:25 +00:00
commit 7090e6f01d
243 changed files with 115122 additions and 0 deletions
@@ -0,0 +1,675 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Asset Comparison Demo - Same Game, Different Assets</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #0f0f1a;
color: #e2e8f0;
min-height: 100vh;
}
.header {
background: #1a1a2e;
padding: 1.5rem 2rem;
border-bottom: 1px solid #334155;
}
.header h1 {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.header p {
color: #94a3b8;
font-size: 0.9rem;
}
.container {
max-width: 1600px;
margin: 0 auto;
padding: 2rem;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
@media (max-width: 1200px) {
.grid { grid-template-columns: 1fr; }
}
.game-panel {
background: #1a1a2e;
border-radius: 0.75rem;
overflow: hidden;
border: 1px solid #334155;
}
.panel-header {
padding: 1rem 1.5rem;
border-bottom: 1px solid #334155;
display: flex;
justify-content: space-between;
align-items: center;
}
.panel-header h2 {
font-size: 1.125rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.badge {
padding: 0.25rem 0.75rem;
background: #6366f1;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
}
.badge.space { background: #3b82f6; }
.badge.nature { background: #22c55e; }
.badge.spooky { background: #8b5cf6; }
.badge.retro { background: #f59e0b; }
.game-frame {
position: relative;
width: 100%;
aspect-ratio: 16/10;
background: #0f0f1a;
}
.game-frame canvas {
width: 100%;
height: 100%;
}
.panel-footer {
padding: 1rem 1.5rem;
border-top: 1px solid #334155;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.tag {
padding: 0.25rem 0.5rem;
background: #252542;
border-radius: 0.25rem;
font-size: 0.75rem;
color: #94a3b8;
}
.controls {
margin-top: 2rem;
padding: 1.5rem;
background: #1a1a2e;
border-radius: 0.75rem;
border: 1px solid #334155;
}
.controls h3 {
margin-bottom: 1rem;
}
.control-row {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.btn {
padding: 0.625rem 1.25rem;
background: #6366f1;
color: white;
border: none;
border-radius: 0.5rem;
cursor: pointer;
font-size: 0.875rem;
transition: background 0.2s;
}
.btn:hover {
background: #818cf8;
}
.btn.secondary {
background: #334155;
}
.btn.secondary:hover {
background: #475569;
}
.code-section {
margin-top: 2rem;
}
.code-section h3 {
margin-bottom: 1rem;
}
.code-tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
.code-tab {
padding: 0.5rem 1rem;
background: #252542;
border: none;
color: #94a3b8;
border-radius: 0.375rem;
cursor: pointer;
}
.code-tab.active {
background: #6366f1;
color: white;
}
.code-block {
background: #0f0f1a;
border: 1px solid #334155;
border-radius: 0.5rem;
padding: 1rem;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 0.75rem;
overflow-x: auto;
white-space: pre;
max-height: 300px;
overflow-y: auto;
}
.instructions {
margin-top: 2rem;
padding: 1.5rem;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
border-radius: 0.75rem;
}
.instructions h3 {
margin-bottom: 0.5rem;
}
.instructions p {
opacity: 0.9;
font-size: 0.9rem;
line-height: 1.6;
}
</style>
</head>
<body>
<header class="header">
<h1>Asset Comparison Demo</h1>
<p>The same dodge-the-obstacles game rendered with 4 different asset combinations</p>
</header>
<div class="container">
<div class="grid">
<!-- Space Theme -->
<div class="game-panel">
<div class="panel-header">
<h2><span>Space Dodger</span></h2>
<span class="badge space">Space Theme</span>
</div>
<div class="game-frame">
<canvas id="game1" width="640" height="400"></canvas>
</div>
<div class="panel-footer">
<span class="tag">space/nebula</span>
<span class="tag">Epic music</span>
<span class="tag">spaceship-cockpit</span>
<span class="tag">HEAD_ONLY</span>
</div>
</div>
<!-- Nature Theme -->
<div class="game-panel">
<div class="panel-header">
<h2><span>Forest Runner</span></h2>
<span class="badge nature">Nature Theme</span>
</div>
<div class="game-frame">
<canvas id="game2" width="640" height="400"></canvas>
</div>
<div class="panel-footer">
<span class="tag">nature/forest</span>
<span class="tag">Playful music</span>
<span class="tag">platformer-standard</span>
<span class="tag">FULL_BODY</span>
</div>
</div>
<!-- Spooky Theme -->
<div class="game-panel">
<div class="panel-header">
<h2><span>Haunted Escape</span></h2>
<span class="badge spooky">Spooky Theme</span>
</div>
<div class="game-frame">
<canvas id="game3" width="640" height="400"></canvas>
</div>
<div class="panel-footer">
<span class="tag">spooky/hauntedHouse</span>
<span class="tag">Mysterious music</span>
<span class="tag">adventure-hero</span>
<span class="tag">FULL_BODY</span>
</div>
</div>
<!-- Retro Theme -->
<div class="game-panel">
<div class="panel-header">
<h2><span>Pixel Dash</span></h2>
<span class="badge retro">Retro Theme</span>
</div>
<div class="game-frame">
<canvas id="game4" width="640" height="400"></canvas>
</div>
<div class="panel-footer">
<span class="tag">retro/synthwave</span>
<span class="tag">Quirky music</span>
<span class="tag">runner</span>
<span class="tag">FULL_BODY</span>
</div>
</div>
</div>
<div class="controls">
<h3>Global Controls</h3>
<div class="control-row">
<button class="btn" onclick="startAll()">Start All Games</button>
<button class="btn secondary" onclick="pauseAll()">Pause All</button>
<button class="btn secondary" onclick="resetAll()">Reset All</button>
</div>
</div>
<div class="instructions">
<h3>How to Play</h3>
<p>
Use <strong>Arrow Keys</strong> or <strong>WASD</strong> to move. Avoid the obstacles!
Each panel shows the same game logic with different visual assets.
Notice how the background, music mood, avatar context, and rendering mode
completely change the feel of the game while the core mechanics remain identical.
</p>
</div>
<div class="code-section">
<h3>Configuration Code</h3>
<div class="code-tabs">
<button class="code-tab active" onclick="showCode('space')">Space</button>
<button class="code-tab" onclick="showCode('nature')">Nature</button>
<button class="code-tab" onclick="showCode('spooky')">Spooky</button>
<button class="code-tab" onclick="showCode('retro')">Retro</button>
</div>
<div class="code-block" id="codeDisplay">const GAME_CONFIG = {
title: "Space Dodger",
assets: {
music: { mood: "Epic", energy: 8, enabled: true },
background: { theme: "space", variant: "nebula", animated: true },
avatarContext: {
mode: "HEAD_ONLY",
context: "spaceship-cockpit",
showAccessories: true
}
}
};</div>
</div>
</div>
<!-- Asset Libraries -->
<script src="/assets/backgrounds/backgroundEngine.js"></script>
<script src="/assets/backgrounds/effects.js"></script>
<script src="/assets/backgrounds/themes/space.js"></script>
<script src="/assets/backgrounds/themes/nature.js"></script>
<script src="/assets/backgrounds/themes/spooky.js"></script>
<script src="/assets/backgrounds/themes/retro.js"></script>
<script src="/assets/backgrounds/backgroundCatalog.js"></script>
<script src="/assets/avatar/avatarData.js"></script>
<script src="/assets/avatar/accessories.js"></script>
<script src="/assets/avatar/animations.js"></script>
<script src="/assets/avatar/avatarRenderer.js"></script>
<script src="/assets/avatar/accessoryRenderer.js"></script>
<script src="/assets/avatar/avatarSystem.js"></script>
<script src="/assets/avatar/contexts/vehicles.js"></script>
<script src="/assets/avatar/contexts/pure.js"></script>
<script src="/assets/avatar/contextRenderer.js"></script>
<script>
// ═══════════════════════════════════════════════════════════════════════
// COMPARISON DEMO - Same game, 4 different asset sets
// ═══════════════════════════════════════════════════════════════════════
const CONFIGS = {
space: {
title: "Space Dodger",
assets: {
music: { mood: "Epic", energy: 8 },
background: { theme: "space", variant: "nebula" },
avatar: { mode: "HEAD_ONLY", context: "spaceship-cockpit" }
},
colors: {
player: '#3b82f6',
obstacle: '#ef4444',
coin: '#fbbf24'
}
},
nature: {
title: "Forest Runner",
assets: {
music: { mood: "Playful", energy: 6 },
background: { theme: "nature", variant: "forest" },
avatar: { mode: "FULL_BODY", context: "platformer-standard" }
},
colors: {
player: '#22c55e',
obstacle: '#a16207',
coin: '#fbbf24'
}
},
spooky: {
title: "Haunted Escape",
assets: {
music: { mood: "Mysterious", energy: 5 },
background: { theme: "spooky", variant: "hauntedHouse" },
avatar: { mode: "FULL_BODY", context: "adventure-hero" }
},
colors: {
player: '#a855f7',
obstacle: '#7c3aed',
coin: '#c084fc'
}
},
retro: {
title: "Pixel Dash",
assets: {
music: { mood: "Quirky", energy: 7 },
background: { theme: "retro", variant: "synthwave" },
avatar: { mode: "FULL_BODY", context: "runner" }
},
colors: {
player: '#f59e0b',
obstacle: '#ec4899',
coin: '#06b6d4'
}
}
};
const playerAvatar = {
faceShape: 'round',
skinTone: 'medium',
hairStyle: 'short',
hairColor: '#3d2314',
eyeShape: 'round',
eyeColor: '#4a7c59'
};
// Game instances
const games = {};
const keys = {};
// Input handling
document.addEventListener('keydown', e => { keys[e.code] = true; });
document.addEventListener('keyup', e => { keys[e.code] = false; });
// ═══════════════════════════════════════════════════════════════════════
// GAME CLASS
// ═══════════════════════════════════════════════════════════════════════
class DodgeGame {
constructor(canvasId, config) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.config = config;
this.reset();
this.running = false;
this.lastTime = 0;
}
reset() {
this.playerX = this.canvas.width / 2;
this.playerY = this.canvas.height - 80;
this.score = 0;
this.obstacles = [];
this.coins = [];
this.animTime = 0;
this.spawnTimer = 0;
this.coinTimer = 0;
}
start() {
this.running = true;
this.lastTime = performance.now();
this.loop();
}
pause() {
this.running = false;
}
loop() {
if (!this.running) return;
const now = performance.now();
const dt = Math.min((now - this.lastTime) / 1000, 0.05);
this.lastTime = now;
this.update(dt);
this.render();
requestAnimationFrame(() => this.loop());
}
update(dt) {
const speed = 200;
// Player movement
if (keys['ArrowLeft'] || keys['KeyA']) {
this.playerX = Math.max(30, this.playerX - speed * dt);
}
if (keys['ArrowRight'] || keys['KeyD']) {
this.playerX = Math.min(this.canvas.width - 30, this.playerX + speed * dt);
}
// Spawn obstacles
this.spawnTimer += dt;
if (this.spawnTimer > 1.5) {
this.spawnTimer = 0;
this.obstacles.push({
x: 50 + Math.random() * (this.canvas.width - 100),
y: -30,
size: 20 + Math.random() * 20
});
}
// Spawn coins
this.coinTimer += dt;
if (this.coinTimer > 2) {
this.coinTimer = 0;
this.coins.push({
x: 50 + Math.random() * (this.canvas.width - 100),
y: -20
});
}
// Update obstacles
this.obstacles = this.obstacles.filter(o => {
o.y += 150 * dt;
// Collision
const dx = this.playerX - o.x;
const dy = this.playerY - o.y;
if (Math.sqrt(dx*dx + dy*dy) < o.size + 25) {
this.reset();
return false;
}
return o.y < this.canvas.height + 50;
});
// Update coins
this.coins = this.coins.filter(c => {
c.y += 120 * dt;
// Collection
const dx = this.playerX - c.x;
const dy = this.playerY - c.y;
if (Math.sqrt(dx*dx + dy*dy) < 35) {
this.score += 10;
return false;
}
return c.y < this.canvas.height + 30;
});
this.score += Math.floor(dt * 5);
this.animTime += dt * 1000;
}
render() {
const { ctx, canvas, config } = this;
// Background
if (window.BackgroundEngine) {
window.BackgroundEngine.render(
ctx,
config.assets.background.theme,
config.assets.background.variant,
canvas.width,
canvas.height,
this.animTime
);
} else {
ctx.fillStyle = '#1a1a2e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Obstacles
ctx.fillStyle = config.colors.obstacle;
this.obstacles.forEach(o => {
ctx.beginPath();
ctx.moveTo(o.x, o.y - o.size);
ctx.lineTo(o.x + o.size, o.y + o.size);
ctx.lineTo(o.x - o.size, o.y + o.size);
ctx.closePath();
ctx.fill();
});
// Coins
ctx.fillStyle = config.colors.coin;
this.coins.forEach(c => {
ctx.beginPath();
ctx.arc(c.x, c.y, 12, 0, Math.PI * 2);
ctx.fill();
});
// Player
ctx.save();
ctx.translate(this.playerX, this.playerY);
const mode = config.assets.avatar.mode;
const dims = {
'HEAD_ONLY': { w: 64, h: 64, offsetY: 32 },
'HEAD_AND_SHOULDERS': { w: 64, h: 96, offsetY: 48 },
'UPPER_BODY': { w: 64, h: 128, offsetY: 64 },
'FULL_BODY': { w: 64, h: 160, offsetY: 80 }
};
const d = dims[mode];
if (window.AvatarSystem) {
ctx.translate(-32, -d.offsetY);
window.AvatarSystem.render(
ctx,
playerAvatar,
mode,
'idle',
this.animTime
);
} else {
// Fallback
ctx.fillStyle = config.colors.player;
ctx.beginPath();
ctx.arc(0, 0, 25, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
// Score
ctx.fillStyle = 'white';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'left';
ctx.fillText(`Score: ${this.score}`, 10, 25);
}
}
// ═══════════════════════════════════════════════════════════════════════
// CONTROLS
// ═══════════════════════════════════════════════════════════════════════
function startAll() {
Object.values(games).forEach(g => g.start());
}
function pauseAll() {
Object.values(games).forEach(g => g.pause());
}
function resetAll() {
Object.values(games).forEach(g => {
g.reset();
g.render();
});
}
function showCode(theme) {
document.querySelectorAll('.code-tab').forEach(t => t.classList.remove('active'));
event.target.classList.add('active');
const config = CONFIGS[theme];
const code = `const GAME_CONFIG = {
title: "${config.title}",
assets: {
music: { mood: "${config.assets.music.mood}", energy: ${config.assets.music.energy}, enabled: true },
background: { theme: "${config.assets.background.theme}", variant: "${config.assets.background.variant}", animated: true },
avatarContext: {
mode: "${config.assets.avatar.mode}",
context: "${config.assets.avatar.context}",
showAccessories: true
}
}
};`;
document.getElementById('codeDisplay').textContent = code;
}
// ═══════════════════════════════════════════════════════════════════════
// INITIALIZE
// ═══════════════════════════════════════════════════════════════════════
window.addEventListener('load', () => {
// Create game instances
games.space = new DodgeGame('game1', CONFIGS.space);
games.nature = new DodgeGame('game2', CONFIGS.nature);
games.spooky = new DodgeGame('game3', CONFIGS.spooky);
games.retro = new DodgeGame('game4', CONFIGS.retro);
// Initial render
Object.values(games).forEach(g => g.render());
// Auto-start after a moment
setTimeout(startAll, 500);
});
</script>
</body>
</html>