Commit
·
0614dda
1
Parent(s):
6eac0ce
Improvements
Browse files- app.py +1 -4
- backend/game_models.py +1 -1
- backend/tools/scene_tools.py +2 -2
- frontend/game_viewer.html +34 -22
app.py
CHANGED
|
@@ -646,10 +646,7 @@ A big challenge in game development, as in other creative professions, is the "b
|
|
| 646 |
bot,
|
| 647 |
[chatbot, provider_state],
|
| 648 |
[chatbot, action_data],
|
| 649 |
-
)
|
| 650 |
-
|
| 651 |
-
# When action_data changes, send postMessage to iframe
|
| 652 |
-
action_data.change(
|
| 653 |
fn=lambda x: x,
|
| 654 |
inputs=[action_data],
|
| 655 |
outputs=[action_data],
|
|
|
|
| 646 |
bot,
|
| 647 |
[chatbot, provider_state],
|
| 648 |
[chatbot, action_data],
|
| 649 |
+
).then(
|
|
|
|
|
|
|
|
|
|
| 650 |
fn=lambda x: x,
|
| 651 |
inputs=[action_data],
|
| 652 |
outputs=[action_data],
|
backend/game_models.py
CHANGED
|
@@ -224,7 +224,7 @@ def create_scene(
|
|
| 224 |
lights: Optional[List[Dict[str, Any]]] = None,
|
| 225 |
environment: Optional[Dict[str, Any]] = None,
|
| 226 |
player: Optional[Dict[str, Any]] = None,
|
| 227 |
-
show_grid: bool =
|
| 228 |
grid_size: float = 100.0,
|
| 229 |
grid_divisions: int = 20,
|
| 230 |
tags: Optional[List[str]] = None
|
|
|
|
| 224 |
lights: Optional[List[Dict[str, Any]]] = None,
|
| 225 |
environment: Optional[Dict[str, Any]] = None,
|
| 226 |
player: Optional[Dict[str, Any]] = None,
|
| 227 |
+
show_grid: bool = False,
|
| 228 |
grid_size: float = 100.0,
|
| 229 |
grid_divisions: int = 20,
|
| 230 |
tags: Optional[List[str]] = None
|
backend/tools/scene_tools.py
CHANGED
|
@@ -434,8 +434,8 @@ def add_brick(
|
|
| 434 |
if not name:
|
| 435 |
name = f"{brick_type.replace('_', ' ').title()}"
|
| 436 |
|
| 437 |
-
# Model path relative to
|
| 438 |
-
model_path = f"
|
| 439 |
|
| 440 |
brick_obj = {
|
| 441 |
"id": brick_id,
|
|
|
|
| 434 |
if not name:
|
| 435 |
name = f"{brick_type.replace('_', ' ').title()}"
|
| 436 |
|
| 437 |
+
# Model path relative to models folder (static_base_url will be prepended by frontend)
|
| 438 |
+
model_path = f"kenney/brick_kit/{BRICK_TYPES[brick_type]}"
|
| 439 |
|
| 440 |
brick_obj = {
|
| 441 |
"id": brick_id,
|
frontend/game_viewer.html
CHANGED
|
@@ -504,8 +504,8 @@
|
|
| 504 |
canvas.height = size;
|
| 505 |
const ctx = canvas.getContext('2d');
|
| 506 |
|
| 507 |
-
// Background -
|
| 508 |
-
ctx.fillStyle = '#
|
| 509 |
ctx.fillRect(0, 0, size, size);
|
| 510 |
|
| 511 |
// Major grid lines - white
|
|
@@ -547,10 +547,8 @@
|
|
| 547 |
blueprintTexture.wrapT = THREE.RepeatWrapping;
|
| 548 |
blueprintTexture.repeat.set(WORLD_SIZE / 5, WORLD_SIZE / 5); // 5 units per texture tile
|
| 549 |
|
| 550 |
-
const groundMaterial = new THREE.
|
| 551 |
-
map: blueprintTexture
|
| 552 |
-
roughness: 0.9,
|
| 553 |
-
metalness: 0.0
|
| 554 |
});
|
| 555 |
const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
|
| 556 |
groundMesh.rotation.x = -Math.PI / 2; // Rotate to be horizontal
|
|
@@ -575,10 +573,8 @@
|
|
| 575 |
wallTexture.wrapS = THREE.RepeatWrapping;
|
| 576 |
wallTexture.wrapT = THREE.RepeatWrapping;
|
| 577 |
|
| 578 |
-
const wallMaterial = new THREE.
|
| 579 |
-
map: wallTexture
|
| 580 |
-
roughness: 0.9,
|
| 581 |
-
metalness: 0.0
|
| 582 |
});
|
| 583 |
|
| 584 |
// North/South walls (along X axis)
|
|
@@ -592,7 +588,7 @@
|
|
| 592 |
const northWallMesh = new THREE.Mesh(nsWallGeometry, nsWallMaterial);
|
| 593 |
northWallMesh.position.set(0, wallHeight / 2, WORLD_HALF);
|
| 594 |
northWallMesh.receiveShadow = true;
|
| 595 |
-
northWallMesh.castShadow =
|
| 596 |
scene.add(northWallMesh);
|
| 597 |
|
| 598 |
const northWallShape = new CANNON.Box(new CANNON.Vec3(WORLD_SIZE / 2, wallHeight / 2, wallThickness / 2));
|
|
@@ -606,7 +602,7 @@
|
|
| 606 |
const southWallMesh = new THREE.Mesh(nsWallGeometry, nsWallMaterial);
|
| 607 |
southWallMesh.position.set(0, wallHeight / 2, -WORLD_HALF);
|
| 608 |
southWallMesh.receiveShadow = true;
|
| 609 |
-
southWallMesh.castShadow =
|
| 610 |
scene.add(southWallMesh);
|
| 611 |
|
| 612 |
const southWallBody = new CANNON.Body({ mass: 0, material: defaultMaterial });
|
|
@@ -625,7 +621,7 @@
|
|
| 625 |
const eastWallMesh = new THREE.Mesh(ewWallGeometry, ewWallMaterial);
|
| 626 |
eastWallMesh.position.set(WORLD_HALF, wallHeight / 2, 0);
|
| 627 |
eastWallMesh.receiveShadow = true;
|
| 628 |
-
eastWallMesh.castShadow =
|
| 629 |
scene.add(eastWallMesh);
|
| 630 |
|
| 631 |
const eastWallShape = new CANNON.Box(new CANNON.Vec3(wallThickness / 2, wallHeight / 2, WORLD_SIZE / 2));
|
|
@@ -639,7 +635,7 @@
|
|
| 639 |
const westWallMesh = new THREE.Mesh(ewWallGeometry, ewWallMaterial);
|
| 640 |
westWallMesh.position.set(-WORLD_HALF, wallHeight / 2, 0);
|
| 641 |
westWallMesh.receiveShadow = true;
|
| 642 |
-
westWallMesh.castShadow =
|
| 643 |
scene.add(westWallMesh);
|
| 644 |
|
| 645 |
const westWallBody = new CANNON.Body({ mass: 0, material: defaultMaterial });
|
|
@@ -648,6 +644,19 @@
|
|
| 648 |
physicsWorld.addBody(westWallBody);
|
| 649 |
wallBodies.push(westWallBody);
|
| 650 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 651 |
// Create player physics body (capsule approximated with cylinder)
|
| 652 |
const playerShape = new CANNON.Cylinder(PLAYER_RADIUS, PLAYER_RADIUS, PLAYER_HEIGHT, 8);
|
| 653 |
playerBody = new CANNON.Body({
|
|
@@ -1333,13 +1342,13 @@
|
|
| 1333 |
break;
|
| 1334 |
// Player configuration actions
|
| 1335 |
case 'setPlayerSpeed':
|
| 1336 |
-
|
| 1337 |
break;
|
| 1338 |
case 'setJumpForce':
|
| 1339 |
-
|
| 1340 |
break;
|
| 1341 |
case 'setGravity':
|
| 1342 |
-
if (
|
| 1343 |
break;
|
| 1344 |
case 'setCameraFov':
|
| 1345 |
if (camera) {
|
|
@@ -1348,8 +1357,8 @@
|
|
| 1348 |
}
|
| 1349 |
break;
|
| 1350 |
case 'setMouseSensitivity':
|
| 1351 |
-
|
| 1352 |
-
if (data.invert_y !== undefined)
|
| 1353 |
break;
|
| 1354 |
case 'setPlayerDimensions':
|
| 1355 |
if (data.height) PLAYER_HEIGHT = data.height;
|
|
@@ -1911,9 +1920,10 @@
|
|
| 1911 |
|
| 1912 |
const spread = config.spread || 1.0;
|
| 1913 |
|
| 1914 |
-
// Use forward spawn position if no position specified or default
|
|
|
|
| 1915 |
let pos = config.position || { x: 0, y: 0, z: 0 };
|
| 1916 |
-
const isDefaultPosition = pos.x === 0 && pos.
|
| 1917 |
|
| 1918 |
if (config.localized !== false && isDefaultPosition) {
|
| 1919 |
// For localized effects, spawn in front of player (don't snap to ground for particles)
|
|
@@ -2258,7 +2268,9 @@
|
|
| 2258 |
return;
|
| 2259 |
}
|
| 2260 |
|
| 2261 |
-
|
|
|
|
|
|
|
| 2262 |
const position = brickData.position || { x: 0, y: 0, z: 0 };
|
| 2263 |
const rotation = brickData.rotation || { x: 0, y: 0, z: 0 };
|
| 2264 |
const color = new THREE.Color(brickData.material?.color || '#ff0000');
|
|
|
|
| 504 |
canvas.height = size;
|
| 505 |
const ctx = canvas.getContext('2d');
|
| 506 |
|
| 507 |
+
// Background - darker blue
|
| 508 |
+
ctx.fillStyle = '#1a5a9e';
|
| 509 |
ctx.fillRect(0, 0, size, size);
|
| 510 |
|
| 511 |
// Major grid lines - white
|
|
|
|
| 547 |
blueprintTexture.wrapT = THREE.RepeatWrapping;
|
| 548 |
blueprintTexture.repeat.set(WORLD_SIZE / 5, WORLD_SIZE / 5); // 5 units per texture tile
|
| 549 |
|
| 550 |
+
const groundMaterial = new THREE.MeshBasicMaterial({
|
| 551 |
+
map: blueprintTexture
|
|
|
|
|
|
|
| 552 |
});
|
| 553 |
const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
|
| 554 |
groundMesh.rotation.x = -Math.PI / 2; // Rotate to be horizontal
|
|
|
|
| 573 |
wallTexture.wrapS = THREE.RepeatWrapping;
|
| 574 |
wallTexture.wrapT = THREE.RepeatWrapping;
|
| 575 |
|
| 576 |
+
const wallMaterial = new THREE.MeshBasicMaterial({
|
| 577 |
+
map: wallTexture
|
|
|
|
|
|
|
| 578 |
});
|
| 579 |
|
| 580 |
// North/South walls (along X axis)
|
|
|
|
| 588 |
const northWallMesh = new THREE.Mesh(nsWallGeometry, nsWallMaterial);
|
| 589 |
northWallMesh.position.set(0, wallHeight / 2, WORLD_HALF);
|
| 590 |
northWallMesh.receiveShadow = true;
|
| 591 |
+
northWallMesh.castShadow = false;
|
| 592 |
scene.add(northWallMesh);
|
| 593 |
|
| 594 |
const northWallShape = new CANNON.Box(new CANNON.Vec3(WORLD_SIZE / 2, wallHeight / 2, wallThickness / 2));
|
|
|
|
| 602 |
const southWallMesh = new THREE.Mesh(nsWallGeometry, nsWallMaterial);
|
| 603 |
southWallMesh.position.set(0, wallHeight / 2, -WORLD_HALF);
|
| 604 |
southWallMesh.receiveShadow = true;
|
| 605 |
+
southWallMesh.castShadow = false;
|
| 606 |
scene.add(southWallMesh);
|
| 607 |
|
| 608 |
const southWallBody = new CANNON.Body({ mass: 0, material: defaultMaterial });
|
|
|
|
| 621 |
const eastWallMesh = new THREE.Mesh(ewWallGeometry, ewWallMaterial);
|
| 622 |
eastWallMesh.position.set(WORLD_HALF, wallHeight / 2, 0);
|
| 623 |
eastWallMesh.receiveShadow = true;
|
| 624 |
+
eastWallMesh.castShadow = false;
|
| 625 |
scene.add(eastWallMesh);
|
| 626 |
|
| 627 |
const eastWallShape = new CANNON.Box(new CANNON.Vec3(wallThickness / 2, wallHeight / 2, WORLD_SIZE / 2));
|
|
|
|
| 635 |
const westWallMesh = new THREE.Mesh(ewWallGeometry, ewWallMaterial);
|
| 636 |
westWallMesh.position.set(-WORLD_HALF, wallHeight / 2, 0);
|
| 637 |
westWallMesh.receiveShadow = true;
|
| 638 |
+
westWallMesh.castShadow = false;
|
| 639 |
scene.add(westWallMesh);
|
| 640 |
|
| 641 |
const westWallBody = new CANNON.Body({ mass: 0, material: defaultMaterial });
|
|
|
|
| 644 |
physicsWorld.addBody(westWallBody);
|
| 645 |
wallBodies.push(westWallBody);
|
| 646 |
|
| 647 |
+
// Ceiling - same material as floor/walls, positioned at wall height
|
| 648 |
+
const ceilingGeometry = new THREE.PlaneGeometry(WORLD_SIZE, WORLD_SIZE);
|
| 649 |
+
const ceilingMaterial = new THREE.MeshBasicMaterial({
|
| 650 |
+
map: blueprintTexture.clone()
|
| 651 |
+
});
|
| 652 |
+
ceilingMaterial.map.repeat.set(WORLD_SIZE / 5, WORLD_SIZE / 5);
|
| 653 |
+
const ceilingMesh = new THREE.Mesh(ceilingGeometry, ceilingMaterial);
|
| 654 |
+
ceilingMesh.rotation.x = Math.PI / 2; // Rotate to face downward
|
| 655 |
+
ceilingMesh.position.y = wallHeight;
|
| 656 |
+
ceilingMesh.receiveShadow = false;
|
| 657 |
+
ceilingMesh.castShadow = false;
|
| 658 |
+
scene.add(ceilingMesh);
|
| 659 |
+
|
| 660 |
// Create player physics body (capsule approximated with cylinder)
|
| 661 |
const playerShape = new CANNON.Cylinder(PLAYER_RADIUS, PLAYER_RADIUS, PLAYER_HEIGHT, 8);
|
| 662 |
playerBody = new CANNON.Body({
|
|
|
|
| 1342 |
break;
|
| 1343 |
// Player configuration actions
|
| 1344 |
case 'setPlayerSpeed':
|
| 1345 |
+
moveSpeed = data.walk_speed || 5.0;
|
| 1346 |
break;
|
| 1347 |
case 'setJumpForce':
|
| 1348 |
+
JUMP_FORCE = data.jump_force || 5.0;
|
| 1349 |
break;
|
| 1350 |
case 'setGravity':
|
| 1351 |
+
if (physicsWorld) physicsWorld.gravity.set(0, data.gravity || -9.82, 0);
|
| 1352 |
break;
|
| 1353 |
case 'setCameraFov':
|
| 1354 |
if (camera) {
|
|
|
|
| 1357 |
}
|
| 1358 |
break;
|
| 1359 |
case 'setMouseSensitivity':
|
| 1360 |
+
mouseSensitivity = data.sensitivity || 0.002;
|
| 1361 |
+
if (data.invert_y !== undefined) invertY = data.invert_y;
|
| 1362 |
break;
|
| 1363 |
case 'setPlayerDimensions':
|
| 1364 |
if (data.height) PLAYER_HEIGHT = data.height;
|
|
|
|
| 1920 |
|
| 1921 |
const spread = config.spread || 1.0;
|
| 1922 |
|
| 1923 |
+
// Use forward spawn position if no position specified or default position
|
| 1924 |
+
// Backend defaults to {0, 1, 0} when no position is provided
|
| 1925 |
let pos = config.position || { x: 0, y: 0, z: 0 };
|
| 1926 |
+
const isDefaultPosition = pos.x === 0 && pos.z === 0 && (pos.y === 0 || pos.y === 1);
|
| 1927 |
|
| 1928 |
if (config.localized !== false && isDefaultPosition) {
|
| 1929 |
// For localized effects, spawn in front of player (don't snap to ground for particles)
|
|
|
|
| 2268 |
return;
|
| 2269 |
}
|
| 2270 |
|
| 2271 |
+
// Use static_base_url from scene data for correct server
|
| 2272 |
+
const staticBase = sceneData.static_base_url || '';
|
| 2273 |
+
const modelPath = staticBase + brickData.model_path;
|
| 2274 |
const position = brickData.position || { x: 0, y: 0, z: 0 };
|
| 2275 |
const rotation = brickData.rotation || { x: 0, y: 0, z: 0 };
|
| 2276 |
const color = new THREE.Color(brickData.material?.color || '#ff0000');
|