feat(seesaw): teetery pre-tilted tipping-plank in the see-saw lab
Best-shot "teetery" tuning (John's call; fine-tune the feel later). The lab see-saw now rests PRE-TILTED (~9° down toward the exit): you roll off a raised deck onto its high end and down the plank, your weight pitches it steeper as you cross the pivot, and you drop off the low end to the floor. Pre-tilt makes the teeter read; a strong fan whose beam covers the whole plank carries the blob over the pivot hump; the downhill line keeps it from wedging. createSeeSaw now snaps the plank back to its rest tilt on race:respawn (a free-tipping plank has no restoring force, so it would otherwise stay wherever it settled and stop being boardable). Verified: rests -8.6deg, pitches to -10.8deg mid-crossing, clears the pivot and drops off the far end. Still lab-only (?course=seesaw), not in a shipped course. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
85c29111fa
commit
2cf2d15623
@ -1,33 +1,42 @@
|
||||
/**
|
||||
* SEE-SAW LAB — a throwaway test course (?course=seesaw) for tuning the see-saw
|
||||
* obstacle in isolation, away from the noise of a real level.
|
||||
* SEE-SAW LAB — a test course (?course=seesaw) for the see-saw obstacle.
|
||||
*
|
||||
* Design that dodges the wedge problem: you roll along a RAISED DECK straight
|
||||
* onto the plank's near end (both at y1.8 — a flush seam, no ramp to misalign),
|
||||
* teeter across, and roll off the FAR end down onto the floor (a drop is never a
|
||||
* wedge). A fan gives the momentum to carry over the pivot.
|
||||
* Best-shot TEETERY design (John's call, fine-tune later): the plank rests
|
||||
* PRE-TILTED — high near end flush with a raised deck, low far end near the
|
||||
* floor. You roll off the deck onto the high end and DOWN the plank; your weight
|
||||
* crossing to the far half pitches it steeper still, then dumps you off the low
|
||||
* end onto the floor. Pre-tilt guarantees the teeter reads; the downhill line
|
||||
* guarantees it never wedges. createSeeSaw snaps the plank back to rest each run
|
||||
* (race:respawn) so it's boardable + teetery every time.
|
||||
*
|
||||
* Knobs to tune for feel (John's call): the see-saw's `maxTilt` (how much it
|
||||
* rocks) and `restTilt` (start it level or pre-tipped), and the fan `force`.
|
||||
* Knobs to fine-tune the feel: the see-saw's `restTilt` (how steep it starts),
|
||||
* `maxTilt` (how far your weight pitches it), the deck top height (must meet the
|
||||
* tilted high end), and the fan `force`.
|
||||
*/
|
||||
import type { CourseSpec } from './spec'
|
||||
|
||||
export const SEESAW_LAB: CourseSpec = {
|
||||
name: 'See-saw Lab',
|
||||
spawn: [0, 2.3, 18],
|
||||
spawn: [0, 2.9, 18],
|
||||
finish: { xMin: -6, xMax: 6, zMin: -32, zMax: -20, yMax: 4 },
|
||||
boxes: [
|
||||
// base floor — catches the drop off the plank and carries the finish
|
||||
{ pos: [0, -0.5, -10], half: [10, 0.5, 32], color: '#efe3c6', roughness: 1 },
|
||||
// raised deck you roll along into the plank's near end (top y1.8)
|
||||
{ pos: [0, 0.9, 8], half: [4, 0.9, 12], color: '#f7edd2', cast: true },
|
||||
// raised deck (top y2.4) — meets the plank's pre-tilted HIGH near end at z-4
|
||||
{ pos: [0, 1.2, 8], half: [4, 1.2, 12], color: '#f7edd2', cast: true },
|
||||
// finish pad on the floor, past the drop
|
||||
{ pos: [0, 0.4, -26], half: [6, 0.4, 4], color: '#FF6EB4', cast: true },
|
||||
],
|
||||
machines: [
|
||||
// fan on the deck — momentum to carry over the see-saw's pivot
|
||||
{ kind: 'fan', cfg: { id: 'sl-fan', position: [0, 2.2, 10], direction: [0, 0, -1], force: 30, range: 16, spread: 4 } },
|
||||
// the see-saw: deck flush into the +Z end (z-4), drops off the -Z end (z-12) to the floor
|
||||
{ kind: 'seesaw', cfg: { id: 'sl-seesaw', position: [0, 1.6, -8], plankSize: [8, 0.4, 3.5], tipAxis: 'z', maxTilt: 0.35 } },
|
||||
// fan on the deck — beam reaches PAST the pivot (range 22 covers the whole
|
||||
// plank z-12..10) so it keeps pushing the blob over the pivot hump, not just
|
||||
// up to it. Strong (force 40) because boarding the high end levels the plank
|
||||
// and kills the downhill assist.
|
||||
{ kind: 'fan', cfg: { id: 'sl-fan', position: [0, 2.5, 10], direction: [0, 0, -1], force: 50, range: 22, spread: 4 } },
|
||||
// the see-saw: pre-tilted DOWN toward -Z. restTilt -0.15 raises the +Z (deck)
|
||||
// end to ~2.4 (flush) and drops the -Z end to ~1.2 (roll off to floor). Small
|
||||
// enough that boarding doesn't build a pivot hump the blob stalls on, but
|
||||
// still visibly teetery + pitches further as your weight crosses.
|
||||
{ kind: 'seesaw', cfg: { id: 'sl-seesaw', position: [0, 1.6, -8], plankSize: [8, 0.4, 3.5], tipAxis: 'z', restTilt: -0.15, maxTilt: 0.4 } },
|
||||
],
|
||||
}
|
||||
|
||||
@ -328,6 +328,15 @@ export function createSeeSaw(world: World, cfg: SeeSawConfig): MachinePart {
|
||||
jd.limits = [-maxTilt, maxTilt]
|
||||
physics.createImpulseJoint(jd, anchor, plankBody, true)
|
||||
|
||||
// A tilting plank has no restoring force, so after a crossing it stays wherever
|
||||
// it settled (neutral equilibrium). Snap it back to its rest tilt each run so a
|
||||
// pre-tilted plank is boardable + teetery every time, not just the first.
|
||||
world.events.on('race:respawn', () => {
|
||||
plankBody.setRotation({ x: q0.x, y: q0.y, z: q0.z, w: q0.w }, true)
|
||||
plankBody.setAngvel({ x: 0, y: 0, z: 0 }, true)
|
||||
plankBody.setLinvel({ x: 0, y: 0, z: 0 }, true)
|
||||
})
|
||||
|
||||
const plank = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(hx * 2, py, hz * 2),
|
||||
new THREE.MeshStandardMaterial({ color: '#a1887f', roughness: 0.7 }),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user