🎮ArcadeLab

UFO vs Spaceship Battle

by BraveMaker47
569 lines19.6 KB
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>UFO vs Spaceship Battle</title>
    <style>
        * {
            box-sizing: border-box;
            user-select: none;
            -webkit-user-select: none;
        }
        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background: #000;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            touch-action: none;
        }
        canvas {
            display: block;
            background: radial-gradient(circle at center, #0B0F19 0%, #020305 100%);
        }
        #ui {
            position: absolute;
            top: 20px;
            left: 20px;
            right: 20px;
            display: flex;
            justify-content: space-between;
            color: #fff;
            font-size: 18px;
            font-weight: bold;
            pointer-events: none;
            text-shadow: 0 0 5px rgba(0, 255, 255, 0.8);
            z-index: 10;
        }
        #controls {
            position: absolute;
            bottom: 20px;
            left: 0;
            right: 0;
            display: flex;
            justify-content: space-between;
            padding: 0 20px;
            z-index: 10;
            pointer-events: none;
        }
        .control-group {
            display: flex;
            gap: 15px;
            pointer-events: auto;
        }
        .btn {
            width: 70px;
            height: 70px;
            background: rgba(255, 255, 255, 0.1);
            border: 2px solid rgba(0, 255, 255, 0.5);
            border-radius: 50%;
            color: #0ff;
            font-size: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            backdrop-filter: blur(5px);
            box-shadow: 0 0 10px rgba(0, 255, 255, 0.2);
            outline: none;
        }
        .btn:active, .btn.pressed {
            background: rgba(0, 255, 255, 0.3);
            box-shadow: 0 0 20px rgba(0, 255, 255, 0.8);
            transform: scale(0.95);
        }
        #btn-fire {
            border-color: rgba(255, 50, 50, 0.8);
            color: #ff3333;
            box-shadow: 0 0 10px rgba(255, 50, 50, 0.3);
        }
        #btn-fire:active, #btn-fire.pressed {
            background: rgba(255, 50, 50, 0.3);
            box-shadow: 0 0 20px rgba(255, 50, 50, 0.8);
        }
        #start-screen {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(2, 3, 5, 0.9);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            z-index: 20;
            color: white;
            text-align: center;
            padding: 20px;
        }
        #start-screen h1 {
            font-size: 32px;
            color: #0ff;
            text-shadow: 0 0 15px rgba(0, 255, 255, 0.8);
            margin-bottom: 10px;
        }
        #start-screen p {
            color: #aaa;
            max-width: 400px;
            font-size: 14px;
            margin-bottom: 30px;
            line-height: 1.5;
        }
        .start-btn {
            background: linear-gradient(135deg, #00ffff 0%, #0088ff 100%);
            border: none;
            color: #000;
            padding: 15px 40px;
            font-size: 18px;
            font-weight: bold;
            border-radius: 30px;
            box-shadow: 0 0 20px rgba(0, 255, 255, 0.6);
            cursor: pointer;
        }
    </style>
</head>
<body>

    <div id="start-screen">
        <h1>UFO vs SPACESHIP</h1>
        <p>Defend Earth's airspace! Control your realistic interceptor ship, dodge alien UFO lasers, and destroy the invasion fleet.</p>
        <button class="start-btn" onclick="startGame()">START MISSION</button>
    </div>

    <div id="ui">
        <div>SCORE: <span id="score">0</span></div>
        <div>SHIELD: <span id="health">100</span>%</div>
    </div>

    <canvas id="gameCanvas"></canvas>

    <div id="controls">
        <div class="control-group">
            <button class="btn" id="btn-left">◀</button>
            <button class="btn" id="btn-right">▶</button>
        </div>
        <div class="control-group">
            <button class="btn" id="btn-fire">🔥</button>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');

        // Resize Canvas
        function resize() {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        }
        window.addEventListener('resize', resize);
        resize();

        // Audio System (Web Audio API)
        let audioCtx = null;
        function initAudio() {
            if (!audioCtx) {
                audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            }
        }

        function playSound(type) {
            if (!audioCtx) return;
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.connect(gain);
            gain.connect(audioCtx.destination);

            const now = audioCtx.currentTime;

            if (type === 'laser') {
                osc.type = 'sawtooth';
                osc.frequency.setValueAtTime(800, now);
                osc.frequency.exponentialRampToValueAtTime(100, now + 0.15);
                gain.gain.setValueAtTime(0.15, now);
                gain.gain.linearRampToValueAtTime(0.01, now + 0.15);
                osc.start(now);
                osc.stop(now + 0.15);
            } else if (type === 'ufo-laser') {
                osc.type = 'sine';
                osc.frequency.setValueAtTime(400, now);
                osc.frequency.exponentialRampToValueAtTime(150, now + 0.2);
                gain.gain.setValueAtTime(0.1, now);
                gain.gain.linearRampToValueAtTime(0.01, now + 0.2);
                osc.start(now);
                osc.stop(now + 0.2);
            } else if (type === 'explosion') {
                osc.type = 'triangle';
                osc.frequency.setValueAtTime(120, now);
                osc.frequency.linearRampToValueAtTime(30, now + 0.4);
                gain.gain.setValueAtTime(0.3, now);
                gain.gain.linearRampToValueAtTime(0.01, now + 0.4);
                osc.start(now);
                osc.stop(now + 0.4);
            }
        }

        // Game Variables
        let gameRunning = false;
        let score = 0;
        let health = 100;
        let stars = [];

        for (let i = 0; i < 100; i++) {
            stars.push({
                x: Math.random() * canvas.width,
                y: Math.random() * canvas.height,
                size: Math.random() * 2,
                speed: Math.random() * 2 + 0.5
            });
        }

        // Player (Realistic Interceptor Spaceship)
        const player = {
            x: canvas.width / 2 - 25,
            y: canvas.height - 120,
            width: 50,
            height: 70,
            speed: 7,
            dx: 0
        };

        let bullets = [];
        let ufos = [];
        let ufoBullets = [];
        let particles = [];

        // Input Tracking
        const keys = { left: false, right: false, fire: false };

        document.getElementById('btn-left').addEventListener('touchstart', () => keys.left = true);
        document.getElementById('btn-left').addEventListener('touchend', () => keys.left = false);
        document.getElementById('btn-left').addEventListener('mousedown', () => keys.left = true);
        document.getElementById('btn-left').addEventListener('mouseup', () => keys.left = false);

        document.getElementById('btn-right').addEventListener('touchstart', () => keys.right = true);
        document.getElementById('btn-right').addEventListener('touchend', () => keys.right = false);
        document.getElementById('btn-right').addEventListener('mousedown', () => keys.right = true);
        document.getElementById('btn-right').addEventListener('mouseup', () => keys.right = false);

        const fireBtn = document.getElementById('btn-fire');
        fireBtn.addEventListener('touchstart', () => { if(!keys.fire) shoot(); keys.fire = true; });
        fireBtn.addEventListener('touchend', () => keys.fire = false);
        fireBtn.addEventListener('mousedown', () => { if(!keys.fire) shoot(); keys.fire = true; });
        fireBtn.addEventListener('mouseup', () => keys.fire = false);

        window.addEventListener('keydown', e => {
            if (e.key === 'ArrowLeft' || e.key === 'a') keys.left = true;
            if (e.key === 'ArrowRight' || e.key === 'd') keys.right = true;
            if (e.key === ' ' || e.key === 'ArrowUp') { if(!keys.fire) shoot(); keys.fire = true; }
        });
        window.addEventListener('keyup', e => {
            if (e.key === 'ArrowLeft' || e.key === 'a') keys.left = false;
            if (e.key === 'ArrowRight' || e.key === 'd') keys.right = false;
            if (e.key === ' ' || e.key === 'ArrowUp') keys.fire = false;
        });

        function shoot() {
            if (!gameRunning) return;
            bullets.push({ x: player.x + player.width / 2 - 3, y: player.y, width: 6, height: 18, speed: 12 });
            playSound('laser');
        }

        function spawnUfo() {
            if (!gameRunning) return;
            const width = 60;
            const height = 40;
            const x = Math.random() * (canvas.width - width - 40) + 20;
            ufos.push({
                x: x,
                y: -50,
                width: width,
                height: height,
                speed: Math.random() * 1.5 + 1.0,
                health: 2,
                shootTimer: Math.random() * 60
            });
        }

        function createExplosion(x, y) {
            playSound('explosion');
            for (let i = 0; i < 20; i++) {
                particles.push({
                    x: x,
                    y: y,
                    vx: (Math.random() - 0.5) * 6,
                    vy: (Math.random() - 0.5) * 6,
                    radius: Math.random() * 3 + 1,
                    color: Math.random() > 0.5 ? '#ff3300' : '#ffcc00',
                    life: 30
                });
            }
        }

        function startGame() {
            initAudio();
            document.getElementById('start-screen').style.display = 'none';
            score = 0;
            health = 100;
            bullets = [];
            ufos = [];
            ufoBullets = [];
            particles = [];
            player.x = canvas.width / 2 - player.width / 2;
            gameRunning = true;
            loop();
        }

        let ufoSpawnInterval = 0;

        function update() {
            // Background Stars
            stars.forEach(star => {
                star.y += star.speed;
                if (star.y > canvas.height) star.y = 0;
            });

            // Player Movement
            if (keys.left && player.x > 10) player.x -= player.speed;
            if (keys.right && player.x < canvas.width - player.width - 10) player.x += player.speed;

            // Bullets Update
            bullets.forEach((b, index) => {
                b.y -= b.speed;
                if (b.y < 0) bullets.splice(index, 1);
            });

            // UFO Bullets Update
            ufoBullets.forEach((ub, index) => {
                ub.y += ub.speed;
                // Collision with player
                if (ub.x < player.x + player.width && ub.x + ub.width > player.x &&
                    ub.y < player.y + player.height && ub.y + ub.height > player.y) {
                    health -= 15;
                    ufoBullets.splice(index, 1);
                    createExplosion(ub.x, ub.y);
                    if (health <= 0) {
                        gameRunning = false;
                        document.getElementById('start-screen').style.display = 'flex';
                        document.querySelector('#start-screen h1').innerText = "MISSION FAILED";
                        document.querySelector('#start-screen p').innerText = "Your interceptor was destroyed. Final Score: " + score;
                    }
                }
                if (ub.y > canvas.height) ufoBullets.splice(index, 1);
            });

            // UFOs Update & Spawning
            ufoSpawnInterval++;
            if (ufoSpawnInterval > 70) {
                spawnUfo();
                ufoSpawnInterval = 0;
            }

            ufos.forEach((ufo, uIndex) => {
                ufo.y += ufo.speed;
                
                // UFO Shooting
                ufo.shootTimer++;
                if (ufo.shootTimer > 90) {
                    ufoBullets.push({ x: ufo.x + ufo.width/2 - 3, y: ufo.y + ufo.height, width: 6, height: 14, speed: 6 });
                    playSound('ufo-laser');
                    ufo.shootTimer = 0;
                }

                // Check collision with player bullets
                bullets.forEach((b, bIndex) => {
                    if (b.x < ufo.x + ufo.width && b.x + b.width > ufo.x &&
                        b.y < ufo.y + ufo.height && b.y + b.height > ufo.y) {
                        ufo.health--;
                        bullets.splice(bIndex, 1);
                        if (ufo.health <= 0) {
                            createExplosion(ufo.x + ufo.width/2, ufo.y + ufo.height/2);
                            ufos.splice(uIndex, 1);
                            score += 100;
                        }
                    }
                });

                // If UFO passes player
                if (ufo.y > canvas.height) {
                    ufos.splice(uIndex, 1);
                    health -= 10;
                    if (health <= 0) {
                        gameRunning = false;
                        document.getElementById('start-screen').style.display = 'flex';
                        document.querySelector('#start-screen h1').innerText = "MISSION FAILED";
                        document.querySelector('#start-screen p').innerText = "Aliens breached defense! Final Score: " + score;
                    }
                }
            });

            // Particles Update
            particles.forEach((p, index) => {
                p.x += p.vx;
                p.y += p.vy;
                p.life--;
                if (p.life <= 0) particles.splice(index, 1);
            });

            // Update UI
            document.getElementById('score').innerText = score;
            document.getElementById('health').innerText = Math.max(0, health);
        }

        function drawRealisticSpaceship(x, y, w, h) {
            ctx.save();
            ctx.translate(x + w/2, y + h/2);

            // Engine Flame Effect
            ctx.fillStyle = '#00ffff';
            ctx.shadowColor = '#00ffff';
            ctx.shadowBlur = 15;
            ctx.beginPath();
            ctx.moveTo(-8, h/2);
            ctx.lineTo(0, h/2 + 15 + Math.random() * 10);
            ctx.lineTo(8, h/2);
            ctx.closePath();
            ctx.fill();

            // Main Fuselage (Metallic Body)
            let grad = ctx.createLinearGradient(-w/2, 0, w/2, 0);
            grad.addColorStop(0, '#3a4454');
            grad.addColorStop(0.5, '#8da4c4');
            grad.addColorStop(1, '#2c3542');
            ctx.fillStyle = grad;
            ctx.shadowBlur = 0;
            ctx.beginPath();
            ctx.moveTo(0, -h/2);
            ctx.lineTo(w/2, h/3);
            ctx.lineTo(w/4, h/2);
            ctx.lineTo(-w/4, h/2);
            ctx.lineTo(-w/2, h/3);
            ctx.closePath();
            ctx.fill();
            ctx.strokeStyle = '#111';
            ctx.stroke();

            // Cockpit Glass (Glowing Cyan)
            ctx.fillStyle = '#00e5ff';
            ctx.shadowColor = '#00e5ff';
            ctx.shadowBlur = 10;
            ctx.beginPath();
            ctx.ellipse(0, -h/6, 8, 20, 0, 0, Math.PI * 2);
            ctx.fill();

            // Wings Details
            ctx.fillStyle = '#1f2630';
            ctx.fillRect(-w/2 - 4, 10, 8, 20);
            ctx.fillRect(w/2 - 4, 10, 8, 20);

            ctx.restore();
        }

        function drawRealisticUFO(x, y, w, h) {
            ctx.save();
            ctx.translate(x + w/2, y + h/2);

            // Glowing Alien Beam/Field below UFO
            let beamGrad = ctx.createLinearGradient(0, 0, 0, 40);
            beamGrad.addColorStop(0, 'rgba(50, 255, 50, 0.4)');
            beamGrad.addColorStop(1, 'rgba(50, 255, 50, 0)');
            ctx.fillStyle = beamGrad;
            ctx.beginPath();
            ctx.moveTo(-w/3, h/2);
            ctx.lineTo(w/3, h/2);
            ctx.lineTo(w/2, h/2 + 40);
            ctx.lineTo(-w/2, h/2 + 40);
            ctx.closePath();
            ctx.fill();

            // Metallic Saucer Body
            let saucerGrad = ctx.createRadialGradient(0, 0, 5, 0, 0, w/2);
            saucerGrad.addColorStop(0, '#b0c4de');
            saucerGrad.addColorStop(0.6, '#4682b4');
            saucerGrad.addColorStop(1, '#191970');
            ctx.fillStyle = saucerGrad;
            ctx.shadowColor = '#00ffcc';
            ctx.shadowBlur = 10;
            ctx.beginPath();
            ctx.ellipse(0, 0, w/2, h/3, 0, 0, Math.PI * 2);
            ctx.fill();
            ctx.strokeStyle = '#00ffff';
            ctx.lineWidth = 1.5;
            ctx.stroke();

            // Alien Dome (Glass Cockpit)
            let domeGrad = ctx.createRadialGradient(-3, -8, 2, 0, -5, w/4);
            domeGrad.addColorStop(0, 'rgba(200, 255, 255, 0.9)');
            domeGrad.addColorStop(0.5, 'rgba(0, 255, 128, 0.6)');
            domeGrad.addColorStop(1, 'rgba(0, 100, 50, 0.4)');
            ctx.fillStyle = domeGrad;
            ctx.beginPath();
            ctx.arc(0, -2, w/4, Math.PI, 0, false);
            ctx.closePath();
            ctx.fill();

            // Lights under UFO
            for (let i = -2; i <= 2; i++) {
                ctx.fillStyle = (Math.floor(Date.now() / 150) + i) % 2 === 0 ? '#ff0055' : '#00ffff';
                ctx.beginPath();
                ctx.arc(i * 10, h/3 - 4, 2.5, 0, Math.PI * 2);
                ctx.fill();
            }

            ctx.restore();
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // Draw Stars
            ctx.fillStyle = '#ffffff';
            stars.forEach(star => {
                ctx.fillRect(star.x, star.y, star.size, star.size);
            });

            // Draw Bullets (Laser beams)
            ctx.fillStyle = '#00ffff';
            ctx.shadowColor = '#00ffff';
            ctx.shadowBlur = 10;
            bullets.forEach(b => {
                ctx.fillRect(b.x, b.y, b.width, b.height);
            });

            // Draw UFO Bullets
            ctx.fillStyle = '#ff3333';
            ctx.shadowColor = '#ff3333';
            ufoBullets.forEach(ub => {
                ctx.fillRect(ub.x, ub.y, ub.width, ub.height);
            });
            ctx.shadowBlur = 0; // Reset shadow

            // Draw Player Spaceship
            drawRealisticSpaceship(player.x, player.y, player.width, player.height);

            // Draw UFOs
            ufos.forEach(ufo => {
                drawRealisticUFO(ufo.x, ufo.y, ufo.width, ufo.height);
            });

            // Draw Particles (Explosions)
            particles.forEach(p => {
                ctx.fillStyle = p.color;
                ctx.beginPath();
                ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
                ctx.fill();
            });
        }

        function loop() {
            if (!gameRunning) return;
            update();
            draw();
            requestAnimationFrame(loop);
        }
    </script>
</body>
</html>

Game Source: UFO vs Spaceship Battle

Creator: BraveMaker47

Libraries: none

Complexity: complex (569 lines, 19.6 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: ufo-vs-spaceship-battle-bravemaker47" to link back to the original. Then publish at arcadelab.ai/publish.