@genex-ai/cli-demo 0.54.0-dev.122 → 0.55.0-dev.124
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 +1 -1
- package/dist/index.js +40 -10
- package/package.json +1 -1
- package/templates/controllers/character/touch-joystick.ts +5 -387
- package/templates/controllers/touch/drag-zone.ts +186 -0
- package/templates/controllers/touch/rotate-overlay.ts +161 -0
- package/templates/controllers/touch/touch-joystick.ts +541 -0
- package/templates/skills/genex-threejs-character-controller/SKILL.md +9 -6
- package/templates/skills/genex-threejs-game-ui/SKILL.md +5 -4
- package/templates/skills/genex-threejs-skill-router/SKILL.md +1 -0
- package/templates/skills/genex-threejs-touch-controls/SKILL.md +157 -0
- package/templates/skills/genex-threejs-vehicle-controllers/SKILL.md +2 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: genex-threejs-touch-controls
|
|
3
|
+
description: Make a Genex Three.js game playable on phones — vendored touch primitives via `npx genex controller touch` (floating/static joystick, virtual buttons, drag zone, rotate-device overlay) plus per-genre wiring recipes (movement, camera look, taps, paddles, action buttons, touch pause). Use when wiring default touch controls into a new game, whenever someone asks to make a game playable on mobile/phone/tablet, or before shipping a game whose input is keyboard/mouse-only.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Genex Three.js Touch Controls
|
|
7
|
+
|
|
8
|
+
Game links get shared in chats, and chats get opened on phones. A game with
|
|
9
|
+
keyboard/mouse-only input *renders* there and then does nothing — a dead screen
|
|
10
|
+
at the exact moment someone was curious enough to tap the link.
|
|
11
|
+
|
|
12
|
+
**Wire touch input by default when a recipe below fits** — it's a few lines,
|
|
13
|
+
invisible on desktop, and never needs mobile testing. **Skip it with a one-line
|
|
14
|
+
reason when it doesn't fit** (typing-driven games, pointer-lock-precision aiming
|
|
15
|
+
with no touch equivalent, anything where touch would contort the architecture) —
|
|
16
|
+
it is guidance, never a requirement. Mobile *layout/HUD polish* and phone-size
|
|
17
|
+
testing stay ask-only either way: the kit's defaults are safe by construction,
|
|
18
|
+
so wiring it adds no verification work.
|
|
19
|
+
|
|
20
|
+
## Get the kit
|
|
21
|
+
|
|
22
|
+
Games on a bundled physics controller (`genex controller character|car|drone`)
|
|
23
|
+
**already have the kit** — those installs ship it. Everything else:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx genex controller touch
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
No extra npm install — plain DOM/CSS classes into `src/controllers/touch/`:
|
|
30
|
+
|
|
31
|
+
| File | Exports | What it is |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `touch/touch-joystick.ts` | `TouchJoystick` | movement stick — static circle, or `floating: true` (appears under the thumb, faint resting hint between touches) |
|
|
34
|
+
| `touch/touch-joystick.ts` | `VirtualButton` | thumb-sized action button with a label and press/release edges |
|
|
35
|
+
| `touch/drag-zone.ts` | `DragZone` | invisible drag surface — per-frame deltas for camera look, normalized position for paddles |
|
|
36
|
+
| `touch/rotate-overlay.ts` | `RotateOverlay` | "rotate your phone" screen, shown only while the device is held the wrong way |
|
|
37
|
+
|
|
38
|
+
## The three rules that apply to every recipe
|
|
39
|
+
|
|
40
|
+
1. **Create the widgets always, show them only on touch devices:**
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { TouchJoystick, VirtualButton } from "./controllers/touch/touch-joystick.ts";
|
|
44
|
+
import { DragZone } from "./controllers/touch/drag-zone.ts";
|
|
45
|
+
|
|
46
|
+
const touch = navigator.maxTouchPoints > 0;
|
|
47
|
+
const joy = new TouchJoystick({ floating: true });
|
|
48
|
+
const btnJump = new VirtualButton({ label: "Jump" });
|
|
49
|
+
[joy, btnJump].forEach((w) => w.setVisible(touch));
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
(`maxTouchPoints > 0` is also true on touch-screen laptops, so the controls
|
|
53
|
+
can appear on some desktops — harmless and intended; they cost nothing when
|
|
54
|
+
untouched.)
|
|
55
|
+
|
|
56
|
+
2. **Give the canvas `touch-action: none`** so drags reach the game instead of
|
|
57
|
+
scrolling/zooming the page:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
renderer.domElement.style.touchAction = "none";
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
3. **Touch inputs merge with keyboard/mouse, they never replace them.** Read
|
|
64
|
+
both every frame and let either drive:
|
|
65
|
+
`jump: kb.space || btnJump.pressed`. Same intents, same code path — a touch
|
|
66
|
+
button and its key must run identical game logic.
|
|
67
|
+
|
|
68
|
+
## Genre recipes
|
|
69
|
+
|
|
70
|
+
Pick what matches the game; most games need exactly one or two of these.
|
|
71
|
+
|
|
72
|
+
- **Hand-rolled WASD / character movement → joystick.** Read `joy.x`/`joy.y`
|
|
73
|
+
(both in [-1, 1], up-positive) wherever the key states are read and take
|
|
74
|
+
whichever is nonzero. Use `floating: true` for action games where the thumb
|
|
75
|
+
lives on the stick; the static circle is fine for slower games. On the
|
|
76
|
+
bundled character controller, pass it through instead — see
|
|
77
|
+
`$genex-threejs-character-controller` (`joystick: { x: joy.x, y: joy.y }`).
|
|
78
|
+
- **Camera look → drag zone on the right half.** Default `DragZone()` is
|
|
79
|
+
exactly that; per frame `const { dx, dy } = look.consumeDelta()` then apply
|
|
80
|
+
to yaw/pitch with the same sensitivity scale as the mouse path. Games on the
|
|
81
|
+
bundled `FollowCamera` skip this — its drag-orbit already works on touch.
|
|
82
|
+
- **Mouse-driven / tap games (click-to-move, tower defense, puzzles, cards) —
|
|
83
|
+
no widgets.** Taps already fire your click/pointer handlers. The recipe is
|
|
84
|
+
hit-target size: anything tappable should be ~44px+ on screen (fatten the
|
|
85
|
+
pick radius or the DOM button, not the art). This is the ~90%-done genre —
|
|
86
|
+
resist the urge to add a joystick it doesn't need.
|
|
87
|
+
- **Paddle / pong / slider → drag zone as an absolute axis.** Reshape the zone
|
|
88
|
+
over the paddle's travel area and read `zone.x` (or `.y`), normalized 0..1,
|
|
89
|
+
while `zone.active` — map it straight to the paddle position.
|
|
90
|
+
- **Discrete actions (jump / shoot / interact / brake / restart) → virtual
|
|
91
|
+
buttons.** One `VirtualButton` per action; first button parks bottom-right
|
|
92
|
+
by default, position the rest yourself
|
|
93
|
+
(`wrapperStyle: { right: "100px", bottom: "48px" }`). Vehicles: the
|
|
94
|
+
enter/exit prompt must be tappable too — `$genex-threejs-vehicle-controllers`
|
|
95
|
+
has that wiring.
|
|
96
|
+
- **Always: a tappable pause.** Escape has no key on a phone — a small pause
|
|
97
|
+
button (top corner, out of thumb arcs) triggering the exact same pause intent.
|
|
98
|
+
`$genex-threejs-game-ui` owns the pause screen itself.
|
|
99
|
+
|
|
100
|
+
## Orientation
|
|
101
|
+
|
|
102
|
+
Declare ONE natural orientation per game (pick by genre: landscape for
|
|
103
|
+
racing/side-scrolling/most action, portrait for stacking/one-thumb casual) and
|
|
104
|
+
use the overlay **only if the wrong orientation genuinely breaks play**:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { RotateOverlay } from "./controllers/touch/rotate-overlay.ts";
|
|
108
|
+
new RotateOverlay({ orientation: "landscape" }); // optional: onChange: (b) => (physics.paused = b)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If the game is merely suboptimal sideways, skip the overlay and just resize.
|
|
112
|
+
The web cannot force an orientation (fullscreen-only lock, none on iOS Safari)
|
|
113
|
+
— the overlay asks; it never traps.
|
|
114
|
+
|
|
115
|
+
## Match the game's style
|
|
116
|
+
|
|
117
|
+
The touch controls are UI like any other — restyle them from the same style
|
|
118
|
+
brief `$genex-threejs-game-ui` locked for this game (the neutral translucent
|
|
119
|
+
defaults are a placeholder look, not art direction):
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
new VirtualButton({
|
|
123
|
+
label: "FIRE",
|
|
124
|
+
capStyle: {
|
|
125
|
+
border: "1.5px solid rgba(255, 120, 40, 0.8)", // the brief's accent
|
|
126
|
+
color: "rgba(255, 210, 180, 0.95)",
|
|
127
|
+
fontFamily: "'Orbitron', system-ui, sans-serif", // the brief's font
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Whatever the restyle, keep the floors the defaults guarantee: tappable targets
|
|
133
|
+
stay ~44px+, labels stay readable over bright AND dark scenes (keep a text
|
|
134
|
+
shadow), controls never cover HUD numbers the player must read, and the
|
|
135
|
+
z-order stays: drag zone (5) under joystick/buttons (10) under pause menus.
|
|
136
|
+
|
|
137
|
+
## Corner cases
|
|
138
|
+
|
|
139
|
+
- **Multi-touch is already handled** — every widget tracks its own pointer and
|
|
140
|
+
captures it, so stick + look-drag + a button all work simultaneously. Extra
|
|
141
|
+
fingers on one widget are ignored, not misread.
|
|
142
|
+
- **No pointer lock on touch.** Mouse-aim games need the drag-zone recipe for
|
|
143
|
+
aiming, or a stated opt-out; the bundled aim mode no-ops on touch by itself
|
|
144
|
+
(`$genex-threejs-camera-direction`).
|
|
145
|
+
- **Safe-area insets are built in** — default positions clear notches and the
|
|
146
|
+
home indicator. If you position widgets yourself near screen edges, keep
|
|
147
|
+
`env(safe-area-inset-*)` in the calc.
|
|
148
|
+
- **The drag zone swallows its touches** — taps inside it don't reach the
|
|
149
|
+
canvas. If the game also needs taps there (tap to shoot), read them from the
|
|
150
|
+
zone (`onChange` + a small-movement threshold) or shrink the zone.
|
|
151
|
+
|
|
152
|
+
## When you skip
|
|
153
|
+
|
|
154
|
+
Say it in one plain line — *"skipped touch controls: the game is
|
|
155
|
+
typing-driven"* — instead of silently shipping a dead screen. Never restructure
|
|
156
|
+
a game's architecture to force touch in; if it doesn't fit, the one-liner is
|
|
157
|
+
the right outcome.
|
|
@@ -82,7 +82,8 @@ games with no on-foot character.
|
|
|
82
82
|
touch interact button**: on phones there is no F key, so the enter prompt
|
|
83
83
|
must be tappable (and an Exit button shown while driving) or the shared
|
|
84
84
|
link is unenterable on mobile. Wire it whenever you wire the touch
|
|
85
|
-
joysticks (`navigator.maxTouchPoints > 0
|
|
85
|
+
joysticks (`navigator.maxTouchPoints > 0`; the shared touch kit + genre
|
|
86
|
+
recipes live in `$genex-threejs-touch-controls`).
|
|
86
87
|
|
|
87
88
|
## Presets at a glance
|
|
88
89
|
|