Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>BeetleCraft</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
- body {
- font-family: 'Courier New', monospace;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- display: flex;
- justify-content: center;
- align-items: center;
- min-height: 100vh;
- overflow: hidden;
- }
- #gameContainer {
- background: rgba(0, 0, 0, 0.9);
- border-radius: 15px;
- padding: 20px;
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
- }
- #gameCanvas {
- border: 3px solid #4a5568;
- border-radius: 10px;
- cursor: crosshair;
- image-rendering: pixelated;
- image-rendering: -moz-crisp-edges;
- image-rendering: crisp-edges;
- }
- #ui {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-top: 15px;
- color: white;
- font-size: 14px;
- }
- .ui-section {
- display: flex;
- gap: 20px;
- align-items: center;
- }
- .hotbar {
- display: flex;
- gap: 5px;
- }
- .hotbar-slot {
- width: 40px;
- height: 40px;
- border: 2px solid #4a5568;
- border-radius: 5px;
- display: flex;
- align-items: center;
- justify-content: center;
- color: white;
- font-size: 10px;
- background: rgba(0, 0, 0, 0.5);
- cursor: pointer;
- transition: all 0.2s;
- }
- .hotbar-slot.active {
- border-color: #fbbf24;
- background: rgba(251, 191, 36, 0.2);
- transform: scale(1.1);
- }
- .hotbar-slot:hover {
- border-color: #8b9dc3;
- }
- #controls {
- position: absolute;
- top: 20px;
- left: 20px;
- color: white;
- background: rgba(0, 0, 0, 0.8);
- padding: 15px;
- border-radius: 10px;
- font-size: 12px;
- line-height: 1.6;
- }
- #startScreen {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- color: white;
- z-index: 100;
- }
- #startScreen h1 {
- font-size: 48px;
- margin-bottom: 10px;
- text-shadow: 3px 3px 6px rgba(0, 0, 0, 0.5);
- animation: pulse 2s infinite;
- }
- #startScreen p {
- font-size: 18px;
- margin-bottom: 30px;
- opacity: 0.9;
- }
- #startButton {
- padding: 15px 40px;
- font-size: 20px;
- background: rgba(255, 255, 255, 0.2);
- border: 2px solid white;
- border-radius: 10px;
- color: white;
- cursor: pointer;
- transition: all 0.3s;
- }
- #startButton:hover {
- background: rgba(255, 255, 255, 0.3);
- transform: scale(1.05);
- }
- @keyframes pulse {
- 0%, 100% { transform: scale(1); }
- 50% { transform: scale(1.05); }
- }
- .particle {
- position: absolute;
- pointer-events: none;
- animation: particleFall 1s ease-out forwards;
- }
- @keyframes particleFall {
- 0% {
- opacity: 1;
- transform: translateY(0) rotate(0deg);
- }
- 100% {
- opacity: 0;
- transform: translateY(30px) rotate(180deg);
- }
- }
- </style>
- </head>
- <body>
- <div id="startScreen">
- <h1>🪲 BeetleCraft 🪲</h1>
- <p>A tiny beetle's big adventure!</p>
- <button id="startButton">Start Adventure</button>
- </div>
- <div id="controls">
- <strong>Controls:</strong><br>
- WASD/Arrows - Move<br>
- Space - Jump<br>
- Left Click - Break block<br>
- Right Click - Place block<br>
- 1-9 - Select block<br>
- R - Reset world
- </div>
- <div id="gameContainer" style="display: none;">
- <canvas id="gameCanvas"></canvas>
- <div id="ui">
- <div class="ui-section">
- <div>Health: <span id="health">♥♥♥♥♥</span></div>
- <div>Score: <span id="score">0</span></div>
- </div>
- <div class="hotbar" id="hotbar">
- <div class="hotbar-slot active" data-slot="0">Dirt</div>
- <div class="hotbar-slot" data-slot="1">Stone</div>
- <div class="hotbar-slot" data-slot="2">Wood</div>
- <div class="hotbar-slot" data-slot="3">Leaf</div>
- <div class="hotbar-slot" data-slot="4">Sand</div>
- <div class="hotbar-slot" data-slot="5">Glass</div>
- <div class="hotbar-slot" data-slot="6">Brick</div>
- <div class="hotbar-slot" data-slot="7">Coal</div>
- <div class="hotbar-slot" data-slot="8">Gold</div>
- </div>
- </div>
- </div>
- <script>
- // Game configuration
- const BLOCK_SIZE = 20;
- const WORLD_WIDTH = 50;
- const WORLD_HEIGHT = 30;
- const GRAVITY = 0.5;
- const JUMP_POWER = 10;
- const MOVE_SPEED = 3;
- // Block types
- const BlockType = {
- AIR: 0,
- DIRT: 1,
- STONE: 2,
- WOOD: 3,
- LEAF: 4,
- SAND: 5,
- GLASS: 6,
- BRICK: 7,
- COAL: 8,
- GOLD: 9,
- GRASS: 10,
- BEDROCK: 11
- };
- // Block colors
- const blockColors = {
- [BlockType.AIR]: null,
- [BlockType.DIRT]: '#8B4513',
- [BlockType.STONE]: '#808080',
- [BlockType.WOOD]: '#8B5A00',
- [BlockType.LEAF]: '#228B22',
- [BlockType.SAND]: '#F4E4C1',
- [BlockType.GLASS]: 'rgba(173, 216, 230, 0.3)',
- [BlockType.BRICK]: '#B22222',
- [BlockType.COAL]: '#2F4F4F',
- [BlockType.GOLD]: '#FFD700',
- [BlockType.GRASS]: '#3CB371',
- [BlockType.BEDROCK]: '#1C1C1C'
- };
- class Beetle {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.vx = 0;
- this.vy = 0;
- this.width = BLOCK_SIZE;
- this.height = BLOCK_SIZE;
- this.onGround = false;
- this.facing = 1; // 1 for right, -1 for left
- this.animFrame = 0;
- this.health = 5;
- // Anti-jitter smooth position tracking
- this.displayX = x;
- this.displayY = y;
- // Previous frame positions for smoothing
- this.prevX = x;
- this.prevY = y;
- // Collision box inset
- this.collisionInset = 3;
- }
- update(world, keys) {
- // Store previous position
- this.prevX = this.x;
- this.prevY = this.y;
- // Horizontal movement with smooth acceleration
- const accel = 0.4;
- if (keys['a'] || keys['ArrowLeft']) {
- this.vx = Math.max(this.vx - accel, -MOVE_SPEED);
- this.facing = -1;
- } else if (keys['d'] || keys['ArrowRight']) {
- this.vx = Math.min(this.vx + accel, MOVE_SPEED);
- this.facing = 1;
- } else {
- // Apply friction
- if (Math.abs(this.vx) > 0.1) {
- this.vx *= 0.88;
- } else {
- this.vx = 0;
- }
- }
- // CRITICAL FIX: Always check if there's ground beneath us
- // This ensures gravity works when walking off ledges
- const groundBelowY = this.y + BLOCK_SIZE + 1;
- const hasGroundBelow = !this.canMoveTo(this.x, groundBelowY, world);
- // Update ground state
- if (!hasGroundBelow && this.onGround) {
- // We just walked off a ledge!
- this.onGround = false;
- }
- // Jumping with ground check
- if ((keys['w'] || keys['ArrowUp'] || keys[' ']) && this.onGround) {
- this.vy = -JUMP_POWER;
- this.onGround = false;
- }
- // Apply gravity when not on ground
- if (!this.onGround) {
- this.vy = Math.min(this.vy + GRAVITY, 12);
- } else {
- // Even when on ground, check if we should fall
- if (!hasGroundBelow) {
- this.vy = 0.1; // Small initial fall velocity
- this.onGround = false;
- }
- }
- // Update position with advanced collision detection
- this.moveWithSmoothCollision(world);
- // Ultra-smooth display position (Enhanced Anti-JitterBugâ„¢)
- const smoothFactor = 0.25;
- this.displayX += (this.x - this.displayX) * smoothFactor;
- this.displayY += (this.y - this.displayY) * smoothFactor;
- // Snap display to actual if very close (prevents micro-jitter)
- if (Math.abs(this.displayX - this.x) < 0.1) this.displayX = this.x;
- if (Math.abs(this.displayY - this.y) < 0.1) this.displayY = this.y;
- // Smooth animation
- if (Math.abs(this.vx) > 0.5) {
- this.animFrame = (this.animFrame + Math.abs(this.vx) * 0.05) % 4;
- }
- }
- moveWithSmoothCollision(world) {
- const steps = 4; // Subdivide movement into smaller steps
- const dx = this.vx / steps;
- const dy = this.vy / steps;
- let movedX = false;
- let movedY = false;
- for (let step = 0; step < steps; step++) {
- // Try horizontal movement
- if (!movedX && Math.abs(dx) > 0.01) {
- const testX = this.x + dx;
- if (this.canMoveTo(testX, this.y, world)) {
- this.x = testX;
- } else {
- // Find the exact position we can move to
- const direction = Math.sign(dx);
- let bestX = this.x;
- for (let i = 0; i < Math.abs(dx); i += 0.5) {
- const checkX = this.x + (i * direction);
- if (this.canMoveTo(checkX, this.y, world)) {
- bestX = checkX;
- } else {
- break;
- }
- }
- this.x = bestX;
- this.vx = 0;
- movedX = true;
- }
- }
- // Try vertical movement
- if (!movedY && Math.abs(dy) > 0.01) {
- const testY = this.y + dy;
- if (this.canMoveTo(this.x, testY, world)) {
- this.y = testY;
- this.onGround = false;
- } else {
- // Find the exact position we can move to
- const direction = Math.sign(dy);
- let bestY = this.y;
- for (let i = 0; i < Math.abs(dy); i += 0.5) {
- const checkY = this.y + (i * direction);
- if (this.canMoveTo(this.x, checkY, world)) {
- bestY = checkY;
- } else {
- break;
- }
- }
- this.y = bestY;
- if (direction > 0) {
- // Hit ground - align to grid
- this.onGround = true;
- const gridY = Math.floor((this.y + BLOCK_SIZE) / BLOCK_SIZE);
- this.y = gridY * BLOCK_SIZE - BLOCK_SIZE;
- }
- this.vy = 0;
- movedY = true;
- }
- }
- }
- // Additional ground check for stability
- if (!this.onGround) {
- const groundCheckY = this.y + 1;
- if (!this.canMoveTo(this.x, groundCheckY, world)) {
- this.onGround = true;
- }
- }
- // Smooth boundary clamping
- const padding = 2;
- this.x = Math.max(padding, Math.min(this.x, (WORLD_WIDTH - 1) * BLOCK_SIZE - padding));
- this.y = Math.max(padding, Math.min(this.y, (WORLD_HEIGHT - 1) * BLOCK_SIZE - padding));
- }
- canMoveTo(x, y, world) {
- // Check collision with smaller inset box for smoother movement
- const inset = this.collisionInset;
- const points = [
- {x: x + inset, y: y + inset},
- {x: x + BLOCK_SIZE - inset, y: y + inset},
- {x: x + inset, y: y + BLOCK_SIZE - inset},
- {x: x + BLOCK_SIZE - inset, y: y + BLOCK_SIZE - inset},
- // Center points for better detection
- {x: x + BLOCK_SIZE/2, y: y + inset},
- {x: x + BLOCK_SIZE/2, y: y + BLOCK_SIZE - inset}
- ];
- for (let point of points) {
- const gridX = Math.floor(point.x / BLOCK_SIZE);
- const gridY = Math.floor(point.y / BLOCK_SIZE);
- if (gridX < 0 || gridX >= WORLD_WIDTH ||
- gridY < 0 || gridY >= WORLD_HEIGHT) {
- return false;
- }
- if (world[gridY] && world[gridY][gridX] !== BlockType.AIR) {
- return false;
- }
- }
- return true;
- }
- draw(ctx, camera) {
- ctx.save();
- // Use smooth display position instead of raw position
- ctx.translate(this.displayX - camera.x + BLOCK_SIZE/2, this.displayY - camera.y + BLOCK_SIZE/2);
- // Smooth rotation based on velocity
- const tilt = this.vx * 0.02;
- ctx.rotate(tilt);
- // Flip beetle based on direction
- ctx.scale(this.facing, 1);
- // Draw beetle body
- ctx.fillStyle = '#1a1a1a';
- ctx.beginPath();
- ctx.ellipse(0, 0, BLOCK_SIZE/2.5, BLOCK_SIZE/3, 0, 0, Math.PI * 2);
- ctx.fill();
- // Draw beetle shell with gradient (Anti-JitterBugâ„¢ Neptune Variety)
- const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, BLOCK_SIZE/2);
- gradient.addColorStop(0, '#6a4c93');
- gradient.addColorStop(0.5, '#7b68a6');
- gradient.addColorStop(0.8, '#5e3c8f');
- gradient.addColorStop(1, '#432874');
- ctx.fillStyle = gradient;
- ctx.beginPath();
- ctx.ellipse(0, -2, BLOCK_SIZE/2.8, BLOCK_SIZE/3.5, 0, 0, Math.PI * 2);
- ctx.fill();
- // Neptune shimmer effect
- ctx.fillStyle = 'rgba(147, 191, 207, 0.3)';
- ctx.beginPath();
- ctx.ellipse(-3, -4, BLOCK_SIZE/6, BLOCK_SIZE/8, -0.3, 0, Math.PI * 2);
- ctx.fill();
- // Draw legs (smoothly animated)
- ctx.strokeStyle = '#000';
- ctx.lineWidth = 1;
- const legOffset = Math.sin(this.animFrame * 1.5) * 1.5;
- for (let i = -1; i <= 1; i++) {
- ctx.beginPath();
- ctx.moveTo(i * 4, 3);
- ctx.lineTo(i * 6, 7 + (i === 0 ? -legOffset : legOffset));
- ctx.stroke();
- }
- // Draw antennae (more stable)
- const antennaWave = Math.sin(Date.now() * 0.002) * 0.5;
- ctx.beginPath();
- ctx.moveTo(5, -5);
- ctx.quadraticCurveTo(8, -8 + antennaWave, 7, -10);
- ctx.moveTo(5, -5);
- ctx.quadraticCurveTo(3, -8 - antennaWave, 2, -10);
- ctx.stroke();
- // Draw eyes
- ctx.fillStyle = '#fff';
- ctx.beginPath();
- ctx.arc(4, -2, 2, 0, Math.PI * 2);
- ctx.arc(4, 2, 2, 0, Math.PI * 2);
- ctx.fill();
- ctx.fillStyle = '#000';
- ctx.beginPath();
- ctx.arc(4.5, -2, 1, 0, Math.PI * 2);
- ctx.arc(4.5, 2, 1, 0, Math.PI * 2);
- ctx.fill();
- ctx.restore();
- }
- }
- class Game {
- constructor() {
- this.canvas = document.getElementById('gameCanvas');
- this.ctx = this.canvas.getContext('2d');
- this.canvas.width = 800;
- this.canvas.height = 600;
- this.world = [];
- this.beetle = null;
- this.camera = { x: 0, y: 0 };
- this.keys = {};
- this.selectedBlock = BlockType.DIRT;
- this.score = 0;
- this.particles = [];
- this.sandUpdateTimer = 0;
- this.sandUpdateInterval = 5; // Update sand every 5 frames for performance
- this.init();
- }
- init() {
- this.generateWorld();
- this.beetle = new Beetle(WORLD_WIDTH * BLOCK_SIZE / 2, 100);
- this.setupEventListeners();
- this.gameLoop();
- }
- generateWorld() {
- // Initialize empty world
- for (let y = 0; y < WORLD_HEIGHT; y++) {
- this.world[y] = [];
- for (let x = 0; x < WORLD_WIDTH; x++) {
- this.world[y][x] = BlockType.AIR;
- }
- }
- // Generate terrain
- const surfaceHeight = 15;
- const noise = this.generateNoise();
- for (let x = 0; x < WORLD_WIDTH; x++) {
- const height = surfaceHeight + Math.floor(noise[x] * 5);
- // Place blocks
- for (let y = height; y < WORLD_HEIGHT; y++) {
- if (y === height) {
- this.world[y][x] = BlockType.GRASS;
- } else if (y < height + 3) {
- // Add some sand patches for testing gravity
- if (x > 10 && x < 20 && Math.random() < 0.7) {
- this.world[y][x] = BlockType.SAND;
- } else {
- this.world[y][x] = BlockType.DIRT;
- }
- } else if (y < height + 7) {
- this.world[y][x] = BlockType.STONE;
- } else if (y === WORLD_HEIGHT - 1) {
- this.world[y][x] = BlockType.BEDROCK;
- } else {
- // Ore generation
- const rand = Math.random();
- if (rand < 0.02) {
- this.world[y][x] = BlockType.COAL;
- } else if (rand < 0.025) {
- this.world[y][x] = BlockType.GOLD;
- } else {
- this.world[y][x] = BlockType.STONE;
- }
- }
- }
- // Generate trees
- if (x > 3 && x < WORLD_WIDTH - 3 && Math.random() < 0.1) {
- this.generateTree(x, height - 1);
- }
- }
- // Add a sand pit for testing gravity
- for (let x = 25; x < 35; x++) {
- for (let y = 10; y < 15; y++) {
- this.world[y][x] = BlockType.SAND;
- }
- }
- }
- generateNoise() {
- const noise = [];
- let seed = Math.random() * 100;
- for (let x = 0; x < WORLD_WIDTH; x++) {
- noise[x] = Math.sin(seed + x * 0.1) * Math.cos(seed * 2 + x * 0.05);
- }
- return noise;
- }
- generateTree(x, y) {
- // Trunk
- for (let i = 0; i < 4; i++) {
- if (y - i >= 0) {
- this.world[y - i][x] = BlockType.WOOD;
- }
- }
- // Leaves
- for (let ly = -5; ly <= -3; ly++) {
- for (let lx = -2; lx <= 2; lx++) {
- if (y + ly >= 0 && x + lx >= 0 && x + lx < WORLD_WIDTH) {
- if (Math.abs(lx) + Math.abs(ly + 4) <= 2) {
- this.world[y + ly][x + lx] = BlockType.LEAF;
- }
- }
- }
- }
- }
- setupEventListeners() {
- // Keyboard controls
- window.addEventListener('keydown', (e) => {
- this.keys[e.key] = true;
- // Block selection
- if (e.key >= '1' && e.key <= '9') {
- const slot = parseInt(e.key) - 1;
- this.selectBlock(slot);
- }
- // Reset world
- if (e.key === 'r' || e.key === 'R') {
- this.generateWorld();
- this.beetle = new Beetle(WORLD_WIDTH * BLOCK_SIZE / 2, 100);
- this.score = 0;
- }
- });
- window.addEventListener('keyup', (e) => {
- this.keys[e.key] = false;
- });
- // Mouse controls
- this.canvas.addEventListener('mousedown', (e) => {
- const rect = this.canvas.getBoundingClientRect();
- const mouseX = e.clientX - rect.left + this.camera.x;
- const mouseY = e.clientY - rect.top + this.camera.y;
- const gridX = Math.floor(mouseX / BLOCK_SIZE);
- const gridY = Math.floor(mouseY / BLOCK_SIZE);
- if (gridX >= 0 && gridX < WORLD_WIDTH && gridY >= 0 && gridY < WORLD_HEIGHT) {
- if (e.button === 0) { // Left click - break
- if (this.world[gridY][gridX] !== BlockType.AIR &&
- this.world[gridY][gridX] !== BlockType.BEDROCK) {
- this.createParticles(gridX * BLOCK_SIZE, gridY * BLOCK_SIZE, this.world[gridY][gridX]);
- this.world[gridY][gridX] = BlockType.AIR;
- this.score += 10;
- }
- } else if (e.button === 2) { // Right click - place
- if (this.world[gridY][gridX] === BlockType.AIR) {
- this.world[gridY][gridX] = this.selectedBlock;
- }
- }
- }
- });
- this.canvas.addEventListener('contextmenu', (e) => {
- e.preventDefault();
- });
- // Hotbar clicks
- document.querySelectorAll('.hotbar-slot').forEach(slot => {
- slot.addEventListener('click', () => {
- this.selectBlock(parseInt(slot.dataset.slot));
- });
- });
- }
- selectBlock(slot) {
- document.querySelectorAll('.hotbar-slot').forEach(s => {
- s.classList.remove('active');
- });
- document.querySelectorAll('.hotbar-slot')[slot].classList.add('active');
- this.selectedBlock = slot + 1; // +1 because AIR is 0
- }
- createParticles(x, y, blockType) {
- for (let i = 0; i < 5; i++) {
- this.particles.push({
- x: x + Math.random() * BLOCK_SIZE,
- y: y + Math.random() * BLOCK_SIZE,
- vx: (Math.random() - 0.5) * 5,
- vy: -Math.random() * 5,
- color: blockColors[blockType],
- life: 30
- });
- }
- }
- updateParticles() {
- this.particles = this.particles.filter(p => {
- p.x += p.vx;
- p.y += p.vy;
- p.vy += 0.3;
- p.life--;
- return p.life > 0;
- });
- }
- updateSand() {
- // Update sand less frequently for performance
- this.sandUpdateTimer++;
- if (this.sandUpdateTimer < this.sandUpdateInterval) return;
- this.sandUpdateTimer = 0;
- // Process from bottom to top to prevent multiple sand blocks from falling through each other
- for (let y = WORLD_HEIGHT - 2; y >= 0; y--) {
- for (let x = 0; x < WORLD_WIDTH; x++) {
- if (this.world[y][x] === BlockType.SAND) {
- // Check if the block below is air
- if (y + 1 < WORLD_HEIGHT && this.world[y + 1][x] === BlockType.AIR) {
- // Move sand down
- this.world[y + 1][x] = BlockType.SAND;
- this.world[y][x] = BlockType.AIR;
- }
- // Check if sand can fall diagonally
- else if (y + 1 < WORLD_HEIGHT) {
- // Try falling left
- if (x > 0 && this.world[y + 1][x - 1] === BlockType.AIR) {
- this.world[y + 1][x - 1] = BlockType.SAND;
- this.world[y][x] = BlockType.AIR;
- }
- // Try falling right
- else if (x < WORLD_WIDTH - 1 && this.world[y + 1][x + 1] === BlockType.AIR) {
- this.world[y + 1][x + 1] = BlockType.SAND;
- this.world[y][x] = BlockType.AIR;
- }
- }
- }
- }
- }
- }
- updateCamera() {
- // Ultra-smooth camera follow with deadzone
- const targetX = this.beetle.x - this.canvas.width / 2;
- const targetY = this.beetle.y - this.canvas.height / 2;
- // Slower camera lerp for smoother following
- const cameraSpeed = 0.08;
- this.camera.x += (targetX - this.camera.x) * cameraSpeed;
- this.camera.y += (targetY - this.camera.y) * cameraSpeed;
- // Round camera position to prevent sub-pixel rendering issues
- this.camera.x = Math.round(this.camera.x * 2) / 2;
- this.camera.y = Math.round(this.camera.y * 2) / 2;
- // Camera bounds with smooth clamping
- this.camera.x = Math.max(0, Math.min(this.camera.x, WORLD_WIDTH * BLOCK_SIZE - this.canvas.width));
- this.camera.y = Math.max(0, Math.min(this.camera.y, WORLD_HEIGHT * BLOCK_SIZE - this.canvas.height));
- }
- draw() {
- // Clear canvas
- this.ctx.fillStyle = '#87CEEB';
- this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
- // Draw clouds
- this.drawClouds();
- // Draw world
- const startX = Math.floor(this.camera.x / BLOCK_SIZE);
- const endX = Math.ceil((this.camera.x + this.canvas.width) / BLOCK_SIZE);
- const startY = Math.floor(this.camera.y / BLOCK_SIZE);
- const endY = Math.ceil((this.camera.y + this.canvas.height) / BLOCK_SIZE);
- for (let y = startY; y <= endY && y < WORLD_HEIGHT; y++) {
- for (let x = startX; x <= endX && x < WORLD_WIDTH; x++) {
- if (this.world[y] && this.world[y][x] !== BlockType.AIR) {
- const blockType = this.world[y][x];
- const drawX = x * BLOCK_SIZE - this.camera.x;
- const drawY = y * BLOCK_SIZE - this.camera.y;
- // Draw block
- this.ctx.fillStyle = blockColors[blockType];
- this.ctx.fillRect(drawX, drawY, BLOCK_SIZE, BLOCK_SIZE);
- // Draw block borders
- this.ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
- this.ctx.strokeRect(drawX, drawY, BLOCK_SIZE, BLOCK_SIZE);
- // Add texture details
- if (blockType === BlockType.GRASS) {
- this.ctx.fillStyle = '#2E7D32';
- this.ctx.fillRect(drawX, drawY, BLOCK_SIZE, 4);
- } else if (blockType === BlockType.WOOD) {
- this.ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
- this.ctx.beginPath();
- this.ctx.moveTo(drawX + 5, drawY);
- this.ctx.lineTo(drawX + 5, drawY + BLOCK_SIZE);
- this.ctx.moveTo(drawX + 15, drawY);
- this.ctx.lineTo(drawX + 15, drawY + BLOCK_SIZE);
- this.ctx.stroke();
- } else if (blockType === BlockType.SAND) {
- // Add sand texture
- this.ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
- for (let i = 0; i < 3; i++) {
- const dotX = drawX + Math.random() * BLOCK_SIZE;
- const dotY = drawY + Math.random() * BLOCK_SIZE;
- this.ctx.fillRect(dotX, dotY, 1, 1);
- }
- }
- }
- }
- }
- // Draw particles
- this.particles.forEach(p => {
- this.ctx.fillStyle = p.color;
- this.ctx.globalAlpha = p.life / 30;
- this.ctx.fillRect(p.x - this.camera.x, p.y - this.camera.y, 4, 4);
- });
- this.ctx.globalAlpha = 1;
- // Draw beetle
- this.beetle.draw(this.ctx, this.camera);
- // Update UI
- document.getElementById('score').textContent = this.score;
- document.getElementById('health').textContent = '♥'.repeat(this.beetle.health);
- }
- drawClouds() {
- this.ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
- const time = Date.now() * 0.00005;
- for (let i = 0; i < 3; i++) {
- const x = ((time * 20 * (i + 1)) % (this.canvas.width + 100)) - 100;
- const y = 50 + i * 40;
- this.ctx.beginPath();
- this.ctx.arc(x, y, 25, 0, Math.PI * 2);
- this.ctx.arc(x + 25, y, 30, 0, Math.PI * 2);
- this.ctx.arc(x + 50, y, 25, 0, Math.PI * 2);
- this.ctx.fill();
- }
- }
- gameLoop() {
- // Update
- this.beetle.update(this.world, this.keys);
- this.updateSand(); // Update sand physics
- this.updateCamera();
- this.updateParticles();
- // Draw
- this.draw();
- // Continue loop
- requestAnimationFrame(() => this.gameLoop());
- }
- }
- // Start game
- document.getElementById('startButton').addEventListener('click', () => {
- document.getElementById('startScreen').style.display = 'none';
- document.getElementById('gameContainer').style.display = 'block';
- const game = new Game();
- });
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment