@maccesar/titools 5.2.0 → 5.2.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/CONTRIBUTING.md +69 -0
- package/README.md +80 -72
- package/agents/ti-pro.md +1 -1
- package/commands/ti-audit.md +2 -2
- package/docs/examples.md +91 -0
- package/docs/knowledge-index.md +57 -0
- package/lib/downloader.js +1 -1
- package/package.json +4 -2
- package/skills/purgetss/references/multi-density-images.md +1 -1
- package/skills/ti-admob-unified/SKILL.md +21 -7
- package/skills/ti-admob-unified/assets/example/app.js +229 -25
- package/skills/ti-admob-unified/assets/example/lib/ads.js +30 -2
- package/skills/ti-admob-unified/assets/example/library-usage.js +33 -1
- package/skills/ti-admob-unified/references/api-reference.md +39 -16
- package/skills/ti-admob-unified/references/banners.md +9 -9
- package/skills/ti-admob-unified/references/contributing.md +14 -9
- package/skills/ti-admob-unified/references/dependencies.md +31 -25
- package/skills/ti-admob-unified/references/fullscreen.md +43 -28
- package/skills/ti-admob-unified/references/game-monetization.md +138 -7
- package/skills/ti-admob-unified/references/getting-started.md +75 -31
- package/skills/ti-admob-unified/references/index.md +5 -2
- package/skills/ti-admob-unified/references/integration.md +8 -6
- package/skills/ti-admob-unified/references/library.md +41 -10
- package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
- package/skills/ti-admob-unified/references/mediation.md +22 -9
- package/skills/ti-admob-unified/references/migration.md +11 -7
- package/skills/ti-admob-unified/references/module-changelog.md +41 -11
- package/skills/ti-admob-unified/references/privacy.md +35 -8
- package/skills/ti-admob-unified/references/production.md +67 -0
- package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
- package/skills/ti-admob-unified/references/roadmap.md +28 -31
- package/skills/ti-admob-unified/references/source-notes.md +51 -4
- package/skills/ti-admob-unified/references/source-snapshot.json +249 -45
- package/skills/ti-admob-unified/references/third-party-notices.md +4 -4
- package/skills/ti-game/SKILL.md +23 -6
- package/skills/ti-game/references/api.md +34 -30
- package/skills/ti-game/references/debugging-performance.md +18 -9
- package/skills/ti-game/references/examples.md +103 -20
- package/skills/ti-game/references/input.md +101 -0
- package/skills/ti-game/references/patterns.md +39 -15
- package/skills/ti-game/references/physics.md +80 -52
- package/skills/ti-game/references/project-setup.md +118 -56
- package/skills/ti-game/references/recipes-action.md +107 -8
- package/skills/ti-game/references/recipes-adventure-effects.md +41 -9
- package/skills/ti-game/references/roadmap.md +10 -3
- package/skills/ti-game/references/source-notes.md +81 -0
- package/skills/ti-game/references/tilemaps.md +12 -4
- package/skills/ti-synthengine/SKILL.md +81 -95
- package/skills/ti-synthengine/assets/example/README.md +84 -0
- package/skills/ti-synthengine/assets/example/app.js +1178 -0
- package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
- package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
- package/skills/ti-synthengine/references/advanced-examples.md +174 -0
- package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
- package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
- package/skills/ti-synthengine/references/api.md +434 -380
- package/skills/ti-synthengine/references/audio-engine.md +157 -230
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- package/skills/ti-synthengine/references/examples.md +123 -253
- package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-physical-validation.json +1918 -0
- package/skills/ti-synthengine/references/guide.md +188 -156
- package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
- package/skills/ti-synthengine/references/performance-review.json +1146 -0
- package/skills/ti-synthengine/references/physical-validation.json +1457 -0
- package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
- package/skills/ti-synthengine/references/recipes.md +423 -599
- package/skills/ti-synthengine/references/recommendations.md +63 -113
- package/skills/ti-synthengine/references/sound-design.md +134 -258
- package/skills/ti-synthengine/references/source-notes.md +93 -105
- package/skills/ti-synthengine/references/source-snapshot.json +379 -32
- package/skills/ti-synthengine/references/stats-performance.md +64 -20
- package/skills/ti-synthengine/references/studio-validation.json +6202 -0
- package/skills/ti-synthengine/references/troubleshooting.md +109 -121
- package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
- package/skills/ti-synthengine/references/unified-validation.json +314 -0
- package/AGENTS-VERCEL-RESEARCH.md +0 -175
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Setting up a ti.game project
|
|
2
2
|
|
|
3
|
-
Installing the module, wiring it into Classic and Alloy projects, organizing art and sound, and the lifecycle work the engine does not do for you.
|
|
3
|
+
Installing the module, wiring it into Classic and Alloy projects, organizing art and sound, and the lifecycle work the engine does not do for you. Reviewed against `macCesar/ti.game@2bf5f82` (2026-10-23), based on `m1ga/ti.game@88e181c`. See [source-notes.md](source-notes.md) for provenance and verification limits.
|
|
4
4
|
|
|
5
5
|
<!-- TOC-START -->
|
|
6
6
|
## Contents
|
|
@@ -14,6 +14,8 @@ Installing the module, wiring it into Classic and Alloy projects, organizing art
|
|
|
14
14
|
- [Lifecycle and cleanup](#lifecycle-and-cleanup)
|
|
15
15
|
- [Debugging and tuning](#debugging-and-tuning)
|
|
16
16
|
- [Platform differences](#platform-differences)
|
|
17
|
+
- [Window resizing, focus and app configuration](#window-resizing-focus-and-app-configuration)
|
|
18
|
+
- [Apple diagnostic workflows](#apple-diagnostic-workflows)
|
|
17
19
|
|
|
18
20
|
<!-- TOC-END -->
|
|
19
21
|
|
|
@@ -25,8 +27,8 @@ There is no registry and no `ti module install` command — Titanium modules are
|
|
|
25
27
|
|
|
26
28
|
```
|
|
27
29
|
modules/
|
|
28
|
-
├── android/ti.game/0.
|
|
29
|
-
└── iphone/ti.game/0.
|
|
30
|
+
├── android/ti.game/1.0.0/
|
|
31
|
+
└── iphone/ti.game/1.0.0/
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
Unzipping the module zip at the project root creates exactly that layout.
|
|
@@ -37,48 +39,53 @@ Then declare it in `tiapp.xml`. The iOS platform key is `iphone`, not `ios`:
|
|
|
37
39
|
|
|
38
40
|
```xml
|
|
39
41
|
<modules>
|
|
40
|
-
<module platform="android" version="0.
|
|
41
|
-
<module platform="iphone" version="0.
|
|
42
|
+
<module platform="android" version="1.0.0">ti.game</module>
|
|
43
|
+
<module platform="iphone" version="1.0.0">ti.game</module>
|
|
42
44
|
</modules>
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
Pinning `version` is worth the extra attribute: with two module versions installed side by side, an unpinned entry silently picks the highest one.
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
### Requirements and source builds
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
| `0.5.0` built before `10a046e` | additionally `Game.createTileLayer()`, native visible-cell tile rendering, tile collision/live edits, and TileLayer participation in `findPath` |
|
|
61
|
-
| `0.5.0` built before `85a723d` | additionally read-only `onWallLeft` / `onWallRight` and the `wallhit` transition event for sprite and TileLayer walls |
|
|
62
|
-
| `0.5.0` built before `05cb60c` | additionally `wallSlideSpeed`, the native downward-speed cap while pressed against a wall |
|
|
63
|
-
| `0.5.0` built before `c216e7f` | on Android only, string-row TileLayers with a nested JS `legend` decode as empty because the proxy rejects the ordinary map object; numeric data is unaffected |
|
|
64
|
-
| `0.5.0` built before `d7d471e` (2026-08-28) | additionally the whole gamepad surface: `buttondown`/`buttonup`, `stick`, `trigger`, `gamepadconnected`/`gamepaddisconnected`, and the `gamepads`/`gamepad`/dead-zone properties |
|
|
65
|
-
| `0.5.0` built before `af2e544` (2026-08-29) | additionally circular horizontal worlds: `gameView.worldWrapX` and per-sprite `wrapWorldX` |
|
|
66
|
-
| `0.5.0` built before `80d8f2b` (2026-08-30) | additionally the `solidimpact` event and `impactThreshold` |
|
|
67
|
-
| `0.5.0` built before `9c45cf6` (2026-09-02) — **this includes the artifacts most people have** | no crash fixes: writing `y`/`zIndex` from JS can abort the GL thread with *Comparison method violates its general contract*; a sound whose load failed throws `IllegalStateException` on the next `play()`; passing a mistyped value to `sheet`/`target`/`head`/`tail`/`font` aborts the process in JNI instead of raising a JS error. Also `pinch`/`rotate` fire per motion event instead of ~10 Hz, two-finger rotation can add a whole turn at the ±180° flip, the tap/drag slop ignores `cameraScale`, `cameraX`/`cameraY`/`cameraScale`/`cameraBounds`/`throttle`/`steering` are dropped when passed to a `create*` call, a removed sprite stays retained as the camera's follow target, `collidesWith`/`solidWith` reject a bare string, and the iOS glitch shader goes NaN after roughly 27 minutes |
|
|
68
|
-
|
|
69
|
-
The newest upstream release is `0.5.0` (2026-09-02), and for once the tag is ahead of the local habit rather than behind it: it sits on `9c45cf6`, so the published zip carries every fix in the row above, while a module built locally from source any time in the previous two weeks reports the same `0.5.0` and does not. Same number, different engine — prefer the release artifact, or rebuild from upstream `main` and let the manifest read `0.6.0`. If a documented call is `undefined`, check the manifest and build commit before hunting for a typo, and rebuild from upstream `main`. More reliable than either is feature detection: read a property **before** writing it (`typeof sprite.hitboxScaleX === 'undefined'`) or probe a method/factory (`typeof Game.createTileLayer === 'function'`). Write-then-read is not proof because Kroll can retain an unknown property on the proxy.
|
|
70
|
-
|
|
71
|
-
Building the module from source:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
cd android
|
|
75
|
-
ti build -p android --build-only # → android/dist/ti.game-android-<version>.zip
|
|
76
|
-
ti build -p ios --build-only # macOS only → ios/dist/ti.game-iphone-<version>.zip
|
|
51
|
+
Both manifests declare version `1.0.0` and minimum Titanium SDK `13.4.0`.
|
|
52
|
+
The documented build SDK is `13.4.1.GA`. Android uses OpenGL ES 2.0.
|
|
53
|
+
Apple uses Metal and requires iOS/iPadOS 15 or later; the package contains
|
|
54
|
+
arm64 devices, arm64/x86_64 Simulator and arm64/x86_64 Mac Catalyst slices.
|
|
55
|
+
Architecture presence does not establish execution on that hardware.
|
|
56
|
+
|
|
57
|
+
From the module repository root:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
(cd android && ti build -p android --build-only --sdk 13.4.1.GA)
|
|
61
|
+
(cd ios && ti build -p ios --build-only --sdk 13.4.1.GA)
|
|
77
62
|
```
|
|
78
63
|
|
|
64
|
+
Apple requires macOS/Xcode. Its `ios/titanium.xcconfig` selects
|
|
65
|
+
`13.4.1.GA`; selecting another SDK in the CLI also requires changing that
|
|
66
|
+
Xcode SDK setting or providing an external override:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
cat > /tmp/ti-game-sdk.xcconfig <<'CONFIG'
|
|
70
|
+
TITANIUM_SDK_VERSION = 13.4.1.GA
|
|
71
|
+
CONFIG
|
|
72
|
+
XCODE_XCCONFIG_FILE=/tmp/ti-game-sdk.xcconfig ti build -p ios --build-only --sdk 13.4.1.GA
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Run the latter command inside `ios/`. Applications need no `default.metallib`:
|
|
76
|
+
shaders are embedded and compiled once during device initialization. A native
|
|
77
|
+
initialization error logs a diagnostic and stops the view; there is no public
|
|
78
|
+
backend selector or production Apple OpenGL fallback.
|
|
79
|
+
|
|
80
|
+
Upstream's nightly is an automated `main` build, not a stable release. Record
|
|
81
|
+
source commit and ZIP SHA-256 for any installed artifact: version `1.0.0`
|
|
82
|
+
alone does not establish the reviewed fork's feature set. Probe a method before
|
|
83
|
+
calling it; read a property before writing it. Unknown Kroll properties can
|
|
84
|
+
round-trip without native support, so write-then-read is not proof.
|
|
85
|
+
|
|
79
86
|
## Classic projects
|
|
80
87
|
|
|
81
|
-
`require('ti.game')` anywhere and add the GameView to a window. The 34 demos in the module's `example/` folder are Classic, each one a CommonJS module exporting a start function (`app.js` is the launcher
|
|
88
|
+
`require('ti.game')` anywhere and add the GameView to a window. The launcher has 34 mobile touch demos and a 35th Mac input demo; the demos in the module's `example/` folder are Classic, each one a CommonJS module exporting a start function (`app.js` is the launcher; `backnav.js`, `keyboard.js`, and `pool-viewport.js` are helpers):
|
|
82
89
|
|
|
83
90
|
```javascript
|
|
84
91
|
// Resources/flappy.js
|
|
@@ -111,8 +118,8 @@ The standard Alloy element for a native module view. `method` names the factory
|
|
|
111
118
|
```xml
|
|
112
119
|
<!-- app/views/index.xml -->
|
|
113
120
|
<Alloy>
|
|
114
|
-
<Window id="index" navBarHidden="true" exitOnClose="true"
|
|
115
|
-
<Module id="gameView" module="ti.game" method="createGameView" backgroundColor="#8ed8f8" />
|
|
121
|
+
<Window id="index" navBarHidden="true" exitOnClose="true" onClose="onClose">
|
|
122
|
+
<Module id="gameView" module="ti.game" method="createGameView" backgroundColor="#8ed8f8" onResize="onResize" />
|
|
116
123
|
<Button id="jumpButton" title="JUMP" onTouchstart="jump" />
|
|
117
124
|
</Window>
|
|
118
125
|
</Alloy>
|
|
@@ -126,10 +133,6 @@ const heroSheet = Game.createSpriteSheet({ image: 'hero.png', frameWidth: 64, fr
|
|
|
126
133
|
|
|
127
134
|
let initialized = false;
|
|
128
135
|
|
|
129
|
-
function onOpen() {
|
|
130
|
-
$.gameView.addEventListener('resize', onResize);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
136
|
function onResize(e) {
|
|
134
137
|
if (initialized) {
|
|
135
138
|
return;
|
|
@@ -270,11 +273,17 @@ const mono = Game.createFont({ image: 'fonts/mono.png', charWidth: 9, charHeight
|
|
|
270
273
|
|
|
271
274
|
These are **bitmap** fonts — do not put them in `app/assets/fonts/` expecting Titanium's native font handling, and do not use a `.ttf` here. To go from a TTF to either format, the module ships `tools/genfont.py`, which rasterizes it into a grid or BMFont atlas.
|
|
272
275
|
|
|
273
|
-
The built-in font covers ASCII 32..126
|
|
276
|
+
The built-in Press Start 2P font covers ASCII 32..126 plus
|
|
277
|
+
`ÁÉÍÓÚÜÑáéíóúüñ¿¡ÄÖäößËÏëïÈèÊêÀà`. Use precomposed characters;
|
|
278
|
+
combining accents and capital `ẞ` are unsupported (`SS` is the documented
|
|
279
|
+
uppercase alternative). Custom grid fonts still default to ASCII. Generate
|
|
280
|
+
a custom font for other characters. The README's older Draw text paragraph
|
|
281
|
+
still says ASCII-only; its current Font contract and both embedded native
|
|
282
|
+
character maps establish this expanded repertoire.
|
|
274
283
|
|
|
275
284
|
## Lifecycle and cleanup
|
|
276
285
|
|
|
277
|
-
The engine handles what it owns: the render loop pauses and resumes with the activity or app, music pauses and resumes with it, and textures survive an EGL context loss. **Everything you created in JS is yours to stop.**
|
|
286
|
+
The engine handles what it owns: the render loop pauses and resumes with the activity or app, music pauses and resumes with it, and Android textures survive an EGL context loss. Apple retains in-flight GPU resources until Metal commands complete. **Everything you created in JS is yours to stop.**
|
|
278
287
|
|
|
279
288
|
A leaked `setInterval` keeps running after the window closes, keeps reading sprite properties from a dead scene and keeps the whole closure alive. A looping `createSound` keeps playing over the next screen.
|
|
280
289
|
|
|
@@ -319,7 +328,7 @@ function listen(target, eventName, handler) {
|
|
|
319
328
|
|
|
320
329
|
### GPU textures: `unload()`
|
|
321
330
|
|
|
322
|
-
Sheets and fonts hold
|
|
331
|
+
Sheets and fonts hold GPU textures. `spriteSheet.unload()` and `font.unload()` schedule texture disposal on the render thread; Apple retains resources already encoded in pending Metal commands until completion. Releasing the proxy does the same, so a sheet that goes out of scope no longer strands its memory.
|
|
323
332
|
|
|
324
333
|
This is for **level streaming** — a game that loads world 2's atlases while world 1's are still resident. It is not a cache: the unload is permanent, and any sprite still pointing at that sheet stops drawing.
|
|
325
334
|
|
|
@@ -343,16 +352,17 @@ alive, keep that GameView attached or create a fresh one on return; rebuilding
|
|
|
343
352
|
only its Sprite session is insufficient. Verify the actual UIKit presentation
|
|
344
353
|
and installed module on-device.
|
|
345
354
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
355
|
+
The reviewed Apple controller implementation uses a shared dispatcher with
|
|
356
|
+
weak per-view receivers (`TGAttachGamepadDevice` / `TGDetachGamepadDevice`).
|
|
357
|
+
Closing one receiver no longer clears another view's physical controller
|
|
358
|
+
handler. Define gameplay input ownership explicitly: retained views may still
|
|
359
|
+
observe gamepad events. Focused keyboard/actions have separate ownership rules
|
|
360
|
+
in [input.md](input.md).
|
|
352
361
|
|
|
353
|
-
Sources:
|
|
354
|
-
`shutdownRendering`) and `ios/Classes/TGGamepadController.m`
|
|
355
|
-
|
|
362
|
+
Sources: `ios/Classes/TiGameGameView.m` (`didMoveToWindow`,
|
|
363
|
+
`shutdownRendering`) and `ios/Classes/TGGamepadController.m` at the reviewed
|
|
364
|
+
pin. Native inspection verifies lifecycle contracts, not a specific navigation
|
|
365
|
+
presentation on a device.
|
|
356
366
|
|
|
357
367
|
### LiveView reloads
|
|
358
368
|
|
|
@@ -373,14 +383,66 @@ Since 2026-08-23 the module retires the previous generation itself: stale render
|
|
|
373
383
|
|
|
374
384
|
## Platform differences
|
|
375
385
|
|
|
376
|
-
The
|
|
386
|
+
The public API is mostly shared. Preserve these platform contracts:
|
|
377
387
|
|
|
378
388
|
- **`Ti.UI.createNotification` (Android Toast) does not exist on iOS.** The demos branch on `Ti.Platform.osname` and fall back to `Ti.UI.createAlertDialog`.
|
|
379
|
-
- **The iOS Simulator renders at a 1x logical drawable** (
|
|
389
|
+
- **The iOS Simulator renders at a 1x logical drawable** (the Metal port retains this policy for comparisons). Simulator timing is not a controlled comparison with physical hardware. Real iPhone and iPad builds keep the device's native screen scale. Judge performance on device.
|
|
380
390
|
- **`logicalDensityFactor` lies in the iOS Simulator** — it reports the device scale while the surface renders at 1x. Derive dp→scene units from the surface instead: `H / Ti.Platform.displayCaps.platformHeight`.
|
|
381
391
|
- **Android's `touchFeedback` ripple** cannot animate over the GL canvas and logs `RippleDrawable` errors; give on-screen buttons manual `backgroundColor` feedback on `touchstart`/`touchend`.
|
|
382
392
|
- **Sound formats**: WAV, MP3 and OGG on Android; WAV, MP3 and M4A on iOS. WAV for effects, MP3 for music covers both.
|
|
383
393
|
- **TileLayer grid configuration**: assign `legend`, `firstGid`, `cols`, and `rows` in `createTileLayer()`. iOS accepts later writes; Android treats them as creation-time inputs.
|
|
384
394
|
- **Android `legend` compatibility**: string-row maps require build `c216e7f` or newer. Older Android proxies ignore a normal nested JS legend object; use numeric data or update the module.
|
|
385
|
-
- **Performance telemetry**: `averagePresentMs` and `presentFailures` exist only on
|
|
395
|
+
- **Performance telemetry**: `averagePresentMs` and `presentFailures` exist only on Apple and are omitted on Android. They measure CPU drawable acquisition/submission and drawable/command failures, not GPU time or confirmed physical presentation.
|
|
386
396
|
- **`theme: 'Theme.Titanium.DayNight.NoTitleBar'`** on the window is the Android idiom for a fullscreen game surface; on iOS use `navBarHidden`/`statusBarHidden` (or the Alloy XML equivalents).
|
|
397
|
+
|
|
398
|
+
## Window resizing, focus and app configuration
|
|
399
|
+
|
|
400
|
+
The guarded first `resize` creates the level once. It does not make that level
|
|
401
|
+
responsive: handle later dimensions to reposition HUD/controls and update
|
|
402
|
+
bounds without resetting gameplay. Native touch points are surface pixels;
|
|
403
|
+
Titanium overlay dimensions are points/dp. Derive their conversion from the
|
|
404
|
+
actual canvas, including safe-area and window changes. Pool's source helper
|
|
405
|
+
uses a centered 380×680 point portrait stage; other demos generally use the
|
|
406
|
+
full available canvas. See [examples.md](examples.md).
|
|
407
|
+
|
|
408
|
+
`extendSafeArea` changes whether window children draw behind system bars.
|
|
409
|
+
For keyboard/mouse, configure the consuming app's indirect-input plist and
|
|
410
|
+
focus ownership before testing; see [input.md](input.md). A fullscreen theme
|
|
411
|
+
alone does not configure launch orientation or guarantee hidden system bars.
|
|
412
|
+
|
|
413
|
+
Orientation and launch-screen policy are Titanium app configuration, absent
|
|
414
|
+
from the module tutorial. Use `ti-ui` and `ti-guides` before adding a fixed
|
|
415
|
+
orientation: check the app's `tiapp.xml`, iPhone/iPad supported orientations,
|
|
416
|
+
launch screen, root window and applicable Android activities before opening
|
|
417
|
+
the first game window. Do not treat the older `ti-ui` orientation summary as
|
|
418
|
+
proof of complete multi-activity or current launch-screen configuration; that
|
|
419
|
+
cross-skill audit remains outside this module refresh.
|
|
420
|
+
|
|
421
|
+
## Apple diagnostic workflows
|
|
422
|
+
|
|
423
|
+
From the source checkout, `python3 tests/apple-renderer/run.py` builds native
|
|
424
|
+
renderer XCTest fixtures (Xcode and XcodeGen required). Its default target is
|
|
425
|
+
iPhone 16e/iOS 18.6; choose another installed runtime with `--destination` and
|
|
426
|
+
`--deployment-target`. Catalyst uses
|
|
427
|
+
`--destination 'platform=macOS,variant=Mac Catalyst' --deployment-target 15.0`.
|
|
428
|
+
`--baseline` compares the preserved OpenGL baseline `127a08c`; this does not
|
|
429
|
+
add an OpenGL backend to the module. Device execution needs
|
|
430
|
+
`--signing-profile`, `--signing-team` and `--signing-identity` together.
|
|
431
|
+
Outputs remain under `/tmp` by default.
|
|
432
|
+
|
|
433
|
+
After building the Apple ZIP, generate an isolated Classic smoke-test app:
|
|
434
|
+
|
|
435
|
+
```sh
|
|
436
|
+
python3 tests/apple-renderer/prepare_demos.py --icon /path/to/DefaultIcon.png
|
|
437
|
+
python3 tests/apple-renderer/prepare_demos.py --icon /path/to/DefaultIcon.png --include-input --output /tmp/ti-game-mac-demos
|
|
438
|
+
python3 tests/apple-renderer/test_prepare_demos.py
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
The helper discovers launcher demos, defaults to SDK `13.4.1.GA` (`--sdk`
|
|
442
|
+
overrides), enables indirect pointer events, and writes diagnostics to
|
|
443
|
+
`metal-demos.json` in the generated app's data directory. Native fixture and
|
|
444
|
+
smoke checks do not replace visual/gameplay review. Current upstream evidence
|
|
445
|
+
records 18 renderer cases on Apple Silicon Catalyst and a physical iPad;
|
|
446
|
+
physical iPhone, Intel execution, complete interactive acceptance and controlled
|
|
447
|
+
performance comparisons remain pending. This skill audit does not rerun or
|
|
448
|
+
extend that hardware evidence.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Action-game recipes
|
|
2
2
|
|
|
3
|
-
Production-oriented patterns distilled from the
|
|
3
|
+
Production-oriented patterns distilled from the action demos and public contracts at `macCesar/ti.game@2bf5f82` (documentation checkout above upstream `m1ga/ti.game@88e181c`; previous skill pin `3bea2f4`). Use [examples.md](examples.md) to locate the full runnable demo and [api.md](api.md) for exact defaults and payloads. The wall-contact API is source/README-backed because upstream has not added a wall-jump demo yet.
|
|
4
4
|
|
|
5
5
|
Use [physics.md](physics.md) for the implemented equations, units, jump tuning,
|
|
6
6
|
inertia versus direct control, and numerical limits. Recipes select native
|
|
@@ -9,6 +9,7 @@ mechanisms; they do not replace that model reference.
|
|
|
9
9
|
<!-- TOC-START -->
|
|
10
10
|
## Contents
|
|
11
11
|
|
|
12
|
+
- [Shared digital actions](#shared-digital-actions)
|
|
12
13
|
- [Tap-to-flap](#tap-to-flap)
|
|
13
14
|
- [Platformer](#platformer)
|
|
14
15
|
- [Top-down / Zelda](#top-down--zelda)
|
|
@@ -21,6 +22,80 @@ mechanisms; they do not replace that model reference.
|
|
|
21
22
|
|
|
22
23
|
<!-- TOC-END -->
|
|
23
24
|
|
|
25
|
+
## Shared digital actions
|
|
26
|
+
|
|
27
|
+
The current `inputmap.js` demonstrates movement, grounded jumps, normalized
|
|
28
|
+
straight/diagonal shots, grounded crouching with fixed feet, separate
|
|
29
|
+
instructions/feedback, and native text-field ownership. [The catalog](examples.md#keyboard-shortcuts-and-shared-helpers)
|
|
30
|
+
lists every shortcut and the Mac-only right/middle mouse limitation. Its
|
|
31
|
+
last-pressed-direction policy differs deliberately from Platformer/Volley,
|
|
32
|
+
where opposite directions cancel and releasing one resumes the other.
|
|
33
|
+
|
|
34
|
+
For keyboard and digital gamepad actions, write native velocity on action
|
|
35
|
+
transitions; choose one gameplay route per action. This curated extraction
|
|
36
|
+
assumes `player`, `RUN_SPEED`, `jump()` and `win` already exist:
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const held = { left: false, right: false };
|
|
40
|
+
gameView.inputMap = {
|
|
41
|
+
gameplay: {
|
|
42
|
+
left: [{ device: 'keyboard', code: 'ArrowLeft' }, { device: 'gamepad', button: 'left' }],
|
|
43
|
+
right: [{ device: 'keyboard', code: 'ArrowRight' }, { device: 'gamepad', button: 'right' }],
|
|
44
|
+
jump: [{ device: 'keyboard', code: 'Space' }, { device: 'gamepad', button: 'a' }]
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function applyDirection() {
|
|
48
|
+
player.velocityX = (Number(held.right) - Number(held.left)) * RUN_SPEED;
|
|
49
|
+
}
|
|
50
|
+
function down(e) {
|
|
51
|
+
if (Object.prototype.hasOwnProperty.call(held, e.action)) {
|
|
52
|
+
held[e.action] = true;
|
|
53
|
+
applyDirection();
|
|
54
|
+
} else if (e.action === 'jump') {
|
|
55
|
+
jump();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function up(e) {
|
|
59
|
+
if (Object.prototype.hasOwnProperty.call(held, e.action)) {
|
|
60
|
+
held[e.action] = false;
|
|
61
|
+
applyDirection();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function focus() {
|
|
65
|
+
gameView.inputContext = 'gameplay';
|
|
66
|
+
gameView.focusInput();
|
|
67
|
+
}
|
|
68
|
+
function release() {
|
|
69
|
+
gameView.inputContext = null;
|
|
70
|
+
held.left = held.right = false;
|
|
71
|
+
applyDirection();
|
|
72
|
+
}
|
|
73
|
+
gameView.addEventListener('actiondown', down);
|
|
74
|
+
gameView.addEventListener('actionup', up);
|
|
75
|
+
win.addEventListener('open', focus);
|
|
76
|
+
win.addEventListener('focus', focus);
|
|
77
|
+
win.addEventListener('blur', release);
|
|
78
|
+
win.addEventListener('close', () => {
|
|
79
|
+
release();
|
|
80
|
+
gameView.removeEventListener('actiondown', down);
|
|
81
|
+
gameView.removeEventListener('actionup', up);
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Native actions aggregate all bindings: releasing one key/controller leaves
|
|
86
|
+
the action held until the last source releases; repeat does not retrigger it.
|
|
87
|
+
A context change releases actions and requires a fresh press. Do not route
|
|
88
|
+
mapped pad buttons through individual button listeners as well. When keeping
|
|
89
|
+
touch buttons, track each touch source separately from the aggregated native
|
|
90
|
+
action, as Platformer/Volley do, so a key release cannot cancel a held touch.
|
|
91
|
+
|
|
92
|
+
With a text field, disable the action context on field focus and clear held
|
|
93
|
+
state; restore it and call `focusInput()` only on explicit canvas/focus-button
|
|
94
|
+
return. A paused context can retain just Escape to resume, as in the demo;
|
|
95
|
+
`timeScale = 0` alone does not disable input. Track/cancel owned game timers
|
|
96
|
+
on close. [Project setup](project-setup.md) owns platform and plist prerequisites;
|
|
97
|
+
[API](api.md) owns map validation, raw events and focus contracts.
|
|
98
|
+
|
|
24
99
|
## Tap-to-flap
|
|
25
100
|
|
|
26
101
|
Gravity plus a replacement vertical velocity per tap, not an accumulated impulse. Scoring and death are collisions with invisible zones. The rotation tween is what sells the arc.
|
|
@@ -68,7 +143,8 @@ pig.addEventListener('collision', (e) => {
|
|
|
68
143
|
});
|
|
69
144
|
```
|
|
70
145
|
|
|
71
|
-
Obstacles are pooled gates: three sprites each (top pipe, bottom pipe, invisible score zone) that scroll left on `velocityX` and get repositioned when they leave the screen.
|
|
146
|
+
Obstacles are pooled gates: three sprites each (top pipe, bottom pipe, invisible score zone) that scroll left on `velocityX` and get repositioned when they leave the screen. The demo wins after five gates; a collision with a pipe or ground ends the
|
|
147
|
+
run, and the next press resets. Freezing the game means setting every scroller's `velocityX = 0` and the player's `gravity = 0` — the engine has no global "stop", but `gameView.timeScale = 0` is one if you want the scene frozen mid-flight.
|
|
72
148
|
|
|
73
149
|
## Platformer
|
|
74
150
|
|
|
@@ -176,7 +252,10 @@ Set `carryRiders: false` on terrain that scrolls under a player who should stay
|
|
|
176
252
|
|
|
177
253
|
## Top-down / Zelda
|
|
178
254
|
|
|
179
|
-
A tile map from a string array, plus `ySort` for depth.
|
|
255
|
+
A tile map from a string array, plus `ySort` for depth. Water tiles and the separate house block movement; trees are nonblocking
|
|
256
|
+
decoration you can walk behind. The abbreviated map below shows the tile
|
|
257
|
+
portion; add the house as a separate `collisionGroup: 'solid'`, `ySort: true`
|
|
258
|
+
sprite at the actors' `zIndex`, as in the demo.
|
|
180
259
|
|
|
181
260
|
```javascript
|
|
182
261
|
const MAP = [
|
|
@@ -204,7 +283,7 @@ for (let row = 0; row < ROWS; row++) {
|
|
|
204
283
|
width: TILE,
|
|
205
284
|
height: TILE,
|
|
206
285
|
zIndex: 0,
|
|
207
|
-
collisionGroup: (kind === 'W') ? 'solid' : null // only water
|
|
286
|
+
collisionGroup: (kind === 'W') ? 'solid' : null // only water tiles block; house is a separate solid
|
|
208
287
|
}));
|
|
209
288
|
}
|
|
210
289
|
}
|
|
@@ -283,6 +362,18 @@ timer = setInterval(() => {
|
|
|
283
362
|
}, 100);
|
|
284
363
|
```
|
|
285
364
|
|
|
365
|
+
The full companion AI in `topdown.js` adds a timed state machine to the
|
|
366
|
+
simple follow extraction above: sit while the player is idle, stand while
|
|
367
|
+
near a moving player, and walk when outside follow distance. After a startup
|
|
368
|
+
grace period it may approach a nearby tree, face the trunk and play `pee`
|
|
369
|
+
for 1800 ms, then wait 12000 ms before another visit. If the player leaves
|
|
370
|
+
more than five tiles away while it approaches, cancel that visit and use a
|
|
371
|
+
4000 ms retry delay. A started `pee` finishes before following resumes.
|
|
372
|
+
Its 100 ms JS decision interval is cleared on window close; native velocity
|
|
373
|
+
and animation still perform all motion. Water tiles also play staggered
|
|
374
|
+
`flow` animations so the shore does not ripple in sync; track any startup
|
|
375
|
+
JS timeouts when adapting the demo for repeated screen entry.
|
|
376
|
+
|
|
286
377
|
## Top-down racer
|
|
287
378
|
|
|
288
379
|
`carMode` gives you throttle, steering and emergent drift. You supply the track and the lap logic.
|
|
@@ -420,11 +511,16 @@ warnTimer = setInterval(() => {
|
|
|
420
511
|
```
|
|
421
512
|
|
|
422
513
|
Rocks are round (`hitboxShape: 'circle'`), drift on random velocities, spin on `angularVelocity`, and are recycled by `visible = false` rather than removed.
|
|
514
|
+
The demo starts five rocks; clearing them starts another wave. The ship keeps
|
|
515
|
+
flying after a hit, loses one of three lives and receives brief invulnerability.
|
|
516
|
+
Its last life blinks red, and zero lives resets the game. The nose-fired pooled
|
|
517
|
+
bolts inherit ship velocity; hold-to-fire only schedules shots, never movement.
|
|
518
|
+
Stop the looping thruster sound on release and every owned sound/timer on close.
|
|
423
519
|
|
|
424
520
|
## Lunar lander
|
|
425
521
|
|
|
426
522
|
Same flight model as Asteroids, with gravity and a touchdown rule. This is a
|
|
427
|
-
curated recipe, not one of the
|
|
523
|
+
curated recipe, not one of the 35 module demos. `land` reports grounding
|
|
428
524
|
after the solid response: live velocity may already be zero or reflected.
|
|
429
525
|
For a static sprite pad, use the snapshotted normal speed in `solidimpact`.
|
|
430
526
|
It is compensated for this frame's acceleration, not the full incoming
|
|
@@ -622,9 +718,8 @@ function hitLane(lane, pad) {
|
|
|
622
718
|
An invisible `misszone` trigger below the pads catches gems that got away and
|
|
623
719
|
counts the miss. `FALL_SPEED = (PAD_Y + NOTE) / 1.6` gives a 1.6 s trip from
|
|
624
720
|
`-NOTE` to `PAD_Y`. At the stated 150 BPM, `60000 / 150 = 400` ms is a quarter
|
|
625
|
-
note; 1.6 s is four quarter notes (eight eighths). The
|
|
626
|
-
|
|
627
|
-
not the engine or the demo's timing without a separate design decision.
|
|
721
|
+
note; 1.6 s is four quarter notes (eight eighths). The current source corrects the older "eighth" comments
|
|
722
|
+
while preserving the timing constants; an old copy of the demo may still have that wording.
|
|
628
723
|
Position windows convert to time with `windowSeconds = windowPx / FALL_SPEED`.
|
|
629
724
|
The demo uses a JS interval and independent music playback, not an audio-clock
|
|
630
725
|
beat scheduler; see [rhythm timing](physics.md#rhythm-timing).
|
|
@@ -660,6 +755,10 @@ ball.addEventListener('collision', (e) => {
|
|
|
660
755
|
});
|
|
661
756
|
```
|
|
662
757
|
|
|
758
|
+
In Volley, touching the floor scores for the opposite side and serves from
|
|
759
|
+
the center toward the side that conceded. The player/opponent stay on their
|
|
760
|
+
own sides of the net; the AI follows the ball on its side before jumping.
|
|
761
|
+
|
|
663
762
|
Note the split: walls and the net are `solidWith` (the engine bounces the ball off them for free), while the players and the floor are `collidesWith` (you decide what happens). For breakout, bricks go in `collidesWith` and are removed with `visible = false` on hit while the paddle and walls stay solid.
|
|
664
763
|
|
|
665
764
|
A `restitution: 1` ball reflects larger impacts without restitution loss;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Adventure, presentation, and effects recipes
|
|
2
2
|
|
|
3
|
-
Production-oriented patterns distilled from the
|
|
3
|
+
Production-oriented patterns distilled from the non-action demos and public contracts at `macCesar/ti.game@2bf5f82` (documentation checkout above upstream `m1ga/ti.game@88e181c`; previous skill pin `3bea2f4`). Use [examples.md](examples.md) to locate the full runnable demo and [api.md](api.md) for exact defaults and payloads.
|
|
4
4
|
|
|
5
5
|
<!-- TOC-START -->
|
|
6
6
|
## Contents
|
|
@@ -52,6 +52,9 @@ sprite.addEventListener('complete', () => {
|
|
|
52
52
|
|
|
53
53
|
```javascript
|
|
54
54
|
sprite.addEventListener('tap', () => {
|
|
55
|
+
if (card.state !== 'hand' || card.played || played) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
55
58
|
if (card.selected) {
|
|
56
59
|
card.selected = false;
|
|
57
60
|
sprite.animate({ x: home.x, y: home.y, rotation: home.rotation, scale: 1, glowOpacity: 0, duration: 150, easing: Game.EASE_OUT });
|
|
@@ -65,16 +68,34 @@ sprite.addEventListener('tap', () => {
|
|
|
65
68
|
});
|
|
66
69
|
```
|
|
67
70
|
|
|
71
|
+
The full card state machine deals five cards, permits at most three selected,
|
|
72
|
+
and accepts Done only after dealing/sweeping ends and exactly three are
|
|
73
|
+
selected. `played` guards the whole hand; `card.played` guards each card.
|
|
74
|
+
Done moves the three selected cards side-by-side, disables their wobble and
|
|
75
|
+
fades their glows while the two remaining cards re-fan at center. It becomes
|
|
76
|
+
Reset; reset marks cards `leaving`, disables wobble, sweeps all five left,
|
|
77
|
+
then redeals after 750 ms. Track/cancel that redeal timeout and the 350 ms
|
|
78
|
+
opening-deal timeout when adapting the demo. The completion listener should
|
|
79
|
+
restore wobble after any tween only for cards still in `hand` and unplayed.
|
|
80
|
+
|
|
68
81
|
**Drag & drop with snapping** (puzzle pieces, board tiles). The drag itself is native; you only decide where it lands:
|
|
69
82
|
|
|
70
83
|
```javascript
|
|
71
84
|
const piece = Game.createSprite({ sheet: sheet, frame: index, x: homeX, y: homeY, width: PIECE, height: PIECE, draggable: true, zIndex: 10 });
|
|
85
|
+
let currentCell = null;
|
|
72
86
|
|
|
73
87
|
piece.addEventListener('press', () => {
|
|
74
88
|
piece.zIndex = 100; // draw above everything while held
|
|
75
89
|
piece.animate({ scale: 1.15, duration: 120, easing: Game.EASE_OUT });
|
|
76
90
|
});
|
|
77
91
|
|
|
92
|
+
piece.addEventListener('dragstart', () => {
|
|
93
|
+
if (currentCell) {
|
|
94
|
+
currentCell.piece = null;
|
|
95
|
+
currentCell = null;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
78
99
|
piece.addEventListener('release', () => {
|
|
79
100
|
piece.animate({ scale: 1, duration: 120, easing: Game.EASE_OUT });
|
|
80
101
|
});
|
|
@@ -95,6 +116,7 @@ piece.addEventListener('dragend', (e) => {
|
|
|
95
116
|
piece.zIndex = 10;
|
|
96
117
|
if (best) {
|
|
97
118
|
best.piece = piece;
|
|
119
|
+
currentCell = best;
|
|
98
120
|
piece.animate({ x: best.x, y: best.y, duration: 150, easing: Game.EASE_OUT });
|
|
99
121
|
} else {
|
|
100
122
|
piece.animate({ x: homeX, y: homeY, duration: 400, easing: Game.EASE_IN_OUT });
|
|
@@ -102,7 +124,13 @@ piece.addEventListener('dragend', (e) => {
|
|
|
102
124
|
});
|
|
103
125
|
```
|
|
104
126
|
|
|
105
|
-
Because every handler is per sprite,
|
|
127
|
+
Because every handler is per sprite, each finger can grab its own piece.
|
|
128
|
+
Release the previous cell on `dragstart` before searching for a new free cell.
|
|
129
|
+
The full puzzle checks all four cells after snapping; when filled, it gives
|
|
130
|
+
each piece a staggered 360-degree celebration tween. Restart clears every
|
|
131
|
+
piece's `currentCell`/occupancy, reduces rotation modulo 360 to avoid unwinding
|
|
132
|
+
previous celebration turns, then tweens position/rotation/scale home. Keep
|
|
133
|
+
those state transitions when building a reusable board.
|
|
106
134
|
|
|
107
135
|
## Point & click adventure
|
|
108
136
|
|
|
@@ -233,9 +261,10 @@ Add `blend: 'add'` for fire, magic and sparks — overlaps bloom instead of cove
|
|
|
233
261
|
## Ropes and chains
|
|
234
262
|
|
|
235
263
|
Rope `damping` retains a fraction of the previous displacement **per update**,
|
|
236
|
-
unlike Sprite `linearDamping`, whose coefficient is in 1/s.
|
|
237
|
-
|
|
238
|
-
|
|
264
|
+
unlike Sprite `linearDamping`, whose coefficient is in 1/s. The current
|
|
265
|
+
Android and Apple updates return at `dt <= 0`: nodes, previous positions,
|
|
266
|
+
constraints and tether response freeze. A newly created rope still initializes
|
|
267
|
+
its drawable geometry, and changing the segment count can rebuild it while paused. See the equations and
|
|
239
268
|
pause boundary in [physics.md](physics.md#verlet-ropes-and-tethers).
|
|
240
269
|
|
|
241
270
|
```javascript
|
|
@@ -294,7 +323,10 @@ gameView.cameraEffect = 'glitch';
|
|
|
294
323
|
gameView.cameraEffect = 'none'; // skips the pass entirely
|
|
295
324
|
```
|
|
296
325
|
|
|
297
|
-
For a
|
|
326
|
+
For a repeated decorative ground plane, use `tileRepeat` on one large sprite
|
|
327
|
+
instead of a grid of decorative sprites; it repeats the texture at native
|
|
328
|
+
density. For maps with individual solid/editable cells, use [TileLayer](tilemaps.md).
|
|
329
|
+
Batching, blend changes and effects determine actual draw-call counts.
|
|
298
330
|
|
|
299
331
|
## Visual effects
|
|
300
332
|
|
|
@@ -343,9 +375,9 @@ gameView.addEventListener('tap', () => {
|
|
|
343
375
|
|
|
344
376
|
**`timeScale`** scales the dt for physics, sheet animations, tweens, particles,
|
|
345
377
|
camera and game-clock timers, while rendering/touch keep running. `0.5` gives
|
|
346
|
-
slow motion; `0` gives zero dt for a pause or hit-stop
|
|
347
|
-
|
|
348
|
-
|
|
378
|
+
slow motion; `0` gives zero dt for a pause or hit-stop, including rope
|
|
379
|
+
integration and tether response in this snapshot. Rendering, touch and direct
|
|
380
|
+
app property writes remain active; pausing is not an input lock. `timescale.js` compares a game-clock timer with an
|
|
349
381
|
independent JS interval; it does not include a rope.
|
|
350
382
|
|
|
351
383
|
**`idleAnimation`** — a per-sprite organic sway that composes with tweens and drags. Cheap life for cards, icons, floating pickups and menu art. Disable it before a tween that must land precisely.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Missing, partial, and out-of-scope features
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Reviewed on 2026-10-23 against upstream native `88e181c` and
|
|
4
|
+
`macCesar/ti.game@2bf5f82`, especially `TODO.md` and both native implementations. Planned is not shipped; partial means the exact boundary below matters.
|
|
4
5
|
|
|
5
6
|
## Not available today
|
|
6
7
|
|
|
@@ -22,7 +23,7 @@ Verified against `ti.game@3bea2f4` (2026-09-02), especially upstream `TODO.md` a
|
|
|
22
23
|
| TypeScript declarations | Planned | No upstream `ti.game.d.ts` exists |
|
|
23
24
|
| Aseprite JSON import | Planned | SpriteSheet atlases support TexturePacker JSON hash/array formats |
|
|
24
25
|
| Tweening `width`, `height`, or `tintColor` | Not implemented | Tween scale/opacity/glowOpacity; other properties are instant writes |
|
|
25
|
-
| A GameView-level `stop()` | Already covered differently | `timeScale = 0`
|
|
26
|
+
| A GameView-level `stop()` | Already covered differently | `timeScale = 0` freezes scene time, including Rope integration/constraints/tether, while rendering/touch continue. `pause()` stops the render loop; see [physics.md](physics.md#verlet-ropes-and-tethers) |
|
|
26
27
|
| Standard Titanium touch events on GameView | By design | Use GameView `press`/`tap`/`release`, sprite events, or Titanium views over the canvas |
|
|
27
28
|
| A per-frame JS event | Deliberately absent | React to discrete events and use native movement; coarse `every()` calls are for decisions, not motion |
|
|
28
29
|
|
|
@@ -35,7 +36,13 @@ Verified against `ti.game@3bea2f4` (2026-09-02), especially upstream `TODO.md` a
|
|
|
35
36
|
- Sprite paths, animation chaining, raycasts, A*, game-clock timers, collision exit, swept movers, and four blend modes.
|
|
36
37
|
- Shape-aware solids: circle and rotated-rect geometry, one-way platforms, carried riders, containment, bilateral circle push, two-sided restitution, `gravityX`, and `linearDamping`.
|
|
37
38
|
- Native wall contacts: `onWallLeft` / `onWallRight`, `wallhit`, TileLayer-wall parity, and `wallSlideSpeed`.
|
|
38
|
-
- Native particles, ropes, effect/music sound backends, car physics, thrust, drag/pinch/rotate, and multi-touch sprites.
|
|
39
|
+
- Native particles, ropes, effect/music sound backends, car physics, thrust, drag/pinch/rotate, and multi-touch sprites. Rope freezes node/history/constraint/tether updates at zero dt; fresh geometry can initialize while paused.
|
|
40
|
+
- Damped powered launch and post-contact rest: effective thrust/gravity/car input survives the 4 px/s cutoff; coasting and blocked tangential drift still settle.
|
|
41
|
+
- Circle-aware swept overlap events (circle/circle and rounded circle/rect/OBB), relative target movement, and periodic offsets; translation only with final-frame shape size/orientation.
|
|
42
|
+
- Resting sweep tangential sliding and periodic path frame-delta preservation.
|
|
43
|
+
- Configurable native digital keyboard/mouse/gamepad actions with contexts, aggregate press/release, raw key events and canvas focus. Mac right/middle mouse action delivery remains unavailable in the reviewed docs; see [input.md](input.md).
|
|
44
|
+
- Extended built-in pixel glyphs for Spanish, German, Dutch and selected French characters; custom font generation still defaults to ASCII.
|
|
45
|
+
- Apple Metal renderer on iPhone, iPad and Mac Catalyst; Android retains OpenGL ES 2.0. This is backend support, not proof of installed-binary/device acceptance.
|
|
39
46
|
- Gamepads: normalized `buttondown`/`buttonup` names, throttled `stick`/`trigger`, connect/disconnect events, `gamepads`/`gamepad` snapshots, dead zone and stick-press hysteresis, and release-everything on background or disconnect.
|
|
40
47
|
- Circular horizontal worlds: `gameView.worldWrapX` plus per-sprite `wrapWorldX`, seam-aware camera, rendering, touch, overlap, solid resolution and swept movement, and a full-width TileLayer that repeats across the seam.
|
|
41
48
|
- Discrete physical impacts: the `solidimpact` event with per-receiver `impactThreshold`, compensated closing speed, shared contact point, opposite normals and mixed restitution, for `block`, `contain` and bilateral `push` responses between sprites.
|