@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.
- package/README.md +25 -2
- package/dist/character-layer.d.ts +15 -2
- package/dist/character-layer.d.ts.map +1 -1
- package/dist/character-layer.js +66 -5
- package/dist/character-layer.js.map +1 -1
- package/dist/character-physics.d.ts +68 -3
- package/dist/character-physics.d.ts.map +1 -1
- package/dist/character-physics.js +108 -9
- package/dist/character-physics.js.map +1 -1
- package/dist/character-runtime.d.ts.map +1 -1
- package/dist/character-runtime.js +18 -8
- package/dist/character-runtime.js.map +1 -1
- package/dist/character-service.d.ts +5 -0
- package/dist/character-service.d.ts.map +1 -1
- package/dist/character-service.js +2 -0
- package/dist/character-service.js.map +1 -1
- package/dist/character-size.d.ts +24 -0
- package/dist/character-size.d.ts.map +1 -0
- package/dist/character-size.js +26 -0
- package/dist/character-size.js.map +1 -0
- package/dist/character-store.d.ts +6 -0
- package/dist/character-store.d.ts.map +1 -1
- package/dist/character-store.js +18 -1
- package/dist/character-store.js.map +1 -1
- package/dist/character-types.d.ts +6 -0
- package/dist/character-types.d.ts.map +1 -1
- package/dist/character-types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +1 -0
- package/dist/plugin.js.map +1 -1
- package/dist/register-character-hud.d.ts.map +1 -1
- package/dist/register-character-hud.js +69 -0
- package/dist/register-character-hud.js.map +1 -1
- 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
|
|
2
|
+
import { type HeightAt } from "./character-physics.js";
|
|
3
3
|
import { type CharacterStore } from "./character-store.js";
|
|
4
|
-
|
|
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,
|
|
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"}
|
package/dist/character-layer.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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":";AAAA,mFAAmF;AACnF,qFAAqF;AACrF,iFAAiF;AACjF,8EAA8E;AAC9E,uFAAuF;AACvF,mFAAmF;AACnF,0EAA0E;AAC1E,OAAO,EAAiB,gBAAgB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;
|
|
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":"AAKA,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,SAAS,CAAC;AAEtD,oDAAoD;AACpD,MAAM,WAAW,SAAS;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uFAAuF;IACvF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yEAAyE;IACzE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,yFAAyF;IACzF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,QAAQ,EAAE,SAQtB,CAAC;AAEF,MAAM,WAAW,aAAa;IAC7B,sBAAsB;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClB;;;;OAIG;IACH,CAAC,EAAE,MAAM,CAAC;IACV,2DAA2D;IAC3D,EAAE,EAAE,MAAM,CAAC;IACX,gEAAgE;IAChE,QAAQ,EAAE,OAAO,CAAC;IAClB,uFAAuF;IACvF,QAAQ,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,+CAA+C;AAC/C,eAAO,MAAM,WAAW,EAAE,
|
|
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
|
-
|
|
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
|
/**
|