@edv4h/usketch-plugin-character 0.6.1 → 0.8.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.
Files changed (39) hide show
  1. package/README.md +25 -2
  2. package/dist/character-layer.d.ts +15 -2
  3. package/dist/character-layer.d.ts.map +1 -1
  4. package/dist/character-layer.js +66 -5
  5. package/dist/character-layer.js.map +1 -1
  6. package/dist/character-physics.d.ts +68 -3
  7. package/dist/character-physics.d.ts.map +1 -1
  8. package/dist/character-physics.js +108 -9
  9. package/dist/character-physics.js.map +1 -1
  10. package/dist/character-runtime.d.ts.map +1 -1
  11. package/dist/character-runtime.js +18 -8
  12. package/dist/character-runtime.js.map +1 -1
  13. package/dist/character-service.d.ts +5 -0
  14. package/dist/character-service.d.ts.map +1 -1
  15. package/dist/character-service.js +2 -0
  16. package/dist/character-service.js.map +1 -1
  17. package/dist/character-size.d.ts +24 -0
  18. package/dist/character-size.d.ts.map +1 -0
  19. package/dist/character-size.js +26 -0
  20. package/dist/character-size.js.map +1 -0
  21. package/dist/character-store.d.ts +6 -0
  22. package/dist/character-store.d.ts.map +1 -1
  23. package/dist/character-store.js +18 -1
  24. package/dist/character-store.js.map +1 -1
  25. package/dist/character-types.d.ts +6 -0
  26. package/dist/character-types.d.ts.map +1 -1
  27. package/dist/character-types.js.map +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +1 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/plugin.d.ts +6 -0
  33. package/dist/plugin.d.ts.map +1 -1
  34. package/dist/plugin.js +1 -0
  35. package/dist/plugin.js.map +1 -1
  36. package/dist/register-character-hud.d.ts.map +1 -1
  37. package/dist/register-character-hud.js +69 -0
  38. package/dist/register-character-hud.js.map +1 -1
  39. package/package.json +1 -1
package/README.md CHANGED
@@ -36,11 +36,18 @@ createCharacterPlugin({
36
36
  as CSS `rotate()` and the viewport `rotation`.
37
37
  - **Render props**: `heading` (world), `screenHeading` (= `heading + viewport.rotation`,
38
38
  what to rotate a top-down sprite by), `moving`, `speed`, `steering`, `slip`,
39
- `drifting`, `zoom`, `appearance`, `name`, `isSelf`, `id`, `elevation`, `z`, `altitude`,
39
+ `drifting`, `zoom`, `size`, `appearance`, `name`, `isSelf`, `id`, `elevation`, `z`, `altitude`,
40
40
  `airborne`, `verticalSpeed`, `hovering`, `hoverGauge` (0 … 1, `null` with hovering
41
41
  off). Each character is placed centered on its screen position
42
42
  in one screen-space layer, so self and remote characters look the same under zoom
43
43
  and camera rotation.
44
+ - **Size** (`size` option / `setSize`, only the look): `size` in the render props is the
45
+ screen px to draw the body at. `{ scale: "screen", value: 36 }` (the default) keeps it
46
+ the same on screen at every zoom, like a map pin. `{ scale: "world", value: 24 }`
47
+ makes it 24 world units, so it scales with the ground (zooming out shrinks it like the
48
+ terrain), kept within `minPx` (default 12) … `maxPx` (default 480) on screen. The
49
+ physics still treat a character as a point: `motion.maxStep` and `jump.height` decide
50
+ what it climbs, not its size.
44
51
  - **Control schemes**
45
52
  - `directional` — keys move in screen directions (camera-relative); the heading
46
53
  turns toward the movement at `turnRate`. With the `rotate` camera, left/right
@@ -141,6 +148,21 @@ createCharacterPlugin({
141
148
  is stepped or jumped onto and stood on (`blockingHeight` / `standingHeight`). A move
142
149
  is checked in stretches of at most `SWEEP_STEP` (4) world units (`sweepToGround`), so
143
150
  a fast character or vehicle can't skip a thin wall within one frame.
151
+ - **Slopes**: on smooth ground (e.g. the elevation plugin's hills) the step alone never
152
+ binds, so `motion.maxSlope` (degrees, default 0 = no limit, HUD "登れる坂の角度")
153
+ limits the slope walked UP; steeper ground blocks like a cliff, and standing on it
154
+ slides down (`slideOnSteep`, half the move speed). The slope is measured with a
155
+ minmod over ±8 world units (`groundSlope`), so a step — the map's flat terrain
156
+ levels, a box's edge — reads as flat and stays `maxStep`'s business. In the air the
157
+ limit doesn't apply (jump up it).
158
+ - **Jumping off a slope** leaves between straight up and the ground's normal:
159
+ `jump.slopeRatio` (0 = up, 1 = the normal, default 0.5, HUD "坂でのジャンプの向き").
160
+ The sideways part (`pose.vx` / `vy`) carries through the air (`pushInAir`) and is
161
+ dropped on landing. Air jumps and the drift hop go straight up.
162
+ - **Leaning**: under a 3D view (Mode 7) the layer lays each character (and its shadow)
163
+ along the ground under it, by `motion.slopeTilt` (0 … 1, default 1, HUD
164
+ "坂に合わせて傾ける") × the 3D transition (`slopeTiltTransform`). The flat board is
165
+ unchanged.
144
166
  - **Input**: held keys are tracked from window `keydown`/`keyup` (capture phase,
145
167
  `event.code`), swallowed while the character is on so single-letter tool
146
168
  shortcuts don't fire. Typing in text fields, and key chords with Ctrl/Cmd/Alt,
@@ -149,7 +171,7 @@ createCharacterPlugin({
149
171
  ## HUD
150
172
 
151
173
  A "キャラクター" settings group (on/off, camera, scheme, drift, drift key, drift hop, jump, jump key, jump height, air
152
- jumps, hover, hover time, hover thrust, speed, turn rate, climbable step, screen position, camera lag) plus actions to toggle the
174
+ jumps, jump direction on slopes, hover, hover time, hover thrust, speed, turn rate, climbable step, climbable slope, lean on slopes, size and its scale, screen position, camera lag) plus actions to toggle the
153
175
  character, to place it at the screen position, and to jump.
154
176
 
155
177
  ## Service
@@ -167,6 +189,7 @@ api?.setJump({ airJumps: 1, hover: { duration: 2 } }); // double jump + hover (h
167
189
  api?.jump();
168
190
  api?.setKeys({ ...api.getKeys(), drift: ["KeyX"] }); // KeyboardEvent.code values
169
191
  api?.setShadowRenderer(myShadow);
192
+ api?.setSize({ scale: "world", value: 24, minPx: 20 }); // scale with the ground (render props `size`)
170
193
  api?.getPose(); // { x, y, heading, speed, moving, steering, slip, drift, z, vz, airborne, hovering, hoverGauge }
171
194
  api?.setVirtualInput({ throttle: 0.8, steer: -0.3 }); // touch stick; null on release
172
195
  api?.setVirtualInput({ throttle: 0.8, steer: 0, jump: true }); // + jump button held
@@ -1,9 +1,22 @@
1
1
  import { type Viewport } from "@edv4h/usketch-shared";
2
- import type { HeightAt } from "./character-physics.js";
2
+ import { type HeightAt } from "./character-physics.js";
3
3
  import { type CharacterStore } from "./character-store.js";
4
- export declare function CharacterLayer({ store, viewport, heightAt, }: {
4
+ /** The 3D view's transition, `0` (flat) … `1` (3D) (see `view3dService` in shared). */
5
+ export interface CharacterView3d {
6
+ amount(): number;
7
+ subscribe(listener: () => void): () => void;
8
+ }
9
+ /**
10
+ * CSS that lays a flat (top-down) sprite along ground whose gradient on screen is
11
+ * `(gx, gy)` (rise per px; height is toward the viewer, `translateZ`): the rotation that
12
+ * takes the sprite's up (+z) to the ground's normal. `null` on flat ground. Pure.
13
+ */
14
+ export declare function slopeTiltTransform(gx: number, gy: number): string | null;
15
+ export declare function CharacterLayer({ store, viewport, heightAt, view3d, }: {
5
16
  store: CharacterStore;
6
17
  viewport: Viewport;
7
18
  heightAt?: HeightAt;
19
+ /** The 3D view: characters lean with the slope only while it is (partly) 3D. */
20
+ view3d?: CharacterView3d;
8
21
  }): import("react").JSX.Element | null;
9
22
  //# sourceMappingURL=character-layer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"character-layer.d.ts","sourceRoot":"","sources":["../src/character-layer.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,QAAQ,EAA4C,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,KAAK,cAAc,EAAgB,MAAM,sBAAsB,CAAC;AA0DzE,wBAAgB,cAAc,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,QAAQ,GACR,EAAE;IACF,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACpB,sCA4GA"}
1
+ {"version":3,"file":"character-layer.d.ts","sourceRoot":"","sources":["../src/character-layer.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,QAAQ,EAA4C,MAAM,uBAAuB,CAAC;AAEhG,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAAE,KAAK,cAAc,EAAgB,MAAM,sBAAsB,CAAC;AAMzE,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC/B,MAAM,IAAI,MAAM,CAAC;IACjB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC5C;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA+BxE;AA2DD,wBAAgB,cAAc,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,MAAkB,GAClB,EAAE;IACF,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,gFAAgF;IAChF,MAAM,CAAC,EAAE,eAAe,CAAC;CACzB,sCAmIA"}
@@ -8,10 +8,51 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  // height — under a 3D view (Mode 7) a jump lifts the body off its shadow.
9
9
  import { viewportRotation, worldToScreen, wrapDeg } from "@edv4h/usketch-shared";
10
10
  import { useSyncExternalStore } from "react";
11
+ import { groundSlope } from "./character-physics.js";
12
+ import { characterSizePx } from "./character-size.js";
11
13
  import { hoverGaugeOf } from "./character-store.js";
12
14
  /** Screen px a shadow floats above its ground, so it doesn't z-fight a raised terrain top. */
13
15
  const SHADOW_LIFT_PX = 0.5;
14
- function Spot({ shadow = false, x, y, lift, renderer, props, }) {
16
+ const NO_VIEW3D = { amount: () => 0, subscribe: () => () => { } };
17
+ /**
18
+ * CSS that lays a flat (top-down) sprite along ground whose gradient on screen is
19
+ * `(gx, gy)` (rise per px; height is toward the viewer, `translateZ`): the rotation that
20
+ * takes the sprite's up (+z) to the ground's normal. `null` on flat ground. Pure.
21
+ */
22
+ export function slopeTiltTransform(gx, gy) {
23
+ const m = Math.hypot(gx, gy);
24
+ if (!(m > 1e-4) || !Number.isFinite(m))
25
+ return null;
26
+ const len = Math.hypot(m, 1);
27
+ const c = 1 / len; // cos of the tilt
28
+ const s = m / len; // sin of the tilt
29
+ const ax = gy / m; // rotation axis: up × normal, normalized
30
+ const ay = -gx / m;
31
+ const k = 1 - c;
32
+ const r = (v) => Math.round(v * 1e6) / 1e6;
33
+ // matrix3d is column-major.
34
+ return `matrix3d(${[
35
+ c + k * ax * ax,
36
+ k * ax * ay,
37
+ -s * ay,
38
+ 0,
39
+ k * ax * ay,
40
+ c + k * ay * ay,
41
+ s * ax,
42
+ 0,
43
+ s * ay,
44
+ -s * ax,
45
+ c,
46
+ 0,
47
+ 0,
48
+ 0,
49
+ 0,
50
+ 1,
51
+ ]
52
+ .map(r)
53
+ .join(", ")})`;
54
+ }
55
+ function Spot({ shadow = false, x, y, lift, tilt, renderer, props, }) {
15
56
  return (_jsx("div", { "data-character-id": shadow ? undefined : props.id, "data-character-shadow": shadow ? props.id : undefined, style: {
16
57
  position: "absolute",
17
58
  left: x,
@@ -29,23 +70,40 @@ function Spot({ shadow = false, x, y, lift, renderer, props, }) {
29
70
  position: "absolute",
30
71
  left: 0,
31
72
  top: 0,
32
- transform: "translate(-50%, -50%)",
73
+ // Centred on the spot, then leaned about it (the box's corner is the spot).
74
+ transform: tilt ? `${tilt} translate(-50%, -50%)` : "translate(-50%, -50%)",
75
+ transformOrigin: tilt ? "0 0" : undefined,
33
76
  transformStyle: "preserve-3d",
34
77
  }, children: renderer(props) }) }));
35
78
  }
36
- export function CharacterLayer({ store, viewport, heightAt, }) {
79
+ export function CharacterLayer({ store, viewport, heightAt, view3d = NO_VIEW3D, }) {
37
80
  const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);
81
+ const amount = useSyncExternalStore(view3d.subscribe, view3d.amount, view3d.amount);
38
82
  const renderer = store.getRenderer();
39
83
  if (!renderer)
40
84
  return null; // headless: no look supplied → draw nothing
41
85
  const shadowRenderer = store.getShadowRenderer();
42
86
  const rot = viewportRotation(viewport);
87
+ const size = characterSizePx(state.size, viewport.zoom);
88
+ // Lean on screen: the world gradient turned with the camera (zoom scales the ground
89
+ // and the height alike, so it stays a rise per px), scaled by the tuning and the 3D
90
+ // transition (a lean on the flat board would only squash the sprite).
91
+ const lean = Math.max(0, Math.min(1, state.motion.slopeTilt)) * Math.max(0, Math.min(1, amount));
92
+ const cosR = Math.cos((rot * Math.PI) / 180);
93
+ const sinR = Math.sin((rot * Math.PI) / 180);
94
+ const tiltAt = (x, y) => {
95
+ if (!(lean > 0) || !heightAt)
96
+ return null;
97
+ const g = groundSlope(heightAt, x, y);
98
+ return slopeTiltTransform((g.x * cosR - g.y * sinR) * lean, (g.x * sinR + g.y * cosR) * lean);
99
+ };
43
100
  const bodies = [];
44
101
  for (const r of state.remotes.values()) {
45
102
  const elevation = heightAt?.(r.x, r.y) ?? 0;
46
103
  const z = Math.max(elevation, r.z ?? elevation);
47
104
  bodies.push({
48
105
  at: worldToScreen(r.x, r.y, viewport),
106
+ tilt: tiltAt(r.x, r.y),
49
107
  props: {
50
108
  id: r.id,
51
109
  isSelf: false,
@@ -57,6 +115,7 @@ export function CharacterLayer({ store, viewport, heightAt, }) {
57
115
  slip: r.slip,
58
116
  drifting: r.drifting,
59
117
  zoom: viewport.zoom,
118
+ size,
60
119
  appearance: r.appearance,
61
120
  name: r.name,
62
121
  elevation,
@@ -77,6 +136,7 @@ export function CharacterLayer({ store, viewport, heightAt, }) {
77
136
  // Self last → drawn on top of the others.
78
137
  bodies.push({
79
138
  at: worldToScreen(pose.x, pose.y, viewport),
139
+ tilt: tiltAt(pose.x, pose.y),
80
140
  props: {
81
141
  id: "self",
82
142
  isSelf: true,
@@ -88,6 +148,7 @@ export function CharacterLayer({ store, viewport, heightAt, }) {
88
148
  slip: pose.slip,
89
149
  drifting: pose.drift !== 0,
90
150
  zoom: viewport.zoom,
151
+ size,
91
152
  appearance: state.appearance,
92
153
  name: state.name,
93
154
  elevation,
@@ -105,9 +166,9 @@ export function CharacterLayer({ store, viewport, heightAt, }) {
105
166
  const key = (props) => (props.isSelf ? "self" : `remote-${props.id}`);
106
167
  // Shadows first, so every body draws over every shadow.
107
168
  const shadowSpots = shadowRenderer
108
- ? bodies.map(({ at, props }) => (_jsx(Spot, { shadow: true, x: at.x, y: at.y, lift: props.elevation * viewport.zoom + SHADOW_LIFT_PX, renderer: shadowRenderer, props: props }, `shadow-${key(props)}`)))
169
+ ? bodies.map(({ at, tilt, props }) => (_jsx(Spot, { shadow: true, x: at.x, y: at.y, lift: props.elevation * viewport.zoom + SHADOW_LIFT_PX, tilt: tilt, renderer: shadowRenderer, props: props }, `shadow-${key(props)}`)))
109
170
  : [];
110
- const bodySpots = bodies.map(({ at, props }) => (_jsx(Spot, { x: at.x, y: at.y, lift: props.z * viewport.zoom, renderer: renderer, props: props }, key(props))));
171
+ const bodySpots = bodies.map(({ at, tilt, props }) => (_jsx(Spot, { x: at.x, y: at.y, lift: props.z * viewport.zoom, tilt: tilt, renderer: renderer, props: props }, key(props))));
111
172
  return (_jsxs("div", { style: {
112
173
  position: "absolute",
113
174
  inset: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"character-layer.js","sourceRoot":"","sources":["../src/character-layer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"character-layer.js","sourceRoot":"","sources":["../src/character-layer.tsx"],"names":[],"mappings":"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"}
@@ -49,6 +49,13 @@ export interface CharacterPose {
49
49
  z: number;
50
50
  /** Vertical speed, world units / s (positive = rising). */
51
51
  vz: number;
52
+ /**
53
+ * Sideways speed in the air, world units / s: a jump off a slope leaves the ground
54
+ * partly along it (see `JumpParams.slopeRatio`). Added to the move while airborne,
55
+ * cleared on landing.
56
+ */
57
+ vx: number;
58
+ vy: number;
52
59
  /** Whether the character is in the air (jumping or falling). */
53
60
  airborne: boolean;
54
61
  /** Hovering: the jump key held while falling slows the descent (see `HoverParams`). */
@@ -75,6 +82,18 @@ export interface MotionParams {
75
82
  /** Tallest step up the character can climb, world units (ground height, e.g. the map
76
83
  * plugin's raised terrain). Stepping DOWN is always allowed. */
77
84
  maxStep: number;
85
+ /**
86
+ * Steepest slope the character can walk up, degrees; `0` = no limit. Steeper ground
87
+ * (an elevation hill) blocks the way up like a cliff, and standing on it slides down.
88
+ * Steps (the map's terrain levels) are left to `maxStep`: see {@link groundSlope}.
89
+ * A jump still clears it.
90
+ */
91
+ maxSlope: number;
92
+ /**
93
+ * How far the character leans with the ground under it in a 3D view (Mode 7), `0`
94
+ * (stays level) … `1` (lies along the slope). Only the look.
95
+ */
96
+ slopeTilt: number;
78
97
  }
79
98
  export declare const DEFAULT_MOTION: MotionParams;
80
99
  /**
@@ -161,7 +180,41 @@ export declare function standingHeight(ground: number, solid: number | null): nu
161
180
  * a wall at an angle glides along it; failing both, the character stays put (a
162
181
  * vehicle also loses its speed, as if it bumped the wall). The heading still turns.
163
182
  */
164
- export declare function constrainToGround(prev: CharacterPose, next: CharacterPose, heightAt: HeightAt, maxStep: number): CharacterPose;
183
+ export declare function constrainToGround(prev: CharacterPose, next: CharacterPose, heightAt: HeightAt, maxStep: number, maxSlope?: number): CharacterPose;
184
+ /** Half-width of the stencil that measures the ground's slope, world units. */
185
+ export declare const SLOPE_PROBE = 8;
186
+ /**
187
+ * The ground's gradient at a point (rise per world unit along x and y), measured
188
+ * {@link SLOPE_PROBE} units to either side with a minmod: a smooth slope reads as its
189
+ * gradient, a step — or a wall — next to the point as flat. Pure.
190
+ */
191
+ export declare function groundSlope(heightAt: HeightAt, x: number, y: number, probe?: number): {
192
+ x: number;
193
+ y: number;
194
+ };
195
+ /** Sliding speed down ground steeper than `maxSlope`, as a fraction of `moveSpeed`. */
196
+ export declare const SLIDE_SPEED = 0.5;
197
+ /**
198
+ * On ground steeper than `motion.maxSlope`, slide `dt` seconds downhill (at
199
+ * {@link SLIDE_SPEED} × `moveSpeed`); walls still block (`blockAt`). Unchanged in the
200
+ * air, on gentle ground, or with no slope limit. Pure.
201
+ */
202
+ export declare function slideOnSteep(pose: CharacterPose, heightAt: HeightAt, blockAt: HeightAt, motion: Pick<MotionParams, "maxSlope" | "maxStep" | "moveSpeed">, dt: number): CharacterPose;
203
+ /** In the air, add the sideways speed a slope jump left (`vx` / `vy`) to a move. Pure. */
204
+ export declare function pushInAir(prev: CharacterPose, next: CharacterPose, dt: number): CharacterPose;
205
+ /**
206
+ * A jump's take-off velocity off ground with gradient `slope`: `speed` along a direction
207
+ * between straight up (`ratio` 0) and the ground's normal (`ratio` 1). On flat ground
208
+ * it is straight up whatever the ratio. Pure.
209
+ */
210
+ export declare function takeoffVelocity(speed: number, slope: {
211
+ x: number;
212
+ y: number;
213
+ }, ratio: number): {
214
+ vx: number;
215
+ vy: number;
216
+ vz: number;
217
+ };
165
218
  /** Longest stretch `sweepToGround` checks in one go, world units: thin walls (a stood-up
166
219
  * shape's base) stay solid even when a frame moves farther than they are thick. */
167
220
  export declare const SWEEP_STEP = 4;
@@ -171,7 +224,7 @@ export declare const SWEEP_STEP = 4;
171
224
  * first stretch that is fully blocked. At most 256 stretches: a move longer than
172
225
  * 256 × `step` (far beyond a frame's, whose `dt` the runtime caps) takes longer ones. Pure.
173
226
  */
174
- export declare function sweepToGround(prev: CharacterPose, next: CharacterPose, heightAt: HeightAt, maxStep: number, step?: number): CharacterPose;
227
+ export declare function sweepToGround(prev: CharacterPose, next: CharacterPose, heightAt: HeightAt, maxStep: number, step?: number, maxSlope?: number): CharacterPose;
175
228
  /**
176
229
  * Jump tuning. The arc is set by its apex `height` and `timeToApex`; gravity and the
177
230
  * take-off speed follow from them (see `jumpArc`).
@@ -195,6 +248,12 @@ export interface JumpParams {
195
248
  airJumpRatio: number;
196
249
  /** Hovering while the key is held in a fall, or `null` = off. */
197
250
  hover: HoverParams | null;
251
+ /**
252
+ * Which way a jump off a slope leaves the ground: `0` = straight up, `1` = along the
253
+ * ground's normal (a steep slope throws it far sideways), in between = a blend. Air
254
+ * jumps and the drift hop always go straight up.
255
+ */
256
+ slopeRatio: number;
198
257
  }
199
258
  /**
200
259
  * Hover tuning. Once the air jumps are used up (or there are none), holding the jump key
@@ -258,8 +317,14 @@ export declare const IDLE_JUMP: JumpTimers;
258
317
  * hop that high, under the same gravity.
259
318
  * - In the air, gravity pulls the feet down (`fallGravity` × while falling) until they
260
319
  * meet the ground. Without `jump` the default arc's gravity still drives falls.
320
+ * - A jump from the ground leaves along `slope` (the ground's gradient there) as far as
321
+ * `jump.slopeRatio` says: part of its speed goes sideways (`vx` / `vy`, see
322
+ * `pushInAir`) until it lands.
261
323
  */
262
- export declare function stepVertical(pose: CharacterPose, timers: JumpTimers, input: MoveInput, dt: number, ground: number, maxStep: number, jump: JumpParams | null, hopHeight?: number): {
324
+ export declare function stepVertical(pose: CharacterPose, timers: JumpTimers, input: MoveInput, dt: number, ground: number, maxStep: number, jump: JumpParams | null, hopHeight?: number, slope?: {
325
+ x: number;
326
+ y: number;
327
+ }): {
263
328
  pose: CharacterPose;
264
329
  timers: JumpTimers;
265
330
  };
@@ -1 +1 @@
1
- {"version":3,"file":"character-physics.d.ts","sourceRoot":"","sources":["../src/character-physics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"character-physics.d.ts","sourceRoot":"","sources":["../src/character-physics.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,8 @@ export const ORIGIN_POSE = {
23
23
  drift: 0,
24
24
  z: 0,
25
25
  vz: 0,
26
+ vx: 0,
27
+ vy: 0,
26
28
  airborne: false,
27
29
  hovering: false,
28
30
  hoverGauge: 1,
@@ -34,6 +36,8 @@ export const DEFAULT_MOTION = {
34
36
  friction: 700,
35
37
  reverseRatio: 0.5,
36
38
  maxStep: 24,
39
+ maxSlope: 0,
40
+ slopeTilt: 1,
37
41
  };
38
42
  export const DEFAULT_DRIFT = {
39
43
  startSpeed: 0.27,
@@ -121,6 +125,8 @@ export function stepCharacter(pose, input, dt, scheme, params, cameraRotation =
121
125
  drift: 0,
122
126
  z: pose.z,
123
127
  vz: pose.vz,
128
+ vx: pose.vx,
129
+ vy: pose.vy,
124
130
  airborne: pose.airborne,
125
131
  hovering: pose.hovering,
126
132
  hoverGauge: pose.hoverGauge,
@@ -163,6 +169,8 @@ export function stepCharacter(pose, input, dt, scheme, params, cameraRotation =
163
169
  drift,
164
170
  z: pose.z,
165
171
  vz: pose.vz,
172
+ vx: pose.vx,
173
+ vy: pose.vy,
166
174
  airborne: pose.airborne,
167
175
  hovering: pose.hovering,
168
176
  hoverGauge: pose.hoverGauge,
@@ -189,14 +197,16 @@ export function standingHeight(ground, solid) {
189
197
  * a wall at an angle glides along it; failing both, the character stays put (a
190
198
  * vehicle also loses its speed, as if it bumped the wall). The heading still turns.
191
199
  */
192
- export function constrainToGround(prev, next, heightAt, maxStep) {
200
+ export function constrainToGround(prev, next, heightAt, maxStep, maxSlope = 0) {
193
201
  if (next.x === prev.x && next.y === prev.y)
194
202
  return next;
195
203
  // Measured from the feet: on the ground that is the ground itself, in the air it is
196
204
  // higher, so a jump clears cliffs the step alone can't.
197
205
  const feet = Math.max(heightAt(prev.x, prev.y), prev.z ?? 0);
198
206
  const limit = feet + Math.max(0, maxStep);
199
- const ok = (x, y) => heightAt(x, y) <= limit;
207
+ // The slope only binds on the ground: in the air the feet clear it like a cliff.
208
+ const steep = !prev.airborne && maxSlope > 0 ? slopeLimit(maxSlope) : Number.POSITIVE_INFINITY;
209
+ const ok = (x, y) => heightAt(x, y) <= limit && risingSlope(heightAt, prev, x, y) <= steep;
200
210
  if (ok(next.x, next.y))
201
211
  return next;
202
212
  if (next.x !== prev.x && ok(next.x, prev.y))
@@ -205,6 +215,86 @@ export function constrainToGround(prev, next, heightAt, maxStep) {
205
215
  return { ...next, x: prev.x };
206
216
  return { ...next, x: prev.x, y: prev.y, speed: 0, moving: false };
207
217
  }
218
+ /** Half-width of the stencil that measures the ground's slope, world units. */
219
+ export const SLOPE_PROBE = 8;
220
+ /**
221
+ * The smaller of two same-signed differences, 0 when they disagree (a "minmod"). Taken
222
+ * over the stretch before and after a point, a smooth slope keeps its gradient while a
223
+ * step (flat on one side of it) gives 0: steps stay `maxStep`'s business.
224
+ */
225
+ function minmod(a, b) {
226
+ if (!(a * b > 0))
227
+ return 0;
228
+ return Math.abs(a) < Math.abs(b) ? a : b;
229
+ }
230
+ /** `tan(maxSlope)` for a limit in degrees (capped below vertical). */
231
+ function slopeLimit(maxSlope) {
232
+ return Math.tan((Math.min(89, maxSlope) * Math.PI) / 180);
233
+ }
234
+ /**
235
+ * The ground's gradient at a point (rise per world unit along x and y), measured
236
+ * {@link SLOPE_PROBE} units to either side with a minmod: a smooth slope reads as its
237
+ * gradient, a step — or a wall — next to the point as flat. Pure.
238
+ */
239
+ export function groundSlope(heightAt, x, y, probe = SLOPE_PROBE) {
240
+ const h = heightAt(x, y);
241
+ if (!Number.isFinite(h))
242
+ return { x: 0, y: 0 };
243
+ const gx = minmod(h - heightAt(x - probe, y), heightAt(x + probe, y) - h) / probe;
244
+ const gy = minmod(h - heightAt(x, y - probe), heightAt(x, y + probe) - h) / probe;
245
+ return { x: Number.isFinite(gx) ? gx : 0, y: Number.isFinite(gy) ? gy : 0 };
246
+ }
247
+ /** The ground's rise per unit at `(x, y)` along the way from `from` to it (0 = flat). */
248
+ function risingSlope(heightAt, from, x, y) {
249
+ const d = Math.hypot(x - from.x, y - from.y);
250
+ if (!(d > 0))
251
+ return 0;
252
+ const ux = ((x - from.x) / d) * SLOPE_PROBE;
253
+ const uy = ((y - from.y) / d) * SLOPE_PROBE;
254
+ const h = heightAt(x, y);
255
+ const rise = minmod(h - heightAt(x - ux, y - uy), heightAt(x + ux, y + uy) - h) / SLOPE_PROBE;
256
+ return Number.isFinite(rise) ? rise : 0;
257
+ }
258
+ /** Sliding speed down ground steeper than `maxSlope`, as a fraction of `moveSpeed`. */
259
+ export const SLIDE_SPEED = 0.5;
260
+ /**
261
+ * On ground steeper than `motion.maxSlope`, slide `dt` seconds downhill (at
262
+ * {@link SLIDE_SPEED} × `moveSpeed`); walls still block (`blockAt`). Unchanged in the
263
+ * air, on gentle ground, or with no slope limit. Pure.
264
+ */
265
+ export function slideOnSteep(pose, heightAt, blockAt, motion, dt) {
266
+ if (pose.airborne || !(motion.maxSlope > 0) || !(dt > 0))
267
+ return pose;
268
+ const g = groundSlope(heightAt, pose.x, pose.y);
269
+ const m = Math.hypot(g.x, g.y);
270
+ if (!(m > slopeLimit(motion.maxSlope)))
271
+ return pose;
272
+ const d = (motion.moveSpeed * SLIDE_SPEED * dt) / m;
273
+ const next = { ...pose, x: pose.x - g.x * d, y: pose.y - g.y * d };
274
+ const moved = sweepToGround(pose, next, blockAt, motion.maxStep);
275
+ return moved.x === pose.x && moved.y === pose.y ? pose : moved;
276
+ }
277
+ /** In the air, add the sideways speed a slope jump left (`vx` / `vy`) to a move. Pure. */
278
+ export function pushInAir(prev, next, dt) {
279
+ if (!prev.airborne || !(dt > 0) || (prev.vx === 0 && prev.vy === 0))
280
+ return next;
281
+ return { ...next, x: next.x + prev.vx * dt, y: next.y + prev.vy * dt };
282
+ }
283
+ /**
284
+ * A jump's take-off velocity off ground with gradient `slope`: `speed` along a direction
285
+ * between straight up (`ratio` 0) and the ground's normal (`ratio` 1). On flat ground
286
+ * it is straight up whatever the ratio. Pure.
287
+ */
288
+ export function takeoffVelocity(speed, slope, ratio) {
289
+ const k = Number.isFinite(ratio) ? Math.min(1, Math.max(0, ratio)) : 0;
290
+ const nl = Math.hypot(slope.x, slope.y, 1);
291
+ const dx = (-slope.x / nl) * k;
292
+ const dy = (-slope.y / nl) * k;
293
+ const dz = 1 - k + k / nl;
294
+ const len = Math.hypot(dx, dy, dz);
295
+ // (`+ 0` turns a -0 from a flat ground into 0.)
296
+ return { vx: (speed * dx) / len + 0, vy: (speed * dy) / len + 0, vz: (speed * dz) / len };
297
+ }
208
298
  /** Longest stretch `sweepToGround` checks in one go, world units: thin walls (a stood-up
209
299
  * shape's base) stay solid even when a frame moves farther than they are thick. */
210
300
  export const SWEEP_STEP = 4;
@@ -214,16 +304,16 @@ export const SWEEP_STEP = 4;
214
304
  * first stretch that is fully blocked. At most 256 stretches: a move longer than
215
305
  * 256 × `step` (far beyond a frame's, whose `dt` the runtime caps) takes longer ones. Pure.
216
306
  */
217
- export function sweepToGround(prev, next, heightAt, maxStep, step = SWEEP_STEP) {
307
+ export function sweepToGround(prev, next, heightAt, maxStep, step = SWEEP_STEP, maxSlope = 0) {
218
308
  const dx = next.x - prev.x;
219
309
  const dy = next.y - prev.y;
220
310
  const n = Math.min(256, Math.ceil(Math.hypot(dx, dy) / Math.max(0.5, step)));
221
311
  if (n <= 1)
222
- return constrainToGround(prev, next, heightAt, maxStep);
312
+ return constrainToGround(prev, next, heightAt, maxStep, maxSlope);
223
313
  let cur = prev;
224
314
  for (let i = 0; i < n; i++) {
225
315
  const target = { ...next, x: cur.x + dx / n, y: cur.y + dy / n };
226
- const moved = constrainToGround(cur, target, heightAt, maxStep);
316
+ const moved = constrainToGround(cur, target, heightAt, maxStep, maxSlope);
227
317
  if (moved.x === cur.x && moved.y === cur.y)
228
318
  return moved;
229
319
  cur = moved;
@@ -247,6 +337,7 @@ export const DEFAULT_JUMP = {
247
337
  airJumps: 0,
248
338
  airJumpRatio: 0.8,
249
339
  hover: null,
340
+ slopeRatio: 0.5,
250
341
  };
251
342
  /** Gravity (world units / s²) and take-off speed (world units / s) of a jump. */
252
343
  export function jumpArc(params) {
@@ -260,6 +351,7 @@ export const IDLE_JUMP = {
260
351
  cut: false,
261
352
  airJumpsUsed: 0,
262
353
  };
354
+ const FLAT = { x: 0, y: 0 };
263
355
  /**
264
356
  * Advance the vertical motion by `dt` seconds, after the horizontal step put the
265
357
  * character over ground of height `ground`.
@@ -279,11 +371,14 @@ export const IDLE_JUMP = {
279
371
  * hop that high, under the same gravity.
280
372
  * - In the air, gravity pulls the feet down (`fallGravity` × while falling) until they
281
373
  * meet the ground. Without `jump` the default arc's gravity still drives falls.
374
+ * - A jump from the ground leaves along `slope` (the ground's gradient there) as far as
375
+ * `jump.slopeRatio` says: part of its speed goes sideways (`vx` / `vy`, see
376
+ * `pushInAir`) until it lands.
282
377
  */
283
- export function stepVertical(pose, timers, input, dt, ground, maxStep, jump, hopHeight = 0) {
378
+ export function stepVertical(pose, timers, input, dt, ground, maxStep, jump, hopHeight = 0, slope = FLAT) {
284
379
  if (dt <= 0)
285
380
  return { pose, timers };
286
- let { z, vz, airborne, hoverGauge } = pose;
381
+ let { z, vz, vx, vy, airborne, hoverGauge } = pose;
287
382
  let hovering = false;
288
383
  let { sinceGround, sincePress, jumped, cut, airJumpsUsed } = timers;
289
384
  if (!airborne) {
@@ -316,7 +411,7 @@ export function stepVertical(pose, timers, input, dt, ground, maxStep, jump, hop
316
411
  const fromGround = !airborne || (!jumped && sinceGround <= jump.coyoteTime);
317
412
  if (sincePress <= jump.bufferTime && fromGround) {
318
413
  airborne = true;
319
- vz = arc.speed;
414
+ ({ vx, vy, vz } = takeoffVelocity(arc.speed, slope, jump.slopeRatio));
320
415
  jumped = true;
321
416
  cut = false;
322
417
  sincePress = Number.POSITIVE_INFINITY;
@@ -358,6 +453,8 @@ export function stepVertical(pose, timers, input, dt, ground, maxStep, jump, hop
358
453
  if (airborne && z <= ground) {
359
454
  z = ground;
360
455
  vz = 0;
456
+ vx = 0;
457
+ vy = 0;
361
458
  airborne = false;
362
459
  hovering = false;
363
460
  jumped = false;
@@ -379,11 +476,13 @@ export function stepVertical(pose, timers, input, dt, ground, maxStep, jump, hop
379
476
  : { sinceGround, sincePress, jumped, cut, airJumpsUsed };
380
477
  const nextPose = z === pose.z &&
381
478
  vz === pose.vz &&
479
+ vx === pose.vx &&
480
+ vy === pose.vy &&
382
481
  airborne === pose.airborne &&
383
482
  hovering === pose.hovering &&
384
483
  hoverGauge === pose.hoverGauge
385
484
  ? pose
386
- : { ...pose, z, vz, airborne, hovering, hoverGauge };
485
+ : { ...pose, z, vz, vx, vy, airborne, hovering, hoverGauge };
387
486
  return { pose: nextPose, timers: nextTimers };
388
487
  }
389
488
  /**