Initial commit
This commit is contained in:
@@ -0,0 +1,392 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title><!-- GAME_TITLE --></title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #1a1a2e;
|
||||
touch-action: none;
|
||||
}
|
||||
canvas { display: block; }
|
||||
#ui {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
color: white;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
text-shadow: 2px 2px 4px #000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="gameCanvas"></canvas>
|
||||
<div id="ui">Score: <span id="score">0</span></div>
|
||||
|
||||
<script>
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// SIMPLE GAME TEMPLATE
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// This is a minimal template without the asset library.
|
||||
// Use for simple games that don't need avatars, procedural music, etc.
|
||||
//
|
||||
// HAIKU: Fill in GAME_CONFIG and implement the game functions below.
|
||||
|
||||
const GAME_CONFIG = {
|
||||
title: "<!-- GAME_TITLE -->",
|
||||
|
||||
// Background color or gradient
|
||||
background: {
|
||||
type: "gradient", // "solid", "gradient", or "pattern"
|
||||
colors: ["#1a1a2e", "#16213e", "#0f3460"], // gradient colors
|
||||
// color: "#1a1a2e" // for solid
|
||||
},
|
||||
|
||||
// Player settings
|
||||
player: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: "#4CAF50",
|
||||
speed: 300 // pixels per second
|
||||
},
|
||||
|
||||
// Game settings
|
||||
// HAIKU: Add game-specific config here
|
||||
gameplay: {
|
||||
// <!-- GAMEPLAY_CONFIG -->
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// SETUP
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Resize to fill screen
|
||||
function resize() {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
}
|
||||
resize();
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
// Game state
|
||||
let state = {
|
||||
// Player position
|
||||
playerX: 0,
|
||||
playerY: 0,
|
||||
playerVelX: 0,
|
||||
playerVelY: 0,
|
||||
|
||||
// Score and status
|
||||
score: 0,
|
||||
gameOver: false,
|
||||
paused: false,
|
||||
|
||||
// Timing
|
||||
lastTime: 0,
|
||||
|
||||
// HAIKU: Add game-specific state
|
||||
// <!-- GAME_STATE -->
|
||||
};
|
||||
|
||||
// Input tracking
|
||||
const keys = {};
|
||||
let mouseX = 0, mouseY = 0;
|
||||
let mouseDown = false;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// UTILITIES
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
function updateScore(points) {
|
||||
state.score += points;
|
||||
document.getElementById('score').textContent = state.score;
|
||||
}
|
||||
|
||||
function endGame() {
|
||||
state.gameOver = true;
|
||||
if (window.parent !== window) {
|
||||
window.parent.postMessage({ type: 'gameOver', score: state.score }, '*');
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (confirm(`Game Over! Score: ${state.score}\n\nPlay again?`)) {
|
||||
restart();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function restart() {
|
||||
state.score = 0;
|
||||
state.gameOver = false;
|
||||
state.playerX = canvas.width / 2;
|
||||
state.playerY = canvas.height / 2;
|
||||
document.getElementById('score').textContent = '0';
|
||||
initGame();
|
||||
}
|
||||
|
||||
function randomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function randomFloat(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function clamp(val, min, max) {
|
||||
return Math.max(min, Math.min(max, val));
|
||||
}
|
||||
|
||||
function distance(x1, y1, x2, y2) {
|
||||
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
||||
}
|
||||
|
||||
function rectCollide(r1, r2) {
|
||||
return r1.x < r2.x + r2.w && r1.x + r1.w > r2.x &&
|
||||
r1.y < r2.y + r2.h && r1.y + r1.h > r2.y;
|
||||
}
|
||||
|
||||
function circleCollide(c1, c2) {
|
||||
return distance(c1.x, c1.y, c2.x, c2.y) < c1.r + c2.r;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// RENDERING HELPERS
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
function drawBackground() {
|
||||
const bg = GAME_CONFIG.background;
|
||||
|
||||
if (bg.type === 'gradient' && bg.colors) {
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
||||
bg.colors.forEach((color, i) => {
|
||||
gradient.addColorStop(i / (bg.colors.length - 1), color);
|
||||
});
|
||||
ctx.fillStyle = gradient;
|
||||
} else {
|
||||
ctx.fillStyle = bg.color || '#1a1a2e';
|
||||
}
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
function drawPlayer() {
|
||||
const p = GAME_CONFIG.player;
|
||||
ctx.fillStyle = p.color;
|
||||
ctx.fillRect(
|
||||
state.playerX - p.width / 2,
|
||||
state.playerY - p.height / 2,
|
||||
p.width,
|
||||
p.height
|
||||
);
|
||||
}
|
||||
|
||||
function drawRect(x, y, w, h, color) {
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect(x, y, w, h);
|
||||
}
|
||||
|
||||
function drawCircle(x, y, r, color) {
|
||||
ctx.fillStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, r, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
function drawText(text, x, y, color, size, align) {
|
||||
ctx.fillStyle = color || 'white';
|
||||
ctx.font = `${size || 20}px Arial`;
|
||||
ctx.textAlign = align || 'left';
|
||||
ctx.fillText(text, x, y);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// INPUT HANDLING
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
keys[e.code] = true;
|
||||
if (e.code === 'Escape') {
|
||||
state.paused = !state.paused;
|
||||
if (!state.paused) requestAnimationFrame(gameLoop);
|
||||
}
|
||||
onKeyDown(e.code);
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', e => {
|
||||
keys[e.code] = false;
|
||||
onKeyUp(e.code);
|
||||
});
|
||||
|
||||
canvas.addEventListener('mousedown', e => {
|
||||
mouseDown = true;
|
||||
mouseX = e.clientX;
|
||||
mouseY = e.clientY;
|
||||
onMouseDown(mouseX, mouseY);
|
||||
});
|
||||
|
||||
canvas.addEventListener('mousemove', e => {
|
||||
mouseX = e.clientX;
|
||||
mouseY = e.clientY;
|
||||
onMouseMove(mouseX, mouseY);
|
||||
});
|
||||
|
||||
canvas.addEventListener('mouseup', e => {
|
||||
mouseDown = false;
|
||||
onMouseUp(e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
canvas.addEventListener('touchstart', e => {
|
||||
e.preventDefault();
|
||||
mouseDown = true;
|
||||
mouseX = e.touches[0].clientX;
|
||||
mouseY = e.touches[0].clientY;
|
||||
onMouseDown(mouseX, mouseY);
|
||||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener('touchmove', e => {
|
||||
e.preventDefault();
|
||||
mouseX = e.touches[0].clientX;
|
||||
mouseY = e.touches[0].clientY;
|
||||
onMouseMove(mouseX, mouseY);
|
||||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener('touchend', e => {
|
||||
mouseDown = false;
|
||||
onMouseUp(mouseX, mouseY);
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// GAME LOOP
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
function gameLoop(time) {
|
||||
if (state.gameOver || state.paused) return;
|
||||
|
||||
const dt = (time - state.lastTime) / 1000;
|
||||
state.lastTime = time;
|
||||
|
||||
// Update
|
||||
update(dt);
|
||||
|
||||
// Render
|
||||
drawBackground();
|
||||
render();
|
||||
drawPlayer();
|
||||
renderUI();
|
||||
|
||||
requestAnimationFrame(gameLoop);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// GAME LOGIC - HAIKU IMPLEMENTS THESE
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Initialize game state
|
||||
*/
|
||||
function initGame() {
|
||||
state.playerX = canvas.width / 2;
|
||||
state.playerY = canvas.height / 2;
|
||||
|
||||
// HAIKU: Initialize your game objects
|
||||
// Example:
|
||||
// state.enemies = [];
|
||||
// state.items = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update game logic each frame
|
||||
* @param {number} dt - Delta time in seconds
|
||||
*/
|
||||
function update(dt) {
|
||||
// HAIKU: Implement game update logic
|
||||
|
||||
// Example: Arrow key movement
|
||||
const speed = GAME_CONFIG.player.speed;
|
||||
if (keys['ArrowLeft'] || keys['KeyA']) state.playerX -= speed * dt;
|
||||
if (keys['ArrowRight'] || keys['KeyD']) state.playerX += speed * dt;
|
||||
if (keys['ArrowUp'] || keys['KeyW']) state.playerY -= speed * dt;
|
||||
if (keys['ArrowDown'] || keys['KeyS']) state.playerY += speed * dt;
|
||||
|
||||
// Keep in bounds
|
||||
const hw = GAME_CONFIG.player.width / 2;
|
||||
const hh = GAME_CONFIG.player.height / 2;
|
||||
state.playerX = clamp(state.playerX, hw, canvas.width - hw);
|
||||
state.playerY = clamp(state.playerY, hh, canvas.height - hh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render game objects (not player, not background)
|
||||
*/
|
||||
function render() {
|
||||
// HAIKU: Render your game objects
|
||||
// Example:
|
||||
// state.enemies.forEach(e => drawCircle(e.x, e.y, e.r, e.color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render UI elements on canvas
|
||||
*/
|
||||
function renderUI() {
|
||||
// HAIKU: Render any additional UI
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle key press
|
||||
*/
|
||||
function onKeyDown(code) {
|
||||
// HAIKU: Handle key press events
|
||||
// if (code === 'Space') jump();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle key release
|
||||
*/
|
||||
function onKeyUp(code) {
|
||||
// HAIKU: Handle key release events
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse/touch down
|
||||
*/
|
||||
function onMouseDown(x, y) {
|
||||
// HAIKU: Handle click/tap
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse/touch move
|
||||
*/
|
||||
function onMouseMove(x, y) {
|
||||
// HAIKU: Handle mouse/touch movement
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse/touch up
|
||||
*/
|
||||
function onMouseUp(x, y) {
|
||||
// HAIKU: Handle click/tap release
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// START
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Expose for parent window
|
||||
window.pauseGame = () => { state.paused = true; };
|
||||
window.resumeGame = () => { state.paused = false; state.lastTime = performance.now(); requestAnimationFrame(gameLoop); };
|
||||
window.gameScore = state.score;
|
||||
|
||||
// Initialize and start
|
||||
initGame();
|
||||
state.lastTime = performance.now();
|
||||
requestAnimationFrame(gameLoop);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user