BLOBBO/src/game.ts
type-two 969f602dd4 Foundation: vite+ts+three+rapier scaffold, frozen contracts, lane docs A-E
Fixed-step world runtime, event bus wire protocol between lanes, integration
seam (game.ts) with placeholder ball. Lanes A/B/C own disjoint dirs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 19:55:43 +10:00

57 lines
2.0 KiB
TypeScript

/**
* INTEGRATION SEAM — owned by integration, not by lanes.
* Composes lane modules into the playable slice. While lanes are in flight this
* boots a placeholder ball on a flat floor so `npm run dev` always shows life.
*/
import * as THREE from 'three'
import type { World } from './contracts'
export function installGame(world: World) {
const { scene, physics, rapier } = world
// floor
const floor = new THREE.Mesh(
new THREE.BoxGeometry(60, 1, 60),
new THREE.MeshStandardMaterial({ color: '#e8d9b8' }))
floor.position.y = -0.5
floor.receiveShadow = true
scene.add(floor)
physics.createCollider(rapier.ColliderDesc.cuboid(30, 0.5, 30)
.setTranslation(0, -0.5, 0))
// placeholder blob-ball (replaced by lane A's createBlob at integration)
const ball = new THREE.Mesh(
new THREE.SphereGeometry(0.6, 32, 24),
new THREE.MeshStandardMaterial({ color: '#F5F5F7', roughness: 0.35 }))
ball.castShadow = true
scene.add(ball)
const body = physics.createRigidBody(
rapier.RigidBodyDesc.dynamic().setTranslation(0, 3, 0))
physics.createCollider(
rapier.ColliderDesc.ball(0.6).setRestitution(0.4).setFriction(1.0), body)
const keys = new Set<string>()
addEventListener('keydown', (e) => keys.add(e.code))
addEventListener('keyup', (e) => keys.delete(e.code))
world.addSystem({
update() {
const f = 18
const impulse = { x: 0, y: 0, z: 0 }
if (keys.has('KeyW') || keys.has('ArrowUp')) impulse.z -= f
if (keys.has('KeyS') || keys.has('ArrowDown')) impulse.z += f
if (keys.has('KeyA') || keys.has('ArrowLeft')) impulse.x -= f
if (keys.has('KeyD') || keys.has('ArrowRight')) impulse.x += f
body.applyImpulse({ x: impulse.x / 60, y: 0, z: impulse.z / 60 }, true)
const p = body.translation()
ball.position.set(p.x, p.y, p.z)
const q = body.rotation()
ball.quaternion.set(q.x, q.y, q.z, q.w)
},
})
world.onFrame(() => {
world.camera.lookAt(ball.position)
})
}