🎮ArcadeLab

Mini Shooter - 2D Battle Game

by CyberEagle48
618 lines21.5 KB
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mini Shooter - 2D Battle Game</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background: #1a1a2e;
            font-family: Arial, sans-serif;
            overflow: hidden;
        }
        #gameContainer {
            position: relative;
        }
        canvas {
            border: 3px solid #e94560;
            border-radius: 10px;
            box-shadow: 0 0 30px rgba(233,69,96,0.3);
        }
        #ui {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            font-size: 16px;
            font-weight: bold;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
        }
        #controls {
            position: absolute;
            bottom: 10px;
            left: 50%;
            transform: translateX(-50%);
            color: rgba(255,255,255,0.7);
            font-size: 12px;
            text-align: center;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
        }
    </style>
</head>
<body>
    <div id="gameContainer">
        <canvas id="gameCanvas"></canvas>
        <div id="ui">
            <div>❤️ Health: <span id="health">100</span></div>
            <div>🔫 Ammo: <span id="ammo">30</span></div>
            <div>💀 Kills: <span id="kills">0</span></div>
            <div>⭐ Score: <span id="score">0</span></div>
        </div>
        <div id="controls">
            🎮 W/A/S/D - Move | Mouse - Aim & Shoot | R - Reload | Space - Jetpack
        </div>
    </div>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        
        // Set canvas size
        canvas.width = 800;
        canvas.height = 500;
        
        // Game variables
        let gameRunning = true;
        
        // Player object
        const player = {
            x: canvas.width / 2,
            y: canvas.height - 100,
            width: 30,
            height: 40,
            speed: 4,
            health: 100,
            maxHealth: 100,
            ammo: 30,
            maxAmmo: 30,
            kills: 0,
            score: 0,
            angle: 0,
            jetpackFuel: 100,
            maxJetpackFuel: 100,
            isJetpackActive: false,
            vy: 0,
            onGround: true
        };
        
        // Arrays for game objects
        let bullets = [];
        let enemies = [];
        let particles = [];
        let powerUps = [];
        
        // Input handling
        const keys = {};
        let mouseX = player.x;
        let mouseY = player.y - 100;
        let mouseDown = false;
        
        // Ground level
        const groundY = canvas.height - 20;
        
        // Platforms
        const platforms = [
            { x: 100, y: groundY - 100, width: 150, height: 15 },
            { x: 350, y: groundY - 150, width: 150, height: 15 },
            { x: 550, y: groundY - 100, width: 150, height: 15 },
            { x: 250, y: groundY - 200, width: 100, height: 15 },
            { x: 500, y: groundY - 200, width: 100, height: 15 }
        ];
        
        // Event listeners
        window.addEventListener('keydown', (e) => {
            keys[e.key.toLowerCase()] = true;
            if (e.key.toLowerCase() === 'r') reload();
        });
        
        window.addEventListener('keyup', (e) => {
            keys[e.key.toLowerCase()] = false;
            if (e.key === ' ') {
                player.isJetpackActive = false;
            }
        });
        
        canvas.addEventListener('mousemove', (e) => {
            const rect = canvas.getBoundingClientRect();
            mouseX = e.clientX - rect.left;
            mouseY = e.clientY - rect.top;
        });
        
        canvas.addEventListener('mousedown', (e) => {
            if (e.button === 0) {
                mouseDown = true;
                shoot();
            }
        });
        
        canvas.addEventListener('mouseup', (e) => {
            if (e.button === 0) {
                mouseDown = false;
            }
        });
        
        canvas.addEventListener('contextmenu', (e) => e.preventDefault());
        
        // Shooting function
        function shoot() {
            if (player.ammo > 0) {
                const angle = Math.atan2(mouseY - player.y, mouseX - player.x);
                bullets.push({
                    x: player.x,
                    y: player.y,
                    vx: Math.cos(angle) * 10,
                    vy: Math.sin(angle) * 10,
                    damage: 20
                });
                player.ammo--;
                updateUI();
            }
        }
        
        // Reload function
        function reload() {
            player.ammo = player.maxAmmo;
            updateUI();
        }
        
        // Spawn enemy
        function spawnEnemy() {
            if (enemies.length < 5) {
                const side = Math.random() < 0.5 ? 'left' : 'right';
                const x = side === 'left' ? -50 : canvas.width + 50;
                const y = groundY - 40;
                
                enemies.push({
                    x: x,
                    y: y,
                    width: 30,
                    height: 40,
                    speed: 1.5 + Math.random() * 1.5,
                    health: 50,
                    maxHealth: 50,
                    shootTimer: 0,
                    shootCooldown: 60 + Math.random() * 40,
                    onGround: true,
                    vy: 0
                });
            }
        }
        
        // Spawn power-up
        function spawnPowerUp() {
            if (powerUps.length < 3 && Math.random() < 0.005) {
                const types = ['health', 'ammo'];
                powerUps.push({
                    x: Math.random() * (canvas.width - 40) + 20,
                    y: Math.random() * (groundY - 50) + 30,
                    width: 20,
                    height: 20,
                    type: types[Math.floor(Math.random() * types.length)]
                });
            }
        }
        
        // Update game state
        function update() {
            if (!gameRunning) return;
            
            // Player movement
            if (keys['a'] || keys['arrowleft']) player.x -= player.speed;
            if (keys['d'] || keys['arrowright']) player.x += player.speed;
            
            // Jetpack
            if ((keys[' '] || keys['w'] || keys['arrowup']) && player.jetpackFuel > 0) {
                player.isJetpackActive = true;
                player.vy = -8;
                player.jetpackFuel -= 0.5;
                player.onGround = false;
                
                // Jetpack particles
                for (let i = 0; i < 3; i++) {
                    particles.push({
                        x: player.x + (Math.random() - 0.5) * 10,
                        y: player.y + player.height / 2,
                        vx: (Math.random() - 0.5) * 2,
                        vy: Math.random() * 3 + 2,
                        life: 20,
                        color: '#ff6b35'
                    });
                }
            } else {
                player.isJetpackActive = false;
                if (player.jetpackFuel < player.maxJetpackFuel) {
                    player.jetpackFuel += 0.2;
                }
            }
            
            // Apply gravity to player
            if (!player.onGround && !player.isJetpackActive) {
                player.vy += 0.8;
            }
            
            player.y += player.vy;
            player.onGround = false;
            
            // Ground collision
            if (player.y + player.height / 2 >= groundY) {
                player.y = groundY - player.height / 2;
                player.vy = 0;
                player.onGround = true;
            }
            
            // Platform collision
            platforms.forEach(platform => {
                if (player.vy > 0 &&
                    player.x > platform.x &&
                    player.x < platform.x + platform.width &&
                    player.y + player.height / 2 >= platform.y &&
                    player.y + player.height / 2 <= platform.y + platform.height + 10) {
                    player.y = platform.y - player.height / 2;
                    player.vy = 0;
                    player.onGround = true;
                }
            });
            
            // Keep player in bounds
            if (player.x < player.width / 2) player.x = player.width / 2;
            if (player.x > canvas.width - player.width / 2) player.x = canvas.width - player.width / 2;
            if (player.y < player.height / 2) player.y = player.height / 2;
            
            // Update bullets
            bullets = bullets.filter(bullet => {
                bullet.x += bullet.vx;
                bullet.y += bullet.vy;
                return bullet.x > -50 && bullet.x < canvas.width + 50 &&
                       bullet.y > -50 && bullet.y < canvas.height + 50;
            });
            
            // Update enemies
            enemies.forEach(enemy => {
                // Enemy AI - move towards player
                if (enemy.x < player.x) enemy.x += enemy.speed;
                if (enemy.x > player.x) enemy.x -= enemy.speed;
                
                // Enemy shooting
                enemy.shootTimer--;
                if (enemy.shootTimer <= 0) {
                    const angle = Math.atan2(player.y - enemy.y, player.x - enemy.x);
                    bullets.push({
                        x: enemy.x,
                        y: enemy.y,
                        vx: Math.cos(angle) * 7,
                        vy: Math.sin(angle) * 7,
                        damage: 10,
                        isEnemy: true
                    });
                    enemy.shootTimer = enemy.shootCooldown;
                }
                
                // Enemy gravity and ground collision
                enemy.vy += 0.8;
                enemy.y += enemy.vy;
                if (enemy.y + enemy.height / 2 >= groundY) {
                    enemy.y = groundY - enemy.height / 2;
                    enemy.vy = 0;
                    enemy.onGround = true;
                }
            });
            
            // Check bullet collisions
            bullets.forEach((bullet, bulletIndex) => {
                if (bullet.isEnemy) {
                    // Enemy bullet hit player
                    const dx = bullet.x - player.x;
                    const dy = bullet.y - player.y;
                    const distance = Math.sqrt(dx * dx + dy * dy);
                    
                    if (distance < player.width / 2 + 5) {
                        player.health -= bullet.damage;
                        bullets.splice(bulletIndex, 1);
                        
                        // Hit particles
                        for (let i = 0; i < 5; i++) {
                            particles.push({
                                x: player.x,
                                y: player.y,
                                vx: (Math.random() - 0.5) * 4,
                                vy: (Math.random() - 0.5) * 4,
                                life: 15,
                                color: '#ff0000'
                            });
                        }
                        
                        if (player.health <= 0) {
                            gameRunning = false;
                            alert('Game Over! Final Score: ' + player.score);
                            location.reload();
                        }
                        updateUI();
                    }
                } else {
                    // Player bullet hit enemy
                    enemies.forEach((enemy, enemyIndex) => {
                        const dx = bullet.x - enemy.x;
                        const dy = bullet.y - enemy.y;
                        const distance = Math.sqrt(dx * dx + dy * dy);
                        
                        if (distance < enemy.width / 2 + 5) {
                            enemy.health -= bullet.damage;
                            bullets.splice(bulletIndex, 1);
                            
                            if (enemy.health <= 0) {
                                // Enemy death
                                for (let i = 0; i < 10; i++) {
                                    particles.push({
                                        x: enemy.x,
                                        y: enemy.y,
                                        vx: (Math.random() - 0.5) * 6,
                                        vy: (Math.random() - 0.5) * 6,
                                        life: 20,
                                        color: '#ff4444'
                                    });
                                }
                                enemies.splice(enemyIndex, 1);
                                player.kills++;
                                player.score += 100;
                                updateUI();
                            }
                        }
                    });
                }
            });
            
            // Check power-up collisions
            powerUps.forEach((powerUp, index) => {
                const dx = player.x - powerUp.x;
                const dy = player.y - powerUp.y;
                const distance = Math.sqrt(dx * dx + dy * dy);
                
                if (distance < 25) {
                    if (powerUp.type === 'health') {
                        player.health = Math.min(player.health + 30, player.maxHealth);
                    } else if (powerUp.type === 'ammo') {
                        player.ammo = player.maxAmmo;
                    }
                    powerUps.splice(index, 1);
                    updateUI();
                }
            });
            
            // Update particles
            particles = particles.filter(particle => {
                particle.x += particle.vx;
                particle.y += particle.vy;
                particle.life--;
                return particle.life > 0;
            });
            
            // Spawn enemies and power-ups
            spawnEnemy();
            spawnPowerUp();
            
            // Continuous shooting
            if (mouseDown && player.ammo > 0) {
                shoot();
            }
        }
        
        // Draw everything
        function draw() {
            // Clear canvas
            ctx.fillStyle = '#0f3460';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // Draw sky gradient
            const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
            gradient.addColorStop(0, '#16213e');
            gradient.addColorStop(0.6, '#1a1a2e');
            gradient.addColorStop(1, '#0f3460');
            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, canvas.width, groundY);
            
            // Draw ground
            ctx.fillStyle = '#533e2d';
            ctx.fillRect(0, groundY, canvas.width, canvas.height - groundY);
            ctx.fillStyle = '#6b4c3a';
            for (let i = 0; i < canvas.width; i += 30) {
                ctx.fillRect(i, groundY, 15, 5);
            }
            
            // Draw platforms
            platforms.forEach(platform => {
                ctx.fillStyle = '#8b7355';
                ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
                ctx.fillStyle = '#a0522d';
                ctx.fillRect(platform.x, platform.y, platform.width, 3);
            });
            
            // Draw power-ups
            powerUps.forEach(powerUp => {
                ctx.fillStyle = powerUp.type === 'health' ? '#ff4444' : '#ffaa00';
                ctx.beginPath();
                ctx.arc(powerUp.x, powerUp.y, 10, 0, Math.PI * 2);
                ctx.fill();
                ctx.fillStyle = 'white';
                ctx.font = '14px Arial';
                ctx.textAlign = 'center';
                ctx.fillText(powerUp.type === 'health' ? '+' : '🔫', powerUp.x, powerUp.y + 5);
            });
            
            // Draw enemies
            enemies.forEach(enemy => {
                // Enemy body
                ctx.save();
                ctx.translate(enemy.x, enemy.y);
                
                const angle = Math.atan2(player.y - enemy.y, player.x - enemy.x);
                ctx.rotate(angle);
                
                // Body
                ctx.fillStyle = '#ff4444';
                ctx.fillRect(-15, -20, 30, 35);
                
                // Head
                ctx.fillStyle = '#cc3333';
                ctx.beginPath();
                ctx.arc(0, -25, 12, 0, Math.PI * 2);
                ctx.fill();
                
                // Gun
                ctx.fillStyle = '#333333';
                ctx.fillRect(10, -10, 15, 4);
                
                // Eyes
                ctx.fillStyle = 'white';
                ctx.beginPath();
                ctx.arc(3, -28, 3, 0, Math.PI * 2);
                ctx.fill();
                ctx.fillStyle = 'black';
                ctx.beginPath();
                ctx.arc(4, -28, 1.5, 0, Math.PI * 2);
                ctx.fill();
                
                ctx.restore();
                
                // Health bar
                ctx.fillStyle = '#ff0000';
                ctx.fillRect(enemy.x - 15, enemy.y - 35, 30, 4);
                ctx.fillStyle = '#00ff00';
                ctx.fillRect(enemy.x - 15, enemy.y - 35, 30 * (enemy.health / enemy.maxHealth), 4);
            });
            
            // Draw player
            ctx.save();
            ctx.translate(player.x, player.y);
            
            const playerAngle = Math.atan2(mouseY - player.y, mouseX - player.x);
            ctx.rotate(playerAngle);
            
            // Player body
            ctx.fillStyle = '#4488ff';
            ctx.fillRect(-15, -20, 30, 35);
            
            // Player head
            ctx.fillStyle = '#3366cc';
            ctx.beginPath();
            ctx.arc(0, -25, 12, 0, Math.PI * 2);
            ctx.fill();
            
            // Helmet
            ctx.fillStyle = '#2255aa';
            ctx.beginPath();
            ctx.arc(0, -27, 14, Math.PI, 0);
            ctx.fill();
            
            // Gun
            ctx.fillStyle = '#555555';
            ctx.fillRect(10, -10, 20, 5);
            ctx.fillStyle = '#777777';
            ctx.fillRect(25, -12, 5, 9);
            
            // Eyes
            ctx.fillStyle = 'white';
            ctx.beginPath();
            ctx.arc(3, -28, 3, 0, Math.PI * 2);
            ctx.fill();
            ctx.fillStyle = 'black';
            ctx.beginPath();
            ctx.arc(4, -28, 1.5, 0, Math.PI * 2);
            ctx.fill();
            
            // Jetpack
            if (player.isJetpackActive) {
                ctx.fillStyle = '#ff6b35';
                ctx.beginPath();
                ctx.arc(-8, 5, 8, 0, Math.PI * 2);
                ctx.fill();
            }
            
            ctx.restore();
            
            // Draw bullets
            bullets.forEach(bullet => {
                ctx.fillStyle = bullet.isEnemy ? '#ff0000' : '#ffff00';
                ctx.beginPath();
                ctx.arc(bullet.x, bullet.y, 3, 0, Math.PI * 2);
                ctx.fill();
                ctx.fillStyle = 'white';
                ctx.beginPath();
                ctx.arc(bullet.x, bullet.y, 1.5, 0, Math.PI * 2);
                ctx.fill();
            });
            
            // Draw particles
            particles.forEach(particle => {
                ctx.fillStyle = particle.color;
                ctx.globalAlpha = particle.life / 20;
                ctx.beginPath();
                ctx.arc(particle.x, particle.y, 2, 0, Math.PI * 2);
                ctx.fill();
                ctx.globalAlpha = 1;
            });
            
            // Draw crosshair
            ctx.strokeStyle = 'white';
            ctx.lineWidth = 1;
            ctx.beginPath();
            ctx.arc(mouseX, mouseY, 15, 0, Math.PI * 2);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(mouseX - 20, mouseY);
            ctx.lineTo(mouseX - 10, mouseY);
            ctx.moveTo(mouseX + 10, mouseY);
            ctx.lineTo(mouseX + 20, mouseY);
            ctx.moveTo(mouseX, mouseY - 20);
            ctx.lineTo(mouseX, mouseY - 10);
            ctx.moveTo(mouseX, mouseY + 10);
            ctx.lineTo(mouseX, mouseY + 20);
            ctx.stroke();
        }
        
        // Update UI
        function updateUI() {
            document.getElementById('health').textContent = Math.max(0, Math.floor(player.health));
            document.getElementById('ammo').textContent = player.ammo;
            document.getElementById('kills').textContent = player.kills;
            document.getElementById('score').textContent = player.score;
            
            // Change health color based on value
            const healthElement = document.getElementById('health');
            if (player.health < 30) {
                healthElement.style.color = '#ff4444';
            } else if (player.health < 60) {
                healthElement.style.color = '#ffaa00';
            } else {
                healthElement.style.color = '#44ff44';
            }
        }
        
        // Game loop
        function gameLoop() {
            update();
            draw();
            requestAnimationFrame(gameLoop);
        }
        
        // Start game
        updateUI();
        gameLoop();
    </script>
</body>
</html>

Game Source: Mini Shooter - 2D Battle Game

Creator: CyberEagle48

Libraries: none

Complexity: complex (618 lines, 21.5 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: mini-shooter-2d-battle-game-cybereagle48" to link back to the original. Then publish at arcadelab.ai/publish.