@woosh/meep-engine 2.139.0 → 2.141.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/package.json +1 -1
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts +3 -3
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.js +4 -4
- package/src/{engine/physics/broadphase/aabb_transform_oriented.d.ts → core/geom/3d/aabb/aabb3_transform_oriented.d.ts} +2 -2
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -0
- package/src/{engine/physics/broadphase/aabb_transform_oriented.js → core/geom/3d/aabb/aabb3_transform_oriented.js} +1 -1
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +21 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.js +25 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts +54 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -0
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts +24 -2
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/AbstractShape3D.js +24 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +148 -0
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/HeightMapShape3D.js +451 -0
- package/src/core/geom/3d/shape/MeshShape3D.d.ts +210 -0
- package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/MeshShape3D.js +593 -0
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +46 -2
- package/src/core/geom/3d/shape/Triangle3D.d.ts +95 -0
- package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/Triangle3D.js +318 -0
- package/src/core/geom/3d/shape/UnionShape3D.js +13 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts +30 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts.map +1 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +64 -0
- package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.js +9 -11
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts +28 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +40 -18
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts +9 -5
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +38 -10
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts +14 -5
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +47 -5
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +75 -13
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts +2 -2
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +1 -1
- package/src/core/geom/vec3/v3_dot_array_array.d.ts +3 -3
- package/src/core/geom/vec3/v3_dot_array_array.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_dot_array_array.js +2 -2
- package/src/core/geom/vec3/v3_negate_array.d.ts +3 -3
- package/src/core/geom/vec3/v3_negate_array.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_negate_array.js +2 -2
- package/src/core/geom/vec3/v3_quat3_apply.d.ts +29 -0
- package/src/core/geom/vec3/v3_quat3_apply.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_quat3_apply.js +39 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts +30 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.js +41 -0
- package/src/core/geom/vec3/v3_triple_cross_product.d.ts +32 -0
- package/src/core/geom/vec3/v3_triple_cross_product.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_triple_cross_product.js +45 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +16 -3
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerController.js +211 -211
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +72 -8
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +37 -5
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +101 -3
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +1789 -1416
- package/src/engine/control/first-person/TODO.md +173 -127
- package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Slide.js +9 -1
- package/src/engine/control/first-person/prototype_first_person_controller.js +88 -2
- package/src/engine/control/first-person/test/buildTestPlayer.d.ts.map +1 -1
- package/src/engine/control/first-person/test/buildTestPlayer.js +9 -1
- package/src/engine/graphics/geometry/CapsuleGeometry.d.ts +42 -0
- package/src/engine/graphics/geometry/CapsuleGeometry.d.ts.map +1 -0
- package/src/engine/graphics/geometry/CapsuleGeometry.js +171 -0
- package/src/engine/physics/BULLET_REVIEW.md +945 -0
- package/src/engine/physics/CANNON_REVIEW.md +1300 -0
- package/src/engine/physics/JOLT_REVIEW.md +913 -0
- package/src/engine/physics/PLAN.md +578 -236
- package/src/engine/physics/RAPIER_REVIEW.md +934 -0
- package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -0
- package/src/engine/physics/REVIEW_002.md +151 -0
- package/src/engine/physics/broadphase/compute_fat_world_aabb.js +2 -2
- package/src/engine/physics/constraint/DofMode.d.ts +28 -0
- package/src/engine/physics/constraint/DofMode.d.ts.map +1 -0
- package/src/engine/physics/constraint/DofMode.js +35 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts +16 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -0
- package/src/engine/physics/constraint/solve_constraints.js +436 -0
- package/src/engine/physics/contact/ManifoldStore.d.ts +83 -10
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +608 -499
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +2 -2
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/Joint.d.ts +179 -0
- package/src/engine/physics/ecs/Joint.d.ts.map +1 -0
- package/src/engine/physics/ecs/Joint.js +234 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +180 -20
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +1423 -1159
- package/src/engine/physics/fluid/FluidField.d.ts +14 -10
- package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidField.js +14 -10
- package/src/engine/physics/fluid/FluidSimulator.js +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts +17 -10
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +18 -11
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts +13 -10
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +18 -13
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts +4 -3
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +15 -11
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +30 -6
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +44 -18
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +6 -6
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +1 -1
- package/src/engine/physics/gjk/expanding_polytope_algorithm.js +68 -22
- package/src/engine/physics/gjk/gjk.d.ts +28 -2
- package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
- package/src/engine/physics/gjk/gjk.js +421 -378
- package/src/engine/physics/gjk/minkowski_support.d.ts +37 -0
- package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -0
- package/src/engine/physics/gjk/minkowski_support.js +75 -0
- package/src/engine/physics/gjk/mpr.d.ts +56 -0
- package/src/engine/physics/gjk/mpr.d.ts.map +1 -0
- package/src/engine/physics/gjk/mpr.js +344 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +20 -5
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +36 -38
- package/src/engine/physics/integration/integrate_position.d.ts +25 -7
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +43 -12
- package/src/engine/physics/integration/integrate_velocity.d.ts +30 -0
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_velocity.js +82 -1
- package/src/engine/physics/island/IslandBuilder.d.ts +4 -1
- package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
- package/src/engine/physics/island/IslandBuilder.js +33 -16
- package/src/engine/physics/narrowphase/PosedShape.d.ts +0 -8
- package/src/engine/physics/narrowphase/PosedShape.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/PosedShape.js +28 -30
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +140 -18
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +30 -0
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/box_triangle_contact.js +811 -0
- package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/capsule_contacts.js +10 -56
- package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +71 -0
- package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/capsule_triangle_contact.js +375 -0
- package/src/engine/physics/narrowphase/compute_penetration.d.ts +91 -0
- package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/compute_penetration.js +396 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts +35 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.js +80 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +31 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +42 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +204 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +42 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +94 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +37 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts +41 -2
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +1497 -382
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/sphere_box_contact.js +16 -23
- package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +48 -0
- package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/sphere_triangle_contact.js +143 -0
- package/src/engine/physics/queries/overlap_shape.d.ts +51 -0
- package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -0
- package/src/engine/physics/queries/overlap_shape.js +183 -0
- package/src/engine/physics/queries/shape_cast.d.ts +56 -0
- package/src/engine/physics/queries/shape_cast.d.ts.map +1 -0
- package/src/engine/physics/queries/shape_cast.js +387 -0
- package/src/engine/physics/solver/solve_contacts.d.ts +146 -32
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +809 -223
- package/src/engine/physics/broadphase/aabb_transform_oriented.d.ts.map +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts +0 -20
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts.map +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.js +0 -83
|
@@ -1,211 +1,211 @@
|
|
|
1
|
-
import Signal from "../../../core/events/signal/Signal.js";
|
|
2
|
-
import Vector2 from "../../../core/geom/Vector2.js";
|
|
3
|
-
import Vector3 from "../../../core/geom/Vector3.js";
|
|
4
|
-
import { AbilitySet } from "./abilities/AbilitySet.js";
|
|
5
|
-
import { FirstPersonPlayerControllerConfig } from "./FirstPersonPlayerControllerConfig.js";
|
|
6
|
-
import { MasterySet } from "./mastery/MasterySet.js";
|
|
7
|
-
import { FirstPersonActionState, FirstPersonLocomotionMode, FirstPersonPose } from "./pose/FirstPersonPose.js";
|
|
8
|
-
import { FirstPersonPosture } from "./pose/FirstPersonPosture.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Intent — what the player (or AI) wants the body to do. All fields are
|
|
12
|
-
* latched ("hold-state") except `look`, which accumulates deltas between
|
|
13
|
-
* fixed updates and is zeroed by the system on consume.
|
|
14
|
-
*
|
|
15
|
-
* The system performs its own edge detection on `jump` (and on `crouch`
|
|
16
|
-
* when toggle-mode is configured). This is what guarantees a held jump
|
|
17
|
-
* button does not produce repeated jumps.
|
|
18
|
-
*/
|
|
19
|
-
class FirstPersonIntent {
|
|
20
|
-
/** [-1..1, -1..1] forward+/right+ in body-local. @type {Vector2} */
|
|
21
|
-
move = new Vector2(0, 0);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Accumulated look delta (radians). x = yaw, y = pitch. Consumed and
|
|
25
|
-
* zeroed by the system in fixedUpdate.
|
|
26
|
-
* @type {Vector2}
|
|
27
|
-
*/
|
|
28
|
-
look = new Vector2(0, 0);
|
|
29
|
-
|
|
30
|
-
/** Hold state — true while jump button is held. @type {boolean} */
|
|
31
|
-
jump = false;
|
|
32
|
-
|
|
33
|
-
/** Hold state — meaning depends on config.crouch.mode. @type {boolean} */
|
|
34
|
-
crouch = false;
|
|
35
|
-
|
|
36
|
-
/** Hold state — true while sprint button is held. @type {boolean} */
|
|
37
|
-
sprint = false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Derived state — read-only outputs. Useful for HUDs, sound systems, AI
|
|
42
|
-
* sensors that need to know "is the player crouched / sprinting / etc."
|
|
43
|
-
*/
|
|
44
|
-
class FirstPersonState {
|
|
45
|
-
grounded = true;
|
|
46
|
-
timeSinceGrounded = 0;
|
|
47
|
-
|
|
48
|
-
/** Set by the physics layer (or the prototype's flat-ground integrator). */
|
|
49
|
-
groundNormal = new Vector3(0, 1, 0);
|
|
50
|
-
surfaceTag = null;
|
|
51
|
-
|
|
52
|
-
speed = 0;
|
|
53
|
-
/** [0..1] vs sprint speed — drives bob amp and FOV kick. */
|
|
54
|
-
speedNormalized = 0;
|
|
55
|
-
/** Horizontal locomotion mode. See {@link FirstPersonLocomotionMode}. */
|
|
56
|
-
locomotionMode = FirstPersonLocomotionMode.Idle;
|
|
57
|
-
|
|
58
|
-
verticalSpeed = 0;
|
|
59
|
-
airborneTime = 0;
|
|
60
|
-
fallDistance = 0;
|
|
61
|
-
|
|
62
|
-
jumpBufferRemaining = 0;
|
|
63
|
-
inJumpAnticipation = false;
|
|
64
|
-
isVariableJumpCut = false;
|
|
65
|
-
/** Set to true while jumping ascent until apex; gates the cut behavior. */
|
|
66
|
-
isAscending = false;
|
|
67
|
-
|
|
68
|
-
exertion = 0;
|
|
69
|
-
breathPhase = 0;
|
|
70
|
-
breathRateHz = 0;
|
|
71
|
-
breathAmplitudeM = 0;
|
|
72
|
-
|
|
73
|
-
stridePhase = 0;
|
|
74
|
-
stepCount = 0;
|
|
75
|
-
/** Which foot fires next: 0 = right, 1 = left. */
|
|
76
|
-
nextFoot = 0;
|
|
77
|
-
|
|
78
|
-
eyeHeight = 1.80;
|
|
79
|
-
|
|
80
|
-
/** Set true when crouch was released but a ceiling above prevents standing. */
|
|
81
|
-
crouchBlocked = false;
|
|
82
|
-
/** True while in crouch pose (independent of intent — see crouchBlocked). */
|
|
83
|
-
crouchActive = false;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Body posture. Set each tick by whichever layer owns motion: the
|
|
87
|
-
* base derives Stand/Crouch from intent; an active ability can
|
|
88
|
-
* force Prone (slide) or Hang (ledge-grab). See
|
|
89
|
-
* {@link FirstPersonPosture}. Drives eye-height target and gait
|
|
90
|
-
* gating (stride, footsteps, bob).
|
|
91
|
-
* @type {number}
|
|
92
|
-
*/
|
|
93
|
-
posture = FirstPersonPosture.Stand;
|
|
94
|
-
|
|
95
|
-
leanRollRad = 0;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Event surface. All signals are sent from fixedUpdate so listeners see
|
|
100
|
-
* consistent state across the same simulation tick.
|
|
101
|
-
*/
|
|
102
|
-
class FirstPersonSignals {
|
|
103
|
-
/** {side:"L"|"R", speed:number, surfaceTag:string?} */
|
|
104
|
-
onFootStep = new Signal();
|
|
105
|
-
/** {peakHeight:number} fired at impulse, after anticipation finishes. */
|
|
106
|
-
onJumpStart = new Signal();
|
|
107
|
-
/** No payload. */
|
|
108
|
-
onJumpApex = new Signal();
|
|
109
|
-
/** {reason:"jump"|"fall"} */
|
|
110
|
-
onLeaveGround = new Signal();
|
|
111
|
-
/** {verticalSpeed:number, kind:"soft"|"hard"} */
|
|
112
|
-
onLand = new Signal();
|
|
113
|
-
onCrouchEnter = new Signal();
|
|
114
|
-
onCrouchExit = new Signal();
|
|
115
|
-
onSprintStart = new Signal();
|
|
116
|
-
onSprintStop = new Signal();
|
|
117
|
-
/** {amplitude:number, rateHz:number} fired at peak inhale (phase 0.25). */
|
|
118
|
-
onBreathIn = new Signal();
|
|
119
|
-
/** {amplitude:number, rateHz:number} fired at peak exhale (phase 0.75). */
|
|
120
|
-
onBreathOut = new Signal();
|
|
121
|
-
/** {ledgeHeight:number} — fired when LedgeGrab activates. */
|
|
122
|
-
onLedgeGrab = new Signal();
|
|
123
|
-
/** {reason:"mantle-up"|"drop"|"slip"|"shuffle-off"} */
|
|
124
|
-
onLedgeRelease = new Signal();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* First-person camera + body controller. See DESIGN.md (sibling file) for
|
|
129
|
-
* the goals, layered architecture, and tuning rationale.
|
|
130
|
-
*
|
|
131
|
-
* Lifecycle: only `config` is serialized. `intent`, `state`, `pose`,
|
|
132
|
-
* `signals`, and `eyeEntity` are transient — rebuilt by the system on
|
|
133
|
-
* link.
|
|
134
|
-
*
|
|
135
|
-
* @author Alex Goldring
|
|
136
|
-
* @copyright Company Named Limited (c) 2026
|
|
137
|
-
*/
|
|
138
|
-
export class FirstPersonPlayerController {
|
|
139
|
-
static typeName = "FirstPersonPlayerController";
|
|
140
|
-
|
|
141
|
-
constructor() {
|
|
142
|
-
this.config = new FirstPersonPlayerControllerConfig();
|
|
143
|
-
this.intent = new FirstPersonIntent();
|
|
144
|
-
this.state = new FirstPersonState();
|
|
145
|
-
this.pose = new FirstPersonPose();
|
|
146
|
-
this.signals = new FirstPersonSignals();
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Mastery layer — registry of {@link MasteryEvaluator}s and the
|
|
150
|
-
* telemetry score they feed. Designers register evaluators here;
|
|
151
|
-
* the controller's system queries them at each decision point.
|
|
152
|
-
* Empty by default (no mastery effects unless the game adds them).
|
|
153
|
-
* @type {MasterySet}
|
|
154
|
-
*/
|
|
155
|
-
this.mastery = new MasterySet();
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Movement abilities (wall-run, mantle, slide, etc.). Empty by
|
|
159
|
-
* default — base locomotion is unchanged unless the game adds
|
|
160
|
-
* abilities here.
|
|
161
|
-
* @type {AbilitySet}
|
|
162
|
-
*/
|
|
163
|
-
this.abilities = new AbilitySet();
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* ID of the entity carrying the eye Transform + Camera. Created by
|
|
167
|
-
* the system on link if -1. Held as a plain number rather than
|
|
168
|
-
* EntityReference to keep serialization simple — the relationship
|
|
169
|
-
* is rebuilt on link anyway.
|
|
170
|
-
* @type {number}
|
|
171
|
-
*/
|
|
172
|
-
this.eyeEntity = -1;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
toJSON() {
|
|
176
|
-
return { config: this.config.toJSON() };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
fromJSON(json) {
|
|
180
|
-
if (json && json.config) {
|
|
181
|
-
this.config.fromJSON(json.config);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* @param {FirstPersonPlayerController} other
|
|
187
|
-
*/
|
|
188
|
-
copy(other) {
|
|
189
|
-
this.config.copy(other.config);
|
|
190
|
-
// intent/state/pose/signals/eyeEntity are transient
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
clone() {
|
|
194
|
-
const c = new FirstPersonPlayerController();
|
|
195
|
-
c.copy(this);
|
|
196
|
-
return c;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
equals(other) {
|
|
200
|
-
// config-only equality (the rest is runtime state)
|
|
201
|
-
return JSON.stringify(this.config.toJSON()) === JSON.stringify(other.config.toJSON());
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
hash() {
|
|
205
|
-
// hashing transient state is undesirable; the config is large; return 0
|
|
206
|
-
return 0;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
FirstPersonPlayerController.ActionState = FirstPersonActionState;
|
|
211
|
-
FirstPersonPlayerController.LocomotionMode = FirstPersonLocomotionMode;
|
|
1
|
+
import Signal from "../../../core/events/signal/Signal.js";
|
|
2
|
+
import Vector2 from "../../../core/geom/Vector2.js";
|
|
3
|
+
import Vector3 from "../../../core/geom/Vector3.js";
|
|
4
|
+
import { AbilitySet } from "./abilities/AbilitySet.js";
|
|
5
|
+
import { FirstPersonPlayerControllerConfig } from "./FirstPersonPlayerControllerConfig.js";
|
|
6
|
+
import { MasterySet } from "./mastery/MasterySet.js";
|
|
7
|
+
import { FirstPersonActionState, FirstPersonLocomotionMode, FirstPersonPose } from "./pose/FirstPersonPose.js";
|
|
8
|
+
import { FirstPersonPosture } from "./pose/FirstPersonPosture.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Intent — what the player (or AI) wants the body to do. All fields are
|
|
12
|
+
* latched ("hold-state") except `look`, which accumulates deltas between
|
|
13
|
+
* fixed updates and is zeroed by the system on consume.
|
|
14
|
+
*
|
|
15
|
+
* The system performs its own edge detection on `jump` (and on `crouch`
|
|
16
|
+
* when toggle-mode is configured). This is what guarantees a held jump
|
|
17
|
+
* button does not produce repeated jumps.
|
|
18
|
+
*/
|
|
19
|
+
class FirstPersonIntent {
|
|
20
|
+
/** [-1..1, -1..1] forward+/right+ in body-local. @type {Vector2} */
|
|
21
|
+
move = new Vector2(0, 0);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Accumulated look delta (radians). x = yaw, y = pitch. Consumed and
|
|
25
|
+
* zeroed by the system in fixedUpdate.
|
|
26
|
+
* @type {Vector2}
|
|
27
|
+
*/
|
|
28
|
+
look = new Vector2(0, 0);
|
|
29
|
+
|
|
30
|
+
/** Hold state — true while jump button is held. @type {boolean} */
|
|
31
|
+
jump = false;
|
|
32
|
+
|
|
33
|
+
/** Hold state — meaning depends on config.crouch.mode. @type {boolean} */
|
|
34
|
+
crouch = false;
|
|
35
|
+
|
|
36
|
+
/** Hold state — true while sprint button is held. @type {boolean} */
|
|
37
|
+
sprint = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Derived state — read-only outputs. Useful for HUDs, sound systems, AI
|
|
42
|
+
* sensors that need to know "is the player crouched / sprinting / etc."
|
|
43
|
+
*/
|
|
44
|
+
class FirstPersonState {
|
|
45
|
+
grounded = true;
|
|
46
|
+
timeSinceGrounded = 0;
|
|
47
|
+
|
|
48
|
+
/** Set by the physics layer (or the prototype's flat-ground integrator). */
|
|
49
|
+
groundNormal = new Vector3(0, 1, 0);
|
|
50
|
+
surfaceTag = null;
|
|
51
|
+
|
|
52
|
+
speed = 0;
|
|
53
|
+
/** [0..1] vs sprint speed — drives bob amp and FOV kick. */
|
|
54
|
+
speedNormalized = 0;
|
|
55
|
+
/** Horizontal locomotion mode. See {@link FirstPersonLocomotionMode}. */
|
|
56
|
+
locomotionMode = FirstPersonLocomotionMode.Idle;
|
|
57
|
+
|
|
58
|
+
verticalSpeed = 0;
|
|
59
|
+
airborneTime = 0;
|
|
60
|
+
fallDistance = 0;
|
|
61
|
+
|
|
62
|
+
jumpBufferRemaining = 0;
|
|
63
|
+
inJumpAnticipation = false;
|
|
64
|
+
isVariableJumpCut = false;
|
|
65
|
+
/** Set to true while jumping ascent until apex; gates the cut behavior. */
|
|
66
|
+
isAscending = false;
|
|
67
|
+
|
|
68
|
+
exertion = 0;
|
|
69
|
+
breathPhase = 0;
|
|
70
|
+
breathRateHz = 0;
|
|
71
|
+
breathAmplitudeM = 0;
|
|
72
|
+
|
|
73
|
+
stridePhase = 0;
|
|
74
|
+
stepCount = 0;
|
|
75
|
+
/** Which foot fires next: 0 = right, 1 = left. */
|
|
76
|
+
nextFoot = 0;
|
|
77
|
+
|
|
78
|
+
eyeHeight = 1.80;
|
|
79
|
+
|
|
80
|
+
/** Set true when crouch was released but a ceiling above prevents standing. */
|
|
81
|
+
crouchBlocked = false;
|
|
82
|
+
/** True while in crouch pose (independent of intent — see crouchBlocked). */
|
|
83
|
+
crouchActive = false;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Body posture. Set each tick by whichever layer owns motion: the
|
|
87
|
+
* base derives Stand/Crouch from intent; an active ability can
|
|
88
|
+
* force Prone (slide) or Hang (ledge-grab). See
|
|
89
|
+
* {@link FirstPersonPosture}. Drives eye-height target and gait
|
|
90
|
+
* gating (stride, footsteps, bob).
|
|
91
|
+
* @type {number}
|
|
92
|
+
*/
|
|
93
|
+
posture = FirstPersonPosture.Stand;
|
|
94
|
+
|
|
95
|
+
leanRollRad = 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Event surface. All signals are sent from fixedUpdate so listeners see
|
|
100
|
+
* consistent state across the same simulation tick.
|
|
101
|
+
*/
|
|
102
|
+
class FirstPersonSignals {
|
|
103
|
+
/** {side:"L"|"R", speed:number, surfaceTag:string?} */
|
|
104
|
+
onFootStep = new Signal();
|
|
105
|
+
/** {peakHeight:number} fired at impulse, after anticipation finishes. */
|
|
106
|
+
onJumpStart = new Signal();
|
|
107
|
+
/** No payload. */
|
|
108
|
+
onJumpApex = new Signal();
|
|
109
|
+
/** {reason:"jump"|"fall"} */
|
|
110
|
+
onLeaveGround = new Signal();
|
|
111
|
+
/** {verticalSpeed:number, kind:"soft"|"hard"} */
|
|
112
|
+
onLand = new Signal();
|
|
113
|
+
onCrouchEnter = new Signal();
|
|
114
|
+
onCrouchExit = new Signal();
|
|
115
|
+
onSprintStart = new Signal();
|
|
116
|
+
onSprintStop = new Signal();
|
|
117
|
+
/** {amplitude:number, rateHz:number} fired at peak inhale (phase 0.25). */
|
|
118
|
+
onBreathIn = new Signal();
|
|
119
|
+
/** {amplitude:number, rateHz:number} fired at peak exhale (phase 0.75). */
|
|
120
|
+
onBreathOut = new Signal();
|
|
121
|
+
/** {ledgeHeight:number} — fired when LedgeGrab activates. */
|
|
122
|
+
onLedgeGrab = new Signal();
|
|
123
|
+
/** {reason:"mantle-up"|"drop"|"slip"|"shuffle-off"} */
|
|
124
|
+
onLedgeRelease = new Signal();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* First-person camera + body controller. See DESIGN.md (sibling file) for
|
|
129
|
+
* the goals, layered architecture, and tuning rationale.
|
|
130
|
+
*
|
|
131
|
+
* Lifecycle: only `config` is serialized. `intent`, `state`, `pose`,
|
|
132
|
+
* `signals`, and `eyeEntity` are transient — rebuilt by the system on
|
|
133
|
+
* link.
|
|
134
|
+
*
|
|
135
|
+
* @author Alex Goldring
|
|
136
|
+
* @copyright Company Named Limited (c) 2026
|
|
137
|
+
*/
|
|
138
|
+
export class FirstPersonPlayerController {
|
|
139
|
+
static typeName = "FirstPersonPlayerController";
|
|
140
|
+
|
|
141
|
+
constructor() {
|
|
142
|
+
this.config = new FirstPersonPlayerControllerConfig();
|
|
143
|
+
this.intent = new FirstPersonIntent();
|
|
144
|
+
this.state = new FirstPersonState();
|
|
145
|
+
this.pose = new FirstPersonPose();
|
|
146
|
+
this.signals = new FirstPersonSignals();
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Mastery layer — registry of {@link MasteryEvaluator}s and the
|
|
150
|
+
* telemetry score they feed. Designers register evaluators here;
|
|
151
|
+
* the controller's system queries them at each decision point.
|
|
152
|
+
* Empty by default (no mastery effects unless the game adds them).
|
|
153
|
+
* @type {MasterySet}
|
|
154
|
+
*/
|
|
155
|
+
this.mastery = new MasterySet();
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Movement abilities (wall-run, mantle, slide, etc.). Empty by
|
|
159
|
+
* default — base locomotion is unchanged unless the game adds
|
|
160
|
+
* abilities here.
|
|
161
|
+
* @type {AbilitySet}
|
|
162
|
+
*/
|
|
163
|
+
this.abilities = new AbilitySet();
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* ID of the entity carrying the eye Transform + Camera. Created by
|
|
167
|
+
* the system on link if -1. Held as a plain number rather than
|
|
168
|
+
* EntityReference to keep serialization simple — the relationship
|
|
169
|
+
* is rebuilt on link anyway.
|
|
170
|
+
* @type {number}
|
|
171
|
+
*/
|
|
172
|
+
this.eyeEntity = -1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
toJSON() {
|
|
176
|
+
return { config: this.config.toJSON() };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
fromJSON(json) {
|
|
180
|
+
if (json && json.config) {
|
|
181
|
+
this.config.fromJSON(json.config);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @param {FirstPersonPlayerController} other
|
|
187
|
+
*/
|
|
188
|
+
copy(other) {
|
|
189
|
+
this.config.copy(other.config);
|
|
190
|
+
// intent/state/pose/signals/eyeEntity are transient
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
clone() {
|
|
194
|
+
const c = new FirstPersonPlayerController();
|
|
195
|
+
c.copy(this);
|
|
196
|
+
return c;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
equals(other) {
|
|
200
|
+
// config-only equality (the rest is runtime state)
|
|
201
|
+
return JSON.stringify(this.config.toJSON()) === JSON.stringify(other.config.toJSON());
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
hash() {
|
|
205
|
+
// hashing transient state is undesirable; the config is large; return 0
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
FirstPersonPlayerController.ActionState = FirstPersonActionState;
|
|
211
|
+
FirstPersonPlayerController.LocomotionMode = FirstPersonLocomotionMode;
|
|
@@ -40,8 +40,27 @@ export class FirstPersonPlayerControllerConfig {
|
|
|
40
40
|
crouchSpeed: number;
|
|
41
41
|
/** [0..1] how much intent influences velocity while airborne. */
|
|
42
42
|
airControl: number;
|
|
43
|
-
/**
|
|
44
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Half-life (seconds) for the mono-exponential approach to top
|
|
45
|
+
* sprint speed on the ground. The controller's ground-accel
|
|
46
|
+
* model is Hill-based:
|
|
47
|
+
* v(t) = v_max · (1 − e^(−t / τ))
|
|
48
|
+
* with τ = halfLife / ln 2. At each tick the velocity halves
|
|
49
|
+
* the remaining gap to `targetSpeed` every `halfLife` seconds.
|
|
50
|
+
*
|
|
51
|
+
* Default `0.116 s` gives ~95 % of `sprintSpeed` reached in
|
|
52
|
+
* 500 ms — the Apex Legends / Titanfall / modern CoD ramp
|
|
53
|
+
* window. The curve is naturally non-uniform: roughly 45 %
|
|
54
|
+
* of top speed in the first 100 ms, then the last 5 % takes
|
|
55
|
+
* 300 ms — so a quick tap-and-go still feels snappy while
|
|
56
|
+
* follow-up moves (slide, sprint-jump) require committed
|
|
57
|
+
* sprint time to be at top speed.
|
|
58
|
+
*
|
|
59
|
+
* Literature: Hill 1927 / Furusawa-Hill 1928 (force-velocity
|
|
60
|
+
* curve, original analytical formulation); Morin & Samozino
|
|
61
|
+
* 2016 (modern sprint profiling using the same mono-exp fit).
|
|
62
|
+
*/
|
|
63
|
+
groundAccelHalfLife: number;
|
|
45
64
|
/** m/s^2 horizontal deceleration toward 0 when intent is zero. */
|
|
46
65
|
groundDecel: number;
|
|
47
66
|
/** m/s^2 horizontal acceleration while airborne. */
|
|
@@ -191,8 +210,21 @@ export class FirstPersonPlayerControllerConfig {
|
|
|
191
210
|
* on the controller; unused if Slide is not in use.
|
|
192
211
|
*/
|
|
193
212
|
slide: {
|
|
194
|
-
/**
|
|
195
|
-
*
|
|
213
|
+
/**
|
|
214
|
+
* Required horizontal speed to start a slide. Below this,
|
|
215
|
+
* crouch + sprint just transitions to a normal crouched walk.
|
|
216
|
+
*
|
|
217
|
+
* The default — `7.5 m/s` — sits between walkSpeed (4.5) and
|
|
218
|
+
* sprintSpeed (9), at ~83 % of sprint. Earning the slide is
|
|
219
|
+
* enforced by the ground-accel model: with
|
|
220
|
+
* `groundAccelHalfLife = 0.116 s`, reaching this threshold
|
|
221
|
+
* from standstill takes ~300 ms of sustained sprint — well
|
|
222
|
+
* past a normal reaction window. Walking off a ledge or
|
|
223
|
+
* accelerating down a slope into a slide is explicitly
|
|
224
|
+
* supported: anything that brings the player's horizontal
|
|
225
|
+
* speed above this threshold lets crouch engage a slide.
|
|
226
|
+
* (Following Apex Legends' velocity-only gate at 200 hu/s.)
|
|
227
|
+
*/
|
|
196
228
|
minEntrySpeed: number;
|
|
197
229
|
/** Auto-exit threshold — slide ends when speed drops below this. */
|
|
198
230
|
endSpeed: number;
|
|
@@ -441,8 +473,27 @@ export class FirstPersonPlayerControllerConfig {
|
|
|
441
473
|
crouchSpeed: number;
|
|
442
474
|
/** [0..1] how much intent influences velocity while airborne. */
|
|
443
475
|
airControl: number;
|
|
444
|
-
/**
|
|
445
|
-
|
|
476
|
+
/**
|
|
477
|
+
* Half-life (seconds) for the mono-exponential approach to top
|
|
478
|
+
* sprint speed on the ground. The controller's ground-accel
|
|
479
|
+
* model is Hill-based:
|
|
480
|
+
* v(t) = v_max · (1 − e^(−t / τ))
|
|
481
|
+
* with τ = halfLife / ln 2. At each tick the velocity halves
|
|
482
|
+
* the remaining gap to `targetSpeed` every `halfLife` seconds.
|
|
483
|
+
*
|
|
484
|
+
* Default `0.116 s` gives ~95 % of `sprintSpeed` reached in
|
|
485
|
+
* 500 ms — the Apex Legends / Titanfall / modern CoD ramp
|
|
486
|
+
* window. The curve is naturally non-uniform: roughly 45 %
|
|
487
|
+
* of top speed in the first 100 ms, then the last 5 % takes
|
|
488
|
+
* 300 ms — so a quick tap-and-go still feels snappy while
|
|
489
|
+
* follow-up moves (slide, sprint-jump) require committed
|
|
490
|
+
* sprint time to be at top speed.
|
|
491
|
+
*
|
|
492
|
+
* Literature: Hill 1927 / Furusawa-Hill 1928 (force-velocity
|
|
493
|
+
* curve, original analytical formulation); Morin & Samozino
|
|
494
|
+
* 2016 (modern sprint profiling using the same mono-exp fit).
|
|
495
|
+
*/
|
|
496
|
+
groundAccelHalfLife: number;
|
|
446
497
|
/** m/s^2 horizontal deceleration toward 0 when intent is zero. */
|
|
447
498
|
groundDecel: number;
|
|
448
499
|
/** m/s^2 horizontal acceleration while airborne. */
|
|
@@ -561,8 +612,21 @@ export class FirstPersonPlayerControllerConfig {
|
|
|
561
612
|
nearWallMaxDistance: number;
|
|
562
613
|
};
|
|
563
614
|
slide: {
|
|
564
|
-
/**
|
|
565
|
-
*
|
|
615
|
+
/**
|
|
616
|
+
* Required horizontal speed to start a slide. Below this,
|
|
617
|
+
* crouch + sprint just transitions to a normal crouched walk.
|
|
618
|
+
*
|
|
619
|
+
* The default — `7.5 m/s` — sits between walkSpeed (4.5) and
|
|
620
|
+
* sprintSpeed (9), at ~83 % of sprint. Earning the slide is
|
|
621
|
+
* enforced by the ground-accel model: with
|
|
622
|
+
* `groundAccelHalfLife = 0.116 s`, reaching this threshold
|
|
623
|
+
* from standstill takes ~300 ms of sustained sprint — well
|
|
624
|
+
* past a normal reaction window. Walking off a ledge or
|
|
625
|
+
* accelerating down a slope into a slide is explicitly
|
|
626
|
+
* supported: anything that brings the player's horizontal
|
|
627
|
+
* speed above this threshold lets crouch engage a slide.
|
|
628
|
+
* (Following Apex Legends' velocity-only gate at 200 hu/s.)
|
|
629
|
+
*/
|
|
566
630
|
minEntrySpeed: number;
|
|
567
631
|
/** Auto-exit threshold — slide ends when speed drops below this. */
|
|
568
632
|
endSpeed: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirstPersonPlayerControllerConfig.d.ts","sourceRoot":"","sources":["../../../../../src/engine/control/first-person/FirstPersonPlayerControllerConfig.js"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH;IACI;QACI,qDAAqD;;QAErD,2BAA2B;;QAE3B;;yBAEiB;;QAEjB,8CAA8C;;QAE9C,iFAAiF;;QAEjF,oFAAoF;;QAEpF;;;;;;;;;WASG;;MAEL;IAMF;;;;QAII,iEAAiE;;QAEjE
|
|
1
|
+
{"version":3,"file":"FirstPersonPlayerControllerConfig.d.ts","sourceRoot":"","sources":["../../../../../src/engine/control/first-person/FirstPersonPlayerControllerConfig.js"],"names":[],"mappings":"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"}
|
|
@@ -45,8 +45,27 @@ export class FirstPersonPlayerControllerConfig {
|
|
|
45
45
|
crouchSpeed: 2.2,
|
|
46
46
|
/** [0..1] how much intent influences velocity while airborne. */
|
|
47
47
|
airControl: 0.5,
|
|
48
|
-
/**
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Half-life (seconds) for the mono-exponential approach to top
|
|
50
|
+
* sprint speed on the ground. The controller's ground-accel
|
|
51
|
+
* model is Hill-based:
|
|
52
|
+
* v(t) = v_max · (1 − e^(−t / τ))
|
|
53
|
+
* with τ = halfLife / ln 2. At each tick the velocity halves
|
|
54
|
+
* the remaining gap to `targetSpeed` every `halfLife` seconds.
|
|
55
|
+
*
|
|
56
|
+
* Default `0.116 s` gives ~95 % of `sprintSpeed` reached in
|
|
57
|
+
* 500 ms — the Apex Legends / Titanfall / modern CoD ramp
|
|
58
|
+
* window. The curve is naturally non-uniform: roughly 45 %
|
|
59
|
+
* of top speed in the first 100 ms, then the last 5 % takes
|
|
60
|
+
* 300 ms — so a quick tap-and-go still feels snappy while
|
|
61
|
+
* follow-up moves (slide, sprint-jump) require committed
|
|
62
|
+
* sprint time to be at top speed.
|
|
63
|
+
*
|
|
64
|
+
* Literature: Hill 1927 / Furusawa-Hill 1928 (force-velocity
|
|
65
|
+
* curve, original analytical formulation); Morin & Samozino
|
|
66
|
+
* 2016 (modern sprint profiling using the same mono-exp fit).
|
|
67
|
+
*/
|
|
68
|
+
groundAccelHalfLife: 0.116,
|
|
50
69
|
/** m/s^2 horizontal deceleration toward 0 when intent is zero. */
|
|
51
70
|
groundDecel: 75, // crisp stops, no skating
|
|
52
71
|
/** m/s^2 horizontal acceleration while airborne. */
|
|
@@ -204,9 +223,22 @@ export class FirstPersonPlayerControllerConfig {
|
|
|
204
223
|
* on the controller; unused if Slide is not in use.
|
|
205
224
|
*/
|
|
206
225
|
slide = {
|
|
207
|
-
/**
|
|
208
|
-
*
|
|
209
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Required horizontal speed to start a slide. Below this,
|
|
228
|
+
* crouch + sprint just transitions to a normal crouched walk.
|
|
229
|
+
*
|
|
230
|
+
* The default — `7.5 m/s` — sits between walkSpeed (4.5) and
|
|
231
|
+
* sprintSpeed (9), at ~83 % of sprint. Earning the slide is
|
|
232
|
+
* enforced by the ground-accel model: with
|
|
233
|
+
* `groundAccelHalfLife = 0.116 s`, reaching this threshold
|
|
234
|
+
* from standstill takes ~300 ms of sustained sprint — well
|
|
235
|
+
* past a normal reaction window. Walking off a ledge or
|
|
236
|
+
* accelerating down a slope into a slide is explicitly
|
|
237
|
+
* supported: anything that brings the player's horizontal
|
|
238
|
+
* speed above this threshold lets crouch engage a slide.
|
|
239
|
+
* (Following Apex Legends' velocity-only gate at 200 hu/s.)
|
|
240
|
+
*/
|
|
241
|
+
minEntrySpeed: 7.5,
|
|
210
242
|
/** Auto-exit threshold — slide ends when speed drops below this. */
|
|
211
243
|
endSpeed: 2.0,
|
|
212
244
|
/** Horizontal deceleration (m/s²) while sliding. Lower than the
|