gradio-game / app.py
akhaliq's picture
akhaliq HF staff
Update app.py
bb13fe7 verified
raw
history blame
9.85 kB
import gradio as gr
demo = gr.Blocks()
with demo:
gr.HTML("""<!DOCTYPE html>
<html>
<head>
<title>Maze Game Demo</title>
<style>
body { margin: 0; overflow: hidden; }
#debug {
position: absolute;
top: 10px;
left: 10px;
color: white;
background: rgba(0, 0, 0, 0.7);
padding: 5px;
font-family: monospace;
}
</style>
</head>
<body>
<div id="debug"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<script>
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Lighting
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(0, 20, 10);
scene.add(dirLight);
// OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// Maze generation
const gridSize = 16;
const cellSize = 4;
const maze = [];
const walls = [];
function createTexture(width, height, type) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (type === 'floor') {
ctx.fillStyle = '#8B4513';
ctx.fillRect(0, 0, width, height);
ctx.strokeStyle = '#654321';
ctx.lineWidth = 2;
ctx.strokeRect(0, 0, width, height);
} else {
ctx.fillStyle = '#666666';
ctx.fillRect(0, 0, width, height);
for (let i = 0; i < 10; i++) {
ctx.fillStyle = `rgba(0, 0, 0, ${Math.random() * 0.3})`;
ctx.fillRect(
Math.random() * width,
Math.random() * height,
Math.random() * width/4,
Math.random() * height/4
);
}
}
return new THREE.CanvasTexture(canvas);
}
const floorTexture = createTexture(256, 256, 'floor');
const wallTexture = createTexture(256, 256, 'wall');
// Generate maze
for (let x = 0; x < gridSize; x++) {
maze[x] = [];
for (let z = 0; z < gridSize; z++) {
const isBoundary = x === 0 || x === gridSize - 1 || z === 0 || z === gridSize - 1;
const isCenter = x >= 6 && x <= 9 && z >= 6 && z <= 9;
const isWall = isBoundary || (!isCenter && Math.random() < 0.15);
maze[x][z] = isWall ? 1 : 0;
if (isWall) {
const wallGeometry = new THREE.BoxGeometry(cellSize, cellSize, cellSize);
const wallMaterial = new THREE.MeshPhongMaterial({ map: wallTexture });
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(x * cellSize, cellSize/2, z * cellSize);
scene.add(wall);
wall.boundingBox = new THREE.Box3().setFromObject(wall);
walls.push(wall);
}
}
}
// Floor
const floorGeometry = new THREE.PlaneGeometry(gridSize * cellSize, gridSize * cellSize);
const floorMaterial = new THREE.MeshPhongMaterial({ map: floorTexture });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
scene.add(floor);
// Soldier setup
const loader = new THREE.GLTFLoader();
const soldiers = [];
const mixers = [];
function createSoldier(team, startX, startZ) {
loader.load('https://threejs.org/examples/models/gltf/Soldier.glb', (gltf) => {
const soldier = gltf.scene;
soldier.scale.set(2, 2, 2);
soldier.position.set(startX, 0, startZ);
soldier.team = team;
soldier.collisionRadius = 1;
soldier.speed = 0.1;
scene.add(soldier);
soldiers.push(soldier);
const mixer = new THREE.AnimationMixer(soldier);
mixers.push(mixer);
const idleAction = mixer.clipAction(gltf.animations[0]);
const runAction = mixer.clipAction(gltf.animations[3]);
soldier.actions = { idle: idleAction, run: runAction };
idleAction.play();
});
}
// Create two teams
createSoldier(1, 28, 28); // Team 1
createSoldier(2, 36, 36); // Team 2
camera.position.set(32, 20, 32);
// Movement controls
const keys = { w: false, a: false, s: false, d: false };
document.addEventListener('keydown', (e) => {
if (e.key in keys) keys[e.key] = true;
});
document.addEventListener('keyup', (e) => {
if (e.key in keys) keys[e.key] = false;
});
function checkCollision(newPosition, soldier) {
const soldierBox = new THREE.Sphere(newPosition, soldier.collisionRadius);
for (const wall of walls) {
if (soldierBox.intersectsBox(wall.boundingBox)) {
return true;
}
}
return false;
}
// Animation loop
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
soldiers.forEach((soldier, index) => {
if (!soldier) return;
const moveDirection = new THREE.Vector3();
if (index === 0) { // Only control first soldier for demo
const camForward = new THREE.Vector3();
camera.getWorldDirection(camForward);
camForward.y = 0;
camForward.normalize();
const camLeft = new THREE.Vector3(-camForward.z, 0, camForward.x);
if (keys.w) moveDirection.add(camForward);
if (keys.s) moveDirection.sub(camForward);
if (keys.a) moveDirection.add(camLeft);
if (keys.d) moveDirection.sub(camLeft);
}
if (moveDirection.length() > 0) {
moveDirection.normalize();
const newPosition = soldier.position.clone()
.add(moveDirection.multiplyScalar(soldier.speed));
if (!checkCollision(newPosition, soldier)) {
soldier.position.copy(newPosition);
const angle = Math.atan2(moveDirection.x, moveDirection.z);
soldier.rotation.y = angle + Math.PI;
soldier.actions.idle.fadeOut(0.2);
soldier.actions.run.fadeIn(0.2);
}
} else {
soldier.actions.run.fadeOut(0.2);
soldier.actions.idle.fadeIn(0.2);
}
if (index === 0) {
controls.target.copy(soldier.position);
controls.update();
// Update debug overlay
document.getElementById('debug').innerHTML = `
Position: ${soldier.position.x.toFixed(2)},
${soldier.position.y.toFixed(2)},
${soldier.position.z.toFixed(2)}<br>
Rotation: ${(soldier.rotation.y * 180 / Math.PI).toFixed(2)}°
`;
}
});
mixers.forEach(mixer => mixer.update(delta));
renderer.render(scene, camera);
}
// Responsive design
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
animate();
</script>
</body>
</html>""")
demo.launch()