threejsgame / index.html
akhaliq's picture
akhaliq HF Staff
Update index.html
27433ae verified
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>📱 Cosmic Dodger</title>
<style>
* { margin: 0; padding: 0; touch-action: none; }
body { overflow: hidden; background: #000; }
#hud {
position: fixed;
top: 15px;
left: 15px;
color: #fff;
font-family: 'Courier New', monospace;
font-size: 26px;
text-shadow: 0 0 10px #00ffff;
z-index: 100;
}
#gameOver {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: #fff;
background: rgba(0,0,0,0.95);
padding: 25px;
border-radius: 15px;
border: 2px solid #ff0066;
width: 80%;
max-width: 300px;
}
#restart {
display: none;
position: fixed;
bottom: 25px;
left: 50%;
transform: translateX(-50%);
padding: 15px 35px;
background: #00ff88;
color: #000;
border: none;
border-radius: 25px;
font-size: 20px;
cursor: pointer;
font-weight: bold;
}
</style>
</head>
<body>
<div id="hud">
<div>SCORE: <span id="score">0</span></div>
<div>LIVES: <span id="lives">3</span></div>
</div>
<div id="gameOver">
<h1 style="color: #ff0066; font-size: 1.8em">GAME OVER</h1>
<p style="font-size: 1.2em">Score: <span id="finalscore">0</span></p>
</div>
<button id="restart" onclick="location.reload()">PLAY AGAIN</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const CONFIG = {
PLAYER_SPEED: 0.25,
ASTEROID_SPEED: 0.15,
PLAYER_SIZE: 0.6,
X_BOUNDS: 6,
Y_BOUNDS: 4
};
let score = 0, lives = 3, isGameOver = false;
let touchStartX = 0, currentX = 0;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Player (Flat Triangle - Mobile Friendly)
const player = new THREE.Mesh(
new THREE.ConeGeometry(CONFIG.PLAYER_SIZE, 1.2, 3),
new THREE.MeshStandardMaterial({
color: 0x00ff88,
emissive: 0x00ff88,
emissiveIntensity: 0.5
})
);
player.rotation.x = Math.PI/2;
player.position.z = 3;
scene.add(player);
// Player Boundary Markers
const boundaryMaterial = new THREE.MeshBasicMaterial({
color: 0x00ffff,
transparent: true,
opacity: 0.2
});
const leftBoundary = new THREE.Mesh(
new THREE.PlaneGeometry(0.5, 10),
boundaryMaterial
);
const rightBoundary = leftBoundary.clone();
leftBoundary.position.x = -CONFIG.X_BOUNDS;
rightBoundary.position.x = CONFIG.X_BOUNDS;
scene.add(leftBoundary, rightBoundary);
// Asteroids (More Visible)
function createAsteroid() {
const asteroid = new THREE.Mesh(
new THREE.IcosahedronGeometry(0.8),
new THREE.MeshStandardMaterial({
color: 0xff4444,
emissive: 0xff0000,
emissiveIntensity: 0.3,
wireframe: true
})
);
asteroid.position.set(
(Math.random() - 0.5) * 12,
(Math.random() - 0.5) * 7,
-40
);
return asteroid;
}
// Starfield (Optimized)
const stars = new THREE.Points(
new THREE.BufferGeometry().setFromPoints(
Array(800).fill().map(() => new THREE.Vector3(
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100
))
),
new THREE.PointsMaterial({
size: 0.2,
color: 0xffffff,
transparent: true,
opacity: 0.7
})
);
scene.add(stars);
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambientLight);
// Controls (Improved Mobile Handling)
function handleMove(x) {
if(isGameOver) return;
currentX = THREE.MathUtils.clamp(
(x - touchStartX) * 0.03,
-CONFIG.X_BOUNDS,
CONFIG.X_BOUNDS
);
player.position.x = currentX;
}
document.addEventListener('touchstart', e => {
e.preventDefault();
touchStartX = e.touches[0].clientX;
});
document.addEventListener('touchmove', e => handleMove(e.touches[0].clientX));
document.addEventListener('mousedown', e => touchStartX = e.clientX);
document.addEventListener('mousemove', e => e.buttons === 1 && handleMove(e.clientX));
// Game Loop
function animate() {
if(isGameOver) return;
// Spawn Logic
if(Math.random() < 0.03 + score/5000) {
const asteroid = createAsteroid();
scene.add(asteroid);
// Animate asteroid
(function move(obj) {
obj.position.z += CONFIG.ASTEROID_SPEED + score/2500;
obj.rotation.x += 0.02;
obj.rotation.y += 0.02;
if(obj.position.z > 5) scene.remove(obj);
else requestAnimationFrame(() => move(obj));
})(asteroid);
}
// Visual Updates
stars.rotation.y += 0.0003;
player.rotation.z = Math.sin(performance.now()*0.005) * 0.3;
// Collision Check
scene.children.forEach(obj => {
if(obj !== player && obj.position.z > 0) {
if(obj.position.distanceTo(player.position) < 1.2) {
lives--;
scene.remove(obj);
updateHUD();
if(lives <= 0) endGame();
}
}
});
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function updateHUD() {
document.getElementById('score').textContent = Math.floor(score);
document.getElementById('lives').textContent = lives;
score += 1 + Math.floor(score/1000);
}
function endGame() {
isGameOver = true;
document.getElementById('gameOver').style.display = 'block';
document.getElementById('restart').style.display = 'block';
document.getElementById('finalscore').textContent = Math.floor(score);
localStorage.setItem('highScore', Math.max(score, localStorage.getItem('highScore') || 0));
}
// Initial Setup
camera.position.z = 8;
animate();
updateHUD();
// Resize Handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Restart Handler
document.addEventListener('touchend', () => isGameOver && location.reload());
document.addEventListener('mouseup', () => isGameOver && location.reload());
</script>
</body>
</html>