@maccesar/titools 4.14.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.
- package/README.md +7 -1
- package/package.json +1 -1
- package/skills/ti-game/SKILL.md +80 -180
- package/skills/ti-game/references/api.md +83 -18
- package/skills/ti-game/references/debugging-performance.md +103 -0
- package/skills/ti-game/references/examples.md +54 -0
- package/skills/ti-game/references/patterns.md +699 -0
- package/skills/ti-game/references/project-setup.md +21 -11
- package/skills/ti-game/references/recipes-action.md +633 -0
- package/skills/ti-game/references/recipes-adventure-effects.md +340 -0
- package/skills/ti-game/references/roadmap.md +44 -63
- package/skills/ti-game/references/tilemaps.md +125 -0
- package/skills/ti-game/references/recipes.md +0 -1574
package/README.md
CHANGED
|
@@ -177,7 +177,7 @@ The knowledge index is based on the latest Titanium SDK documentation. If your p
|
|
|
177
177
|
| ti-expert | Architecture and implementation | Starting point for most tasks |
|
|
178
178
|
| purgetss | Utility-first styling | UI styling and animations |
|
|
179
179
|
| ti-ui | UI/UX patterns | Complex layouts, ListViews, platform UI |
|
|
180
|
-
| ti-game | 2D games with the `ti.game` module | Sprites, physics,
|
|
180
|
+
| ti-game | 2D games with the `ti.game` module | Sprites, physics, tile layers, collisions, pathfinding, text/performance HUDs, particles, camera |
|
|
181
181
|
| ti-api | Complete Titanium API reference | Looking up properties, methods, events |
|
|
182
182
|
| ti-guides | SDK fundamentals | tiapp.xml, Hyperloop, distribution, JDK/Xcode compatibility, release notes |
|
|
183
183
|
| ti-howtos | Native feature integration | Location, push, media, platform APIs |
|
|
@@ -443,6 +443,8 @@ When it activates:
|
|
|
443
443
|
- Named anchors and percentage strings (`anchor: 'bottom'`, `hitboxScaleY: '55%'`) wherever the engine takes a ratio
|
|
444
444
|
- Particle emitters, Verlet ropes, camera follow/zoom/shake/effects, `scrollFactor` parallax, low-latency sound
|
|
445
445
|
- Game-clock timers that obey pause and slow motion, texture `unload()` for level streaming, and what LiveView reloads clean up on their own
|
|
446
|
+
- Native `TileLayer` maps with visible-cell rendering, Tiled GIDs, solid/one-way cells, live edits and A* integration
|
|
447
|
+
- Collision-shape overlays plus the expandable performance HUD and opt-in `performance` telemetry event
|
|
446
448
|
- Drag & drop, pinch, rotate, multitouch controls
|
|
447
449
|
|
|
448
450
|
Example prompts:
|
|
@@ -455,6 +457,8 @@ Example prompts:
|
|
|
455
457
|
"Add a score HUD that stays put while the camera follows the player."
|
|
456
458
|
"Make the guard walk a patrol route and turn around before the ledge."
|
|
457
459
|
"Tap to walk, but the player should go around the tree instead of through it."
|
|
460
|
+
"Render a 200x200 Tiled map without creating one sprite per tile."
|
|
461
|
+
"Show FPS, p95 frame time, draw calls and texture switches while I tune this scene."
|
|
458
462
|
```
|
|
459
463
|
|
|
460
464
|
Key rules:
|
|
@@ -463,7 +467,9 @@ Key rules:
|
|
|
463
467
|
- `solidWith` blocks, `collidesWith` reports — they are independent
|
|
464
468
|
- A sprite with size but no sheet is an invisible trigger (scores, goals, walls)
|
|
465
469
|
- Add a whole level with one `gameView.add([...])` call
|
|
470
|
+
- Use `Game.createTileLayer()` for large/static grids; keep sprites for interactive cells and actors
|
|
466
471
|
- HUDs are `Game.createText` sprites with `screenFixed: true`, not `Ti.UI.Label` overlays
|
|
472
|
+
- `debug: true` means collision shapes; use `debug: { hud: true }` or `performance` for telemetry
|
|
467
473
|
- Timers that drive game logic belong on the game clock (`gameView.every`), so a paused game stops spawning
|
|
468
474
|
|
|
469
475
|
---
|
package/package.json
CHANGED
package/skills/ti-game/SKILL.md
CHANGED
|
@@ -1,50 +1,60 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ti-game
|
|
3
|
-
description: "Use when
|
|
4
|
-
argument-hint: "[genre or feature]"
|
|
3
|
+
description: "Use when a Titanium app declares ti.game in tiapp.xml, imports require('ti.game'), or needs a 2D game built or reviewed with that native engine: sprites, native movement, collisions, tile layers, camera, HUD, audio, particles, or touch. Covers Alloy and Classic on Android/iOS. Do not use for unrelated game engines or ordinary Titanium UI."
|
|
5
4
|
allowed-tools: Read, Grep, Glob, Edit, Write, Bash(node *)
|
|
6
5
|
---
|
|
7
6
|
|
|
8
|
-
# ti.game — 2D
|
|
7
|
+
# ti.game — native 2D games for Titanium SDK
|
|
9
8
|
|
|
10
|
-
`ti.game` is
|
|
9
|
+
`ti.game` is Michael Gangolf's OpenGL ES 2.0 sprite engine for Titanium. JavaScript describes the scene and reacts to discrete events; Android and iOS run rendering, animation, physics, collision, gestures, audio, particles, ropes, tile layers, camera work, and telemetry natively.
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
This skill is verified against upstream `main` at **`d587081` (2026-08-28)**. Both manifests still say **`0.5.0`**, although important features landed after that version was introduced. Treat the commit or build date as the capability marker; feature-detect an installed build when the exact artifact is unknown.
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
The skill applies to **Classic and Alloy** projects. Nothing here requires PurgeTSS.
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
## Required workflow
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
Open the references relevant to the task before proposing code. Do not answer from familiarity with Phaser, Godot, Box2D, or an older `ti.game` build.
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
| Task | Read first |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| Exact methods, properties, defaults, event payloads, percentages | [references/api.md](references/api.md) |
|
|
22
|
+
| Install, Classic/Alloy structure, assets, cleanup, lifecycle | [references/project-setup.md](references/project-setup.md) |
|
|
23
|
+
| Choose a runnable upstream example | [references/examples.md](references/examples.md) |
|
|
24
|
+
| Reusable level, pooling, HUD, collision, path, timer patterns | [references/patterns.md](references/patterns.md) |
|
|
25
|
+
| Platformer, racer, shooter, runner, rhythm, ball-game recipes | [references/recipes-action.md](references/recipes-action.md) |
|
|
26
|
+
| Cards, point-and-click, particles, ropes, camera, effects | [references/recipes-adventure-effects.md](references/recipes-adventure-effects.md) |
|
|
27
|
+
| Large maps, Tiled data, tile collision, live edits, pathfinding | [references/tilemaps.md](references/tilemaps.md) |
|
|
28
|
+
| Hitbox overlays, performance HUD, telemetry, tuning | [references/debugging-performance.md](references/debugging-performance.md) |
|
|
29
|
+
| Missing, partial, and deliberately out-of-scope features | [references/roadmap.md](references/roadmap.md) |
|
|
30
|
+
|
|
31
|
+
In the response, cite factual guidance with `[source: references/<file>.md]`. If a relevant reference cannot be consulted, prefix the claim with `FROM_MEMORY (unverified):` instead of silently filling the gap.
|
|
32
|
+
|
|
33
|
+
The module's demos are integration evidence, not production templates. Preserve the API combination they prove, but remove launcher plumbing, repeated navigation controls, and avoidable global state when adapting them.
|
|
34
|
+
|
|
35
|
+
## The engine rule
|
|
21
36
|
|
|
22
|
-
|
|
37
|
+
**JS describes and reacts. The engine runs every frame.**
|
|
23
38
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
| Drag
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
| Walking around obstacles to a tapped point | your own A* in JS, or a straight line through the wall | `gameView.findPath(from, to, opts)` → `sprite.followPath(path)` |
|
|
44
|
-
| A spawn wave or an AI tick that respects pause | `setInterval` | `gameView.every(ms, cb)` |
|
|
45
|
-
| Smoke, sparks, explosions | pooled sprites in JS | `Game.createEmitter(...)` |
|
|
46
|
-
| A swinging chain or cape | verlet solver in JS | `Game.createRope(...)` |
|
|
47
|
-
| Slow motion or a pause that still draws | pausing your own timers | `gameView.timeScale = 0.5` / `0` |
|
|
39
|
+
Do not move sprites from `setInterval`, `requestAnimationFrame`, or a JS loop. Property writes cross the Kroll bridge; native properties and methods keep frame-by-frame work on the render thread. Coarse timers are appropriate for decisions such as enemy targeting or spawn cadence, preferably through `gameView.after()` / `every()` when they must obey pause and `timeScale`.
|
|
40
|
+
|
|
41
|
+
| Intent | Native mechanism |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| Fall or move continuously | `gravity`, `gravityX`, `velocityX`, `velocityY` |
|
|
44
|
+
| Move to a point | `animate()` or `followPath()` |
|
|
45
|
+
| Drive or fly | `carMode` + `throttle`/`steering`, or `thrust` + `angularVelocity` |
|
|
46
|
+
| Drag, pinch, rotate | `draggable`, `pinchable`, `rotatable` |
|
|
47
|
+
| Follow the player | `gameView.follow()` |
|
|
48
|
+
| Walk around obstacles | `findPath()` → `followPath()` |
|
|
49
|
+
| Test line of sight or hitscan | `raycast()` |
|
|
50
|
+
| Fast projectile collision | `swept: true` on the mover |
|
|
51
|
+
| Repeat a large map efficiently | `Game.createTileLayer()` |
|
|
52
|
+
| Attach a label, health bar, or turret | `attachTo()` |
|
|
53
|
+
| Score or in-game label | `Game.createText()`; usually `screenFixed: true` for a HUD |
|
|
54
|
+
| Smoke, sparks, explosions | `Game.createEmitter()` |
|
|
55
|
+
| Chain, tether, cape | `Game.createRope()` |
|
|
56
|
+
| Pause or slow the scene while still drawing | `gameView.timeScale` |
|
|
57
|
+
| Measure FPS, frame time, and draw calls | `debug: { hud: true }` or the `performance` event |
|
|
48
58
|
|
|
49
59
|
## Minimal scene
|
|
50
60
|
|
|
@@ -53,34 +63,31 @@ const Game = require('ti.game');
|
|
|
53
63
|
|
|
54
64
|
const win = Ti.UI.createWindow({
|
|
55
65
|
backgroundColor: '#000',
|
|
56
|
-
navBarHidden: true,
|
|
57
|
-
theme: 'Theme.Titanium.DayNight.NoTitleBar'
|
|
66
|
+
navBarHidden: true,
|
|
67
|
+
theme: 'Theme.Titanium.DayNight.NoTitleBar'
|
|
68
|
+
});
|
|
69
|
+
const gameView = Game.createGameView({
|
|
70
|
+
backgroundColor: '#8ed8f8',
|
|
71
|
+
maxFps: 60
|
|
58
72
|
});
|
|
59
|
-
const gameView = Game.createGameView({ backgroundColor: '#8ed8f8' });
|
|
60
|
-
|
|
61
73
|
const sheet = Game.createSpriteSheet({
|
|
62
74
|
image: 'assets/hero.png',
|
|
63
75
|
frameWidth: 32,
|
|
64
76
|
frameHeight: 48,
|
|
65
|
-
smoothing: false
|
|
77
|
+
smoothing: false
|
|
66
78
|
});
|
|
67
79
|
|
|
68
80
|
let initialized = false;
|
|
69
|
-
gameView.addEventListener('resize', (
|
|
81
|
+
gameView.addEventListener('resize', ({ width, height }) => {
|
|
70
82
|
if (initialized) {
|
|
71
83
|
return;
|
|
72
84
|
}
|
|
73
85
|
initialized = true;
|
|
74
|
-
buildLevel(e.width, e.height);
|
|
75
|
-
});
|
|
76
86
|
|
|
77
|
-
function buildLevel(W, H) {
|
|
78
87
|
const hero = Game.createSprite({
|
|
79
|
-
sheet
|
|
80
|
-
x:
|
|
81
|
-
y:
|
|
82
|
-
width: 96,
|
|
83
|
-
height: 144,
|
|
88
|
+
sheet,
|
|
89
|
+
x: width / 2,
|
|
90
|
+
y: height / 2,
|
|
84
91
|
animations: {
|
|
85
92
|
idle: { frames: [0], fps: 1, loop: true },
|
|
86
93
|
walk: { frames: [1, 2], fps: 6, loop: true }
|
|
@@ -88,147 +95,40 @@ function buildLevel(W, H) {
|
|
|
88
95
|
});
|
|
89
96
|
hero.play('idle');
|
|
90
97
|
gameView.add(hero);
|
|
91
|
-
}
|
|
98
|
+
});
|
|
92
99
|
|
|
93
100
|
win.add(gameView);
|
|
94
101
|
win.open();
|
|
95
102
|
```
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
`Ti.Platform.displayCaps` includes the system bars and reports **points** on iOS, while the scene lives in surface **pixels**. Sizing a level from it puts bottom-anchored sprites below the visible area. The GameView fires `resize` with the real surface size — that is the coordinate space. The guard flag matters: `resize` fires again on rotation and on some lifecycle transitions, and rebuilding the level there would duplicate every sprite.
|
|
100
|
-
|
|
101
|
-
When you genuinely need dp→scene units (to keep a floor above 80dp on-screen buttons), measure the surface rather than trusting the density factor, because the iOS Simulator renders at 1x while still reporting the device scale:
|
|
102
|
-
|
|
103
|
-
```javascript
|
|
104
|
-
const density = Ti.Platform.osname === 'android'
|
|
105
|
-
? Ti.Platform.displayCaps.logicalDensityFactor
|
|
106
|
-
: H / Ti.Platform.displayCaps.platformHeight;
|
|
107
|
-
const buttonZone = Math.round(130 * density);
|
|
108
|
-
const groundTop = H - buttonZone;
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Coordinates
|
|
112
|
-
|
|
113
|
-
Top-left origin, y-down, surface pixels. Touch coordinates map 1:1. `(x, y)` is the sprite's **anchor** (default `0.5/0.5` = center, so `y` is the middle of the sprite, not its top). Rotation in degrees, positive clockwise. All JS durations are milliseconds.
|
|
114
|
-
|
|
115
|
-
## Pick a movement model
|
|
116
|
-
|
|
117
|
-
| Model | Properties | Fits |
|
|
118
|
-
| --- | --- | --- |
|
|
119
|
-
| Plain velocity | `velocityX/Y` (px/s), `gravity` (px/s²) | Flappy, projectiles, falling objects, notes |
|
|
120
|
-
| Platformer | velocity + `solidWith`, read-only `onGround`, `land` event | Mario-style run & jump |
|
|
121
|
-
| Bouncing body | `restitution` (0..1) on top of `solidWith` | Balls, pinball, breakout |
|
|
122
|
-
| Car | `carMode: true`, `throttle`, `steering` (-1..1) | Top-down racer with emergent drift |
|
|
123
|
-
| Newtonian flight | `thrust` (px/s² along heading), `angularVelocity` (deg/s), `maxSpeed` | Asteroids, lunar lander |
|
|
124
|
-
| Tween | `animate({ x, y, duration, easing })` | Point-and-click walking, UI cards, patrols |
|
|
125
|
-
|
|
126
|
-
Gravity is per sprite, not global — set it on each body that should fall. A sprite with no `gravity` and no velocity simply stays put.
|
|
127
|
-
|
|
128
|
-
## Block versus react: `solidWith` and `collidesWith` are independent
|
|
129
|
-
|
|
130
|
-
- `solidWith: ['ground']` **blocks** movement against sprites tagged `collisionGroup: 'ground'`. The engine pushes the sprite out along the axis of least penetration, sets read-only `onGround` and fires `land`.
|
|
131
|
-
- `collidesWith: ['coin', 'enemy']` **reports** overlap. Fires `collision` once per overlap-enter and `collisionend` once on separation (both carry `group`, `other`, `x`, `y`). Nothing is blocked.
|
|
132
|
-
|
|
133
|
-
A sprite can use both — a player that stands on platforms (`solidWith`) and dies on spikes (`collidesWith`).
|
|
134
|
-
|
|
135
|
-
The enter/exit pair is the trigger lifecycle other engines call `OnTriggerEnter`/`Exit`: water that tints the hero while he is in it, a pressure plate holding a door open exactly while something rests on it. Removing or hiding the partner mid-contact also counts as separation, so a despawned object still closes its door. There is deliberately **no** per-frame "stay" event — keep that state in JS.
|
|
136
|
-
|
|
137
|
-
A fast mover can slip through a thin target between frames: it never overlaps on any single frame, so the discrete test misses. `swept: true` on the *mover* tests its movement as a path — `collision` fires for whatever the path crossed, and `solidWith` walls stop it at the impact point instead of letting it teleport through.
|
|
138
|
-
|
|
139
|
-
When the question is not "did we touch?" but "what is over there?", the answer is `gameView.raycast(x0, y0, x1, y1, ['group'])` — line of sight, a ledge probe ahead of a walker's feet, a hitscan shot. It returns `null` or the nearest hit (`{ x, y, distance, group, sprite, normal }`), needs nothing but a `collisionGroup` on the targets, and replaces the old trick of parking an invisible probe sprite where the ray would land. It is a discrete query: call it from a decision timer or a tap handler, never every frame.
|
|
140
|
-
|
|
141
|
-
And when the question is "how do I get over there?", `gameView.findPath(from, to, { cellSize, groups, clearance, bounds })` runs grid A* around the same tagged sprites and hands back waypoints for `followPath` — tap-to-walk that rounds a tree, a hound re-routing to the player on an 800 ms timer. Also discrete: the walk itself runs natively between queries, so pathfind on taps and AI ticks, never per frame. Pass `clearance` of about half the walker's width, since the route is a line for the sprite's **center**.
|
|
142
|
-
|
|
143
|
-
**A sprite with `width`/`height` but no `sheet` renders nothing and works as an invisible trigger.** Score zones, goals, checkpoints, ceilings, kill floors and walls are all built this way in the official demos. This is the idiomatic way to add logic to a scene without art.
|
|
144
|
-
|
|
145
|
-
Tune fairness with `hitboxScale` (art rarely fills its frame; slightly small hitboxes feel better), `hitboxScaleX`/`hitboxScaleY` when the drawing fills its frame by a different fraction on each axis (a 20×44 hero in a 32×48 frame wants `0.62` wide and `0.92` tall — the two multiply `hitboxScale` rather than replace it), and `hitboxShape: 'circle'` for balls and rocks — circles also resolve against solids along the contact normal, so they bounce off corners diagonally instead of like a box. Turn on `debug: true` per sprite, or on the GameView for everything, to see green collision AABBs, blue touch bounds and the orange anchor dot.
|
|
146
|
-
|
|
147
|
-
The hitbox shrinks **around the anchor**, which is why the anchor is half of that tuning and why both now have readable forms: `anchor: 'bottom'` with `hitboxScaleY: '55%'` is the same sprite as `anchorY: 1` with `hitboxScaleY: 0.55`, and says what it is doing. Every ratio the engine exposes takes a percentage string, and the anchors take names (`'left'`, `'bottom-right'`, `'center'`); numbers keep working untouched. [api.md](references/api.md) lists exactly which properties are ratios — coordinates, sizes, degrees and speeds are not, and neither is the car model's `grip`/`drag`.
|
|
148
|
-
|
|
149
|
-
## Performance rules that actually matter
|
|
150
|
-
|
|
151
|
-
- **One texture = one draw call.** The batcher accumulates up to 1000 quads and flushes on texture switch. A scene whose sprites all share one sheet renders in a single draw call regardless of sprite count. Pack art into as few sheets as possible; reuse one white particle texture and tint it at runtime.
|
|
152
|
-
- **Know what else breaks the batch**, since none of it is visible on screen: every blend-mode transition in draw order flushes — there are four (`'normal'`, `'add'`, `'multiply'`, `'screen'`), so group same-blend sprites into their own `zIndex` band and the frame switches a handful of times instead of constantly; each glowing sprite costs 2 extra draw calls per frame, plus 2 more while a `flash()` runs; ropes, skid marks and debug overlays always draw with normal blending, so interleaving them with non-normal content flushes too.
|
|
153
|
-
- **Bulk-add a level.** `gameView.add([spriteA, spriteB, emitter, rope])` crosses the bridge once and commits under one scene lock. Collect a level's objects in an array and add them in one call; `add(object)` still works for single objects.
|
|
154
|
-
- **Keep collision group lists targeted.** Cost is O(colliders × candidates) per frame. A bullet checking `['asteroid']` tests 5 sprites, not the whole scene.
|
|
155
|
-
- **Pool, don't create.** Bullets, notes, obstacles and enemies are created once (usually 3–10 of them), then recycled by setting `visible = false` and zeroing velocity. Creating sprites mid-game costs a bridge hop and a scene lock.
|
|
156
|
-
- **Read properties in handlers, not in loops.** `ball.x` is a synchronous snapshot of the live native value — perfect inside a `collision` handler or an 80 ms AI tick, wasteful in a tight loop.
|
|
157
|
-
- **`maxFps: 60`** on the GameView keeps 120 Hz ProMotion displays from doubling render work. `0` (default) follows the display refresh rate.
|
|
158
|
-
- **Judge performance on device.** The iOS Simulator's OpenGL translation layer is disproportionately slow and renders at a 1x drawable; real devices keep native screen scale and run far smoother.
|
|
159
|
-
|
|
160
|
-
## Text is a sprite
|
|
161
|
-
|
|
162
|
-
Text draws **inside** the scene: `Game.createText({ text: 'SCORE 0' })` returns a sprite made of glyph quads, so it z-sorts, tweens, tints, flashes, glows, wobbles on `idleAnimation` and takes `tap` events like any other sprite — a text button needs no overlay view. With no `font` it uses a pixel font embedded in the module (nothing to ship); `Game.createFont({...})` loads a BMFont/AngelCode atlas or a monospace grid image. There is no font size — scale the sprite, with integer values for crisp pixels.
|
|
163
|
-
|
|
164
|
-
```javascript
|
|
165
|
-
const score = Game.createText({ text: 'SCORE 0', screenFixed: true, x: W / 2, y: H * 0.08, scale: 3, zIndex: 100 });
|
|
166
|
-
gameView.add(score);
|
|
167
|
-
score.text = 'SCORE 10'; // re-lays out natively
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
One long string wraps itself: `maxWidth` breaks lines on word boundaries and re-wraps every time `text` changes, so a dialog box needs no hand-broken `\n`. It is measured in font-space px, *before* `scale` — divide the screen width you want by the scale you are drawing at.
|
|
104
|
+
## Non-negotiable invariants
|
|
171
105
|
|
|
172
|
-
|
|
106
|
+
- Build surface-dependent content from the GameView's guarded `resize` event. `Ti.Platform.displayCaps` is not the scene surface and can include system bars or different units.
|
|
107
|
+
- Coordinates use a top-left origin, y increases downward, and `(x, y)` is the sprite anchor. Durations crossing the JS boundary are milliseconds.
|
|
108
|
+
- `solidWith` blocks; `collidesWith` reports `collision`/`collisionend`. They are independent. A sized sprite without a sheet is a valid invisible solid or trigger.
|
|
109
|
+
- Put the hitbox shape on the object whose geometry matters. A round peg needs `hitboxShape: 'circle'`; a ramp needs `'rotatedRect'`.
|
|
110
|
+
- A `TileLayer` replaces one-sprite-per-tile maps when the map is large. Its visible-cell renderer, cell collision, live edits, and pathfinding integration are native.
|
|
111
|
+
- Collect initial sprites, emitters, ropes, and tile layers and add them in one `gameView.add([...])` call.
|
|
112
|
+
- Use `Game.createText()` for scene text. Use Titanium views only for UI the engine cannot draw, such as native input controls or system-font interfaces.
|
|
113
|
+
- Stop app-owned JS timers, looping sounds, and listeners on close/controller cleanup. `removeAllSprites()` clears Sprite/Text objects only; explicitly remove tracked emitters, ropes, and tile layers. `SpriteSheet.unload()` and `Font.unload()` are permanent level-streaming operations, not cache eviction.
|
|
114
|
+
- Check [references/roadmap.md](references/roadmap.md) before naming an API that merely exists in another engine.
|
|
173
115
|
|
|
174
|
-
|
|
116
|
+
## Source priority
|
|
175
117
|
|
|
176
|
-
|
|
118
|
+
When sources disagree, use this order:
|
|
177
119
|
|
|
178
|
-
|
|
120
|
+
1. Android and iOS native proxy/engine code for actual behavior and parity.
|
|
121
|
+
2. `example/*.js` for combinations exercised by the module, not code-quality conventions.
|
|
122
|
+
3. Upstream `README.md` and `TUTORIAL.md` for intended public usage.
|
|
123
|
+
4. Upstream `TODO.md` for what remains partial or absent.
|
|
179
124
|
|
|
180
|
-
|
|
125
|
+
Known upstream prose drift at the pinned commit: the README says 26 demos although there are 33; its camera row names the wrong HUD corner; and it says every `TileLayer` property is live although Android treats `legend`, `firstGid`, `cols`, and `rows` as creation-time configuration. The references already normalize those differences.
|
|
181
126
|
|
|
182
|
-
|
|
127
|
+
## Before returning code
|
|
183
128
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
clearInterval(aiTimer);
|
|
191
|
-
clearTimeout(spawnTimer);
|
|
192
|
-
music.stop(); // a looping track outlives the window otherwise
|
|
193
|
-
gameView.pause();
|
|
194
|
-
gameView.removeAllSprites();
|
|
195
|
-
});
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## Reference files
|
|
199
|
-
|
|
200
|
-
Read the one that matches the task instead of guessing — the API surface below was verified against the module source, not reconstructed from memory.
|
|
201
|
-
|
|
202
|
-
| Need | Reference |
|
|
203
|
-
| --- | --- |
|
|
204
|
-
| Exact properties, methods, events, defaults for every object | [api.md](references/api.md) |
|
|
205
|
-
| Working code per genre: flappy, platformer, top-down, racer, asteroids, endless runner, rhythm, cards, point & click, particles, ropes, camera — plus patrol paths, raycast probes, pathfinding, parallax and game-clock timers | [recipes.md](references/recipes.md) |
|
|
206
|
-
| Install the module, tiapp.xml, Alloy vs Classic wiring, assets, sprite sheets, atlases, lifecycle, testing | [project-setup.md](references/project-setup.md) |
|
|
207
|
-
| What does NOT exist yet, what is coming, and how to work around it today | [roadmap.md](references/roadmap.md) |
|
|
208
|
-
|
|
209
|
-
When this skill runs out — a brand-new feature, a demo's exact code, a build question — read upstream itself at https://github.com/m1ga/ti.game. Each file there is authoritative for something different:
|
|
210
|
-
|
|
211
|
-
| Upstream file | What it is authoritative for |
|
|
212
|
-
| --- | --- |
|
|
213
|
-
| `README.md` | The module's own API documentation, and the demo index. Canonical, but it has lagged the code: verify a defaulted value against the source before relying on it |
|
|
214
|
-
| `example/` | 26 runnable demos, one per feature area — the reference implementations this skill's recipes are distilled from |
|
|
215
|
-
| `TODO.md` | What the maintainer has planned, with priorities. The source of [roadmap.md](references/roadmap.md) |
|
|
216
|
-
| `tutorial.md` | A step-by-step first scene (sprite, animation, tap-to-move). For a human learning the module, not for looking things up |
|
|
217
|
-
| `ios/README.md` | The iOS port's threading, coordinates and touch constants. Mostly for module developers; the app-facing facts are already here |
|
|
218
|
-
| `AGENTS.md` | Rules for changing the module itself — irrelevant to building a game with it |
|
|
219
|
-
| `android/src/ti/game/`, `ios/Classes/` | The truth. Defaults, clamping and edge cases in this skill were read here, not from prose |
|
|
220
|
-
|
|
221
|
-
## Before writing game code, check these
|
|
222
|
-
|
|
223
|
-
1. Is the level built inside a guarded `resize` handler, using `e.width`/`e.height`?
|
|
224
|
-
2. Does any timer move a sprite? Replace it with velocity, gravity, a tween, `carMode` or `thrust`.
|
|
225
|
-
3. Are all the level's objects added in one `gameView.add([...])` call?
|
|
226
|
-
4. Does every moving body have the right model — `solidWith` to be blocked, `collidesWith` to be notified?
|
|
227
|
-
5. Is `hitboxScale` set on sprites whose art does not fill the frame — with `hitboxScaleX`/`hitboxScaleY` when the fit differs per axis, and the `anchor` the box shrinks around named rather than left as a bare `1`?
|
|
228
|
-
6. Do triggers (scores, goals, kill zones) use sheet-less sprites instead of visible art?
|
|
229
|
-
7. Is every `createSound`, `setInterval` and `setTimeout` stopped when the window closes?
|
|
230
|
-
8. Is the HUD `Game.createText` with `screenFixed: true`, rather than a stack of overlaid labels?
|
|
231
|
-
9. Does anything travel far enough per frame to tunnel? Give it `swept: true`.
|
|
232
|
-
10. Does a timer drive game logic (spawn waves, AI ticks, respawns)? Move it to `gameView.every`/`after` so it obeys pause and slow motion.
|
|
233
|
-
11. Does anything ride on top of a moving sprite — a name tag, a health bar, a turret, a hitbox? `attachTo` pins it natively; a timer copying coordinates is the old workaround.
|
|
234
|
-
12. Are you using an API that actually exists? Anything involving tilemaps, full parent/child transforms, joysticks, gamepads or slopes — check [roadmap.md](references/roadmap.md) first.
|
|
129
|
+
- Confirm the installed module build exposes every API used; the manifest version alone is insufficient.
|
|
130
|
+
- Keep per-frame work native and JS event-driven.
|
|
131
|
+
- Prefer a demo-backed combination, then refactor it into focused, maintainable code.
|
|
132
|
+
- Preserve Android/iOS parity; do not rely on an Android-only permissive argument shape.
|
|
133
|
+
- State partial engine behavior honestly, especially slopes, tile triggers/raycast, parenting, audio polish, and input devices.
|
|
134
|
+
- Cite the references that support the result.
|