@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InputBuffer } from "../input/inputBuffer.js";
|
|
1
2
|
/**
|
|
2
3
|
* Movement core (pure model).
|
|
3
4
|
*
|
|
@@ -83,6 +84,16 @@ export interface PlayerMotionState {
|
|
|
83
84
|
jumpOffset: number;
|
|
84
85
|
grounded: boolean;
|
|
85
86
|
jumpHeld: boolean;
|
|
87
|
+
/** Integrated time, ms; the clock jump buffering and coyote time read. */
|
|
88
|
+
clockMs: number;
|
|
89
|
+
/** `clockMs` of the last grounded frame; `null` after a jump spends it. */
|
|
90
|
+
groundedAtMs: number | null;
|
|
91
|
+
/** `clockMs` of the last landing; `null` before the first. */
|
|
92
|
+
landedAtMs: number | null;
|
|
93
|
+
/** Rising from the player's own jump, so releasing jump may cut it. */
|
|
94
|
+
jumpRising: boolean;
|
|
95
|
+
/** Ended the last frame in the air. */
|
|
96
|
+
wasAirborne: boolean;
|
|
86
97
|
}
|
|
87
98
|
/** @internal */
|
|
88
99
|
export declare function createPlayerMotionState(): PlayerMotionState;
|
|
@@ -100,6 +111,32 @@ export interface MovementTuningOverrides {
|
|
|
100
111
|
jumpVelocity?: number;
|
|
101
112
|
/** Fraction of walk speed while backpedalling; overrides {@link MOVEMENT_TUNING.backpedalSpeedMultiplier}. */
|
|
102
113
|
backpedalSpeedMultiplier?: number;
|
|
114
|
+
/** Ground velocity response, 1/s: how fast the body reaches its target velocity (default 26). */
|
|
115
|
+
groundAcceleration?: number;
|
|
116
|
+
/** Air velocity response, 1/s: air control (default 12; `0` locks the jump arc). */
|
|
117
|
+
airAcceleration?: number;
|
|
118
|
+
/** Ground stopping response with no input, 1/s (default 18; lower slides). */
|
|
119
|
+
groundFriction?: number;
|
|
120
|
+
/** Sprint speed as a multiple of walk speed (default 2.25). */
|
|
121
|
+
runSpeedMultiplier?: number;
|
|
122
|
+
/** Crouch speed as a multiple of walk speed (default 0.45). */
|
|
123
|
+
crouchSpeedMultiplier?: number;
|
|
124
|
+
/** A jump pressed up to this many ms before landing still fires on landing (default 0). Needs {@link MotionFrameOptions.buffer}. */
|
|
125
|
+
jumpBufferMs?: number;
|
|
126
|
+
/** A jump pressed up to this many ms after walking off a ledge still fires (default 0). */
|
|
127
|
+
coyoteMs?: number;
|
|
128
|
+
/** Rising speed kept when jump is released early, 0..1 (default 1: no cut). Lower makes a tap a hop. */
|
|
129
|
+
jumpCutFactor?: number;
|
|
130
|
+
/** Gravity multiplier near the peak, while vertical speed is under {@link MovementTuningOverrides.apexSpeed} (default 1). */
|
|
131
|
+
apexGravityScale?: number;
|
|
132
|
+
/** Vertical speed under which the apex gravity scale applies, m/s (default 1.5). */
|
|
133
|
+
apexSpeed?: number;
|
|
134
|
+
/** Gravity multiplier while falling (default 1). Above 1 drops faster than it rose. */
|
|
135
|
+
fallGravityScale?: number;
|
|
136
|
+
/** After landing, jumps wait and walk speed ramps back from {@link MovementTuningOverrides.landingSpeedScale} over this many ms (default 0). */
|
|
137
|
+
landingRecoveryMs?: number;
|
|
138
|
+
/** Walk speed share right after landing when `landingRecoveryMs` is set (default 0.5). */
|
|
139
|
+
landingSpeedScale?: number;
|
|
103
140
|
}
|
|
104
141
|
/**
|
|
105
142
|
* Per-frame dynamic modifiers for {@link advancePlayerMotion} — state the caller
|
|
@@ -111,6 +148,8 @@ export interface MotionFrameOptions {
|
|
|
111
148
|
speedScale?: number;
|
|
112
149
|
/** Suppress gravity/jump integration and hold the avatar afloat this frame (e.g. swimming). Default false. */
|
|
113
150
|
floating?: boolean;
|
|
151
|
+
/** Buffers jump presses for `jumpBufferMs`. Without it a jump fires only on the press frame. */
|
|
152
|
+
buffer?: InputBuffer;
|
|
114
153
|
}
|
|
115
154
|
/**
|
|
116
155
|
* Advance one frame of avatar kinematics. Mutates `motion` (velocity, jump,
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Movement core (pure model).
|
|
3
|
-
*
|
|
4
|
-
* This module owns the raw key model, the pure translation from held keys
|
|
5
|
-
* into a frame-independent {@link MovementIntent}, and the kinematics
|
|
6
|
-
* integration. Keeping it framework-free means the kinematics controller,
|
|
7
|
-
* the keyboard handler and the on-screen touch D-pad all speak the same
|
|
8
|
-
* vocabulary, and the translation is unit testable without a renderer.
|
|
9
|
-
* DOM capture lives in `@jgengine/shell`'s GamePlayerShell (keyboard and
|
|
10
|
-
* touch controls) and its camera rigs (pointer lock, the per-frame look
|
|
11
|
-
* channel).
|
|
12
|
-
*/
|
|
13
1
|
/** @internal */
|
|
14
2
|
export function createEmptyMovementKeys() {
|
|
15
3
|
return { w: false, a: false, s: false, d: false, shift: false, control: false, c: false, space: false };
|
|
@@ -90,9 +78,9 @@ export const MOVEMENT_TUNING = {
|
|
|
90
78
|
function resolveTargetSpeed(intent, baseSpeed, tuning) {
|
|
91
79
|
const backpedal = tuning?.backpedalSpeedMultiplier ?? MOVEMENT_TUNING.backpedalSpeedMultiplier;
|
|
92
80
|
const speedMultiplier = intent.crouching
|
|
93
|
-
? MOVEMENT_TUNING.crouchSpeedMultiplier
|
|
81
|
+
? (tuning?.crouchSpeedMultiplier ?? MOVEMENT_TUNING.crouchSpeedMultiplier)
|
|
94
82
|
: intent.running
|
|
95
|
-
? MOVEMENT_TUNING.runSpeedMultiplier
|
|
83
|
+
? (tuning?.runSpeedMultiplier ?? MOVEMENT_TUNING.runSpeedMultiplier)
|
|
96
84
|
: intent.forward < 0
|
|
97
85
|
? backpedal
|
|
98
86
|
: 1;
|
|
@@ -107,6 +95,11 @@ export function createPlayerMotionState() {
|
|
|
107
95
|
jumpOffset: 0,
|
|
108
96
|
grounded: true,
|
|
109
97
|
jumpHeld: false,
|
|
98
|
+
clockMs: 0,
|
|
99
|
+
groundedAtMs: 0,
|
|
100
|
+
landedAtMs: null,
|
|
101
|
+
jumpRising: false,
|
|
102
|
+
wasAirborne: false,
|
|
110
103
|
};
|
|
111
104
|
}
|
|
112
105
|
/**
|
|
@@ -122,7 +115,19 @@ export function createPlayerMotionState() {
|
|
|
122
115
|
*/
|
|
123
116
|
export function advancePlayerMotion(motion, intent, forwardX, forwardZ, baseSpeed, rawDeltaSeconds, tuning, options) {
|
|
124
117
|
const deltaSeconds = Math.min(rawDeltaSeconds, MOVEMENT_TUNING.maxFrameSeconds);
|
|
125
|
-
const
|
|
118
|
+
const now = (motion.clockMs ?? 0) + deltaSeconds * 1000;
|
|
119
|
+
motion.clockMs = now;
|
|
120
|
+
if (motion.grounded && motion.wasAirborne)
|
|
121
|
+
motion.landedAtMs = now;
|
|
122
|
+
const airborneAtStart = !motion.grounded;
|
|
123
|
+
const landingRecoveryMs = tuning?.landingRecoveryMs ?? 0;
|
|
124
|
+
const landedAtMs = motion.landedAtMs ?? null;
|
|
125
|
+
const recovery = landingRecoveryMs > 0 && motion.grounded && landedAtMs !== null && now - landedAtMs < landingRecoveryMs
|
|
126
|
+
? (now - landedAtMs) / landingRecoveryMs
|
|
127
|
+
: 1;
|
|
128
|
+
const landingSpeedScale = tuning?.landingSpeedScale ?? 0.5;
|
|
129
|
+
const recoverySpeedScale = landingSpeedScale + (1 - landingSpeedScale) * recovery;
|
|
130
|
+
const targetSpeed = resolveTargetSpeed(intent, baseSpeed, tuning) * (options?.speedScale ?? 1) * recoverySpeedScale;
|
|
126
131
|
const gravityAcceleration = tuning?.gravityAcceleration ?? MOVEMENT_TUNING.gravityAcceleration;
|
|
127
132
|
const jumpVelocity = tuning?.jumpVelocity ?? MOVEMENT_TUNING.jumpVelocity;
|
|
128
133
|
let targetVelocityX = 0;
|
|
@@ -161,29 +166,64 @@ export function advancePlayerMotion(motion, intent, forwardX, forwardZ, baseSpee
|
|
|
161
166
|
targetVelocityZ = 0;
|
|
162
167
|
}
|
|
163
168
|
}
|
|
164
|
-
const acceleration = motion.grounded
|
|
169
|
+
const acceleration = motion.grounded
|
|
170
|
+
? (tuning?.groundAcceleration ?? MOVEMENT_TUNING.groundAcceleration)
|
|
171
|
+
: (tuning?.airAcceleration ?? MOVEMENT_TUNING.airAcceleration);
|
|
165
172
|
const accelerationBlend = 1 - Math.exp(-acceleration * deltaSeconds);
|
|
166
173
|
motion.horizontalVelocityX += (targetVelocityX - motion.horizontalVelocityX) * accelerationBlend;
|
|
167
174
|
motion.horizontalVelocityZ += (targetVelocityZ - motion.horizontalVelocityZ) * accelerationBlend;
|
|
168
175
|
if (!intent.moving && motion.grounded) {
|
|
169
|
-
const friction = Math.exp(-MOVEMENT_TUNING.groundFriction * deltaSeconds);
|
|
176
|
+
const friction = Math.exp(-(tuning?.groundFriction ?? MOVEMENT_TUNING.groundFriction) * deltaSeconds);
|
|
170
177
|
motion.horizontalVelocityX *= friction;
|
|
171
178
|
motion.horizontalVelocityZ *= friction;
|
|
172
179
|
}
|
|
173
180
|
const floating = options?.floating === true;
|
|
174
181
|
const jumpPressed = intent.jumping;
|
|
175
|
-
if (
|
|
182
|
+
if (motion.grounded)
|
|
183
|
+
motion.groundedAtMs = now;
|
|
184
|
+
const coyoteMs = tuning?.coyoteMs ?? 0;
|
|
185
|
+
const groundedAtMs = motion.groundedAtMs ?? null;
|
|
186
|
+
const canJump = !intent.crouching &&
|
|
187
|
+
!floating &&
|
|
188
|
+
recovery >= 1 &&
|
|
189
|
+
(motion.grounded ||
|
|
190
|
+
(coyoteMs > 0 && motion.verticalVelocity <= 0 && groundedAtMs !== null && now - groundedAtMs <= coyoteMs));
|
|
191
|
+
const pressEdge = jumpPressed && !motion.jumpHeld;
|
|
192
|
+
const buffer = options?.buffer;
|
|
193
|
+
if (buffer !== undefined) {
|
|
194
|
+
if (pressEdge)
|
|
195
|
+
buffer.press("jump", now);
|
|
196
|
+
else if (!jumpPressed && motion.jumpHeld)
|
|
197
|
+
buffer.release("jump", now);
|
|
198
|
+
}
|
|
199
|
+
const jump = canJump && (buffer === undefined ? pressEdge : buffer.consume("jump", now, tuning?.jumpBufferMs ?? 0));
|
|
200
|
+
if (jump) {
|
|
176
201
|
motion.verticalVelocity = jumpVelocity;
|
|
177
202
|
motion.grounded = false;
|
|
203
|
+
motion.groundedAtMs = null;
|
|
204
|
+
motion.jumpRising = true;
|
|
205
|
+
}
|
|
206
|
+
else if (motion.jumpRising && !jumpPressed && motion.jumpHeld && motion.verticalVelocity > 0) {
|
|
207
|
+
motion.verticalVelocity *= Math.max(0, Math.min(1, tuning?.jumpCutFactor ?? 1));
|
|
208
|
+
motion.jumpRising = false;
|
|
178
209
|
}
|
|
179
210
|
motion.jumpHeld = jumpPressed;
|
|
180
211
|
if (floating) {
|
|
181
212
|
motion.verticalVelocity = 0;
|
|
182
213
|
motion.jumpOffset = 0;
|
|
183
214
|
motion.grounded = true;
|
|
215
|
+
motion.jumpRising = false;
|
|
184
216
|
}
|
|
185
217
|
else if (!motion.grounded || motion.verticalVelocity > 0) {
|
|
186
|
-
|
|
218
|
+
const apexSpeed = tuning?.apexSpeed ?? 1.5;
|
|
219
|
+
const gravityScale = Math.abs(motion.verticalVelocity) < apexSpeed
|
|
220
|
+
? (tuning?.apexGravityScale ?? 1)
|
|
221
|
+
: motion.verticalVelocity < 0
|
|
222
|
+
? (tuning?.fallGravityScale ?? 1)
|
|
223
|
+
: 1;
|
|
224
|
+
motion.verticalVelocity -= gravityAcceleration * gravityScale * deltaSeconds;
|
|
225
|
+
if (motion.verticalVelocity <= 0)
|
|
226
|
+
motion.jumpRising = false;
|
|
187
227
|
motion.jumpOffset += motion.verticalVelocity * deltaSeconds;
|
|
188
228
|
if (motion.jumpOffset <= 0) {
|
|
189
229
|
motion.jumpOffset = 0;
|
|
@@ -191,6 +231,9 @@ export function advancePlayerMotion(motion, intent, forwardX, forwardZ, baseSpee
|
|
|
191
231
|
motion.grounded = true;
|
|
192
232
|
}
|
|
193
233
|
}
|
|
234
|
+
if (airborneAtStart && motion.grounded)
|
|
235
|
+
motion.landedAtMs = now;
|
|
236
|
+
motion.wasAirborne = !motion.grounded;
|
|
194
237
|
return {
|
|
195
238
|
stepX: motion.horizontalVelocityX * deltaSeconds,
|
|
196
239
|
stepZ: motion.horizontalVelocityZ * deltaSeconds,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { PhysicsConfig } from "../game/defineGame.js";
|
|
2
|
+
import type { PlayerMovementConfig } from "../game/playableGame.js";
|
|
3
|
+
/** The walk character {@link measureMovement} drives: the same fields a game passes to `defineGame`. */
|
|
4
|
+
export interface MovementProbeSubject {
|
|
5
|
+
/** `defineGame({ movement })`; `feel` and `backpedalMult` shape the walk. */
|
|
6
|
+
movement?: PlayerMovementConfig;
|
|
7
|
+
/** `defineGame({ physics })`; `gravity` and `jumpVelocity` shape the jump. */
|
|
8
|
+
physics?: PhysicsConfig;
|
|
9
|
+
/** The player entity's `movement.walkSpeed` (default `2`, the controller default). */
|
|
10
|
+
walkSpeed?: number;
|
|
11
|
+
}
|
|
12
|
+
/** Scenario settings for {@link measureMovement}; every field has a default. */
|
|
13
|
+
export interface MovementProbeOptions {
|
|
14
|
+
/** Fixed tick, s (default `1/60`). */
|
|
15
|
+
dt?: number;
|
|
16
|
+
/** Measure with sprint held (default `false`). */
|
|
17
|
+
sprint?: boolean;
|
|
18
|
+
/** How long jump is held for `tapJumpHeight`, s (default `0.1`). */
|
|
19
|
+
tapSeconds?: number;
|
|
20
|
+
}
|
|
21
|
+
/** Deterministic walk-feel metrics. `Infinity` means the target was never reached. */
|
|
22
|
+
export interface MovementReport {
|
|
23
|
+
/** Steady ground speed holding forward, m/s. */
|
|
24
|
+
topSpeed: number;
|
|
25
|
+
/** Standing start until speed reaches 90% of `topSpeed`, s. */
|
|
26
|
+
timeToTopSpeed: number;
|
|
27
|
+
/** Distance slid after releasing input at `topSpeed`, until speed is under 0.05 m/s, m. */
|
|
28
|
+
stopDistance: number;
|
|
29
|
+
/** Seconds from releasing input at `topSpeed` until speed is under 0.05 m/s. */
|
|
30
|
+
stopTime: number;
|
|
31
|
+
/** Seconds from reversing direction at `topSpeed` until speed along the new heading reaches 90% of `topSpeed`. */
|
|
32
|
+
turnAroundTime: number;
|
|
33
|
+
/** Peak height of a held standing jump, m. */
|
|
34
|
+
jumpHeight: number;
|
|
35
|
+
/** Peak height of a standing jump released after `tapSeconds`, m. Lower than `jumpHeight` when `jumpCutFactor` is set. */
|
|
36
|
+
tapJumpHeight: number;
|
|
37
|
+
/** Seconds from takeoff to the jump's peak. */
|
|
38
|
+
apexTime: number;
|
|
39
|
+
/** Seconds from takeoff to landing. */
|
|
40
|
+
airTime: number;
|
|
41
|
+
/** Sideways distance covered by strafing from the first airborne tick of a standing jump until landing, m. */
|
|
42
|
+
airControlReach: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Drives a walk character through fixed scenarios (standing start, release to stop, reversal, a standing
|
|
46
|
+
* jump, a tapped jump, a strafed jump) on the same integrator `stepPlayerMovement` uses, and reports the feel metrics a
|
|
47
|
+
* test can assert. Deterministic: the same subject and options always produce the same report, so a feel
|
|
48
|
+
* change shows up as a number moving. Collision, terrain and swimming are out of scope; it measures flat ground.
|
|
49
|
+
* @capability movement-metrics measure a walking character's feel as numbers — time to top speed, stop distance, jump height and apex, air control, turn-around
|
|
50
|
+
*/
|
|
51
|
+
export declare function measureMovement(subject?: MovementProbeSubject, options?: MovementProbeOptions): MovementReport;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createInputBuffer } from "../input/inputBuffer.js";
|
|
2
|
+
import { advancePlayerMotion, createEmptyMovementKeys, createPlayerMotionState, resolveMovementIntent, } from "./movementModel.js";
|
|
3
|
+
import { resolvePlayerMovementTuning } from "./playerMovement.js";
|
|
4
|
+
const ARRIVED = 0.9;
|
|
5
|
+
const STOPPED = 0.05;
|
|
6
|
+
const SETTLE_SECONDS = 5;
|
|
7
|
+
const LIMIT_SECONDS = 10;
|
|
8
|
+
function held(keys) {
|
|
9
|
+
return resolveMovementIntent({ ...createEmptyMovementKeys(), ...keys }, true);
|
|
10
|
+
}
|
|
11
|
+
function speedOf(motion) {
|
|
12
|
+
return Math.hypot(motion.horizontalVelocityX, motion.horizontalVelocityZ);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Drives a walk character through fixed scenarios (standing start, release to stop, reversal, a standing
|
|
16
|
+
* jump, a tapped jump, a strafed jump) on the same integrator `stepPlayerMovement` uses, and reports the feel metrics a
|
|
17
|
+
* test can assert. Deterministic: the same subject and options always produce the same report, so a feel
|
|
18
|
+
* change shows up as a number moving. Collision, terrain and swimming are out of scope; it measures flat ground.
|
|
19
|
+
* @capability movement-metrics measure a walking character's feel as numbers — time to top speed, stop distance, jump height and apex, air control, turn-around
|
|
20
|
+
*/
|
|
21
|
+
export function measureMovement(subject = {}, options = {}) {
|
|
22
|
+
const dt = options.dt ?? 1 / 60;
|
|
23
|
+
const sprint = options.sprint === true;
|
|
24
|
+
const walkSpeed = subject.walkSpeed ?? 2;
|
|
25
|
+
const tuning = resolvePlayerMovementTuning({
|
|
26
|
+
...(subject.movement === undefined ? {} : { movement: subject.movement }),
|
|
27
|
+
...(subject.physics === undefined ? {} : { physics: subject.physics }),
|
|
28
|
+
}).physics;
|
|
29
|
+
const buffer = createInputBuffer({ windowMs: 0 });
|
|
30
|
+
const step = (motion, intent, forwardZ = 1, forwardX = 0) => advancePlayerMotion(motion, intent, forwardX, forwardZ, walkSpeed, dt, tuning, { buffer });
|
|
31
|
+
const forward = held({ w: true, shift: sprint });
|
|
32
|
+
const idle = held({});
|
|
33
|
+
const ticks = (seconds) => Math.ceil(seconds / dt);
|
|
34
|
+
const fresh = () => {
|
|
35
|
+
buffer.restore({ windowMs: 0, actions: {} });
|
|
36
|
+
return createPlayerMotionState();
|
|
37
|
+
};
|
|
38
|
+
const settled = () => {
|
|
39
|
+
const motion = fresh();
|
|
40
|
+
for (let i = 0; i < ticks(SETTLE_SECONDS); i += 1)
|
|
41
|
+
step(motion, forward);
|
|
42
|
+
return motion;
|
|
43
|
+
};
|
|
44
|
+
const topSpeed = speedOf(settled());
|
|
45
|
+
let timeToTopSpeed = Number.POSITIVE_INFINITY;
|
|
46
|
+
{
|
|
47
|
+
const motion = fresh();
|
|
48
|
+
for (let i = 1; i <= ticks(LIMIT_SECONDS); i += 1) {
|
|
49
|
+
step(motion, forward);
|
|
50
|
+
if (speedOf(motion) >= topSpeed * ARRIVED) {
|
|
51
|
+
timeToTopSpeed = i * dt;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let stopDistance = Number.POSITIVE_INFINITY;
|
|
57
|
+
let stopTime = Number.POSITIVE_INFINITY;
|
|
58
|
+
{
|
|
59
|
+
const motion = settled();
|
|
60
|
+
let distance = 0;
|
|
61
|
+
for (let i = 1; i <= ticks(LIMIT_SECONDS); i += 1) {
|
|
62
|
+
const moved = step(motion, idle);
|
|
63
|
+
distance += Math.hypot(moved.stepX, moved.stepZ);
|
|
64
|
+
if (speedOf(motion) < STOPPED) {
|
|
65
|
+
stopDistance = distance;
|
|
66
|
+
stopTime = i * dt;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let turnAroundTime = Number.POSITIVE_INFINITY;
|
|
72
|
+
{
|
|
73
|
+
const motion = settled();
|
|
74
|
+
for (let i = 1; i <= ticks(LIMIT_SECONDS); i += 1) {
|
|
75
|
+
step(motion, forward, -1);
|
|
76
|
+
if (-motion.horizontalVelocityZ >= topSpeed * ARRIVED) {
|
|
77
|
+
turnAroundTime = i * dt;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const jump = (airborne, releaseAfter = Number.POSITIVE_INFINITY) => {
|
|
83
|
+
const motion = fresh();
|
|
84
|
+
const takeoff = held({ space: true });
|
|
85
|
+
const released = held({ d: airborne.right !== 0, shift: sprint });
|
|
86
|
+
let height = 0;
|
|
87
|
+
let apex = 0;
|
|
88
|
+
let lateral = 0;
|
|
89
|
+
let landed = Number.POSITIVE_INFINITY;
|
|
90
|
+
for (let i = 1; i <= ticks(LIMIT_SECONDS); i += 1) {
|
|
91
|
+
const moved = step(motion, i === 1 ? takeoff : i * dt > releaseAfter ? released : airborne);
|
|
92
|
+
lateral += moved.stepX;
|
|
93
|
+
if (motion.jumpOffset > height) {
|
|
94
|
+
height = motion.jumpOffset;
|
|
95
|
+
apex = i * dt;
|
|
96
|
+
}
|
|
97
|
+
if (motion.grounded && i > 1) {
|
|
98
|
+
landed = i * dt;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { height, apex, lateral, landed };
|
|
103
|
+
};
|
|
104
|
+
const standing = jump(held({ space: true }));
|
|
105
|
+
const strafed = jump(held({ space: true, d: true, shift: sprint }));
|
|
106
|
+
const tapped = jump(held({ space: true }), options.tapSeconds ?? 0.1);
|
|
107
|
+
return {
|
|
108
|
+
topSpeed,
|
|
109
|
+
timeToTopSpeed,
|
|
110
|
+
stopDistance,
|
|
111
|
+
stopTime,
|
|
112
|
+
turnAroundTime,
|
|
113
|
+
jumpHeight: standing.height,
|
|
114
|
+
tapJumpHeight: tapped.height,
|
|
115
|
+
apexTime: standing.apex,
|
|
116
|
+
airTime: standing.landed,
|
|
117
|
+
airControlReach: Math.abs(strafed.lateral),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -2,14 +2,24 @@ import type { PhysicsConfig } from "../game/defineGame.js";
|
|
|
2
2
|
import type { PlayerMovementConfig, VoxelCollisionConfig } from "../game/playableGame.js";
|
|
3
3
|
import type { GameContext } from "../runtime/gameContext.js";
|
|
4
4
|
import type { InputFrame } from "../runtime/inputSnapshot.js";
|
|
5
|
+
import { type InputBufferSnapshot } from "../input/inputBuffer.js";
|
|
6
|
+
import { type CharacterControllerConfig, type CharacterControllerState } from "./characterController.js";
|
|
5
7
|
import { type TerrainField } from "../world/terrain.js";
|
|
6
8
|
import type { WorldFeature } from "../world/features.js";
|
|
7
|
-
import {
|
|
9
|
+
import type { PhysicsBackend } from "../physics/physicsBackend.js";
|
|
10
|
+
import { type MovementTuningOverrides, type PlayerMotionState } from "./movementModel.js";
|
|
11
|
+
import { type FreeFlightState } from "./freeFlight.js";
|
|
12
|
+
import { type VoxelPlayerBody } from "./voxelController.js";
|
|
8
13
|
/** The resolved, per-world movement configuration {@link stepPlayerMovement} integrates against — the same inputs the shell FrameDriver used to read piecemeal, gathered into one struct so single-player and host movement run identical math. */
|
|
9
14
|
export interface PlayerMovementTuning {
|
|
10
15
|
collision?: VoxelCollisionConfig;
|
|
11
16
|
movement?: PlayerMovementConfig;
|
|
12
17
|
physics?: MovementTuningOverrides;
|
|
18
|
+
/** Optional physics-backed capsule controller; when present it replaces terrain/AABB walking collision. */
|
|
19
|
+
controller?: {
|
|
20
|
+
backend: PhysicsBackend;
|
|
21
|
+
capsule: CharacterControllerConfig;
|
|
22
|
+
};
|
|
13
23
|
ground: TerrainField;
|
|
14
24
|
hasTerrain: boolean;
|
|
15
25
|
}
|
|
@@ -28,6 +38,22 @@ export declare function resolvePlayerMovementTuning(opts: {
|
|
|
28
38
|
}): PlayerMovementTuning;
|
|
29
39
|
/** One player's current heading (radians), integrated by {@link stepPlayerMovement} — the shell reads it back into its camera/aim yaw. */
|
|
30
40
|
export declare function playerMovementHeading(ctx: GameContext, userId: string): number;
|
|
41
|
+
/** One player's serializable movement state: heading, facing, velocities, jump latch and controller capsule. The entity pose lives in the entity store. */
|
|
42
|
+
export interface PlayerMovementSnapshot {
|
|
43
|
+
heading: number;
|
|
44
|
+
facing: number | null;
|
|
45
|
+
voxelBody: VoxelPlayerBody | null;
|
|
46
|
+
motion: PlayerMotionState | null;
|
|
47
|
+
flight: FreeFlightState | null;
|
|
48
|
+
controller: CharacterControllerState | null;
|
|
49
|
+
controllerJumpHeld: boolean;
|
|
50
|
+
/** Buffered jump presses; absent in snapshots taken before jump buffering existed. */
|
|
51
|
+
jumpBuffer?: InputBufferSnapshot | null;
|
|
52
|
+
}
|
|
53
|
+
/** Copy one player's movement state for prediction, rollback or a save; `null` when the player has not moved yet. */
|
|
54
|
+
export declare function snapshotPlayerMovement(ctx: GameContext, userId: string): PlayerMovementSnapshot | null;
|
|
55
|
+
/** Put a player's movement state back to a {@link snapshotPlayerMovement} copy, so the next {@link stepPlayerMovement} replays from there. */
|
|
56
|
+
export declare function restorePlayerMovement(ctx: GameContext, userId: string, snapshot: PlayerMovementSnapshot): void;
|
|
31
57
|
/** Drop a player's retained movement state (heading + kinematic body) — call on leave so a rejoin starts fresh instead of resuming stale velocity. */
|
|
32
58
|
export declare function forgetPlayerMovement(ctx: GameContext, userId: string): void;
|
|
33
59
|
/**
|
|
@@ -36,6 +62,7 @@ export declare function forgetPlayerMovement(ctx: GameContext, userId: string):
|
|
|
36
62
|
* so single-player and server-authoritative movement are identical. Reads the player's controlled entity, terrain,
|
|
37
63
|
* scene solids, and pending motion impulses; writes the entity pose via `setPose`. Retains heading + kinematic body
|
|
38
64
|
* per `userId` on the `ctx`. Pass `heading` to override the internally-integrated yaw (the shell owns yaw for its
|
|
39
|
-
* camera); omit it and the controller turns from the frame's `turnLeft`/`turnRight` actions.
|
|
65
|
+
* camera); omit it and the controller turns from the frame's `turnLeft`/`turnRight` actions. Pass `pitch` for
|
|
66
|
+
* 6DOF spectator flight when `alignWithLook` is set.
|
|
40
67
|
*/
|
|
41
|
-
export declare function stepPlayerMovement(ctx: GameContext, userId: string, input: InputFrame, dt: number, tuning: PlayerMovementTuning, heading?: number): void;
|
|
68
|
+
export declare function stepPlayerMovement(ctx: GameContext, userId: string, input: InputFrame, dt: number, tuning: PlayerMovementTuning, heading?: number, pitch?: number): void;
|