Spaces:
Running
Running
import streamlit as st | |
from streamlit.components.v1 import html | |
# Define the HTML content with the Three.js game | |
game_html = """ | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Galaxxon - Three.js Arcade Game</title> | |
<style> | |
body { margin: 0; overflow: hidden; background: #000; } | |
canvas { display: block; width: 100%; height: 100%; } | |
</style> | |
</head> | |
<body> | |
<script type="module"> | |
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js'; | |
let scene, camera, renderer, player, enemies = [], bullets = [], obstacles = []; | |
let clock = new THREE.Clock(); | |
let moveLeft = false, moveRight = false, moveUp = false, moveDown = false, shoot = false; | |
function init() { | |
scene = new THREE.Scene(); | |
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
renderer = new THREE.WebGLRenderer(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
document.body.appendChild(renderer.domElement); | |
camera.position.set(0, 20, 30); | |
camera.lookAt(0, 0, 0); | |
const playerGeometry = new THREE.BoxGeometry(1, 1, 1); | |
const playerMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); | |
player = new THREE.Mesh(playerGeometry, playerMaterial); | |
player.position.y = -10; | |
scene.add(player); | |
spawnEnemies(); | |
spawnObstacles(); | |
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); | |
scene.add(ambientLight); | |
window.addEventListener('keydown', onKeyDown); | |
window.addEventListener('keyup', onKeyUp); | |
window.addEventListener('resize', onWindowResize); | |
animate(); | |
} | |
function spawnEnemies() { | |
const enemyGeometry = new THREE.BoxGeometry(0.8, 0.8, 0.8); | |
const enemyMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); | |
for (let i = 0; i < 5; i++) { | |
for (let j = 0; j < 3; j++) { | |
const enemy = new THREE.Mesh(enemyGeometry, enemyMaterial); | |
enemy.position.set(i * 2 - 4, j * 2 + 5, 0); | |
enemies.push(enemy); | |
scene.add(enemy); | |
} | |
} | |
} | |
function spawnObstacles() { | |
const obstacleGeometry = new THREE.BoxGeometry(2, 2, 2); | |
const obstacleMaterial = new THREE.MeshBasicMaterial({ color: 0x808080 }); | |
for (let i = 0; i < 3; i++) { | |
const obstacle = new THREE.Mesh(obstacleGeometry, obstacleMaterial); | |
obstacle.position.set(Math.random() * 20 - 10, Math.random() * 10, 0); | |
obstacles.push(obstacle); | |
scene.add(obstacle); | |
} | |
} | |
function onKeyDown(event) { | |
switch (event.code) { | |
case 'ArrowLeft': case 'KeyA': moveLeft = true; break; | |
case 'ArrowRight': case 'KeyD': moveRight = true; break; | |
case 'ArrowUp': case 'KeyW': moveUp = true; break; | |
case 'ArrowDown': case 'KeyS': moveDown = true; break; | |
case 'Space': shoot = true; break; | |
} | |
} | |
function onKeyUp(event) { | |
switch (event.code) { | |
case 'ArrowLeft': case 'KeyA': moveLeft = false; break; | |
case 'ArrowRight': case 'KeyD': moveRight = false; break; | |
case 'ArrowUp': case 'KeyW': moveUp = false; break; | |
case 'ArrowDown': case 'KeyS': moveDown = false; break; | |
case 'Space': shoot = false; break; | |
} | |
} | |
function updatePlayer(delta) { | |
const speed = 10; | |
if (moveLeft && player.position.x > -15) player.position.x -= speed * delta; | |
if (moveRight && player.position.x < 15) player.position.x += speed * delta; | |
if (moveUp && player.position.y < 0) player.position.y += speed * delta; | |
if (moveDown && player.position.y > -15) player.position.y -= speed * delta; | |
if (shoot && clock.getElapsedTime() > 0.2) { | |
shootBullet(); | |
clock = new THREE.Clock(); | |
} | |
} | |
function shootBullet() { | |
const bulletGeometry = new THREE.SphereGeometry(0.2, 8, 8); | |
const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 }); | |
const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial); | |
bullet.position.copy(player.position); | |
bullet.position.y += 1; | |
bullets.push(bullet); | |
scene.add(bullet); | |
} | |
function updateBullets(delta) { | |
const bulletSpeed = 20; | |
for (let i = bullets.length - 1; i >= 0; i--) { | |
bullets[i].position.y += bulletSpeed * delta; | |
if (bullets[i].position.y > 20) { | |
scene.remove(bullets[i]); | |
bullets.splice(i, 1); | |
} else { | |
checkCollisions(bullets[i], i); | |
} | |
} | |
} | |
function checkCollisions(bullet, bulletIndex) { | |
for (let i = enemies.length - 1; i >= 0; i--) { | |
if (bullet.position.distanceTo(enemies[i].position) < 1) { | |
scene.remove(enemies[i]); | |
enemies.splice(i, 1); | |
scene.remove(bullet); | |
bullets.splice(bulletIndex, 1); | |
break; | |
} | |
} | |
} | |
function updateEnemies(delta) { | |
const enemySpeed = 2; | |
enemies.forEach(enemy => { | |
enemy.position.y -= enemySpeed * delta; | |
if (enemy.position.y < -15) enemy.position.y = 15; | |
}); | |
} | |
function updateObstacles(delta) { | |
const obstacleSpeed = 1; | |
obstacles.forEach(obstacle => { | |
obstacle.position.y -= obstacleSpeed * delta; | |
if (obstacle.position.y < -15) obstacle.position.y = 15; | |
}); | |
} | |
function onWindowResize() { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
} | |
function animate() { | |
requestAnimationFrame(animate); | |
const delta = clock.getDelta(); | |
updatePlayer(delta); | |
updateBullets(delta); | |
updateEnemies(delta); | |
updateObstacles(delta); | |
renderer.render(scene, camera); | |
} | |
init(); | |
</script> | |
</body> | |
</html> | |
""" | |
# Streamlit app | |
st.title("Galaxxon - A Three.js Arcade Game") | |
st.write("Use WASD or Arrow Keys to move, Spacebar to shoot. Destroy red enemies and avoid gray obstacles!") | |
# Render the HTML game | |
html(game_html, height=600, scrolling=False) | |
st.write("Note: The game runs in your browser. Ensure you have an internet connection for Three.js to load.") |