pixel-cosmos-invaders / index.html
Amano13's picture
Create a retro-modern space invaders clone
7be8661 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pixel Cosmos Invaders</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Orbitron', sans-serif;
overflow: hidden;
background: #0a0a0a;
}
.game-container {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.vanta-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.game-title {
font-family: 'Press Start 2P', cursive;
font-size: 3rem;
background: linear-gradient(45deg, #ff00cc, #3333ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 30px rgba(255, 0, 204, 0.5);
margin-bottom: 2rem;
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
from {
text-shadow: 0 0 20px rgba(255, 0, 204, 0.5);
}
to {
text-shadow: 0 0 30px rgba(51, 51, 255, 0.8), 0 0 40px rgba(51, 51, 255, 0.6);
}
}
.game-canvas {
background: rgba(10, 10, 30, 0.8);
border: 3px solid #ff00cc;
border-radius: 10px;
box-shadow: 0 0 50px rgba(255, 0, 204, 0.3),
inset 0 0 50px rgba(51, 51, 255, 0.2);
}
.controls {
display: flex;
gap: 2rem;
margin-top: 2rem;
}
.btn {
font-family: 'Press Start 2P', cursive;
padding: 1rem 2rem;
background: linear-gradient(45deg, #ff00cc, #3333ff);
border: none;
border-radius: 8px;
color: white;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
font-size: 0.8rem;
box-shadow: 0 0 20px rgba(255, 0, 204, 0.4);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 0 30px rgba(51, 51, 255, 0.6);
}
.stats {
display: flex;
gap: 3rem;
margin-top: 1.5rem;
color: #00ffcc;
font-size: 1.2rem;
}
.stat-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.mobile-controls {
display: none;
margin-top: 2rem;
gap: 1rem;
}
.mobile-btn {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(45deg, #ff00cc, #3333ff);
border: none;
color: white;
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 20px rgba(255, 0, 204, 0.4);
}
@media (max-width: 768px) {
.game-title {
font-size: 1.5rem;
}
.controls {
flex-direction: column;
gap: 1rem;
}
.mobile-controls {
display: flex;
}
.stats {
flex-direction: column;
gap: 1rem;
font-size: 1rem;
}
}
</style>
</head>
<body>
<div class="game-container">
<div class="vanta-background" id="vanta-bg"></div>
<h1 class="game-title">PIXEL COSMOS INVADERS</h1>
<canvas id="gameCanvas" class="game-canvas" width="800" height="600"></canvas>
<div class="controls">
<button class="btn" onclick="startGame()">
<i data-feather="play"></i> START GAME
</button>
<button class="btn" onclick="pauseGame()">
<i data-feather="pause"></i> PAUSE
</button>
<button class="btn" onclick="resetGame()">
<i data-feather="refresh-cw"></i> RESTART
</button>
</div>
<div class="mobile-controls">
<button class="mobile-btn" onmousedown="moveLeft()" ontouchstart="moveLeft()">
<i data-feather="chevron-left"></i>
</button>
<button class="mobile-btn" onmousedown="shoot()" ontouchstart="shoot()">
<i data-feather="zap"></i>
</button>
<button class="mobile-btn" onmousedown="moveRight()" ontouchstart="moveRight()">
<i data-feather="chevron-right"></i>
</button>
</div>
<div class="stats">
<div class="stat-item">
<i data-feather="target"></i>
<span>SCORE: <span id="score">0</span></span>
</div>
<div class="stat-item">
<i data-feather="heart"></i>
<span>LIVES: <span id="lives">3</span></span>
</div>
<div class="stat-item">
<i data-feather="award"></i>
<span>LEVEL: <span id="level">1</span></span>
</div>
</div>
</div>
<script>
// Vanta.js background
VANTA.GLOBE({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0xff00cc,
backgroundColor: 0x0a0a0a,
size: 0.8
});
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let gameRunning = false;
let score = 0;
let lives = 3;
let level = 1;
// Player
const player = {
x: canvas.width / 2 - 25,
y: canvas.height - 50,
width: 50,
height: 30,
speed: 8,
movingLeft: false,
movingRight: false
};
// Bullets
const bullets = [];
const bulletSpeed = 10;
// Enemies
const enemies = [];
const enemyRows = 5;
const enemyCols = 10;
let enemySpeed = 1;
let enemyDirection = 1;
// Initialize enemies
function initEnemies() {
enemies.length = 0;
for (let row = 0; row < enemyRows; row++) {
for (let col = 0; col < enemyCols; col++) {
enemies.push({
x: col * 60 + 50,
y: row * 50 + 50,
width: 40,
height: 30,
alive: true
});
}
}
}
// Draw player
function drawPlayer() {
ctx.fillStyle = '#3333ff';
ctx.beginPath();
ctx.moveTo(player.x, player.y + player.height);
ctx.lineTo(player.x + player.width / 2, player.y);
ctx.lineTo(player.x + player.width, player.y + player.height);
ctx.closePath();
ctx.fill();
// Glow effect
ctx.shadowColor = '#3333ff';
ctx.shadowBlur = 20;
ctx.fill();
ctx.shadowBlur = 0;
}
// Draw bullets
function drawBullets() {
ctx.fillStyle = '#ff00cc';
bullets.forEach(bullet => {
ctx.beginPath();
ctx.arc(bullet.x, bullet.y, 5, 0, Math.PI * 2);
ctx.fill();
ctx.shadowColor = '#ff00cc';
ctx.shadowBlur = 15;
ctx.fill();
ctx.shadowBlur = 0;
});
}
// Draw enemies
function drawEnemies() {
ctx.fillStyle = '#00ffcc';
enemies.forEach(enemy => {
if (enemy.alive) {
ctx.beginPath();
ctx.arc(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2, 15, 0, Math.PI * 2);
ctx.fill();
ctx.shadowColor = '#00ffcc';
ctx.shadowBlur = 15;
ctx.fill();
ctx.shadowBlur = 0;
// Alien details
ctx.fillStyle = '#ff00cc';
ctx.beginPath();
ctx.arc(enemy.x + enemy.width / 2 - 8, enemy.y + enemy.height / 2 - 5, 3, 0, Math.PI * 2);
ctx.arc(enemy.x + enemy.width / 2 + 8, enemy.y + enemy.height / 2 - 5, 3, 0, Math.PI * 2);
ctx.fill();
}
});
}
// Update game state
function update() {
// Move player
if (player.movingLeft && player.x > 0) {
player.x -= player.speed;
}
if (player.movingRight && player.x < canvas.width - player.width) {
player.x += player.speed;
}
// Move bullets
for (let i = bullets.length - 1; i >= 0; i--) {
bullets[i].y -= bulletSpeed;
// Remove bullets that go off screen
if (bullets[i].y < 0) {
bullets.splice(i, 1);
}
}
// Move enemies
let edgeReached = false;
enemies.forEach(enemy => {
if (enemy.alive) {
enemy.x += enemySpeed * enemyDirection;
if (enemy.x <= 0 || enemy.x + enemy.width >= canvas.width) {
edgeReached = true;
}
}
});
// Change direction if edge reached
if (edgeReached) {
enemyDirection *= -1;
enemies.forEach(enemy => {
if (enemy.alive) {
enemy.y += 20;
}
});
}
// Check collisions
checkCollisions();
// Check level completion
if (enemies.every(enemy => !enemy.alive)) {
level++;
document.getElementById('level').textContent = level;
enemySpeed += 0.5;
initEnemies();
}
}
// Check collisions
function checkCollisions() {
// Bullet-enemy collisions
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (enemy.alive &&
bullet.x > enemy.x &&
bullet.x < enemy.x + enemy.width &&
bullet.y > enemy.y &&
bullet.y < enemy.y + enemy.height) {
enemy.alive = false;
bullets.splice(bulletIndex, 1);
score += 100;
document.getElementById('score').textContent = score;
}
});
});
// Enemy-player collisions
enemies.forEach(enemy => {
if (enemy.alive &&
enemy.y + enemy.height >= player.y &&
enemy.x < player.x + player.width &&
enemy.x + enemy.width > player.x) {
lives--;
document.getElementById('lives').textContent = lives;
enemy.alive = false;
if (lives <= 0) {
gameOver();
}
}
});
}
// Draw everything
function draw() {
// Clear canvas
ctx.fillStyle = 'rgba(10, 10, 30, 0.3)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPlayer();
drawBullets();
drawEnemies();
}
// Game loop
function gameLoop() {
if (gameRunning) {
update();
draw();
requestAnimationFrame(gameLoop);
}
}
// Start game
function startGame() {
if (!gameRunning) {
gameRunning = true;
gameLoop();
}
}
// Pause game
function pauseGame() {
gameRunning = !gameRunning;
if (gameRunning) {
gameLoop();
}
}
// Reset game
function resetGame() {
gameRunning = false;
score = 0;
lives = 3;
level = 1;
bullets.length = 0;
document.getElementById('score').textContent = score;
document.getElementById('lives').textContent = lives;
document.getElementById('level').textContent = level;
initEnemies();
player.x = canvas.width / 2 - 25;
player.y = canvas.height - 50;
}
// Game over
function gameOver() {
gameRunning = false;
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ff00cc';
ctx.font = '48px "Press Start 2P"';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2);
ctx.font = '24px "Press Start 2P"';
ctx.fillText(`FINAL SCORE: ${score}`, canvas.width / 2, canvas.height / 2 + 50);
}
// Shoot bullet
function shoot() {
if (gameRunning) {
bullets.push({
x: player.x + player.width / 2,
y: player.y
});
}
}
// Mobile controls
function moveLeft() {
player.movingLeft = true;
}
function moveRight() {
player.movingRight = true;
}
// Keyboard controls
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') player.movingLeft = true;
if (e.key === 'ArrowRight') player.movingRight = true;
if (e.key === ' ') shoot();
});
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowLeft') player.movingLeft = false;
if (e.key === 'ArrowRight') player.movingRight = false;
});
// Touch end events for mobile
document.addEventListener('touchend', () => {
player.movingLeft = false;
player.movingRight = false;
});
document.addEventListener('mouseup', () => {
player.movingLeft = false;
player.movingRight = false;
});
// Initialize game
initEnemies();
feather.replace();
// Responsive canvas
function resizeCanvas() {
const container = canvas.parentElement;
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
const scale = Math.min(containerWidth / 800, containerHeight / 700);
canvas.style.width = (800 * scale) + 'px';
canvas.style.height = (600 * scale) + 'px';
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
</script>
</body>
</html>