UFO vs Spaceship Battle
by BraveMaker47569 lines19.6 KB
<!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.