diff options
-rw-r--r-- | html/voxels/index.html | 22 | ||||
-rw-r--r-- | html/voxels/js/game.js | 233 |
2 files changed, 255 insertions, 0 deletions
diff --git a/html/voxels/index.html b/html/voxels/index.html new file mode 100644 index 0000000..4f60ff2 --- /dev/null +++ b/html/voxels/index.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> + <title>Isometric Game</title> + <style> + body { + margin: 0; + overflow: hidden; + } + canvas { + display: block; + width: 100vw; + height: 100vh; + background: #f0f0f0; + } + </style> +</head> +<body> + <canvas id="gameCanvas"></canvas> + <script src="js/game.js"></script> +</body> +</html> \ No newline at end of file diff --git a/html/voxels/js/game.js b/html/voxels/js/game.js new file mode 100644 index 0000000..98d3476 --- /dev/null +++ b/html/voxels/js/game.js @@ -0,0 +1,233 @@ +class IsometricGame { + constructor() { + this.canvas = document.getElementById('gameCanvas'); + this.ctx = this.canvas.getContext('2d'); + + // Grid properties + this.gridSize = 10; + this.tileWidth = 50; + this.tileHeight = 25; + + // Player properties + this.player = { + x: 0, + y: 0, + targetX: 0, + targetY: 0, + size: 20, + path: [], // Array to store waypoints + currentWaypoint: null + }; + + // Handle window resize + this.resizeCanvas(); + window.addEventListener('resize', () => this.resizeCanvas()); + + this.setupEventListeners(); + this.gameLoop(); + } + + resizeCanvas() { + this.canvas.width = window.innerWidth; + this.canvas.height = window.innerHeight; + + // Recalculate grid offset to center it + this.offsetX = this.canvas.width / 2; + this.offsetY = this.canvas.height / 3; + + // Scale tile size based on screen size + const minDimension = Math.min(this.canvas.width, this.canvas.height); + const scaleFactor = minDimension / 800; // 800 is our reference size + this.tileWidth = 50 * scaleFactor; + this.tileHeight = 25 * scaleFactor; + this.player.size = 20 * scaleFactor; + } + + toIsometric(x, y) { + return { + x: (x - y) * this.tileWidth / 2, + y: (x + y) * this.tileHeight / 2 + }; + } + + fromIsometric(screenX, screenY) { + // Convert screen coordinates back to grid coordinates + screenX -= this.offsetX; + screenY -= this.offsetY; + + const x = (screenX / this.tileWidth + screenY / this.tileHeight) / 1; + const y = (screenY / this.tileHeight - screenX / this.tileWidth) / 1; + + return { x: Math.round(x), y: Math.round(y) }; + } + + drawGrid() { + for (let x = 0; x < this.gridSize; x++) { + for (let y = 0; y < this.gridSize; y++) { + const iso = this.toIsometric(x, y); + + // Draw tile + this.ctx.beginPath(); + this.ctx.moveTo(iso.x + this.offsetX, iso.y + this.offsetY - this.tileHeight/2); + this.ctx.lineTo(iso.x + this.offsetX + this.tileWidth/2, iso.y + this.offsetY); + this.ctx.lineTo(iso.x + this.offsetX, iso.y + this.offsetY + this.tileHeight/2); + this.ctx.lineTo(iso.x + this.offsetX - this.tileWidth/2, iso.y + this.offsetY); + this.ctx.closePath(); + + this.ctx.strokeStyle = '#666'; + this.ctx.stroke(); + this.ctx.fillStyle = '#fff'; + this.ctx.fill(); + } + } + } + + drawPlayer() { + // Convert player grid position to isometric coordinates + const iso = this.toIsometric(this.player.x, this.player.y); + + // Draw player shadow (slightly offset and darker) + this.ctx.beginPath(); + this.ctx.ellipse( + iso.x + this.offsetX, + iso.y + this.offsetY + 2, + this.player.size * 0.8, + this.player.size * 0.3, + 0, + 0, + Math.PI * 2 + ); + this.ctx.fillStyle = 'rgba(0,0,0,0.2)'; + this.ctx.fill(); + + // Draw player "body" (rectangle) + this.ctx.beginPath(); + this.ctx.fillStyle = '#ff4444'; + this.ctx.strokeStyle = '#aa0000'; + + // Draw a thin rectangle for the body + const bodyHeight = this.player.size * 2; + const bodyWidth = this.player.size * 0.8; + + this.ctx.save(); + this.ctx.translate(iso.x + this.offsetX, iso.y + this.offsetY); + this.ctx.scale(1, 0.5); // Apply isometric perspective + this.ctx.fillRect(-bodyWidth/2, -bodyHeight, bodyWidth, bodyHeight); + this.ctx.strokeRect(-bodyWidth/2, -bodyHeight, bodyWidth, bodyHeight); + this.ctx.restore(); + + // Draw player head (circle) + this.ctx.beginPath(); + this.ctx.ellipse( + iso.x + this.offsetX, + iso.y + this.offsetY - this.player.size, + this.player.size, + this.player.size * 0.5, + 0, + 0, + Math.PI * 2 + ); + this.ctx.fillStyle = '#ff4444'; + this.ctx.fill(); + this.ctx.strokeStyle = '#aa0000'; + this.ctx.stroke(); + } + + findPath(startX, startY, endX, endY) { + // Simple pathfinding that follows grid edges + const path = []; + + // First move along X axis + if (startX !== endX) { + const stepX = startX < endX ? 1 : -1; + for (let x = startX + stepX; stepX > 0 ? x <= endX : x >= endX; x += stepX) { + path.push({ x: x, y: startY }); + } + } + + // Then move along Y axis + if (startY !== endY) { + const stepY = startY < endY ? 1 : -1; + for (let y = startY + stepY; stepY > 0 ? y <= endY : y >= endY; y += stepY) { + path.push({ x: endX, y: y }); + } + } + + return path; + } + + updatePlayer() { + const speed = 0.1; + + // If we don't have a current waypoint but have a path, get next waypoint + if (!this.player.currentWaypoint && this.player.path.length > 0) { + this.player.currentWaypoint = this.player.path.shift(); + } + + // Move towards current waypoint + if (this.player.currentWaypoint) { + const dx = this.player.currentWaypoint.x - this.player.x; + const dy = this.player.currentWaypoint.y - this.player.y; + + // Move towards waypoint + this.player.x += dx * speed; + this.player.y += dy * speed; + + // Check if we're close enough to waypoint to consider it reached + const distance = Math.sqrt(dx * dx + dy * dy); + if (distance < 0.1) { + this.player.x = this.player.currentWaypoint.x; + this.player.y = this.player.currentWaypoint.y; + this.player.currentWaypoint = null; + } + } + } + + setupEventListeners() { + this.canvas.addEventListener('click', (e) => { + const rect = this.canvas.getBoundingClientRect(); + const clickX = e.clientX - rect.left; + const clickY = e.clientY - rect.top; + + const gridPos = this.fromIsometric(clickX, clickY); + + // Only move if within grid bounds + if (gridPos.x >= 0 && gridPos.x < this.gridSize && + gridPos.y >= 0 && gridPos.y < this.gridSize) { + + // Set target and calculate path + this.player.targetX = Math.round(gridPos.x); + this.player.targetY = Math.round(gridPos.y); + + // Calculate new path + this.player.path = this.findPath( + Math.round(this.player.x), + Math.round(this.player.y), + this.player.targetX, + this.player.targetY + ); + + // Clear current waypoint to start new path + this.player.currentWaypoint = null; + } + }); + } + + gameLoop() { + // Clear canvas + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + + // Draw game elements + this.drawGrid(); + this.updatePlayer(); + this.drawPlayer(); + + // Continue game loop + requestAnimationFrame(() => this.gameLoop()); + } +} + +// Start the game when the page loads +window.onload = () => { + new IsometricGame(); +}; \ No newline at end of file |