@jgengine/core 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +265 -2
- package/dist/ai/decisionGraph.d.ts +96 -0
- package/dist/ai/decisionGraph.js +212 -0
- package/dist/ai/groupAssist.d.ts +10 -0
- package/dist/ai/groupAssist.js +11 -0
- package/dist/ai/mobBrainGraph.d.ts +3 -0
- package/dist/ai/mobBrainGraph.js +25 -0
- package/dist/ai/pursuit.d.ts +1 -1
- package/dist/ai/pursuit.js +1 -1
- package/dist/ai/tacticalQueries.d.ts +30 -0
- package/dist/ai/tacticalQueries.js +45 -0
- package/dist/ai/threat.d.ts +8 -0
- package/dist/ai/threat.js +10 -0
- package/dist/anim/animGraph.d.ts +155 -0
- package/dist/anim/animGraph.js +414 -0
- package/dist/anim/boneTexture.d.ts +55 -0
- package/dist/anim/boneTexture.js +43 -0
- package/dist/anim/footPlacement.d.ts +57 -0
- package/dist/anim/footPlacement.js +67 -0
- package/dist/anim/ikSolver.d.ts +51 -0
- package/dist/anim/ikSolver.js +124 -0
- package/dist/anim/locomotionGraph.d.ts +35 -0
- package/dist/anim/locomotionGraph.js +62 -0
- package/dist/assets/spriteAtlas.d.ts +21 -0
- package/dist/assets/spriteAtlas.js +103 -0
- package/dist/audio/audioFalloff.d.ts +12 -0
- package/dist/audio/doppler.d.ts +21 -0
- package/dist/audio/doppler.js +32 -0
- package/dist/audio/engineLayers.d.ts +83 -0
- package/dist/audio/engineLayers.js +115 -0
- package/dist/combat/abilityKit.d.ts +1 -0
- package/dist/combat/abilityKit.js +1 -0
- package/dist/combat/death.d.ts +7 -0
- package/dist/combat/shotOrigin.d.ts +6 -0
- package/dist/combat/shotOrigin.js +6 -0
- package/dist/combat/weaponFeedback.d.ts +36 -0
- package/dist/combat/weaponFeedback.js +42 -0
- package/dist/combat/weaponHandling.d.ts +161 -0
- package/dist/combat/weaponHandling.js +202 -0
- package/dist/combat/weaponPresentation.d.ts +115 -0
- package/dist/combat/weaponPresentation.js +96 -0
- package/dist/devtools/warnOnce.d.ts +8 -0
- package/dist/devtools/warnOnce.js +18 -0
- package/dist/economy/currency.d.ts +16 -2
- package/dist/economy/currency.js +55 -14
- package/dist/economy/wallet.d.ts +5 -4
- package/dist/economy/wallet.js +8 -4
- package/dist/editor/commandApply.js +1 -0
- package/dist/editor/commands.d.ts +4 -1
- package/dist/editor/document.d.ts +2 -1
- package/dist/editor/document.js +92 -1
- package/dist/editor/environment.d.ts +3 -0
- package/dist/editor/environment.js +24 -0
- package/dist/editor/index.d.ts +4 -2
- package/dist/editor/index.js +1 -1
- package/dist/editor/types.d.ts +20 -0
- package/dist/faction/reputation.d.ts +6 -0
- package/dist/faction/reputation.js +11 -0
- package/dist/game/cameraConfig.d.ts +59 -1
- package/dist/game/chat.d.ts +22 -1
- package/dist/game/chat.js +48 -19
- package/dist/game/controlGate.d.ts +6 -0
- package/dist/game/controlGate.js +30 -1
- package/dist/game/defineGame.d.ts +10 -1
- package/dist/game/events.d.ts +14 -1
- package/dist/game/lootTable.d.ts +4 -0
- package/dist/game/lootTable.js +16 -0
- package/dist/game/pixelPerfect.d.ts +25 -0
- package/dist/game/pixelPerfect.js +29 -0
- package/dist/game/playableGame.d.ts +167 -6
- package/dist/game/systemRuntime.js +6 -1
- package/dist/game/toasts.d.ts +8 -0
- package/dist/game/toasts.js +7 -0
- package/dist/game/vfxInstance.d.ts +9 -0
- package/dist/game/vfxInstance.js +12 -1
- package/dist/game/viewports.d.ts +43 -0
- package/dist/game/viewports.js +46 -0
- package/dist/game/worldItem.d.ts +5 -1
- package/dist/game/worldItem.js +0 -1
- package/dist/input/actionBindings.d.ts +17 -2
- package/dist/input/actionBindings.js +24 -11
- package/dist/input/actionContexts.d.ts +45 -0
- package/dist/input/actionContexts.js +113 -0
- package/dist/input/axisInput.d.ts +4 -0
- package/dist/input/axisInput.js +4 -0
- package/dist/input/axisShaper.d.ts +73 -0
- package/dist/input/axisShaper.js +105 -0
- package/dist/input/gamepadModel.d.ts +79 -0
- package/dist/input/gamepadModel.js +108 -0
- package/dist/input/haptics.d.ts +50 -0
- package/dist/input/haptics.js +74 -0
- package/dist/input/inputBuffer.d.ts +40 -0
- package/dist/input/inputBuffer.js +59 -0
- package/dist/input/lookChannel.d.ts +10 -0
- package/dist/input/lookChannel.js +6 -0
- package/dist/input/pointer.d.ts +2 -0
- package/dist/input/pointer.js +4 -0
- package/dist/item/affix.d.ts +6 -0
- package/dist/item/affix.js +6 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +196 -1
- package/dist/movement/characterController.d.ts +76 -0
- package/dist/movement/characterController.js +216 -0
- package/dist/movement/freeFlight.d.ts +119 -0
- package/dist/movement/freeFlight.js +294 -0
- package/dist/movement/glideModel.d.ts +3 -0
- package/dist/movement/glideModel.js +6 -0
- package/dist/movement/grappleSwing.d.ts +2 -0
- package/dist/movement/grappleSwing.js +5 -1
- package/dist/movement/movementModel.d.ts +39 -0
- package/dist/movement/movementModel.js +62 -19
- package/dist/movement/movementProbe.d.ts +51 -0
- package/dist/movement/movementProbe.js +119 -0
- package/dist/movement/playerMovement.d.ts +30 -3
- package/dist/movement/playerMovement.js +304 -7
- package/dist/movement/serverStep.d.ts +5 -0
- package/dist/movement/serverStep.js +6 -0
- package/dist/movement/solidObstacles.d.ts +39 -8
- package/dist/movement/solidObstacles.js +71 -6
- package/dist/movement/voxelController.js +7 -7
- package/dist/multiplayer/poseSyncGate.d.ts +3 -1
- package/dist/multiplayer/poseSyncGate.js +9 -1
- package/dist/multiplayer/presenceContract.d.ts +6 -0
- package/dist/multiplayer/presenceModel.d.ts +2 -0
- package/dist/multiplayer/presenceModel.js +11 -0
- package/dist/nav/navFromEnvironment.d.ts +9 -3
- package/dist/nav/navFromEnvironment.js +18 -12
- package/dist/nav/navMesh.d.ts +74 -0
- package/dist/nav/navMesh.js +570 -0
- package/dist/physics/aircraftDynamics.d.ts +390 -0
- package/dist/physics/aircraftDynamics.js +704 -0
- package/dist/physics/boatDynamics.d.ts +116 -0
- package/dist/physics/boatDynamics.js +160 -0
- package/dist/physics/drivableVehicle.d.ts +22 -8
- package/dist/physics/drivableVehicle.js +4 -2
- package/dist/physics/flightDynamics.d.ts +29 -4
- package/dist/physics/flightDynamics.js +28 -5
- package/dist/physics/handlingProbe.d.ts +249 -0
- package/dist/physics/handlingProbe.js +606 -0
- package/dist/physics/kinematicVehicle.d.ts +37 -2
- package/dist/physics/kinematicVehicle.js +15 -0
- package/dist/physics/physicsBackend.d.ts +203 -0
- package/dist/physics/physicsBackend.js +49 -0
- package/dist/physics/physicsBackendConformance.d.ts +27 -0
- package/dist/physics/physicsBackendConformance.js +151 -0
- package/dist/physics/physicsWorldBackend.d.ts +45 -0
- package/dist/physics/physicsWorldBackend.js +518 -0
- package/dist/physics/vehicleBackendLink.d.ts +49 -0
- package/dist/physics/vehicleBackendLink.js +102 -0
- package/dist/physics/vehicleDynamics.d.ts +374 -0
- package/dist/physics/vehicleDynamics.js +737 -0
- package/dist/physics/worldColliders.d.ts +13 -0
- package/dist/physics/worldColliders.js +128 -0
- package/dist/puzzle/cellGrid.d.ts +15 -9
- package/dist/puzzle/cellGrid.js +13 -9
- package/dist/random/rng.d.ts +12 -1
- package/dist/random/rng.js +20 -3
- package/dist/render/environment.d.ts +23 -0
- package/dist/render/environment.js +1 -0
- package/dist/render/lookPreset.d.ts +19 -6
- package/dist/render/lookPreset.js +56 -4
- package/dist/render/postProcessing.d.ts +40 -1
- package/dist/render/postProcessing.js +1 -1
- package/dist/render/sprite2d.d.ts +38 -0
- package/dist/render/sprite2d.js +40 -0
- package/dist/runtime/adapter.d.ts +20 -1
- package/dist/runtime/adapter.js +8 -1
- package/dist/runtime/cameraDirector.d.ts +17 -3
- package/dist/runtime/cameraDirector.js +25 -0
- package/dist/runtime/commandInput.d.ts +52 -0
- package/dist/runtime/commandInput.js +71 -0
- package/dist/runtime/commandRunner.d.ts +39 -7
- package/dist/runtime/commandRunner.js +35 -5
- package/dist/runtime/context/combat.d.ts +1 -0
- package/dist/runtime/context/combat.js +2 -1
- package/dist/runtime/context/deathLoot.d.ts +2 -0
- package/dist/runtime/context/deathLoot.js +1 -0
- package/dist/runtime/gameContext.js +18 -1
- package/dist/runtime/gameContextTypes.d.ts +33 -7
- package/dist/runtime/gameRuntime.d.ts +23 -5
- package/dist/runtime/gameRuntime.js +21 -4
- package/dist/runtime/headlessRunner.d.ts +10 -0
- package/dist/runtime/headlessRunner.js +26 -8
- package/dist/runtime/hostPolicy.d.ts +3 -2
- package/dist/runtime/hostPolicy.js +8 -10
- package/dist/runtime/hostedGameRunner.js +34 -6
- package/dist/runtime/hostedWorldSession.d.ts +12 -16
- package/dist/runtime/hostedWorldSession.js +29 -6
- package/dist/runtime/hostedWorldStore.d.ts +20 -0
- package/dist/runtime/hostedWorldStore.js +1 -0
- package/dist/runtime/inputRecorder.d.ts +30 -0
- package/dist/runtime/inputRecorder.js +59 -0
- package/dist/runtime/inputSnapshot.d.ts +14 -0
- package/dist/runtime/inputSnapshot.js +11 -0
- package/dist/runtime/localPlayers.d.ts +73 -0
- package/dist/runtime/localPlayers.js +119 -0
- package/dist/runtime/poseInterpolation.d.ts +36 -0
- package/dist/runtime/poseInterpolation.js +91 -0
- package/dist/runtime/prediction.d.ts +45 -0
- package/dist/runtime/prediction.js +62 -0
- package/dist/runtime/simContext.d.ts +45 -0
- package/dist/runtime/simContext.js +65 -0
- package/dist/runtime/simLoop.d.ts +52 -0
- package/dist/runtime/simLoop.js +72 -0
- package/dist/runtime/simSnapshot.d.ts +32 -0
- package/dist/runtime/simSnapshot.js +60 -0
- package/dist/runtime/snapshot.d.ts +8 -0
- package/dist/runtime/snapshotBuffer.d.ts +33 -0
- package/dist/runtime/snapshotBuffer.js +58 -0
- package/dist/runtime/transport.d.ts +41 -2
- package/dist/runtime/transport.js +7 -1
- package/dist/runtime/worldChunks.d.ts +2 -0
- package/dist/runtime/worldChunks.js +14 -0
- package/dist/runtime/worldProjection.d.ts +9 -0
- package/dist/runtime/worldProjection.js +17 -1
- package/dist/runtime/worldSnapshot.d.ts +6 -0
- package/dist/runtime/worldSnapshot.js +3 -0
- package/dist/scene/behaviorRuntime.d.ts +29 -1
- package/dist/scene/behaviorRuntime.js +71 -4
- package/dist/scene/behaviors.d.ts +12 -1
- package/dist/scene/builtinSceneKinds.js +10 -0
- package/dist/scene/sceneKinds.d.ts +9 -0
- package/dist/scene/vehicleSeat.d.ts +5 -1
- package/dist/scene/vehicleSeat.js +5 -1
- package/dist/sensor/concealment.d.ts +6 -0
- package/dist/sensor/concealment.js +8 -0
- package/dist/sensor/freezeMonitor.d.ts +6 -0
- package/dist/sensor/freezeMonitor.js +8 -0
- package/dist/sensor/perception.d.ts +70 -0
- package/dist/sensor/perception.js +189 -0
- package/dist/sensor/recordingBuffer.d.ts +3 -0
- package/dist/sensor/recordingBuffer.js +8 -0
- package/dist/session/roundState.d.ts +4 -0
- package/dist/session/roundState.js +11 -0
- package/dist/settings/frameRateLimit.d.ts +16 -0
- package/dist/settings/frameRateLimit.js +37 -0
- package/dist/settings/graphicsProfile.d.ts +21 -0
- package/dist/settings/graphicsProfile.js +48 -0
- package/dist/settings/graphicsSettings.d.ts +40 -0
- package/dist/settings/graphicsSettings.js +81 -0
- package/dist/settings/settingsModel.d.ts +9 -1
- package/dist/settings/settingsModel.js +10 -0
- package/dist/stats/eventMeter.d.ts +1 -1
- package/dist/stats/eventMeter.js +1 -1
- package/dist/time/accrueSince.d.ts +10 -0
- package/dist/time/accrueSince.js +9 -0
- package/dist/time/rateWindow.d.ts +16 -0
- package/dist/time/rateWindow.js +14 -0
- package/dist/time/serverTick.d.ts +21 -3
- package/dist/time/serverTick.js +14 -13
- package/dist/vfx/feedbackMixer.d.ts +61 -0
- package/dist/vfx/feedbackMixer.js +106 -0
- package/dist/vfx/particleDirector.d.ts +68 -0
- package/dist/vfx/particleDirector.js +81 -0
- package/dist/visibility/simulationCulling.d.ts +8 -1
- package/dist/visibility/simulationCulling.js +7 -0
- package/dist/visibility/spatialIndex.d.ts +21 -0
- package/dist/visibility/spatialIndex.js +16 -0
- package/dist/world/authoredSolids.d.ts +46 -0
- package/dist/world/authoredSolids.js +75 -0
- package/dist/world/buildingGenerator.js +2 -2
- package/dist/world/buildingKit.d.ts +3 -1
- package/dist/world/buildings.d.ts +46 -2
- package/dist/world/buildings.js +53 -3
- package/dist/world/cityContent.js +103 -25
- package/dist/world/cityKind.d.ts +139 -0
- package/dist/world/cityKind.js +367 -16
- package/dist/world/features.d.ts +56 -3
- package/dist/world/features.js +13 -2
- package/dist/world/footprintGrid.d.ts +7 -0
- package/dist/world/footprintGrid.js +16 -0
- package/dist/world/interiors.d.ts +1 -0
- package/dist/world/interiors.js +5 -3
- package/dist/world/lod.d.ts +6 -0
- package/dist/world/lod.js +8 -0
- package/dist/world/placement.d.ts +7 -0
- package/dist/world/placement.js +2 -0
- package/dist/world/placementController.d.ts +1 -1
- package/dist/world/placementController.js +7 -0
- package/dist/world/raceCircuit.d.ts +168 -0
- package/dist/world/raceCircuit.js +797 -0
- package/dist/world/terraform.d.ts +8 -0
- package/dist/world/terraform.js +7 -0
- package/dist/world/terrain.d.ts +5 -1
- package/dist/world/terrain.js +5 -0
- package/dist/world/territory.d.ts +97 -0
- package/dist/world/territory.js +182 -0
- package/dist/world/walls.d.ts +7 -0
- package/dist/world/walls.js +7 -0
- package/dist/world/worldSolids.d.ts +84 -0
- package/dist/world/worldSolids.js +240 -0
- package/dist/world.d.ts +5 -3
- package/dist/world.js +4 -2
- package/package.json +1 -1
- package/skills/ce-handoff/SKILL.md +2 -2
- package/skills/game-design/SKILL.md +11 -1
- package/skills/jgengine/SKILL.md +9 -5
- package/skills/jgengine/api.md +205 -101
- package/skills/jgengine/capabilities.md +56 -0
- package/skills/jgengine/recipes/minimal-game.md +3 -0
- package/skills/jgengine-combat/SKILL.md +5 -0
- package/skills/jgengine-combat/api.md +138 -103
- package/skills/jgengine-combat/capabilities.md +29 -1
- package/skills/jgengine-combat/recipes/weapon-feel.md +50 -0
- package/skills/jgengine-gameplay/SKILL.md +10 -0
- package/skills/jgengine-gameplay/api.md +501 -453
- package/skills/jgengine-gameplay/capabilities.md +36 -0
- package/skills/jgengine-gameplay/reference-systems.md +4 -0
- package/skills/jgengine-world/SKILL.md +21 -3
- package/skills/jgengine-world/api.md +1172 -625
- package/skills/jgengine-world/capabilities.md +229 -3
- package/skills/jgengine-world/recipes/character-animation.md +39 -0
- package/skills/jgengine-world/recipes/controls.md +44 -0
- package/skills/jgengine-world/recipes/flight-feel.md +76 -0
- package/skills/jgengine-world/recipes/npc-senses.md +43 -0
- package/skills/jgengine-world/recipes/vehicle-feel.md +85 -0
- package/skills/jgengine-world/reference.md +31 -2
- package/skills/jgengine-world/visibility.md +112 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { AxisInput } from "../input/axisInput.js";
|
|
2
|
+
/** How the boat turns: a rudder needs water flowing past it; an outboard swings its own thrust. */
|
|
3
|
+
export type BoatSteering = {
|
|
4
|
+
kind: "rudder";
|
|
5
|
+
/** Rudder area, m². */
|
|
6
|
+
area: number;
|
|
7
|
+
/** Largest rudder angle, rad (default `0.6`). */
|
|
8
|
+
maxAngle?: number;
|
|
9
|
+
/** Share of prop thrust that washes over the rudder as flow, `0..1` (default `0.5`) — steering at low speed. */
|
|
10
|
+
propWash?: number;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "outboard";
|
|
13
|
+
/** Largest thrust angle, rad (default `0.5`). */
|
|
14
|
+
maxAngle?: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A powered hull in physical units. Drag rises toward hull speed (`√(g·L)·1.34`) and then falls away once the hull
|
|
18
|
+
* planes, the keel resists sliding sideways, and the prop's thrust fades as the boat approaches the prop's speed.
|
|
19
|
+
*/
|
|
20
|
+
export interface BoatTuning {
|
|
21
|
+
/** Displacement, kg. */
|
|
22
|
+
massKg: number;
|
|
23
|
+
/** Waterline length, m; sets hull speed and the rudder's lever arm. */
|
|
24
|
+
length: number;
|
|
25
|
+
/** Beam, m; sets the visual roll response. */
|
|
26
|
+
beam: number;
|
|
27
|
+
/** Peak prop thrust, N. */
|
|
28
|
+
maxThrust: number;
|
|
29
|
+
/** Speed at which prop thrust reaches zero, m/s. */
|
|
30
|
+
propSpeed: number;
|
|
31
|
+
/** Reverse thrust as a share of `maxThrust` (default `0.4`). */
|
|
32
|
+
reverseScale?: number;
|
|
33
|
+
/** Hull drag coefficient × wetted frontal area, m², before planing (default `0.3 · beam`). */
|
|
34
|
+
dragArea?: number;
|
|
35
|
+
/** Speed where the hull planes and drag drops, m/s; omit for a displacement hull that never planes. */
|
|
36
|
+
planingSpeed?: number;
|
|
37
|
+
/** Drag multiplier once fully planing, `0..1` (default `0.35`). */
|
|
38
|
+
planingDrag?: number;
|
|
39
|
+
/** Keel lateral resistance, N per (m/s)² of sideways speed (default `massKg · 6`). */
|
|
40
|
+
keel?: number;
|
|
41
|
+
/** Extra yaw damping, N·m per rad/s, on top of the keel resisting rotation along the hull (default `0`). */
|
|
42
|
+
yawDamping?: number;
|
|
43
|
+
steering: BoatSteering;
|
|
44
|
+
/** Rudder or outboard slew rate, rad/s (default `2`). */
|
|
45
|
+
steerRate?: number;
|
|
46
|
+
/** Largest bow-up pitch while climbing onto the plane, rad (default `0.12`). */
|
|
47
|
+
bowRise?: number;
|
|
48
|
+
/** Largest heel in a hard turn, rad (default `0.15`); planing hulls lean into the turn. */
|
|
49
|
+
maxHeel?: number;
|
|
50
|
+
/** Integration substep, s (default `1/240`). */
|
|
51
|
+
maxSubstep?: number;
|
|
52
|
+
}
|
|
53
|
+
/** World hooks for a boat instance. */
|
|
54
|
+
export interface BoatOptions {
|
|
55
|
+
position?: readonly [number, number, number];
|
|
56
|
+
heading?: number;
|
|
57
|
+
/** Water surface height, m, under a point at a time (waves); default flat at the spawn height. */
|
|
58
|
+
waterHeight?: (x: number, z: number, time: number) => number;
|
|
59
|
+
/** Surface current, m/s, `[vx, vz]`; the hull drags toward it. */
|
|
60
|
+
current?: (x: number, z: number) => readonly [number, number];
|
|
61
|
+
}
|
|
62
|
+
/** Serializable boat state. */
|
|
63
|
+
export interface BoatState {
|
|
64
|
+
x: number;
|
|
65
|
+
y: number;
|
|
66
|
+
z: number;
|
|
67
|
+
heading: number;
|
|
68
|
+
vx: number;
|
|
69
|
+
vz: number;
|
|
70
|
+
yawRate: number;
|
|
71
|
+
steerAngle: number;
|
|
72
|
+
time: number;
|
|
73
|
+
bodyPitch: number;
|
|
74
|
+
bodyRoll: number;
|
|
75
|
+
}
|
|
76
|
+
/** One boat tick: pose plus the telemetry wake, spray and engine sound read. */
|
|
77
|
+
export interface BoatStep {
|
|
78
|
+
position: readonly [number, number, number];
|
|
79
|
+
heading: number;
|
|
80
|
+
forwardSpeed: number;
|
|
81
|
+
lateralSpeed: number;
|
|
82
|
+
yawRate: number;
|
|
83
|
+
/** Rudder or outboard angle, rad, positive steers like `steer > 0`. */
|
|
84
|
+
steerAngle: number;
|
|
85
|
+
/** `0` displacement … `1` fully planing. */
|
|
86
|
+
planing: number;
|
|
87
|
+
/** Thrust in use as a share of `maxThrust`, `0..1` — engine load. */
|
|
88
|
+
engineLoad: number;
|
|
89
|
+
bodyPitch: number;
|
|
90
|
+
bodyRoll: number;
|
|
91
|
+
/** Always `0`: the hull rides the water surface. Present so `tickDrivableVehicle` can drive it. */
|
|
92
|
+
airOffset: number;
|
|
93
|
+
airborne: boolean;
|
|
94
|
+
}
|
|
95
|
+
/** A force-based powered hull on the same tick/snapshot/retune contract as `VehicleDynamics`. */
|
|
96
|
+
export interface BoatDynamics {
|
|
97
|
+
tick(dt: number, input: AxisInput): BoatStep;
|
|
98
|
+
pose(): {
|
|
99
|
+
position: readonly [number, number, number];
|
|
100
|
+
heading: number;
|
|
101
|
+
};
|
|
102
|
+
velocity(): readonly [number, number];
|
|
103
|
+
applyImpulse(dvx: number, dvz: number): void;
|
|
104
|
+
retune(next: BoatTuning): void;
|
|
105
|
+
tuning(): BoatTuning;
|
|
106
|
+
snapshot(): BoatState;
|
|
107
|
+
restore(state: BoatState): void;
|
|
108
|
+
resetTo(position: readonly [number, number, number], heading: number): void;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Creates a {@link BoatDynamics}: throttle drives the prop, brake reverses it, steer swings the rudder or outboard.
|
|
112
|
+
* A rudder boat barely turns at rest unless the prop is washing over it; an outboard turns at rest because it swings
|
|
113
|
+
* the thrust itself. Deterministic for a given `dt` sequence.
|
|
114
|
+
* @capability boat-dynamics force-based powered hull — hull-speed drag hump, planing, keel, rudder that needs flow or outboard thrust
|
|
115
|
+
*/
|
|
116
|
+
export declare function createBoatDynamics(initial: BoatTuning, options?: BoatOptions): BoatDynamics;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const GRAVITY = 9.81;
|
|
2
|
+
const WATER_DENSITY = 1025;
|
|
3
|
+
const DEFAULT_SUBSTEP = 1 / 240;
|
|
4
|
+
function forwardOf(heading) {
|
|
5
|
+
return [Math.sin(heading), Math.cos(heading)];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates a {@link BoatDynamics}: throttle drives the prop, brake reverses it, steer swings the rudder or outboard.
|
|
9
|
+
* A rudder boat barely turns at rest unless the prop is washing over it; an outboard turns at rest because it swings
|
|
10
|
+
* the thrust itself. Deterministic for a given `dt` sequence.
|
|
11
|
+
* @capability boat-dynamics force-based powered hull — hull-speed drag hump, planing, keel, rudder that needs flow or outboard thrust
|
|
12
|
+
*/
|
|
13
|
+
export function createBoatDynamics(initial, options = {}) {
|
|
14
|
+
let tuning = initial;
|
|
15
|
+
const spawnY = options.position?.[1] ?? 0;
|
|
16
|
+
const water = options.waterHeight ?? (() => spawnY);
|
|
17
|
+
const current = options.current;
|
|
18
|
+
const state = fresh(options.position ?? [0, 0, 0], options.heading ?? 0);
|
|
19
|
+
state.y = water(state.x, state.z, 0);
|
|
20
|
+
const telemetry = { planing: 0, engineLoad: 0 };
|
|
21
|
+
function fresh(position, heading) {
|
|
22
|
+
return { x: position[0], y: position[1], z: position[2], heading, vx: 0, vz: 0, yawRate: 0, steerAngle: 0, time: 0, bodyPitch: 0, bodyRoll: 0 };
|
|
23
|
+
}
|
|
24
|
+
function substep(h, input) {
|
|
25
|
+
const t = tuning;
|
|
26
|
+
const m = t.massKg;
|
|
27
|
+
const [fx, fz] = forwardOf(state.heading);
|
|
28
|
+
let relX = state.vx;
|
|
29
|
+
let relZ = state.vz;
|
|
30
|
+
if (current !== undefined) {
|
|
31
|
+
const [cx, cz] = current(state.x, state.z);
|
|
32
|
+
relX -= cx;
|
|
33
|
+
relZ -= cz;
|
|
34
|
+
}
|
|
35
|
+
const u = relX * fx + relZ * fz;
|
|
36
|
+
const v = -relX * fz + relZ * fx;
|
|
37
|
+
const r = state.yawRate;
|
|
38
|
+
const throttle = Math.max(0, Math.min(1, input.throttle));
|
|
39
|
+
const reverse = Math.max(0, Math.min(1, input.brake));
|
|
40
|
+
const steerInput = Math.max(-1, Math.min(1, input.steer));
|
|
41
|
+
const steering = t.steering;
|
|
42
|
+
const maxAngle = steering.maxAngle ?? (steering.kind === "rudder" ? 0.6 : 0.5);
|
|
43
|
+
const targetAngle = -steerInput * maxAngle;
|
|
44
|
+
const slew = (t.steerRate ?? 2) * h;
|
|
45
|
+
state.steerAngle += Math.max(-slew, Math.min(slew, targetAngle - state.steerAngle));
|
|
46
|
+
const propFade = Math.max(0, 1 - Math.max(0, u) / Math.max(0.1, t.propSpeed));
|
|
47
|
+
const thrust = throttle * t.maxThrust * propFade - reverse * t.maxThrust * (t.reverseScale ?? 0.4);
|
|
48
|
+
telemetry.engineLoad = Math.max(throttle * propFade, reverse);
|
|
49
|
+
let forceLong = 0;
|
|
50
|
+
let forceLat = 0;
|
|
51
|
+
let yawMoment = 0;
|
|
52
|
+
const lever = t.length * 0.45;
|
|
53
|
+
if (steering.kind === "outboard") {
|
|
54
|
+
// The motor swings its own thrust, so the stern is pushed sideways even at rest.
|
|
55
|
+
forceLong += thrust * Math.cos(state.steerAngle);
|
|
56
|
+
const side = thrust * Math.sin(state.steerAngle);
|
|
57
|
+
forceLat += side;
|
|
58
|
+
yawMoment += lever * side;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
forceLong += thrust;
|
|
62
|
+
const flow = Math.max(0, u) + (steering.propWash ?? 0.5) * Math.sqrt(Math.max(0, thrust) / (WATER_DENSITY * Math.max(0.05, steering.area)));
|
|
63
|
+
// Rudder lift ≈ ½ρ·A·flow²·π·α (thin foil), stalled past ~0.6 rad. It pushes the stern away from the turn.
|
|
64
|
+
const angle = Math.max(-0.6, Math.min(0.6, state.steerAngle));
|
|
65
|
+
const lift = 0.5 * WATER_DENSITY * steering.area * flow * flow * Math.PI * angle;
|
|
66
|
+
forceLat += lift;
|
|
67
|
+
yawMoment += lever * lift;
|
|
68
|
+
}
|
|
69
|
+
const hullSpeed = 1.34 * Math.sqrt(GRAVITY * t.length);
|
|
70
|
+
const planeSpeed = t.planingSpeed;
|
|
71
|
+
const planing = planeSpeed === undefined ? 0 : Math.max(0, Math.min(1, (Math.abs(u) - planeSpeed * 0.8) / (planeSpeed * 0.4)));
|
|
72
|
+
telemetry.planing = planing;
|
|
73
|
+
// Wave-making drag climbs toward hull speed; a planing hull rides over it and sheds most of the hull drag.
|
|
74
|
+
// A displacement hull that never planes keeps climbing its own bow wave, so past hull speed the drag is a wall.
|
|
75
|
+
const over = Math.abs(u) / hullSpeed - 1;
|
|
76
|
+
const hump = planeSpeed === undefined && over > 0
|
|
77
|
+
? 3.5 + 60 * over * over
|
|
78
|
+
: 1 + 2.5 * Math.exp(-((over / 0.35) ** 2));
|
|
79
|
+
const dragArea = (t.dragArea ?? 0.3 * t.beam) * (1 - planing * (1 - (t.planingDrag ?? 0.35))) * (planeSpeed === undefined ? hump : 1 + (hump - 1) * (1 - planing));
|
|
80
|
+
forceLong -= 0.5 * WATER_DENSITY * dragArea * u * Math.abs(u) * 0.01;
|
|
81
|
+
// A planing hull runs on a fraction of its bottom, so it grips the water less sideways and turns more freely.
|
|
82
|
+
const keel = (t.keel ?? m * 6) * (1 - 0.7 * planing);
|
|
83
|
+
forceLat -= keel * v * Math.abs(v) + m * 0.8 * v;
|
|
84
|
+
// A turning hull drags every station of the keel sideways at r·x; integrated over the length that resists yaw.
|
|
85
|
+
const length4 = t.length ** 4;
|
|
86
|
+
yawMoment -= keel * r * Math.abs(r) * (length4 / 80) + m * 0.8 * r * (t.length * t.length) / 12 + (t.yawDamping ?? 0) * r;
|
|
87
|
+
const iz = Math.max(1, m * t.length * t.length / 12);
|
|
88
|
+
let du = forceLong / m - r * v;
|
|
89
|
+
const dv = forceLat / m + r * u;
|
|
90
|
+
if (h > 0 && thrust === 0 && Math.sign(u + du * h) !== Math.sign(u) && u !== 0)
|
|
91
|
+
du = -u / h;
|
|
92
|
+
const nu = u + du * h;
|
|
93
|
+
const nv = v + dv * h;
|
|
94
|
+
const nr = r + (yawMoment / iz) * h;
|
|
95
|
+
state.heading += nr * h;
|
|
96
|
+
const [nfx, nfz] = forwardOf(state.heading);
|
|
97
|
+
let nvx = nfx * nu - nfz * nv;
|
|
98
|
+
let nvz = nfz * nu + nfx * nv;
|
|
99
|
+
if (current !== undefined) {
|
|
100
|
+
const [cx, cz] = current(state.x, state.z);
|
|
101
|
+
nvx += cx;
|
|
102
|
+
nvz += cz;
|
|
103
|
+
}
|
|
104
|
+
state.vx = nvx;
|
|
105
|
+
state.vz = nvz;
|
|
106
|
+
state.yawRate = nr;
|
|
107
|
+
state.x += nvx * h;
|
|
108
|
+
state.z += nvz * h;
|
|
109
|
+
state.time += h;
|
|
110
|
+
state.y = water(state.x, state.z, state.time);
|
|
111
|
+
const response = 1 - Math.exp(-4 * h);
|
|
112
|
+
const climb = planeSpeed === undefined ? 0 : Math.max(0, Math.min(1, Math.abs(u) / planeSpeed)) * (1 - planing);
|
|
113
|
+
const pitchTarget = -(t.bowRise ?? 0.12) * climb;
|
|
114
|
+
const heelTarget = Math.max(-(t.maxHeel ?? 0.15), Math.min(t.maxHeel ?? 0.15, (r * u) / GRAVITY * (planing > 0.5 ? -0.4 : 0.25)));
|
|
115
|
+
state.bodyPitch += (pitchTarget - state.bodyPitch) * response;
|
|
116
|
+
state.bodyRoll += (heelTarget - state.bodyRoll) * response;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
tick(dt, input) {
|
|
120
|
+
const maxSub = Math.max(1 / 2000, tuning.maxSubstep ?? DEFAULT_SUBSTEP);
|
|
121
|
+
const steps = dt > 0 ? Math.max(1, Math.ceil(dt / maxSub - 1e-9)) : 0;
|
|
122
|
+
const h = steps > 0 ? dt / steps : 0;
|
|
123
|
+
for (let i = 0; i < steps; i += 1)
|
|
124
|
+
substep(h, input);
|
|
125
|
+
const [fx, fz] = forwardOf(state.heading);
|
|
126
|
+
return {
|
|
127
|
+
position: [state.x, state.y, state.z],
|
|
128
|
+
heading: state.heading,
|
|
129
|
+
forwardSpeed: state.vx * fx + state.vz * fz,
|
|
130
|
+
lateralSpeed: -state.vx * fz + state.vz * fx,
|
|
131
|
+
yawRate: state.yawRate,
|
|
132
|
+
steerAngle: -state.steerAngle,
|
|
133
|
+
planing: telemetry.planing,
|
|
134
|
+
engineLoad: telemetry.engineLoad,
|
|
135
|
+
bodyPitch: state.bodyPitch,
|
|
136
|
+
bodyRoll: state.bodyRoll,
|
|
137
|
+
airOffset: 0,
|
|
138
|
+
airborne: false,
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
pose: () => ({ position: [state.x, state.y, state.z], heading: state.heading }),
|
|
142
|
+
velocity: () => [state.vx, state.vz],
|
|
143
|
+
applyImpulse(dvx, dvz) {
|
|
144
|
+
state.vx += dvx;
|
|
145
|
+
state.vz += dvz;
|
|
146
|
+
},
|
|
147
|
+
retune(next) {
|
|
148
|
+
tuning = next;
|
|
149
|
+
},
|
|
150
|
+
tuning: () => tuning,
|
|
151
|
+
snapshot: () => ({ ...state }),
|
|
152
|
+
restore(next) {
|
|
153
|
+
Object.assign(state, next);
|
|
154
|
+
},
|
|
155
|
+
resetTo(position, heading) {
|
|
156
|
+
Object.assign(state, fresh(position, heading));
|
|
157
|
+
state.y = water(state.x, state.z, 0);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import type { AxisInput } from "../input/axisInput.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { KinematicVehicleModifiers, KinematicVehicleStep } from "./kinematicVehicle.js";
|
|
3
3
|
/** World-space `[x, y, z]` for a drivable vehicle's resolved pose. */
|
|
4
4
|
export type DrivableVehiclePosition = readonly [number, number, number];
|
|
5
|
+
/** The pose fields {@link tickDrivableVehicle} reads from a sim step; `KinematicVehicleStep` and `VehicleDynamicsStep` both carry them. */
|
|
6
|
+
export interface DrivableSimStep {
|
|
7
|
+
position: readonly [number, number, number];
|
|
8
|
+
heading: number;
|
|
9
|
+
bodyPitch: number;
|
|
10
|
+
bodyRoll: number;
|
|
11
|
+
airOffset: number;
|
|
12
|
+
}
|
|
13
|
+
/** Any ground-vehicle sim {@link tickDrivableVehicle} can drive: `KinematicVehicle` or `VehicleDynamics`. */
|
|
14
|
+
export interface DrivableSim<TModifiers, TStep extends DrivableSimStep> {
|
|
15
|
+
tick(dt: number, axis: AxisInput, modifiers?: TModifiers): TStep;
|
|
16
|
+
}
|
|
5
17
|
/** Options for {@link tickDrivableVehicle} — ground snapping and per-tick tuning modifiers. */
|
|
6
|
-
export interface DrivableVehicleOptions {
|
|
18
|
+
export interface DrivableVehicleOptions<TModifiers = KinematicVehicleModifiers> {
|
|
7
19
|
/** Resamples world-space Y each tick (terrain height, a ramp, a bridge deck); omit to keep the sim's own flat `y`. */
|
|
8
20
|
groundHeight?: (x: number, z: number) => number;
|
|
9
|
-
modifiers?:
|
|
21
|
+
modifiers?: TModifiers;
|
|
10
22
|
}
|
|
11
23
|
/** A `setPose`-ready patch — spread straight into `entities.setPose(vehicleId, drive.pose)`. */
|
|
12
24
|
export interface DrivableVehiclePose {
|
|
@@ -17,15 +29,17 @@ export interface DrivableVehiclePose {
|
|
|
17
29
|
dt: number;
|
|
18
30
|
}
|
|
19
31
|
/** {@link tickDrivableVehicle}'s result — the ready-to-apply pose patch plus the raw sim step for HUD/telemetry reads. */
|
|
20
|
-
export interface DrivableVehicleStep {
|
|
32
|
+
export interface DrivableVehicleStep<TStep extends DrivableSimStep = KinematicVehicleStep> {
|
|
21
33
|
pose: DrivableVehiclePose;
|
|
22
|
-
step:
|
|
34
|
+
step: TStep;
|
|
23
35
|
}
|
|
24
36
|
/**
|
|
25
|
-
* Connects an `AxisInput` sample straight through a
|
|
26
|
-
* for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle
|
|
37
|
+
* Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or
|
|
38
|
+
* `VehicleDynamics`) to a scene entity's pose for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle
|
|
27
39
|
* game hand-rolled. Ground-snaps the result when `groundHeight` is given (terrain-following cars, not
|
|
28
40
|
* just flat racetracks). Pair with `scene/vehicleSeat` for who is allowed to drive and where the camera
|
|
29
41
|
* points; this function only steps the sim and shapes the pose patch, nothing else.
|
|
42
|
+
*
|
|
43
|
+
* @capability drivable-vehicle drive a car from throttle/steer/handbrake input each tick: step the vehicle sim and pose its entity
|
|
30
44
|
*/
|
|
31
|
-
export declare function tickDrivableVehicle(vehicle:
|
|
45
|
+
export declare function tickDrivableVehicle<TStep extends DrivableSimStep = KinematicVehicleStep, TModifiers = KinematicVehicleModifiers>(vehicle: DrivableSim<TModifiers, TStep>, dt: number, axis: AxisInput, options?: DrivableVehicleOptions<TModifiers>): DrivableVehicleStep<TStep>;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Connects an `AxisInput` sample straight through a
|
|
3
|
-
* for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle
|
|
2
|
+
* Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or
|
|
3
|
+
* `VehicleDynamics`) to a scene entity's pose for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle
|
|
4
4
|
* game hand-rolled. Ground-snaps the result when `groundHeight` is given (terrain-following cars, not
|
|
5
5
|
* just flat racetracks). Pair with `scene/vehicleSeat` for who is allowed to drive and where the camera
|
|
6
6
|
* points; this function only steps the sim and shapes the pose patch, nothing else.
|
|
7
|
+
*
|
|
8
|
+
* @capability drivable-vehicle drive a car from throttle/steer/handbrake input each tick: step the vehicle sim and pose its entity
|
|
7
9
|
*/
|
|
8
10
|
export function tickDrivableVehicle(vehicle, dt, axis, options = {}) {
|
|
9
11
|
const step = vehicle.tick(dt, axis, options.modifiers);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AxisBinding } from "../input/axisInput.js";
|
|
2
|
+
import type { ChaseCameraTuning } from "../runtime/cameraDirector.js";
|
|
1
3
|
import { type GravityField } from "./gravityField.js";
|
|
2
4
|
/** Three-dimensional world-space vector used by the flight model. */
|
|
3
5
|
export type FlightVector = readonly [number, number, number];
|
|
@@ -23,7 +25,12 @@ export interface FlightControlRates {
|
|
|
23
25
|
damping: number;
|
|
24
26
|
stability: number;
|
|
25
27
|
}
|
|
26
|
-
/**
|
|
28
|
+
/** Axes an aircraft can bind — which actions drive each flight control. */
|
|
29
|
+
export type AircraftAxis = "pitch" | "roll" | "yaw" | "throttle" | "collective" | "airbrake" | "afterburner" | "vectoring";
|
|
30
|
+
/**
|
|
31
|
+
* Data-first physical tuning shared by all aircraft instances of one catalog type.
|
|
32
|
+
* @deprecated Rotation is commanded from rates with pitch clamped to ±0.49π, so it can't loop, and mass and stall angle are fixed. Use `createRigidAircraft` (`physics/aircraftDynamics`), whose rotation comes from surface forces on an inertia tensor.
|
|
33
|
+
*/
|
|
27
34
|
export interface AircraftTuning {
|
|
28
35
|
kind: AircraftKind;
|
|
29
36
|
mass: number;
|
|
@@ -41,6 +48,10 @@ export interface AircraftTuning {
|
|
|
41
48
|
groundEffectHeight?: number;
|
|
42
49
|
vtolTransitionSpeed?: number;
|
|
43
50
|
groundClearance?: number;
|
|
51
|
+
/** Per-axis bindings for this aircraft — which actions drive each control. Unlisted axes keep the defaults (WASD + flightThrottleUp/Down etc.). */
|
|
52
|
+
bindings?: Partial<Record<AircraftAxis, AxisBinding>>;
|
|
53
|
+
/** Camera POV while this aircraft is piloted — chase tuning overlay applied on mount, cleared on dismount. */
|
|
54
|
+
camera?: ChaseCameraTuning | null;
|
|
44
55
|
}
|
|
45
56
|
/** Spawn state and injectable world-field samplers for an aircraft instance. */
|
|
46
57
|
export interface AircraftOptions {
|
|
@@ -65,6 +76,15 @@ export interface AircraftStep {
|
|
|
65
76
|
grounded: boolean;
|
|
66
77
|
gForce: number;
|
|
67
78
|
}
|
|
79
|
+
/** Serializable integrator state of an {@link AircraftDynamics}: what `restore` needs to resume bit-for-bit. */
|
|
80
|
+
export interface AircraftDynamicsState {
|
|
81
|
+
position: FlightVector;
|
|
82
|
+
velocity: FlightVector;
|
|
83
|
+
rotation: FlightVector;
|
|
84
|
+
angular: FlightVector;
|
|
85
|
+
rotor: number;
|
|
86
|
+
lastVerticalSpeed: number;
|
|
87
|
+
}
|
|
68
88
|
/** Stateful six-degree-of-freedom aircraft simulation. */
|
|
69
89
|
export interface AircraftDynamics {
|
|
70
90
|
tick(dt: number, input: FlightControlInput): AircraftStep;
|
|
@@ -75,8 +95,13 @@ export interface AircraftDynamics {
|
|
|
75
95
|
velocity(): FlightVector;
|
|
76
96
|
resetTo(position: FlightVector, rotation?: FlightVector): void;
|
|
77
97
|
setVelocity(velocity: FlightVector): void;
|
|
98
|
+
/** Swap tuning in place (damage, upgrades, a live tuning panel) without losing pose or momentum. */
|
|
99
|
+
retune(next: AircraftTuning): void;
|
|
100
|
+
snapshot(): AircraftDynamicsState;
|
|
101
|
+
restore(state: AircraftDynamicsState): void;
|
|
78
102
|
}
|
|
79
|
-
/**
|
|
80
|
-
*
|
|
103
|
+
/**
|
|
104
|
+
* Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
|
|
105
|
+
* @deprecated Rotation is commanded from rates with pitch clamped to ±0.49π, so it can't loop, and mass and stall angle are fixed. Use `createRigidAircraft` (`physics/aircraftDynamics`), whose rotation comes from surface forces on an inertia tensor.
|
|
81
106
|
*/
|
|
82
|
-
export declare function createAircraftDynamics(
|
|
107
|
+
export declare function createAircraftDynamics(initialTuning: AircraftTuning, options?: AircraftOptions): AircraftDynamics;
|
|
@@ -29,18 +29,20 @@ function wrapAngle(value) {
|
|
|
29
29
|
angle += Math.PI * 2;
|
|
30
30
|
return angle;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
32
|
+
/**
|
|
33
|
+
* Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
|
|
34
|
+
* @deprecated Rotation is commanded from rates with pitch clamped to ±0.49π, so it can't loop, and mass and stall angle are fixed. Use `createRigidAircraft` (`physics/aircraftDynamics`), whose rotation comes from surface forces on an inertia tensor.
|
|
34
35
|
*/
|
|
35
|
-
export function createAircraftDynamics(
|
|
36
|
+
export function createAircraftDynamics(initialTuning, options = {}) {
|
|
37
|
+
let tuning = initialTuning;
|
|
36
38
|
let position = [...(options.position ?? [0, 0, 0])];
|
|
37
39
|
let velocity = [...(options.velocity ?? [0, 0, 0])];
|
|
38
40
|
let rotation = [...(options.rotation ?? [0, 0, 0])];
|
|
39
41
|
let angular = [0, 0, 0];
|
|
40
42
|
let rotor = 0;
|
|
41
43
|
let lastVerticalSpeed = velocity[1];
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
let gravity = Math.abs(tuning.gravity ?? 9.81);
|
|
45
|
+
let gravityField = options.gravityField ?? uniformGravity([0, -gravity, 0]);
|
|
44
46
|
const windAt = options.wind ?? (() => [0, 0, 0]);
|
|
45
47
|
const groundAt = options.groundHeight ?? (() => 0);
|
|
46
48
|
return {
|
|
@@ -161,5 +163,26 @@ export function createAircraftDynamics(tuning, options = {}) {
|
|
|
161
163
|
velocity = [...nextVelocity];
|
|
162
164
|
lastVerticalSpeed = velocity[1];
|
|
163
165
|
},
|
|
166
|
+
retune(next) {
|
|
167
|
+
tuning = next;
|
|
168
|
+
gravity = Math.abs(next.gravity ?? 9.81);
|
|
169
|
+
gravityField = options.gravityField ?? uniformGravity([0, -gravity, 0]);
|
|
170
|
+
},
|
|
171
|
+
snapshot: () => ({
|
|
172
|
+
position: [...position],
|
|
173
|
+
velocity: [...velocity],
|
|
174
|
+
rotation: [...rotation],
|
|
175
|
+
angular: [...angular],
|
|
176
|
+
rotor,
|
|
177
|
+
lastVerticalSpeed,
|
|
178
|
+
}),
|
|
179
|
+
restore(state) {
|
|
180
|
+
position = [...state.position];
|
|
181
|
+
velocity = [...state.velocity];
|
|
182
|
+
rotation = [...state.rotation];
|
|
183
|
+
angular = [...state.angular];
|
|
184
|
+
rotor = state.rotor;
|
|
185
|
+
lastVerticalSpeed = state.lastVerticalSpeed;
|
|
186
|
+
},
|
|
164
187
|
};
|
|
165
188
|
}
|