@maccesar/titools 4.15.0 → 4.16.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.
@@ -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. Verified against `ti.game@d587081` (2026-08-28).
4
4
 
5
5
  <!-- TOC-START -->
6
6
  ## Contents
@@ -56,8 +56,10 @@ Pinning `version` is worth the extra attribute: with two module versions install
56
56
  | `0.4.0` built before 2026-08-26 | additionally `sprite.attachTo()` / `detach()` / `attachedTo`, and `gameView.follow()` still aborts the app on Android if it is handed anything that is not a sprite |
57
57
  | `0.4.0` built on 2026-08-26 but before `83b7863` | additionally the opacity an attachment inherits from its target, the `anchor` property, and percentage strings on every ratio — a build in this window has `attachTo` but treats `'55%'` as a number it cannot read |
58
58
  | `0.4.0` at its last commit (`b780051`) | additionally everything the `0.5.0` bump carries: `hitboxShape: 'rotatedRect'`, circular solids resolved as circles, `solidMode`, `gravityX`, `linearDamping`, and `restitution` read off both sides of a contact. It also still pulls a swept sprite back to where its frame started when it begins the frame already touching a solid, so a `swept: true` body parked on a slope creeps and then breaks loose as if launched |
59
+ | `0.5.0` built before `d042060` | the performance HUD object form (`debug: { hud, hitbox, hudFont }`) and the `performance` event |
60
+ | `0.5.0` built before `10a046e` | additionally `Game.createTileLayer()`, native visible-cell tile rendering, tile collision/live edits, and TileLayer participation in `findPath` |
59
61
 
60
- The published releases are pre-releases and lag `main`: the newest one is still `0.4.0` (2026-08-20), which is now two manifest numbers and a physics release behind. If a call that this skill documents is `undefined` at runtime, check the manifest version *and the build date* before hunting for a typo, and rebuild from upstream `main`. More reliable than either is feature-detecting in code: read the property **before** ever writing it (`typeof sprite.hitboxScaleX === 'undefined'` means the build predates it). Write-then-read always says yes, because an unknown property is stored on the Kroll proxy and reads back exactly as it was set. A method has no such trap — `typeof sprite.attachTo === 'function'` is a clean probe whatever the app did earlier.
62
+ The newest local release tag is still `0.4.0` (2026-08-20), so tagged artifacts lag one manifest bump plus the HUD and TileLayer work that landed after `0.5.0` was introduced. 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.
61
63
 
62
64
  Building the module from source:
63
65
 
@@ -69,7 +71,7 @@ ti build -p ios --build-only # macOS only → ios/dist/ti.game-iphone-<ve
69
71
 
70
72
  ## Classic projects
71
73
 
72
- `require('ti.game')` anywhere and add the GameView to a window. The 32 demos in the module's `example/` folder are Classic, each one a CommonJS module exporting a start function (`app.js` is the launcher, not a demo):
74
+ `require('ti.game')` anywhere and add the GameView to a window. The 33 demos in the module's `example/` folder are Classic, each one a CommonJS module exporting a start function (`app.js` is the launcher, not a demo):
73
75
 
74
76
  ```javascript
75
77
  // Resources/flappy.js
@@ -270,6 +272,8 @@ The engine handles what it owns: the render loop pauses and resumes with the act
270
272
  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.
271
273
 
272
274
  ```javascript
275
+ const levelObjects = []; // keep emitters, ropes, and TileLayers here
276
+
273
277
  function cleanup() {
274
278
  clearInterval(aiTimer);
275
279
  clearTimeout(spawnTimer);
@@ -280,7 +284,10 @@ function cleanup() {
280
284
  target.removeEventListener(eventName, handler);
281
285
  });
282
286
  gameView.pause();
283
- gameView.removeAllSprites();
287
+ gameView.removeAllSprites(); // Sprite/Text only
288
+ levelObjects.forEach((object) => { // emitters, ropes, tile layers
289
+ gameView.remove(object);
290
+ });
284
291
  }
285
292
 
286
293
  // Classic
@@ -301,7 +308,7 @@ function listen(target, eventName, handler) {
301
308
  }
302
309
  ```
303
310
 
304
- For restart-without-teardown (a retry button), `removeAllSprites()` plus rebuilding is simplest, but pooling and repositioning is cheaper: reset positions, zero velocities, `clearTweens()`, and re-`play()` the animations.
311
+ `removeAllSprites()` is named literally: it clears Sprite/Text objects and their attachment state, but leaves emitters, ropes, and tile layers in the scene. Track non-sprite scene objects in `levelObjects` and remove them explicitly. For restart-without-teardown, pooling and repositioning is cheaper than rebuilding: reset positions, zero velocities, `clearTweens()`, and re-`play()` animations.
305
312
 
306
313
  ### GPU textures: `unload()`
307
314
 
@@ -328,7 +335,7 @@ Since 2026-08-23 the module retires the previous generation itself: stale render
328
335
 
329
336
  ## Debugging and tuning
330
337
 
331
- - **`debug: true` on the GameView** draws collision shapes for the whole scene; on a single sprite, only that one. Green = collision AABB with `hitboxScale` and the per-axis `hitboxScaleX`/`hitboxScaleY` applied, blue = sprite/touch bounds (the full drawn frame, which is also the touch area), orange dot = anchor. This is the fastest way to explain a hit that should not have registered.
338
+ - **`debug: true` on the GameView** draws collision shapes for the whole scene; object form separates hitboxes from the performance HUD: `debug: { hitbox: true, hud: 'topRight' }`. On a sprite or TileLayer, `debug` remains a boolean. See [debugging-performance.md](debugging-performance.md) for colors, telemetry payloads, platform-only keys, and a tuning workflow.
332
339
  - **Physics that feels wrong is usually a units problem.** Speeds are px/s and accelerations px/s², both in surface pixels — which is why the demos express them as fractions of `W` and `H`. A gravity of `20` is imperceptible; `H * 2.2` is a snappy platformer.
333
340
  - **Sprite is invisible?** Check in order: was it added to the GameView, does it have a sheet, is `width`/`height` non-zero, is it inside the surface bounds, and is its `zIndex` below a background sprite.
334
341
  - **Collision never fires?** `collisionGroup` goes on the *target*, `collidesWith` on the *mover*. Both need to be set, and `visible: false` disables collision entirely.
@@ -344,4 +351,6 @@ The JS API is identical on Android and iOS. What differs is around it:
344
351
  - **`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`.
345
352
  - **Android's `touchFeedback` ripple** cannot animate over the GL canvas and logs `RippleDrawable` errors; give on-screen buttons manual `backgroundColor` feedback on `touchstart`/`touchend`.
346
353
  - **Sound formats**: WAV, MP3 and OGG on Android; WAV, MP3 and M4A on iOS. WAV for effects, MP3 for music covers both.
354
+ - **TileLayer grid configuration**: assign `legend`, `firstGid`, `cols`, and `rows` in `createTileLayer()`. iOS accepts later writes; Android treats them as creation-time inputs.
355
+ - **Performance telemetry**: `averagePresentMs` and `presentFailures` exist only on iOS and are omitted on Android.
347
356
  - **`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).