cosmic-clash / index.html
cristianoaredes's picture
Add 2 files
cd3fd43 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cosmic Clash - Space War Game</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cannon-es@0.19.0/dist/cannon-es.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.min.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
font-family: 'Orbitron', sans-serif;
}
#game-container {
position: relative;
width: 100vw;
height: 100vh;
}
#ui-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
color: white;
}
#start-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
#game-over-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
.btn {
pointer-events: auto;
background: linear-gradient(45deg, #4f46e5, #8b5cf6);
color: white;
border: none;
padding: 12px 24px;
font-size: 18px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
margin-top: 20px;
font-family: 'Orbitron', sans-serif;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(79, 70, 229, 0.3);
}
.title {
font-size: 4rem;
background: linear-gradient(45deg, #8b5cf6, #4f46e5);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 20px;
text-shadow: 0 0 10px rgba(139, 92, 246, 0.5);
}
.subtitle {
font-size: 1.5rem;
color: #a5b4fc;
margin-bottom: 40px;
}
.health-bar {
height: 20px;
background: rgba(255, 0, 0, 0.2);
border-radius: 10px;
overflow: hidden;
margin-bottom: 10px;
}
.health-fill {
height: 100%;
background: linear-gradient(90deg, #ef4444, #f59e0b);
transition: width 0.3s;
}
.score-display {
font-size: 1.5rem;
color: white;
text-shadow: 0 0 5px #4f46e5;
}
.notification {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.7);
color: white;
padding: 15px 30px;
border-radius: 8px;
font-size: 1.5rem;
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.pulse {
animation: pulse 2s infinite;
}
</style>
</head>
<body>
<div id="game-container">
<div id="ui-overlay">
<div class="absolute top-0 left-0 p-4">
<div class="health-bar w-64">
<div id="health-fill" class="health-fill" style="width: 100%"></div>
</div>
<div id="score" class="score-display">Score: 0</div>
</div>
<div id="notification" class="notification"></div>
</div>
<div id="start-screen">
<h1 class="title pulse">COSMIC CLASH</h1>
<p class="subtitle">Defend the galaxy against the alien invasion</p>
<button id="start-btn" class="btn">Start Mission</button>
</div>
<div id="game-over-screen">
<h1 class="title">MISSION FAILED</h1>
<p id="final-score" class="subtitle">Your score: 0</p>
<button id="restart-btn" class="btn">Try Again</button>
</div>
</div>
<script>
// Game variables
let scene, camera, renderer, world, physicsWorld;
let playerShip, enemyShips = [], projectiles = [], explosions = [], stars = [];
let score = 0;
let playerHealth = 100;
let gameStarted = false;
let lastEnemySpawnTime = 0;
let enemySpawnInterval = 2000; // ms
let keys = {};
// DOM elements
const startScreen = document.getElementById('start-screen');
const gameOverScreen = document.getElementById('game-over-screen');
const startBtn = document.getElementById('start-btn');
const restartBtn = document.getElementById('restart-btn');
const healthFill = document.getElementById('health-fill');
const scoreDisplay = document.getElementById('score');
const notification = document.getElementById('notification');
const finalScore = document.getElementById('final-score');
// Initialize the game
function init() {
// Create Three.js scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
scene.fog = new THREE.FogExp2(0x000000, 0.001);
// Create camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 30);
camera.lookAt(0, 0, 0);
// Create renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.getElementById('game-container').prepend(renderer.domElement);
// Create physics world
physicsWorld = new CANNON.World();
physicsWorld.gravity.set(0, 0, 0);
physicsWorld.broadphase = new CANNON.NaiveBroadphase();
physicsWorld.solver.iterations = 10;
// Add event listeners
window.addEventListener('resize', onWindowResize);
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
// Create game elements
createLights();
createStars();
createPlayerShip();
// Start animation loop
animate();
}
// Start the game
function startGame() {
gameStarted = true;
startScreen.style.display = 'none';
playerHealth = 100;
score = 0;
updateUI();
showNotification('ENGAGE!', 2000);
// Reset player position
playerShip.mesh.position.set(0, 0, 0);
playerShip.body.position.set(0, 0, 0);
playerShip.body.velocity.set(0, 0, 0);
// Clear any existing enemies and projectiles
enemyShips.forEach(enemy => {
scene.remove(enemy.mesh);
physicsWorld.removeBody(enemy.body);
});
projectiles.forEach(projectile => {
scene.remove(projectile.mesh);
physicsWorld.removeBody(projectile.body);
});
explosions.forEach(explosion => {
scene.remove(explosion);
});
enemyShips = [];
projectiles = [];
explosions = [];
}
// Restart the game
function restartGame() {
gameOverScreen.style.display = 'none';
startGame();
}
// Game over
function gameOver() {
gameStarted = false;
finalScore.textContent = `Your score: ${score}`;
gameOverScreen.style.display = 'flex';
}
// Create lights
function createLights() {
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
scene.add(directionalLight);
const pointLight = new THREE.PointLight(0x4f46e5, 2, 50);
pointLight.position.set(0, 0, 10);
scene.add(pointLight);
}
// Create starfield background
function createStars() {
const starGeometry = new THREE.BufferGeometry();
const starMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.1,
transparent: true,
opacity: 0.8
});
const starVertices = [];
for (let i = 0; i < 5000; i++) {
const x = (Math.random() - 0.5) * 2000;
const y = (Math.random() - 0.5) * 2000;
const z = (Math.random() - 0.5) * 2000;
starVertices.push(x, y, z);
}
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3));
const starField = new THREE.Points(starGeometry, starMaterial);
scene.add(starField);
}
// Create player ship
function createPlayerShip() {
const geometry = new THREE.ConeGeometry(2, 4, 4);
const material = new THREE.MeshPhongMaterial({
color: 0x4f46e5,
emissive: 0x4f46e5,
emissiveIntensity: 0.5,
shininess: 30
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, 0);
mesh.rotation.y = Math.PI;
mesh.castShadow = true;
scene.add(mesh);
// Create physics body
const shape = new CANNON.Box(new CANNON.Vec3(1.5, 1.5, 2));
const body = new CANNON.Body({
mass: 5,
shape: shape,
position: new CANNON.Vec3(0, 0, 0),
linearDamping: 0.5
});
physicsWorld.addBody(body);
// Add engine glow
const engineGlowGeometry = new THREE.ConeGeometry(1.5, 3, 4);
const engineGlowMaterial = new THREE.MeshBasicMaterial({
color: 0x8b5cf6,
transparent: true,
opacity: 0.7
});
const engineGlow = new THREE.Mesh(engineGlowGeometry, engineGlowMaterial);
engineGlow.position.z = -2;
mesh.add(engineGlow);
playerShip = {
mesh: mesh,
body: body,
engineGlow: engineGlow,
lastShot: 0,
shotDelay: 300
};
}
// Create enemy ship
function createEnemyShip() {
const x = (Math.random() - 0.5) * 40;
const y = (Math.random() - 0.5) * 20;
const z = -50;
const geometry = new THREE.OctahedronGeometry(1.5);
const material = new THREE.MeshPhongMaterial({
color: 0xef4444,
emissive: 0xef4444,
emissiveIntensity: 0.3,
shininess: 20
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(x, y, z);
mesh.castShadow = true;
scene.add(mesh);
// Create physics body
const shape = new CANNON.Sphere(1.5);
const body = new CANNON.Body({
mass: 3,
shape: shape,
position: new CANNON.Vec3(x, y, z),
linearDamping: 0.3
});
physicsWorld.addBody(body);
// Add red glow
const glowGeometry = new THREE.SphereGeometry(2);
const glowMaterial = new THREE.MeshBasicMaterial({
color: 0xef4444,
transparent: true,
opacity: 0.3
});
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
mesh.add(glow);
const enemy = {
mesh: mesh,
body: body,
glow: glow,
health: 100,
lastShot: 0,
shotDelay: 1500 + Math.random() * 1000
};
enemyShips.push(enemy);
return enemy;
}
// Create projectile
function createProjectile(position, velocity, isPlayer) {
const geometry = new THREE.SphereGeometry(0.2);
const material = new THREE.MeshPhongMaterial({
color: isPlayer ? 0x4f46e5 : 0xef4444,
emissive: isPlayer ? 0x4f46e5 : 0xef4444,
emissiveIntensity: 0.5
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.copy(position);
mesh.castShadow = true;
scene.add(mesh);
// Create physics body
const shape = new CANNON.Sphere(0.2);
const body = new CANNON.Body({
mass: 0.1,
shape: shape,
position: new CANNON.Vec3(position.x, position.y, position.z),
velocity: new CANNON.Vec3(velocity.x, velocity.y, velocity.z)
});
body.timeCreated = Date.now();
physicsWorld.addBody(body);
const projectile = {
mesh: mesh,
body: body,
isPlayer: isPlayer,
timeToLive: 3000 // ms
};
projectiles.push(projectile);
return projectile;
}
// Create explosion
function createExplosion(position, scale = 1) {
const particleCount = 100;
const particles = new THREE.BufferGeometry();
const particleMaterial = new THREE.PointsMaterial({
color: 0xffa500,
size: 0.2 * scale,
transparent: true,
opacity: 1,
blending: THREE.AdditiveBlending
});
const posArray = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount * 3; i++) {
posArray[i] = (Math.random() - 0.5) * 5 * scale;
}
particles.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
const particleSystem = new THREE.Points(particles, particleMaterial);
particleSystem.position.copy(position);
scene.add(particleSystem);
explosions.push({
mesh: particleSystem,
createdAt: Date.now(),
duration: 1000
});
return particleSystem;
}
// Handle keyboard input
function onKeyDown(event) {
keys[event.code] = true;
// Space to shoot
if (event.code === 'Space' && gameStarted) {
shoot();
}
}
function onKeyUp(event) {
keys[event.code] = false;
}
// Player shooting
function shoot() {
const now = Date.now();
if (now - playerShip.lastShot < playerShip.shotDelay) return;
playerShip.lastShot = now;
// Calculate projectile position and direction
const position = new THREE.Vector3().copy(playerShip.mesh.position);
position.z += 2; // Move slightly in front of the ship
const direction = new THREE.Vector3(0, 0, 1);
playerShip.mesh.localToWorld(direction);
direction.sub(playerShip.mesh.position).normalize().multiplyScalar(30);
// Create projectile
createProjectile(position, direction, true);
}
// Enemy shooting
function enemyShoot(enemy) {
const now = Date.now();
if (now - enemy.lastShot < enemy.shotDelay) return;
enemy.lastShot = now;
// Calculate direction to player
const direction = new THREE.Vector3().subVectors(
playerShip.mesh.position,
enemy.mesh.position
).normalize().multiplyScalar(20);
// Create projectile
createProjectile(enemy.mesh.position, direction, false);
}
// Update player movement based on input
function updatePlayerMovement(deltaTime) {
if (!gameStarted) return;
const speed = 15;
const rotationSpeed = 3;
const force = new CANNON.Vec3(0, 0, 0);
// Movement
if (keys['ArrowUp'] || keys['KeyW']) {
force.z -= speed;
playerShip.engineGlow.scale.z = 1.5;
} else {
playerShip.engineGlow.scale.z = 1;
}
if (keys['ArrowDown'] || keys['KeyS']) force.z += speed;
if (keys['ArrowLeft'] || keys['KeyA']) force.x -= speed;
if (keys['ArrowRight'] || keys['KeyD']) force.x += speed;
// Apply force
playerShip.body.force.copy(force);
// Rotation based on movement
const targetRotationX = (force.x / speed) * 0.2;
const targetRotationY = (force.z / speed) * 0.1;
playerShip.mesh.rotation.x = THREE.MathUtils.lerp(
playerShip.mesh.rotation.x,
targetRotationY,
0.1
);
playerShip.mesh.rotation.z = THREE.MathUtils.lerp(
playerShip.mesh.rotation.z,
-targetRotationX,
0.1
);
// Keep player within bounds
const maxX = 25;
const maxY = 15;
const maxZ = 10;
if (Math.abs(playerShip.body.position.x) > maxX) {
playerShip.body.position.x = Math.sign(playerShip.body.position.x) * maxX;
playerShip.body.velocity.x = 0;
}
if (Math.abs(playerShip.body.position.y) > maxY) {
playerShip.body.position.y = Math.sign(playerShip.body.position.y) * maxY;
playerShip.body.velocity.y = 0;
}
if (Math.abs(playerShip.body.position.z) > maxZ) {
playerShip.body.position.z = Math.sign(playerShip.body.position.z) * maxZ;
playerShip.body.velocity.z = 0;
}
}
// Update enemies
function updateEnemies(deltaTime) {
// Spawn new enemies
const now = Date.now();
if (now - lastEnemySpawnTime > enemySpawnInterval) {
lastEnemySpawnTime = now;
createEnemyShip();
// Gradually increase difficulty
enemySpawnInterval = Math.max(500, enemySpawnInterval - 10);
}
// Update existing enemies
for (let i = 0; i < enemyShips.length; i++) {
const enemy = enemyShips[i];
// Move toward player
const direction = new CANNON.Vec3().copy(playerShip.body.position).vsub(enemy.body.position);
direction.normalize();
direction.scale(5, direction);
enemy.body.force.copy(direction);
// Try to shoot at player
if (Math.random() < 0.02) {
enemyShoot(enemy);
}
// Update mesh position to match physics body
enemy.mesh.position.copy(enemy.body.position);
enemy.mesh.quaternion.copy(enemy.body.quaternion);
// Make enemy face player
enemy.mesh.lookAt(playerShip.mesh.position);
// Pulsing glow effect
enemy.glow.scale.setScalar(1 + Math.sin(now * 0.005) * 0.2);
}
}
// Update projectiles
function updateProjectiles(deltaTime) {
for (let i = projectiles.length - 1; i >= 0; i--) {
const projectile = projectiles[i];
// Update mesh position to match physics body
projectile.mesh.position.copy(projectile.body.position);
// Check time to live
if (Date.now() - projectile.body.timeCreated > projectile.timeToLive) {
removeProjectile(i);
continue;
}
// Check collisions
if (projectile.isPlayer) {
// Player projectile hitting enemies
for (let j = 0; j < enemyShips.length; j++) {
const enemy = enemyShips[j];
const distance = projectile.mesh.position.distanceTo(enemy.mesh.position);
if (distance < 2) { // Simple collision detection
enemy.health -= 25;
if (enemy.health <= 0) {
// Enemy destroyed
createExplosion(enemy.mesh.position, 2);
scene.remove(enemy.mesh);
physicsWorld.removeBody(enemy.body);
enemyShips.splice(j, 1);
score += 100;
updateUI();
showNotification('TARGET DESTROYED!', 1000);
} else {
// Enemy hit
createExplosion(projectile.mesh.position, 0.5);
score += 10;
updateUI();
}
removeProjectile(i);
break;
}
}
} else {
// Enemy projectile hitting player
const distance = projectile.mesh.position.distanceTo(playerShip.mesh.position);
if (distance < 2) { // Simple collision detection
playerHealth -= 10;
createExplosion(projectile.mesh.position, 0.5);
removeProjectile(i);
updateUI();
if (playerHealth <= 0) {
// Player destroyed
createExplosion(playerShip.mesh.position, 3);
gameOver();
} else {
showNotification('HULL DAMAGE!', 1000);
}
}
}
}
}
// Remove projectile
function removeProjectile(index) {
const projectile = projectiles[index];
scene.remove(projectile.mesh);
physicsWorld.removeBody(projectile.body);
projectiles.splice(index, 1);
}
// Update explosions
function updateExplosions() {
const now = Date.now();
for (let i = explosions.length - 1; i >= 0; i--) {
const explosion = explosions[i];
const elapsed = now - explosion.createdAt;
const progress = elapsed / explosion.duration;
if (progress >= 1) {
scene.remove(explosion.mesh);
explosions.splice(i, 1);
} else {
// Fade out
explosion.mesh.material.opacity = 1 - progress;
explosion.mesh.material.size = 0.2 * (1 - progress);
}
}
}
// Update UI
function updateUI() {
healthFill.style.width = `${playerHealth}%`;
scoreDisplay.textContent = `Score: ${score}`;
}
// Show notification
function showNotification(text, duration) {
notification.textContent = text;
notification.style.opacity = 1;
setTimeout(() => {
notification.style.opacity = 0;
}, duration);
}
// Handle window resize
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// Animation loop
function animate() {
requestAnimationFrame(animate);
const time = Date.now();
const deltaTime = 16; // Approximate for simplicity
if (gameStarted) {
// Update physics
physicsWorld.step(1/60);
// Update player ship mesh to match physics body
playerShip.mesh.position.copy(playerShip.body.position);
playerShip.mesh.quaternion.copy(playerShip.body.quaternion);
// Update game elements
updatePlayerMovement(deltaTime);
updateEnemies(deltaTime);
updateProjectiles(deltaTime);
updateExplosions();
// Update camera to follow player with slight delay
const targetCameraPos = new THREE.Vector3(
playerShip.mesh.position.x,
playerShip.mesh.position.y + 10,
playerShip.mesh.position.z + 30
);
camera.position.lerp(targetCameraPos, 0.05);
camera.lookAt(playerShip.mesh.position);
}
renderer.render(scene, camera);
}
// Initialize the game when the page loads
window.addEventListener('DOMContentLoaded', () => {
init();
// Add event listeners for buttons after initialization
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', restartGame);
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=cristianoaredes/cosmic-clash" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>