Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,230 +1,235 @@
|
|
1 |
import gradio as gr
|
2 |
|
3 |
-
gr.
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
<
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
<body>
|
21 |
-
<div id="debug"></div>
|
22 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
23 |
-
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
|
24 |
-
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
|
25 |
-
<script>
|
26 |
-
// Scene setup
|
27 |
-
const scene = new THREE.Scene();
|
28 |
-
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
29 |
-
const renderer = new THREE.WebGLRenderer();
|
30 |
-
renderer.setSize(window.innerWidth, window.innerHeight);
|
31 |
-
document.body.appendChild(renderer.domElement);
|
32 |
-
|
33 |
-
// Lighting
|
34 |
-
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
|
35 |
-
scene.add(hemiLight);
|
36 |
-
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
37 |
-
dirLight.position.set(0, 20, 10);
|
38 |
-
scene.add(dirLight);
|
39 |
-
|
40 |
-
// OrbitControls
|
41 |
-
const controls = new THREE.OrbitControls(camera, renderer.domElement);
|
42 |
-
controls.enableDamping = true;
|
43 |
-
|
44 |
-
// Maze generation
|
45 |
-
const gridSize = 16;
|
46 |
-
const cellSize = 4;
|
47 |
-
const maze = [];
|
48 |
-
const walls = [];
|
49 |
-
|
50 |
-
function createTexture(width, height, type) {
|
51 |
-
const canvas = document.createElement('canvas');
|
52 |
-
canvas.width = width;
|
53 |
-
canvas.height = height;
|
54 |
-
const ctx = canvas.getContext('2d');
|
55 |
-
|
56 |
-
if (type === 'floor') {
|
57 |
-
ctx.fillStyle = '#8B4513';
|
58 |
-
ctx.fillRect(0, 0, width, height);
|
59 |
-
ctx.strokeStyle = '#654321';
|
60 |
-
ctx.lineWidth = 2;
|
61 |
-
ctx.strokeRect(0, 0, width, height);
|
62 |
-
} else {
|
63 |
-
ctx.fillStyle = '#666666';
|
64 |
-
ctx.fillRect(0, 0, width, height);
|
65 |
-
for (let i = 0; i < 10; i++) {
|
66 |
-
ctx.fillStyle = `rgba(0, 0, 0, ${Math.random() * 0.3})`;
|
67 |
-
ctx.fillRect(
|
68 |
-
Math.random() * width,
|
69 |
-
Math.random() * height,
|
70 |
-
Math.random() * width/4,
|
71 |
-
Math.random() * height/4
|
72 |
-
);
|
73 |
-
}
|
74 |
-
}
|
75 |
-
return new THREE.CanvasTexture(canvas);
|
76 |
-
}
|
77 |
-
|
78 |
-
const floorTexture = createTexture(256, 256, 'floor');
|
79 |
-
const wallTexture = createTexture(256, 256, 'wall');
|
80 |
-
|
81 |
-
// Generate maze
|
82 |
-
for (let x = 0; x < gridSize; x++) {
|
83 |
-
maze[x] = [];
|
84 |
-
for (let z = 0; z < gridSize; z++) {
|
85 |
-
const isBoundary = x === 0 || x === gridSize - 1 || z === 0 || z === gridSize - 1;
|
86 |
-
const isCenter = x >= 6 && x <= 9 && z >= 6 && z <= 9;
|
87 |
-
const isWall = isBoundary || (!isCenter && Math.random() < 0.15);
|
88 |
-
maze[x][z] = isWall ? 1 : 0;
|
89 |
-
|
90 |
-
if (isWall) {
|
91 |
-
const wallGeometry = new THREE.BoxGeometry(cellSize, cellSize, cellSize);
|
92 |
-
const wallMaterial = new THREE.MeshPhongMaterial({ map: wallTexture });
|
93 |
-
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
|
94 |
-
wall.position.set(x * cellSize, cellSize/2, z * cellSize);
|
95 |
-
scene.add(wall);
|
96 |
-
wall.boundingBox = new THREE.Box3().setFromObject(wall);
|
97 |
-
walls.push(wall);
|
98 |
-
}
|
99 |
}
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
});
|
150 |
-
|
151 |
-
function checkCollision(newPosition, soldier) {
|
152 |
-
const soldierBox = new THREE.Sphere(newPosition, soldier.collisionRadius);
|
153 |
-
for (const wall of walls) {
|
154 |
-
if (soldierBox.intersectsBox(wall.boundingBox)) {
|
155 |
-
return true;
|
156 |
}
|
|
|
157 |
}
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
if (keys.a) moveDirection.add(camLeft);
|
181 |
-
if (keys.d) moveDirection.sub(camLeft);
|
182 |
}
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
}
|
197 |
-
} else {
|
198 |
-
soldier.actions.run.fadeOut(0.2);
|
199 |
-
soldier.actions.idle.fadeIn(0.2);
|
200 |
-
}
|
201 |
-
|
202 |
-
if (index === 0) {
|
203 |
-
controls.target.copy(soldier.position);
|
204 |
-
controls.update();
|
205 |
-
|
206 |
-
// Update debug overlay
|
207 |
-
document.getElementById('debug').innerHTML = `
|
208 |
-
Position: ${soldier.position.x.toFixed(2)},
|
209 |
-
${soldier.position.y.toFixed(2)},
|
210 |
-
${soldier.position.z.toFixed(2)}<br>
|
211 |
-
Rotation: ${(soldier.rotation.y * 180 / Math.PI).toFixed(2)}°
|
212 |
-
`;
|
213 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
214 |
});
|
|
|
|
|
|
|
|
|
|
|
215 |
|
216 |
-
|
217 |
-
renderer.render(scene, camera);
|
218 |
-
}
|
219 |
-
|
220 |
-
// Responsive design
|
221 |
-
window.addEventListener('resize', () => {
|
222 |
-
camera.aspect = window.innerWidth / window.innerHeight;
|
223 |
-
camera.updateProjectionMatrix();
|
224 |
-
renderer.setSize(window.innerWidth, window.innerHeight);
|
225 |
-
});
|
226 |
-
|
227 |
-
animate();
|
228 |
-
</script>
|
229 |
-
</body>
|
230 |
-
</html>""").launch()
|
|
|
1 |
import gradio as gr
|
2 |
|
3 |
+
demo = gr.Blocks()
|
4 |
+
|
5 |
+
with demo:
|
6 |
+
gr.HTML("""<!DOCTYPE html>
|
7 |
+
<html>
|
8 |
+
<head>
|
9 |
+
<title>Maze Game Demo</title>
|
10 |
+
<style>
|
11 |
+
body { margin: 0; overflow: hidden; }
|
12 |
+
#debug {
|
13 |
+
position: absolute;
|
14 |
+
top: 10px;
|
15 |
+
left: 10px;
|
16 |
+
color: white;
|
17 |
+
background: rgba(0, 0, 0, 0.7);
|
18 |
+
padding: 5px;
|
19 |
+
font-family: monospace;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
}
|
21 |
+
</style>
|
22 |
+
</head>
|
23 |
+
<body>
|
24 |
+
<div id="debug"></div>
|
25 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
26 |
+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
|
27 |
+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
|
28 |
+
<script>
|
29 |
+
// Scene setup
|
30 |
+
const scene = new THREE.Scene();
|
31 |
+
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
32 |
+
const renderer = new THREE.WebGLRenderer();
|
33 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
34 |
+
document.body.appendChild(renderer.domElement);
|
35 |
+
|
36 |
+
// Lighting
|
37 |
+
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
|
38 |
+
scene.add(hemiLight);
|
39 |
+
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
40 |
+
dirLight.position.set(0, 20, 10);
|
41 |
+
scene.add(dirLight);
|
42 |
+
|
43 |
+
// OrbitControls
|
44 |
+
const controls = new THREE.OrbitControls(camera, renderer.domElement);
|
45 |
+
controls.enableDamping = true;
|
46 |
+
|
47 |
+
// Maze generation
|
48 |
+
const gridSize = 16;
|
49 |
+
const cellSize = 4;
|
50 |
+
const maze = [];
|
51 |
+
const walls = [];
|
52 |
+
|
53 |
+
function createTexture(width, height, type) {
|
54 |
+
const canvas = document.createElement('canvas');
|
55 |
+
canvas.width = width;
|
56 |
+
canvas.height = height;
|
57 |
+
const ctx = canvas.getContext('2d');
|
58 |
|
59 |
+
if (type === 'floor') {
|
60 |
+
ctx.fillStyle = '#8B4513';
|
61 |
+
ctx.fillRect(0, 0, width, height);
|
62 |
+
ctx.strokeStyle = '#654321';
|
63 |
+
ctx.lineWidth = 2;
|
64 |
+
ctx.strokeRect(0, 0, width, height);
|
65 |
+
} else {
|
66 |
+
ctx.fillStyle = '#666666';
|
67 |
+
ctx.fillRect(0, 0, width, height);
|
68 |
+
for (let i = 0; i < 10; i++) {
|
69 |
+
ctx.fillStyle = `rgba(0, 0, 0, ${Math.random() * 0.3})`;
|
70 |
+
ctx.fillRect(
|
71 |
+
Math.random() * width,
|
72 |
+
Math.random() * height,
|
73 |
+
Math.random() * width/4,
|
74 |
+
Math.random() * height/4
|
75 |
+
);
|
76 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
}
|
78 |
+
return new THREE.CanvasTexture(canvas);
|
79 |
}
|
80 |
+
|
81 |
+
const floorTexture = createTexture(256, 256, 'floor');
|
82 |
+
const wallTexture = createTexture(256, 256, 'wall');
|
83 |
+
|
84 |
+
// Generate maze
|
85 |
+
for (let x = 0; x < gridSize; x++) {
|
86 |
+
maze[x] = [];
|
87 |
+
for (let z = 0; z < gridSize; z++) {
|
88 |
+
const isBoundary = x === 0 || x === gridSize - 1 || z === 0 || z === gridSize - 1;
|
89 |
+
const isCenter = x >= 6 && x <= 9 && z >= 6 && z <= 9;
|
90 |
+
const isWall = isBoundary || (!isCenter && Math.random() < 0.15);
|
91 |
+
maze[x][z] = isWall ? 1 : 0;
|
92 |
+
|
93 |
+
if (isWall) {
|
94 |
+
const wallGeometry = new THREE.BoxGeometry(cellSize, cellSize, cellSize);
|
95 |
+
const wallMaterial = new THREE.MeshPhongMaterial({ map: wallTexture });
|
96 |
+
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
|
97 |
+
wall.position.set(x * cellSize, cellSize/2, z * cellSize);
|
98 |
+
scene.add(wall);
|
99 |
+
wall.boundingBox = new THREE.Box3().setFromObject(wall);
|
100 |
+
walls.push(wall);
|
101 |
+
}
|
|
|
|
|
102 |
}
|
103 |
+
}
|
104 |
+
|
105 |
+
// Floor
|
106 |
+
const floorGeometry = new THREE.PlaneGeometry(gridSize * cellSize, gridSize * cellSize);
|
107 |
+
const floorMaterial = new THREE.MeshPhongMaterial({ map: floorTexture });
|
108 |
+
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
|
109 |
+
floor.rotation.x = -Math.PI / 2;
|
110 |
+
scene.add(floor);
|
111 |
+
|
112 |
+
// Soldier setup
|
113 |
+
const loader = new THREE.GLTFLoader();
|
114 |
+
const soldiers = [];
|
115 |
+
const mixers = [];
|
116 |
+
|
117 |
+
function createSoldier(team, startX, startZ) {
|
118 |
+
loader.load('https://threejs.org/examples/models/gltf/Soldier.glb', (gltf) => {
|
119 |
+
const soldier = gltf.scene;
|
120 |
+
soldier.scale.set(2, 2, 2);
|
121 |
+
soldier.position.set(startX, 0, startZ);
|
122 |
+
soldier.team = team;
|
123 |
+
soldier.collisionRadius = 1;
|
124 |
+
soldier.speed = 0.1;
|
125 |
+
scene.add(soldier);
|
126 |
+
soldiers.push(soldier);
|
127 |
+
|
128 |
+
const mixer = new THREE.AnimationMixer(soldier);
|
129 |
+
mixers.push(mixer);
|
130 |
|
131 |
+
const idleAction = mixer.clipAction(gltf.animations[0]);
|
132 |
+
const runAction = mixer.clipAction(gltf.animations[3]);
|
133 |
+
|
134 |
+
soldier.actions = { idle: idleAction, run: runAction };
|
135 |
+
idleAction.play();
|
136 |
+
});
|
137 |
+
}
|
138 |
+
|
139 |
+
// Create two teams
|
140 |
+
createSoldier(1, 28, 28); // Team 1
|
141 |
+
createSoldier(2, 36, 36); // Team 2
|
142 |
+
|
143 |
+
camera.position.set(32, 20, 32);
|
144 |
+
|
145 |
+
// Movement controls
|
146 |
+
const keys = { w: false, a: false, s: false, d: false };
|
147 |
+
document.addEventListener('keydown', (e) => {
|
148 |
+
if (e.key in keys) keys[e.key] = true;
|
149 |
+
});
|
150 |
+
document.addEventListener('keyup', (e) => {
|
151 |
+
if (e.key in keys) keys[e.key] = false;
|
152 |
+
});
|
153 |
+
|
154 |
+
function checkCollision(newPosition, soldier) {
|
155 |
+
const soldierBox = new THREE.Sphere(newPosition, soldier.collisionRadius);
|
156 |
+
for (const wall of walls) {
|
157 |
+
if (soldierBox.intersectsBox(wall.boundingBox)) {
|
158 |
+
return true;
|
159 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
}
|
161 |
+
return false;
|
162 |
+
}
|
163 |
+
|
164 |
+
// Animation loop
|
165 |
+
const clock = new THREE.Clock();
|
166 |
+
function animate() {
|
167 |
+
requestAnimationFrame(animate);
|
168 |
+
const delta = clock.getDelta();
|
169 |
+
|
170 |
+
soldiers.forEach((soldier, index) => {
|
171 |
+
if (!soldier) return;
|
172 |
+
|
173 |
+
const moveDirection = new THREE.Vector3();
|
174 |
+
if (index === 0) { // Only control first soldier for demo
|
175 |
+
const camForward = new THREE.Vector3();
|
176 |
+
camera.getWorldDirection(camForward);
|
177 |
+
camForward.y = 0;
|
178 |
+
camForward.normalize();
|
179 |
+
const camLeft = new THREE.Vector3(-camForward.z, 0, camForward.x);
|
180 |
+
|
181 |
+
if (keys.w) moveDirection.add(camForward);
|
182 |
+
if (keys.s) moveDirection.sub(camForward);
|
183 |
+
if (keys.a) moveDirection.add(camLeft);
|
184 |
+
if (keys.d) moveDirection.sub(camLeft);
|
185 |
+
}
|
186 |
+
|
187 |
+
if (moveDirection.length() > 0) {
|
188 |
+
moveDirection.normalize();
|
189 |
+
const newPosition = soldier.position.clone()
|
190 |
+
.add(moveDirection.multiplyScalar(soldier.speed));
|
191 |
+
|
192 |
+
if (!checkCollision(newPosition, soldier)) {
|
193 |
+
soldier.position.copy(newPosition);
|
194 |
+
const angle = Math.atan2(moveDirection.x, moveDirection.z);
|
195 |
+
soldier.rotation.y = angle + Math.PI;
|
196 |
+
|
197 |
+
soldier.actions.idle.fadeOut(0.2);
|
198 |
+
soldier.actions.run.fadeIn(0.2);
|
199 |
+
}
|
200 |
+
} else {
|
201 |
+
soldier.actions.run.fadeOut(0.2);
|
202 |
+
soldier.actions.idle.fadeIn(0.2);
|
203 |
+
}
|
204 |
+
|
205 |
+
if (index === 0) {
|
206 |
+
controls.target.copy(soldier.position);
|
207 |
+
controls.update();
|
208 |
+
|
209 |
+
// Update debug overlay
|
210 |
+
document.getElementById('debug').innerHTML = `
|
211 |
+
Position: ${soldier.position.x.toFixed(2)},
|
212 |
+
${soldier.position.y.toFixed(2)},
|
213 |
+
${soldier.position.z.toFixed(2)}<br>
|
214 |
+
Rotation: ${(soldier.rotation.y * 180 / Math.PI).toFixed(2)}°
|
215 |
+
`;
|
216 |
+
}
|
217 |
+
});
|
218 |
+
|
219 |
+
mixers.forEach(mixer => mixer.update(delta));
|
220 |
+
renderer.render(scene, camera);
|
221 |
+
}
|
222 |
+
|
223 |
+
// Responsive design
|
224 |
+
window.addEventListener('resize', () => {
|
225 |
+
camera.aspect = window.innerWidth / window.innerHeight;
|
226 |
+
camera.updateProjectionMatrix();
|
227 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
228 |
});
|
229 |
+
|
230 |
+
animate();
|
231 |
+
</script>
|
232 |
+
</body>
|
233 |
+
</html>""")
|
234 |
|
235 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|