@edv4h/usketch-plugin-character 0.2.3 → 0.3.1
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 +35 -7
- package/dist/character-input.d.ts +9 -4
- package/dist/character-input.d.ts.map +1 -1
- package/dist/character-input.js +151 -11
- package/dist/character-input.js.map +1 -1
- package/dist/character-layer.d.ts +3 -1
- package/dist/character-layer.d.ts.map +1 -1
- package/dist/character-layer.js +29 -6
- package/dist/character-layer.js.map +1 -1
- package/dist/character-physics.d.ts +55 -2
- package/dist/character-physics.d.ts.map +1 -1
- package/dist/character-physics.js +90 -9
- package/dist/character-physics.js.map +1 -1
- package/dist/character-runtime.d.ts.map +1 -1
- package/dist/character-runtime.js +13 -5
- package/dist/character-runtime.js.map +1 -1
- package/dist/character-service.d.ts +6 -2
- 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-store.d.ts +8 -1
- package/dist/character-store.d.ts.map +1 -1
- package/dist/character-store.js +40 -4
- package/dist/character-store.js.map +1 -1
- package/dist/character-sync.d.ts +7 -1
- package/dist/character-sync.d.ts.map +1 -1
- package/dist/character-sync.js +13 -1
- package/dist/character-sync.js.map +1 -1
- package/dist/character-types.d.ts +13 -0
- package/dist/character-types.d.ts.map +1 -1
- package/dist/character-types.js +1 -0
- package/dist/character-types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +7 -2
- 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 +12 -0
- package/dist/register-character-hud.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ createCharacterPlugin({
|
|
|
23
23
|
keys: { up: ["KeyW", "ArrowUp"] }, // KeyboardEvent.code values
|
|
24
24
|
cameraMode: "rotate", // "fixed" | "rotate" | "free"
|
|
25
25
|
controlScheme: "vehicle", // "directional" | "vehicle"
|
|
26
|
+
drift: true, // vehicle: Space + steer drifts (or tuning overrides)
|
|
26
27
|
screenAnchor: { x: 0.5, y: 0.75 },
|
|
27
28
|
});
|
|
28
29
|
```
|
|
@@ -32,8 +33,8 @@ createCharacterPlugin({
|
|
|
32
33
|
- **Heading**: degrees, `0` = up (world −y), clockwise-positive — the same convention
|
|
33
34
|
as CSS `rotate()` and the viewport `rotation`.
|
|
34
35
|
- **Render props**: `heading` (world), `screenHeading` (= `heading + viewport.rotation`,
|
|
35
|
-
what to rotate a top-down sprite by), `moving`, `speed`, `
|
|
36
|
-
`name`, `isSelf`, `id`. Each character is placed centered on its screen position
|
|
36
|
+
what to rotate a top-down sprite by), `moving`, `speed`, `steering`, `slip`,
|
|
37
|
+
`drifting`, `zoom`, `appearance`, `name`, `isSelf`, `id`, `elevation`. Each character is placed centered on its screen position
|
|
37
38
|
in one screen-space layer, so self and remote characters look the same under zoom
|
|
38
39
|
and camera rotation.
|
|
39
40
|
- **Control schemes**
|
|
@@ -41,7 +42,26 @@ createCharacterPlugin({
|
|
|
41
42
|
turns toward the movement at `turnRate`. With the `rotate` camera, left/right
|
|
42
43
|
therefore circle (the camera follows the turn).
|
|
43
44
|
- `vehicle` — up/down throttle and reverse, left/right steer. Steering scales
|
|
44
|
-
with speed (a stopped car can't turn) and inverts in reverse.
|
|
45
|
+
with speed (a stopped car can't turn) and inverts in reverse. The held steering
|
|
46
|
+
(-1 … 1) is in the pose and render props, e.g. to turn the front wheels.
|
|
47
|
+
- **Drift** (`vehicle`, opt-in via `drift`): hold the drift key (Space) and steer at
|
|
48
|
+
speed to start an arcade drift toward the steering. It turns tighter than normal
|
|
49
|
+
steering and slides the body by `slip` degrees (draw the car at `heading + slip`;
|
|
50
|
+
it keeps traveling along `heading`). Steering into the drift tightens it,
|
|
51
|
+
countersteering opens it (never spins). Releasing the key, slowing below
|
|
52
|
+
`stopSpeed` or reversing ends it, and the slip eases back. Tuning (`DriftParams`):
|
|
53
|
+
`startSpeed` / `stopSpeed` (fractions of `moveSpeed`), `turnRate` / `steerRate`
|
|
54
|
+
(multiples of `turnRate`), `slipAngle` / `steerSlipAngle` (degrees), `recoveryRate`
|
|
55
|
+
(1/s). The drift key is only captured while drift is on.
|
|
56
|
+
- **Keyboard focus**: movement keys are captured on `window` while the character is
|
|
57
|
+
on, except where the page needs them: nothing is taken while a text field has focus
|
|
58
|
+
or focus is inside a dialog / menu (`dialog`, `role="dialog"`, `alertdialog`, `menu`;
|
|
59
|
+
a dialog that contains the board itself doesn't count), arrow keys are left to
|
|
60
|
+
widgets that use them (range / radio inputs, `role="slider"`, `radio`, `tab`,
|
|
61
|
+
`option`, `combobox`, `menubar` items, `grid`, `treeitem` …) while WASD keeps
|
|
62
|
+
driving, and Space is left to buttons, links and other controls it activates. Focus
|
|
63
|
+
is read through `composedPath()`, so web-component dialogs count too. Moving focus
|
|
64
|
+
to such a target releases the held keys it uses.
|
|
45
65
|
- **Camera modes**
|
|
46
66
|
- `fixed` — follow, world stays upright.
|
|
47
67
|
- `rotate` — follow and turn the world by `-heading` (character always faces up).
|
|
@@ -51,8 +71,15 @@ createCharacterPlugin({
|
|
|
51
71
|
following. Turning the character off turns the camera back upright.
|
|
52
72
|
- **Multiplayer**: pass `wsProvider` (anything with a y-protocols `awareness`). Your
|
|
53
73
|
character is published under the `character` awareness field (~20 Hz, only when it
|
|
54
|
-
changes) and others are eased between
|
|
74
|
+
changes, including `steering`, `slip` and `drifting`) and others are eased between
|
|
75
|
+
updates. It's ephemeral: a character
|
|
55
76
|
vanishes when its owner disconnects or turns it off. Nothing is stored as shapes.
|
|
77
|
+
- **Ground height**: when a plugin provides `groundHeightService` (from
|
|
78
|
+
`@edv4h/usketch-shared`, e.g. the map plugin's raised terrain), moving onto
|
|
79
|
+
ground more than `motion.maxStep` (default 24 world units, HUD "登れる段差") higher
|
|
80
|
+
than where the character stands is blocked; it slides along the cliff instead.
|
|
81
|
+
Stepping down is always allowed. The render props carry `elevation`, and under a
|
|
82
|
+
3D view (Mode 7) the character is lifted onto the ground it stands on.
|
|
56
83
|
- **Input**: held keys are tracked from window `keydown`/`keyup` (capture phase,
|
|
57
84
|
`event.code`), swallowed while the character is on so single-letter tool
|
|
58
85
|
shortcuts don't fire. Typing in text fields, and key chords with Ctrl/Cmd/Alt,
|
|
@@ -60,8 +87,8 @@ createCharacterPlugin({
|
|
|
60
87
|
|
|
61
88
|
## HUD
|
|
62
89
|
|
|
63
|
-
A "キャラクター" settings group (on/off, camera, scheme, speed, turn rate,
|
|
64
|
-
position, camera lag) plus actions to toggle the character and to place it at the
|
|
90
|
+
A "キャラクター" settings group (on/off, camera, scheme, drift, speed, turn rate,
|
|
91
|
+
climbable step, screen position, camera lag) plus actions to toggle the character and to place it at the
|
|
65
92
|
screen position.
|
|
66
93
|
|
|
67
94
|
## Service
|
|
@@ -73,7 +100,8 @@ const api = getCharacterApi(app.services);
|
|
|
73
100
|
api?.enable();
|
|
74
101
|
api?.setCameraMode("rotate");
|
|
75
102
|
api?.setRenderer(myRenderer);
|
|
76
|
-
api?.
|
|
103
|
+
api?.setDrift({ slipAngle: 35 }); // true / false / tuning overrides
|
|
104
|
+
api?.getPose(); // { x, y, heading, speed, moving, steering, slip, drift }
|
|
77
105
|
```
|
|
78
106
|
|
|
79
107
|
## Limitations
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MoveInput } from "./character-physics.js";
|
|
2
2
|
import type { CharacterKeys } from "./character-types.js";
|
|
3
3
|
export interface KeyInput {
|
|
4
|
-
/** The currently held directions. */
|
|
4
|
+
/** The currently held directions (and the drift key). */
|
|
5
5
|
read(): MoveInput;
|
|
6
6
|
/** Whether any bound key is held. */
|
|
7
7
|
anyHeld(): boolean;
|
|
@@ -11,8 +11,13 @@ export interface KeyInput {
|
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Listen for the bound keys on `window`. `isActive()` gates everything: while it is
|
|
14
|
-
* false no key is captured (and none is swallowed).
|
|
15
|
-
*
|
|
14
|
+
* false no key is captured (and none is swallowed). Keys are left alone while focus is
|
|
15
|
+
* in a text field or inside a dialog / menu, arrow keys while a widget that uses them
|
|
16
|
+
* (slider, radio, tab …) has focus, and the drift key on controls Space activates.
|
|
17
|
+
* Held keys are cleared on window blur and tab hide, and released when focus moves to
|
|
18
|
+
* a target that uses them, so a key released elsewhere can't get stuck "down".
|
|
16
19
|
*/
|
|
17
|
-
export declare function createKeyInput(initialKeys: CharacterKeys, isActive: () => boolean
|
|
20
|
+
export declare function createKeyInput(initialKeys: CharacterKeys, isActive: () => boolean,
|
|
21
|
+
/** Whether the drift key does anything now (else it is left to the host, e.g. Space-pan). */
|
|
22
|
+
isDriftActive?: () => boolean): KeyInput;
|
|
18
23
|
//# sourceMappingURL=character-input.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"character-input.d.ts","sourceRoot":"","sources":["../src/character-input.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,WAAW,QAAQ;IACxB,
|
|
1
|
+
{"version":3,"file":"character-input.d.ts","sourceRoot":"","sources":["../src/character-input.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,WAAW,QAAQ;IACxB,yDAAyD;IACzD,IAAI,IAAI,SAAS,CAAC;IAClB,qCAAqC;IACrC,OAAO,IAAI,OAAO,CAAC;IACnB,wDAAwD;IACxD,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC,OAAO,IAAI,IAAI,CAAC;CAChB;AAkJD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC7B,WAAW,EAAE,aAAa,EAC1B,QAAQ,EAAE,MAAM,OAAO;AACvB,6FAA6F;AAC7F,aAAa,GAAE,MAAM,OAAqB,GACxC,QAAQ,CAwFV"}
|
package/dist/character-input.js
CHANGED
|
@@ -10,9 +10,115 @@ const NON_TEXT_INPUTS = new Set([
|
|
|
10
10
|
"file",
|
|
11
11
|
"image",
|
|
12
12
|
]);
|
|
13
|
-
|
|
13
|
+
/** ARIA widgets Space activates or types into. */
|
|
14
|
+
const SPACE_ROLES = new Set([
|
|
15
|
+
"button",
|
|
16
|
+
"checkbox",
|
|
17
|
+
"combobox",
|
|
18
|
+
"link",
|
|
19
|
+
"menuitem",
|
|
20
|
+
"menuitemcheckbox",
|
|
21
|
+
"menuitemradio",
|
|
22
|
+
"option",
|
|
23
|
+
"radio",
|
|
24
|
+
"slider",
|
|
25
|
+
"switch",
|
|
26
|
+
"tab",
|
|
27
|
+
"textbox",
|
|
28
|
+
]);
|
|
29
|
+
const asElement = (target) => {
|
|
14
30
|
const el = target;
|
|
15
|
-
|
|
31
|
+
return el && typeof el.tagName === "string" ? el : null;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The focused element and its ancestors, innermost first. `composedPath()` reaches into
|
|
35
|
+
* (open) shadow roots, so a web-component dialog or slider is seen as itself rather than
|
|
36
|
+
* as its retargeted host; without it, the light-DOM parent chain.
|
|
37
|
+
*/
|
|
38
|
+
function focusPath(e) {
|
|
39
|
+
const raw = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
40
|
+
const path = raw.map(asElement).filter((el) => el !== null);
|
|
41
|
+
if (path.length > 0)
|
|
42
|
+
return path;
|
|
43
|
+
const chain = [];
|
|
44
|
+
for (let el = asElement(e.target); el; el = asElement(el.parentElement))
|
|
45
|
+
chain.push(el);
|
|
46
|
+
return chain;
|
|
47
|
+
}
|
|
48
|
+
const roleOf = (el) => el && typeof el.getAttribute === "function" ? el.getAttribute("role") : null;
|
|
49
|
+
/** Elements Space activates (or types into): the drift key must not steal it from them. */
|
|
50
|
+
function isSpaceTarget(el) {
|
|
51
|
+
if (!el)
|
|
52
|
+
return false;
|
|
53
|
+
if (["INPUT", "TEXTAREA", "SELECT", "BUTTON", "A", "SUMMARY"].includes(el.tagName))
|
|
54
|
+
return true;
|
|
55
|
+
if (el.isContentEditable === true)
|
|
56
|
+
return true;
|
|
57
|
+
const role = roleOf(el);
|
|
58
|
+
return role !== null && SPACE_ROLES.has(role);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Overlays that own the keyboard while focus is inside them (a dialog or menu opened
|
|
62
|
+
* mid-drive). Persistent containers such as a menubar or a sidebar listbox are not
|
|
63
|
+
* here: their items are arrow-key widgets, which keep WASD driving.
|
|
64
|
+
*/
|
|
65
|
+
const KEY_OWNER_SELECTOR = 'dialog, [role="dialog"], [role="alertdialog"], [role="menu"]';
|
|
66
|
+
/** Marks the board's layers, to tell a host dialog that wraps the whole board apart. */
|
|
67
|
+
const BOARD_SELECTOR = "[data-layer-id]";
|
|
68
|
+
/** `<input type>`s that move their value or selection with the arrow keys. */
|
|
69
|
+
const ARROW_INPUTS = new Set(["range", "radio"]);
|
|
70
|
+
/** ARIA widgets (and composite containers that keep focus) that use the arrow keys. */
|
|
71
|
+
const ARROW_ROLES = new Set([
|
|
72
|
+
"combobox",
|
|
73
|
+
"grid",
|
|
74
|
+
"gridcell",
|
|
75
|
+
"listbox",
|
|
76
|
+
"menubar",
|
|
77
|
+
"menuitem",
|
|
78
|
+
"menuitemcheckbox",
|
|
79
|
+
"menuitemradio",
|
|
80
|
+
"option",
|
|
81
|
+
"radio",
|
|
82
|
+
"radiogroup",
|
|
83
|
+
"scrollbar",
|
|
84
|
+
"separator",
|
|
85
|
+
"slider",
|
|
86
|
+
"spinbutton",
|
|
87
|
+
"tab",
|
|
88
|
+
"tablist",
|
|
89
|
+
"toolbar",
|
|
90
|
+
"tree",
|
|
91
|
+
"treegrid",
|
|
92
|
+
"treeitem",
|
|
93
|
+
]);
|
|
94
|
+
/**
|
|
95
|
+
* Focus is inside a dialog or menu: every bound key is left to it. A dialog that holds
|
|
96
|
+
* the board itself (the host shows the board in a modal) doesn't count, or the
|
|
97
|
+
* character could never be driven there.
|
|
98
|
+
*/
|
|
99
|
+
function isInKeyOwner(path) {
|
|
100
|
+
const owner = path.find((el) => typeof el.matches === "function" && el.matches(KEY_OWNER_SELECTOR));
|
|
101
|
+
if (!owner)
|
|
102
|
+
return false;
|
|
103
|
+
return typeof owner.querySelector !== "function" || owner.querySelector(BOARD_SELECTOR) === null;
|
|
104
|
+
}
|
|
105
|
+
/** The focused element uses the arrow keys itself, so arrow bindings are left to it. */
|
|
106
|
+
function isArrowTarget(el) {
|
|
107
|
+
if (!el)
|
|
108
|
+
return false;
|
|
109
|
+
if (el.tagName === "INPUT" && ARROW_INPUTS.has((el.type ?? "").toLowerCase()))
|
|
110
|
+
return true;
|
|
111
|
+
const role = roleOf(el);
|
|
112
|
+
return role !== null && ARROW_ROLES.has(role);
|
|
113
|
+
}
|
|
114
|
+
/** Whether a key acts as an arrow (by code, or by key: numpad 4/6 with NumLock off). */
|
|
115
|
+
const isArrowKey = (code, key) => code.startsWith("Arrow") || (key ?? "").startsWith("Arrow");
|
|
116
|
+
/** Focus where no bound key may be taken: typing, or a dialog / menu. */
|
|
117
|
+
function blocksAllKeys(path) {
|
|
118
|
+
return isTextInput(path[0]) || isInKeyOwner(path);
|
|
119
|
+
}
|
|
120
|
+
function isTextInput(el) {
|
|
121
|
+
if (!el)
|
|
16
122
|
return false;
|
|
17
123
|
const tag = el.tagName;
|
|
18
124
|
if (tag === "INPUT")
|
|
@@ -21,27 +127,43 @@ function isTextInput(target) {
|
|
|
21
127
|
}
|
|
22
128
|
/**
|
|
23
129
|
* Listen for the bound keys on `window`. `isActive()` gates everything: while it is
|
|
24
|
-
* false no key is captured (and none is swallowed).
|
|
25
|
-
*
|
|
130
|
+
* false no key is captured (and none is swallowed). Keys are left alone while focus is
|
|
131
|
+
* in a text field or inside a dialog / menu, arrow keys while a widget that uses them
|
|
132
|
+
* (slider, radio, tab …) has focus, and the drift key on controls Space activates.
|
|
133
|
+
* Held keys are cleared on window blur and tab hide, and released when focus moves to
|
|
134
|
+
* a target that uses them, so a key released elsewhere can't get stuck "down".
|
|
26
135
|
*/
|
|
27
|
-
export function createKeyInput(initialKeys, isActive
|
|
136
|
+
export function createKeyInput(initialKeys, isActive,
|
|
137
|
+
/** Whether the drift key does anything now (else it is left to the host, e.g. Space-pan). */
|
|
138
|
+
isDriftActive = () => false) {
|
|
28
139
|
let keys = initialKeys;
|
|
29
|
-
|
|
140
|
+
/** Held codes → the `key` they produced (to tell numpad arrows from digits). */
|
|
141
|
+
const held = new Map();
|
|
30
142
|
const directionOf = (code) => {
|
|
31
143
|
for (const dir of ["up", "down", "left", "right"]) {
|
|
32
144
|
if (keys[dir].includes(code))
|
|
33
145
|
return dir;
|
|
34
146
|
}
|
|
147
|
+
if (keys.drift?.includes(code) && isDriftActive())
|
|
148
|
+
return "drift";
|
|
35
149
|
return null;
|
|
36
150
|
};
|
|
37
151
|
const onKeyDown = (e) => {
|
|
38
|
-
if (!isActive()
|
|
152
|
+
if (!isActive())
|
|
39
153
|
return;
|
|
40
154
|
if (e.ctrlKey || e.metaKey || e.altKey)
|
|
41
155
|
return; // leave chords (Cmd+S …) alone
|
|
42
|
-
|
|
156
|
+
const dir = directionOf(e.code);
|
|
157
|
+
if (!dir)
|
|
158
|
+
return;
|
|
159
|
+
const path = focusPath(e);
|
|
160
|
+
if (blocksAllKeys(path))
|
|
43
161
|
return;
|
|
44
|
-
|
|
162
|
+
if (dir === "drift" && isSpaceTarget(path[0]))
|
|
163
|
+
return;
|
|
164
|
+
if (isArrowKey(e.code, e.key) && isArrowTarget(path[0]))
|
|
165
|
+
return;
|
|
166
|
+
held.set(e.code, e.key ?? "");
|
|
45
167
|
e.preventDefault();
|
|
46
168
|
e.stopImmediatePropagation();
|
|
47
169
|
};
|
|
@@ -56,24 +178,41 @@ export function createKeyInput(initialKeys, isActive) {
|
|
|
56
178
|
if (typeof document !== "undefined" && document.hidden)
|
|
57
179
|
clear();
|
|
58
180
|
};
|
|
181
|
+
// Focus moving to a target that uses a held key hands that key to it: release it, or
|
|
182
|
+
// the character keeps going and the key's keyup is swallowed (a button that Space
|
|
183
|
+
// activates on keyup would never fire). Keys the new target ignores stay held.
|
|
184
|
+
const onFocusIn = (e) => {
|
|
185
|
+
const path = focusPath(e);
|
|
186
|
+
if (blocksAllKeys(path)) {
|
|
187
|
+
clear();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const owned = [...held].filter(([code, key]) => (isArrowKey(code, key) && isArrowTarget(path[0])) ||
|
|
191
|
+
((keys.drift?.includes(code) ?? false) && isSpaceTarget(path[0])));
|
|
192
|
+
for (const [code] of owned)
|
|
193
|
+
held.delete(code);
|
|
194
|
+
};
|
|
59
195
|
const hasWindow = typeof window !== "undefined";
|
|
60
196
|
if (hasWindow) {
|
|
61
197
|
window.addEventListener("keydown", onKeyDown, true);
|
|
62
198
|
window.addEventListener("keyup", onKeyUp, true);
|
|
63
199
|
window.addEventListener("blur", clear);
|
|
64
200
|
document.addEventListener("visibilitychange", onVisibility);
|
|
201
|
+
document.addEventListener("focusin", onFocusIn);
|
|
65
202
|
}
|
|
66
|
-
const isHeld = (dir) => keys[dir]
|
|
203
|
+
const isHeld = (dir) => keys[dir]?.some((c) => held.has(c)) ?? false;
|
|
67
204
|
return {
|
|
68
205
|
read: () => ({
|
|
69
206
|
up: isHeld("up"),
|
|
70
207
|
down: isHeld("down"),
|
|
71
208
|
left: isHeld("left"),
|
|
72
209
|
right: isHeld("right"),
|
|
210
|
+
drift: isHeld("drift"),
|
|
73
211
|
}),
|
|
74
212
|
anyHeld: () => held.size > 0,
|
|
75
213
|
setKeys(next) {
|
|
76
|
-
|
|
214
|
+
// Rebinding movement keeps the drift key unless `drift` is given (`[]` unbinds).
|
|
215
|
+
keys = { ...next, drift: next.drift ?? keys.drift };
|
|
77
216
|
held.clear();
|
|
78
217
|
},
|
|
79
218
|
dispose() {
|
|
@@ -83,6 +222,7 @@ export function createKeyInput(initialKeys, isActive) {
|
|
|
83
222
|
window.removeEventListener("keyup", onKeyUp, true);
|
|
84
223
|
window.removeEventListener("blur", clear);
|
|
85
224
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
225
|
+
document.removeEventListener("focusin", onFocusIn);
|
|
86
226
|
},
|
|
87
227
|
};
|
|
88
228
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"character-input.js","sourceRoot":"","sources":["../src/character-input.ts"],"names":[],"mappings":"AAkBA,6FAA6F;AAC7F,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC/B,UAAU;IACV,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;CACP,CAAC,CAAC;AAEH,
|
|
1
|
+
{"version":3,"file":"character-input.js","sourceRoot":"","sources":["../src/character-input.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type Viewport } from "@edv4h/usketch-shared";
|
|
2
|
+
import type { HeightAt } from "./character-physics.js";
|
|
2
3
|
import type { CharacterStore } from "./character-store.js";
|
|
3
|
-
export declare function CharacterLayer({ store, viewport }: {
|
|
4
|
+
export declare function CharacterLayer({ store, viewport, heightAt, }: {
|
|
4
5
|
store: CharacterStore;
|
|
5
6
|
viewport: Viewport;
|
|
7
|
+
heightAt?: HeightAt;
|
|
6
8
|
}): import("react").JSX.Element | null;
|
|
7
9
|
//# sourceMappingURL=character-layer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"character-layer.d.ts","sourceRoot":"","sources":["../src/character-layer.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,QAAQ,EAA4C,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"character-layer.d.ts","sourceRoot":"","sources":["../src/character-layer.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,QAAQ,EAA4C,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAsC3D,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,sCA+EA"}
|
package/dist/character-layer.js
CHANGED
|
@@ -6,10 +6,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
6
6
|
// `screenHeading` math under camera rotation).
|
|
7
7
|
import { viewportRotation, worldToScreen, wrapDeg } from "@edv4h/usketch-shared";
|
|
8
8
|
import { useSyncExternalStore } from "react";
|
|
9
|
-
function Spot({ x, y, renderer, props, }) {
|
|
10
|
-
return (_jsx("div", { "data-character-id": props.id, style: {
|
|
9
|
+
function Spot({ x, y, lift, renderer, props, }) {
|
|
10
|
+
return (_jsx("div", { "data-character-id": props.id, style: {
|
|
11
|
+
position: "absolute",
|
|
12
|
+
left: x,
|
|
13
|
+
top: y,
|
|
14
|
+
width: 0,
|
|
15
|
+
height: 0,
|
|
16
|
+
pointerEvents: "none",
|
|
17
|
+
transform: lift > 0 ? `translateZ(${lift}px)` : undefined,
|
|
18
|
+
}, children: _jsx("div", { style: { position: "absolute", left: 0, top: 0, transform: "translate(-50%, -50%)" }, children: renderer(props) }) }));
|
|
11
19
|
}
|
|
12
|
-
export function CharacterLayer({ store, viewport }) {
|
|
20
|
+
export function CharacterLayer({ store, viewport, heightAt, }) {
|
|
13
21
|
const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);
|
|
14
22
|
const renderer = store.getRenderer();
|
|
15
23
|
if (!renderer)
|
|
@@ -18,36 +26,51 @@ export function CharacterLayer({ store, viewport }) {
|
|
|
18
26
|
const spots = [];
|
|
19
27
|
for (const r of state.remotes.values()) {
|
|
20
28
|
const p = worldToScreen(r.x, r.y, viewport);
|
|
21
|
-
|
|
29
|
+
const elevation = heightAt?.(r.x, r.y) ?? 0;
|
|
30
|
+
spots.push(_jsx(Spot, { x: p.x, y: p.y, lift: elevation * viewport.zoom, renderer: renderer, props: {
|
|
22
31
|
id: r.id,
|
|
23
32
|
isSelf: false,
|
|
24
33
|
heading: r.heading,
|
|
25
34
|
screenHeading: wrapDeg(r.heading + rot),
|
|
26
35
|
moving: r.moving,
|
|
27
36
|
speed: r.speed,
|
|
37
|
+
steering: r.steering,
|
|
38
|
+
slip: r.slip,
|
|
39
|
+
drifting: r.drifting,
|
|
28
40
|
zoom: viewport.zoom,
|
|
29
41
|
appearance: r.appearance,
|
|
30
42
|
name: r.name,
|
|
43
|
+
elevation,
|
|
31
44
|
} }, `remote-${r.id}`));
|
|
32
45
|
}
|
|
33
46
|
if (state.enabled && state.spawned) {
|
|
34
47
|
const { pose } = state;
|
|
35
48
|
const p = worldToScreen(pose.x, pose.y, viewport);
|
|
49
|
+
const elevation = heightAt?.(pose.x, pose.y) ?? 0;
|
|
36
50
|
// Self last → drawn on top of the others.
|
|
37
|
-
spots.push(_jsx(Spot, { x: p.x, y: p.y, renderer: renderer, props: {
|
|
51
|
+
spots.push(_jsx(Spot, { x: p.x, y: p.y, lift: elevation * viewport.zoom, renderer: renderer, props: {
|
|
38
52
|
id: "self",
|
|
39
53
|
isSelf: true,
|
|
40
54
|
heading: pose.heading,
|
|
41
55
|
screenHeading: wrapDeg(pose.heading + rot),
|
|
42
56
|
moving: pose.moving,
|
|
43
57
|
speed: pose.speed,
|
|
58
|
+
steering: pose.steering,
|
|
59
|
+
slip: pose.slip,
|
|
60
|
+
drifting: pose.drift !== 0,
|
|
44
61
|
zoom: viewport.zoom,
|
|
45
62
|
appearance: state.appearance,
|
|
46
63
|
name: state.name,
|
|
64
|
+
elevation,
|
|
47
65
|
} }, "self"));
|
|
48
66
|
}
|
|
49
67
|
if (spots.length === 0)
|
|
50
68
|
return null;
|
|
51
|
-
return _jsx("div", { style: {
|
|
69
|
+
return (_jsx("div", { style: {
|
|
70
|
+
position: "absolute",
|
|
71
|
+
inset: 0,
|
|
72
|
+
pointerEvents: "none",
|
|
73
|
+
transformStyle: "preserve-3d",
|
|
74
|
+
}, children: spots }));
|
|
52
75
|
}
|
|
53
76
|
//# sourceMappingURL=character-layer.js.map
|
|
@@ -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,+CAA+C;AAC/C,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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"}
|
|
@@ -5,6 +5,8 @@ export interface MoveInput {
|
|
|
5
5
|
down: boolean;
|
|
6
6
|
left: boolean;
|
|
7
7
|
right: boolean;
|
|
8
|
+
/** Vehicle: the drift key (only acts when drift is enabled). */
|
|
9
|
+
drift?: boolean;
|
|
8
10
|
}
|
|
9
11
|
export declare const NO_INPUT: MoveInput;
|
|
10
12
|
export interface CharacterPose {
|
|
@@ -17,7 +19,18 @@ export interface CharacterPose {
|
|
|
17
19
|
speed: number;
|
|
18
20
|
/** Whether the character moved this step. */
|
|
19
21
|
moving: boolean;
|
|
22
|
+
/** Vehicle: the held steering, -1 (left) … 1 (right). 0 for `directional`. */
|
|
23
|
+
steering: number;
|
|
24
|
+
/**
|
|
25
|
+
* Vehicle: body yaw relative to `heading`, degrees (clockwise-positive). The car
|
|
26
|
+
* travels along `heading` but is drawn at `heading + slip` while drifting.
|
|
27
|
+
*/
|
|
28
|
+
slip: number;
|
|
29
|
+
/** Vehicle: the running drift's direction, -1 (left) / 1 (right), 0 = not drifting. */
|
|
30
|
+
drift: -1 | 0 | 1;
|
|
20
31
|
}
|
|
32
|
+
/** A pose at rest at the origin, facing up. */
|
|
33
|
+
export declare const ORIGIN_POSE: CharacterPose;
|
|
21
34
|
export interface MotionParams {
|
|
22
35
|
/** Top speed, world units / s. */
|
|
23
36
|
moveSpeed: number;
|
|
@@ -29,8 +42,37 @@ export interface MotionParams {
|
|
|
29
42
|
friction: number;
|
|
30
43
|
/** Vehicle: reverse top speed as a fraction of `moveSpeed`. */
|
|
31
44
|
reverseRatio: number;
|
|
45
|
+
/** Tallest step up the character can climb, world units (ground height, e.g. the map
|
|
46
|
+
* plugin's raised terrain). Stepping DOWN is always allowed. */
|
|
47
|
+
maxStep: number;
|
|
32
48
|
}
|
|
33
49
|
export declare const DEFAULT_MOTION: MotionParams;
|
|
50
|
+
/**
|
|
51
|
+
* Vehicle drift tuning: holding the drift key while steering at speed starts an
|
|
52
|
+
* arcade drift (a tighter turn with the body slid into it); releasing the key,
|
|
53
|
+
* slowing down or reversing ends it, and the slip eases back.
|
|
54
|
+
*/
|
|
55
|
+
export interface DriftParams {
|
|
56
|
+
/** Speed that starts a drift, as a fraction of `moveSpeed`. */
|
|
57
|
+
startSpeed: number;
|
|
58
|
+
/** Speed below which a drift ends, as a fraction of `moveSpeed`. */
|
|
59
|
+
stopSpeed: number;
|
|
60
|
+
/** Turn rate while drifting, as a multiple of `turnRate`. */
|
|
61
|
+
turnRate: number;
|
|
62
|
+
/**
|
|
63
|
+
* How much steering tightens (into the drift) or opens (against it) the turn,
|
|
64
|
+
* as a multiple of `turnRate`. Capped at the drift `turnRate`, so
|
|
65
|
+
* countersteering never turns the car the other way.
|
|
66
|
+
*/
|
|
67
|
+
steerRate: number;
|
|
68
|
+
/** Body slip while drifting, degrees. */
|
|
69
|
+
slipAngle: number;
|
|
70
|
+
/** Extra slip when steering into the drift, degrees. */
|
|
71
|
+
steerSlipAngle: number;
|
|
72
|
+
/** How fast the slip follows its target (1/s, exponential). */
|
|
73
|
+
recoveryRate: number;
|
|
74
|
+
}
|
|
75
|
+
export declare const DEFAULT_DRIFT: DriftParams;
|
|
34
76
|
/** Unit vector of a heading in world space (0° → (0, -1), 90° → (1, 0)). */
|
|
35
77
|
export declare function headingVector(deg: number): {
|
|
36
78
|
x: number;
|
|
@@ -49,7 +91,18 @@ export declare function turnToward(from: number, to: number, maxStep: number): n
|
|
|
49
91
|
* rather than snapping keeps a heading-following camera from spinning.)
|
|
50
92
|
* - `vehicle`: up/down are throttle/reverse along the heading, left/right steer.
|
|
51
93
|
* Steering authority scales with speed (a stopped car can't turn) and inverts
|
|
52
|
-
* when reversing, like a car.
|
|
94
|
+
* when reversing, like a car. With `driftParams`, holding the drift key while
|
|
95
|
+
* steering at speed drifts (see `DriftParams`).
|
|
96
|
+
*/
|
|
97
|
+
export declare function stepCharacter(pose: CharacterPose, input: MoveInput, dt: number, scheme: ControlScheme, params: MotionParams, cameraRotation?: number, driftParams?: DriftParams | null): CharacterPose;
|
|
98
|
+
/** Ground height at a world point (world units; see `groundHeightService`). */
|
|
99
|
+
export type HeightAt = (x: number, y: number) => number;
|
|
100
|
+
/**
|
|
101
|
+
* Keep a step from `prev` to `next` on climbable ground: moving onto ground more than
|
|
102
|
+
* `maxStep` higher than where the character stands is blocked. A blocked move first
|
|
103
|
+
* tries to slide along the cliff (keep only the x or only the y part), so walking into
|
|
104
|
+
* a wall at an angle glides along it; failing both, the character stays put (a
|
|
105
|
+
* vehicle also loses its speed, as if it bumped the wall). The heading still turns.
|
|
53
106
|
*/
|
|
54
|
-
export declare function
|
|
107
|
+
export declare function constrainToGround(prev: CharacterPose, next: CharacterPose, heightAt: HeightAt, maxStep: number): CharacterPose;
|
|
55
108
|
//# sourceMappingURL=character-physics.d.ts.map
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"character-physics.d.ts","sourceRoot":"","sources":["../src/character-physics.ts"],"names":[],"mappings":"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"}
|