@maccesar/titools 5.1.1 → 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 +112 -72
- package/agents/ti-pro.md +5 -1
- package/commands/ti-audit.md +5 -1
- package/docs/examples.md +91 -0
- package/docs/knowledge-index.md +57 -0
- package/lib/config.js +2 -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 +58 -0
- package/skills/ti-admob-unified/agents/openai.yaml +4 -0
- package/skills/ti-admob-unified/assets/example/app.js +1207 -0
- package/skills/ti-admob-unified/assets/example/lib/ads.js +646 -0
- package/skills/ti-admob-unified/assets/example/library-usage.js +136 -0
- package/skills/ti-admob-unified/references/api-reference.md +615 -0
- package/skills/ti-admob-unified/references/banners.md +435 -0
- package/skills/ti-admob-unified/references/contributing.md +233 -0
- package/skills/ti-admob-unified/references/dependencies.md +91 -0
- package/skills/ti-admob-unified/references/fullscreen.md +238 -0
- package/skills/ti-admob-unified/references/game-monetization.md +148 -0
- package/skills/ti-admob-unified/references/getting-started.md +319 -0
- package/skills/ti-admob-unified/references/index.md +33 -0
- package/skills/ti-admob-unified/references/integration.md +87 -0
- package/skills/ti-admob-unified/references/library.md +220 -0
- package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
- package/skills/ti-admob-unified/references/mediation.md +145 -0
- package/skills/ti-admob-unified/references/migration.md +259 -0
- package/skills/ti-admob-unified/references/module-changelog.md +84 -0
- package/skills/ti-admob-unified/references/module-license.txt +21 -0
- package/skills/ti-admob-unified/references/privacy.md +281 -0
- 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 +143 -0
- package/skills/ti-admob-unified/references/source-notes.md +80 -0
- package/skills/ti-admob-unified/references/source-snapshot.json +644 -0
- package/skills/ti-admob-unified/references/third-party-notices.md +22 -0
- package/skills/ti-game/SKILL.md +31 -5
- package/skills/ti-game/references/api.md +36 -24
- package/skills/ti-game/references/debugging-performance.md +22 -7
- package/skills/ti-game/references/examples.md +113 -20
- package/skills/ti-game/references/input.md +101 -0
- package/skills/ti-game/references/patterns.md +56 -10
- package/skills/ti-game/references/physics.md +530 -0
- package/skills/ti-game/references/project-setup.md +131 -47
- package/skills/ti-game/references/recipes-action.md +149 -18
- package/skills/ti-game/references/recipes-adventure-effects.md +47 -4
- package/skills/ti-game/references/roadmap.md +11 -4
- package/skills/ti-game/references/source-notes.md +81 -0
- package/skills/ti-game/references/tilemaps.md +13 -4
- package/skills/ti-game-assets/SKILL.md +53 -0
- package/skills/ti-game-assets/agents/openai.yaml +4 -0
- package/skills/ti-game-assets/references/alpha.md +26 -0
- package/skills/ti-game-assets/references/art-direction.md +37 -0
- package/skills/ti-game-assets/references/integration.md +49 -0
- package/skills/ti-game-assets/references/manifest.example.json +429 -0
- package/skills/ti-game-assets/references/manifest.md +39 -0
- package/skills/ti-game-assets/references/offline-audio.md +33 -0
- package/skills/ti-game-assets/references/prompts.md +33 -0
- package/skills/ti-game-assets/references/repeating-textures.md +17 -0
- package/skills/ti-game-assets/references/responsible-ads.md +31 -0
- package/skills/ti-game-assets/references/sound-assets.md +42 -0
- package/skills/ti-game-assets/references/vector-sources.md +21 -0
- package/skills/ti-game-assets/scripts/alpha_tools.py +160 -0
- package/skills/ti-game-assets/scripts/requirements.txt +1 -0
- package/skills/ti-game-assets/scripts/validate_manifest.py +267 -0
- package/skills/ti-synthengine/SKILL.md +77 -67
- 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 +573 -153
- package/skills/ti-synthengine/references/audio-engine.md +272 -124
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- package/skills/ti-synthengine/references/examples.md +355 -106
- 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 +426 -75
- 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 +434 -420
- package/skills/ti-synthengine/references/recommendations.md +72 -85
- package/skills/ti-synthengine/references/sound-design.md +375 -155
- package/skills/ti-synthengine/references/source-notes.md +109 -0
- package/skills/ti-synthengine/references/source-snapshot.json +452 -0
- package/skills/ti-synthengine/references/stats-performance.json +2992 -0
- package/skills/ti-synthengine/references/stats-performance.md +82 -0
- package/skills/ti-synthengine/references/studio-validation.json +6202 -0
- package/skills/ti-synthengine/references/troubleshooting.md +123 -78
- 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
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
# Physical models and equations
|
|
2
|
+
|
|
3
|
+
Use this reference to choose movement by its intended feel, derive parameters,
|
|
4
|
+
or explain frame-rate-dependent behavior. A camera angle is not a physics
|
|
5
|
+
model: a top-down character, car, spaceship and billiard ball use different
|
|
6
|
+
rules. The [demo catalog](examples.md) covers action, platforms, ball games,
|
|
7
|
+
adventures, board interactions and visual demonstrations.
|
|
8
|
+
|
|
9
|
+
Reviewed on 2026-10-23 against native upstream
|
|
10
|
+
[`88e181c`](https://github.com/m1ga/ti.game/tree/88e181cb85526d8146d0be6e09b2b90c2551baad)
|
|
11
|
+
and the documentation refresh in `macCesar/ti.game@2bf5f82`. These source
|
|
12
|
+
contracts do not identify the contents of an installed binary.
|
|
13
|
+
|
|
14
|
+
Each equation is labeled **Engine** (native implementation), **Demo**
|
|
15
|
+
(application rule) or **Analytical** (a mathematical model for tuning).
|
|
16
|
+
An analytical equation does not imply a matching public property exists.
|
|
17
|
+
JS selects controls and reacts to events; frame-by-frame integration stays native.
|
|
18
|
+
|
|
19
|
+
<!-- TOC-START -->
|
|
20
|
+
## Contents
|
|
21
|
+
|
|
22
|
+
- [Units and conventions](#units-and-conventions)
|
|
23
|
+
- [Model selection across the demos](#model-selection-across-the-demos)
|
|
24
|
+
- [Sprite integration and constant acceleration](#sprite-integration-and-constant-acceleration)
|
|
25
|
+
- [Thrust, inertia and linear damping](#thrust-inertia-and-linear-damping)
|
|
26
|
+
- [Projectiles and additive impulses](#projectiles-and-additive-impulses)
|
|
27
|
+
- [Arcade car equations](#arcade-car-equations)
|
|
28
|
+
- [Direct character control](#direct-character-control)
|
|
29
|
+
- [Jumps, flaps and scrolling runners](#jumps-flaps-and-scrolling-runners)
|
|
30
|
+
- [Landing and impact measurements](#landing-and-impact-measurements)
|
|
31
|
+
- [Static reflection and arcade ball sports](#static-reflection-and-arcade-ball-sports)
|
|
32
|
+
- [Equal-mass billiards and circular containment](#equal-mass-billiards-and-circular-containment)
|
|
33
|
+
- [Slopes and contact friction](#slopes-and-contact-friction)
|
|
34
|
+
- [Verlet ropes and tethers](#verlet-ropes-and-tethers)
|
|
35
|
+
- [Swept movement and periodic worlds](#swept-movement-and-periodic-worlds)
|
|
36
|
+
- [Paths, tweens and navigation](#paths-tweens-and-navigation)
|
|
37
|
+
- [Rhythm timing](#rhythm-timing)
|
|
38
|
+
- [Particle integration](#particle-integration)
|
|
39
|
+
- [Time step and frame rate](#time-step-and-frame-rate)
|
|
40
|
+
- [What needs a different model](#what-needs-a-different-model)
|
|
41
|
+
|
|
42
|
+
<!-- TOC-END -->
|
|
43
|
+
|
|
44
|
+
## Units and conventions
|
|
45
|
+
|
|
46
|
+
| Quantity | Units |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| Position, width, height, path distance | Surface pixels, from guarded GameView `resize` |
|
|
49
|
+
| `velocityX/Y`, `maxSpeed` | px/s |
|
|
50
|
+
| `gravity`, `gravityX`, `thrust`, `enginePower` | px/s² |
|
|
51
|
+
| Native `dt` | Seconds |
|
|
52
|
+
| JS `duration`, `after`, `every`, particle lifetime | Milliseconds |
|
|
53
|
+
| `rotation` | Clockwise degrees; 0 points up |
|
|
54
|
+
| `angularVelocity`, `turnRate` | degrees/s |
|
|
55
|
+
| `linearDamping`, car `drag`, car `grip` | 1/s coefficients, not percentage strings |
|
|
56
|
+
| Rope `damping` | Retained displacement fraction per update |
|
|
57
|
+
| `restitution` | Dimensionless, 0..1 |
|
|
58
|
+
|
|
59
|
+
Origin is top-left; y increases downwards. Position is the sprite anchor.
|
|
60
|
+
For `θ = rotation·π/180`, forward `f=(sin θ,−cos θ)` and right
|
|
61
|
+
`r=(cos θ,sin θ)`. `n` below is a unit contact normal.
|
|
62
|
+
|
|
63
|
+
## Model selection across the demos
|
|
64
|
+
|
|
65
|
+
| Model | Examples | What determines movement |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| Inertial ship | Asteroids | Thrust adds velocity along heading; rotation does not redirect existing momentum |
|
|
68
|
+
| Gravity and replacement jump/flap | Platformer, Flappy, Skate, Flip | Vertical acceleration plus a chosen takeoff velocity |
|
|
69
|
+
| Direct character control | Topdown | Velocity follows input immediately, including diagonal normalization |
|
|
70
|
+
| Arcade car | Racing | Forward/lateral velocity, throttle, steering, drag and grip |
|
|
71
|
+
| Ball with static-surface reflection | Volley, Plinko, Circles, Slopes | Gravity, geometry normals and restitution |
|
|
72
|
+
| Equal-mass circle bodies | Pool, Plinko, Drum, Circles | Bilateral normal impulse and separation |
|
|
73
|
+
| Circular containment | Drum | Keep ball centers inside radius `R−r` |
|
|
74
|
+
| Position constraints | Rope | Verlet displacement and link-distance relaxation |
|
|
75
|
+
| Fast body or instant query | Swept, Raycast, Worldwrap | Swept movement versus a discrete ray query |
|
|
76
|
+
| Prescribed route | Path, Maze, Pointclick, Tilemap, Demoscene | Distance traveled along a path; A* chooses waypoints |
|
|
77
|
+
| Prescribed interpolation | Cards, Puzzle, Basic | Tween destination and easing; drag/snap rules |
|
|
78
|
+
| Timing window | Rhythm | Constant velocity and distance-to-pad acceptance |
|
|
79
|
+
| Ballistic effects | Particles | Native emitter rate, gravity and lifetime; no Sprite solid response |
|
|
80
|
+
| Cross-cutting systems | Camera, Blend, Text, Hitbox, Zones, Timescale | Presentation, geometry, events or clock; not separate locomotion models |
|
|
81
|
+
|
|
82
|
+
Names map to `example/<name>.js`, with lowercase spelling. These groups cover
|
|
83
|
+
the launcher entries; the Configurable input/Keyboard demos exercise input rather
|
|
84
|
+
than add physics models. Lunar Lander in the action recipes is a curated
|
|
85
|
+
composition, not an extra upstream demo. Use [examples.md](examples.md) for
|
|
86
|
+
the exact filenames and [api.md](api.md) for public properties.
|
|
87
|
+
|
|
88
|
+
## Sprite integration and constant acceleration
|
|
89
|
+
|
|
90
|
+
**Engine**, without contacts or position overrides:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
vNext = v + a·dt
|
|
94
|
+
pNext = p + vNext·dt
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This is semi-implicit Euler: acceleration before position. In Sprite, the
|
|
98
|
+
order is car integration, angular velocity, thrust and its speed cap,
|
|
99
|
+
gravity, linear damping, position, scroll wrap, path, animation, tween and
|
|
100
|
+
idle wobble. Scene solid resolution, post-contact damping, and attachment
|
|
101
|
+
passes can change that result again.
|
|
102
|
+
Do not combine several position writers expecting their displacements to add.
|
|
103
|
+
|
|
104
|
+
**Analytical**, constant acceleration with no cap/contact:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
v(t) = v0 + a·t
|
|
108
|
+
p(t) = p0 + v0·t + 0.5·a·t²
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
That position is the continuous solution, not the exact discrete output.
|
|
112
|
+
At constant step, `t=N·dt`, Euler gives
|
|
113
|
+
`pN=p0+v0·t+0.5·a·(t²+t·dt)`. Multiplying by dt is necessary but does
|
|
114
|
+
not eliminate integration error.
|
|
115
|
+
|
|
116
|
+
**Demo Wind**: gravityX values −900..900 px/s², leaves with vertical gravity
|
|
117
|
+
260 and a puck with gravity 0. Constant sideways acceleration is an arcade
|
|
118
|
+
wind field; it is not drag calculated from relative air velocity.
|
|
119
|
+
|
|
120
|
+
Sources: [Android Sprite.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Sprite.java),
|
|
121
|
+
[iOS TGSprite.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/ios/Classes/TGSprite.m).
|
|
122
|
+
|
|
123
|
+
## Thrust, inertia and linear damping
|
|
124
|
+
|
|
125
|
+
**Engine**:
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
rotation += angularVelocity·dt
|
|
129
|
+
f = (sin θ,−cos θ) # updated heading
|
|
130
|
+
v += f·thrust·dt
|
|
131
|
+
if |v| > maxSpeed: v *= maxSpeed/|v|
|
|
132
|
+
v += (gravityX,gravity)·dt
|
|
133
|
+
if linearDamping > 0:
|
|
134
|
+
v *= max(0,1−linearDamping·dt)
|
|
135
|
+
if effectively coasting and |v| < 4 px/s: v = (0,0)
|
|
136
|
+
p += v·dt
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The thrust cap runs only in its active branch; direct velocity writes are
|
|
140
|
+
not capped. Asteroids uses 220 degrees/s turning and both thrust and speed
|
|
141
|
+
cap `0.9·min(W,H)`, with no ship damping. Turning changes the nose, not the
|
|
142
|
+
velocity already accumulated. Assisted flight can deliberately add damping,
|
|
143
|
+
but it is resistance everywhere, not contact-only friction.
|
|
144
|
+
|
|
145
|
+
**Analytical** damping: `dv/dt=−cv`, hence `v(t)=v0·exp(−ct)` and speed
|
|
146
|
+
half-life `ln(2)/c`. With constant aligned acceleration, `dv/dt=a−cv`
|
|
147
|
+
tends to `a/c`. The engine approximates damping using `1−c·dt`, then
|
|
148
|
+
applies its rest threshold. Box2D documents the same continuous decay but
|
|
149
|
+
uses `1/(1+c·dt)`; do not substitute that for ti.game's implemented formula.
|
|
150
|
+
[Box2D damping reference](https://box2d.org/documentation/md_simulation.html).
|
|
151
|
+
|
|
152
|
+
### Powered launch and post-contact rest
|
|
153
|
+
|
|
154
|
+
The current engine preserves acceleration below the 4 px/s cutoff. At rest,
|
|
155
|
+
thrust 220 and damping 1.2 at a 1/60 s step produce
|
|
156
|
+
`220/60·(1−1.2/60)=3.593` px/s; that powered step now survives. Damping
|
|
157
|
+
still applies during acceleration. An unopposed powered equilibrium may remain
|
|
158
|
+
below 4 px/s without being forced to zero.
|
|
159
|
+
|
|
160
|
+
**Engine** tracks the vector sum of thrust, gravity, gravityX and car motor
|
|
161
|
+
acceleration. A tolerance of `1e−6·sum(abs(input acceleration magnitudes))`
|
|
162
|
+
treats floating-point cancellation residue as coasting. After solid resolution,
|
|
163
|
+
`finishDamping()` evaluates the remaining speed and work `v·a`. Below 4 px/s,
|
|
164
|
+
nonpositive or negligible work allows the body to settle. A force cancelled
|
|
165
|
+
by another force, or pressing into a blocked surface, therefore does not
|
|
166
|
+
protect residual tangential drift. An inward rect-wall velocity retained for
|
|
167
|
+
held input is excluded from that tangential test and preserved separately.
|
|
168
|
+
|
|
169
|
+
**Historical build boundary:** `3bea2f4` erased every damped velocity below
|
|
170
|
+
4 px/s before position integration, including powered launch. Verify the build
|
|
171
|
+
before diagnosing that behavior. Do not carry its mandatory damping-off
|
|
172
|
+
workaround into current source. Choosing damping only while coasting remains
|
|
173
|
+
a valid application feel, but intentionally removes powered-flight drag.
|
|
174
|
+
|
|
175
|
+
`tests/test_linear_damping.py` executes production Java/Objective-C update,
|
|
176
|
+
car and post-contact methods at 30/60/120 FPS. Contacts in that fixture are
|
|
177
|
+
velocity projections; it does not certify complete scene/device behavior.
|
|
178
|
+
|
|
179
|
+
### Coasting brake feel reference
|
|
180
|
+
|
|
181
|
+
**Application profile**, Titanium Asteroids autopilot, recorded 2026-10-12:
|
|
182
|
+
set `linearDamping: 0` while thrust is active and `linearDamping: 1.2` s⁻¹
|
|
183
|
+
when thrust stops. Native braking then multiplies velocity by
|
|
184
|
+
`max(0, 1-1.2*dt)`, with the installed engine's rest policy. The current source preserves
|
|
185
|
+
acceleration below the cutoff; this older application profile remains an
|
|
186
|
+
optional control feel rather than a required engine workaround.
|
|
187
|
+
|
|
188
|
+
César liked the speed of this braking as a reference for other games,
|
|
189
|
+
especially arcade racing, although he did not choose it as the preferred
|
|
190
|
+
feel for Asteroids. Keep the coefficient and activation condition together
|
|
191
|
+
when comparing the feel. This is an optional tuning example, not a default
|
|
192
|
+
for all games or an upstream demo preset. In a racer, evaluate longitudinal
|
|
193
|
+
braking and lateral grip separately before adopting it.
|
|
194
|
+
|
|
195
|
+
## Projectiles and additive impulses
|
|
196
|
+
|
|
197
|
+
**Demo Asteroids**, nose launch:
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
pBullet = pShip + f·0.55·SHIP_SIZE
|
|
201
|
+
vBullet = vShip + f·BULLET_SPEED
|
|
202
|
+
BULLET_SPEED = 1.2·min(W,H)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Inherited ship velocity is a deliberate application choice. A fixed
|
|
206
|
+
world-speed projectile instead assigns only `f·speed`.
|
|
207
|
+
|
|
208
|
+
**Analytical** impulse: `Δv=J/m`. The demos add a chosen velocity change
|
|
209
|
+
directly; Sprite has no configurable rigid-body mass. `v+=Δv` bends existing
|
|
210
|
+
motion, whereas `v=vNew` replaces it. Pool strikes are additive, Flappy's
|
|
211
|
+
vertical flap is a replacement, and Drum's SHAKE replaces velocities while
|
|
212
|
+
its contact-driven TUMBLE adds increments.
|
|
213
|
+
|
|
214
|
+
## Arcade car equations
|
|
215
|
+
|
|
216
|
+
**Engine carMode**, forward/right axes from the old heading:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
vForward = v·f; vLateral = v·r
|
|
220
|
+
steerFactor = clamp(vForward/(0.25·maxSpeed),−1,1)
|
|
221
|
+
rotation += steering·turnRate·steerFactor·dt
|
|
222
|
+
vForward += throttle·enginePower·dt
|
|
223
|
+
vForward *= max(0,1−drag·dt)
|
|
224
|
+
vLateral *= max(0,1−grip·dt)
|
|
225
|
+
vForward = clamp(vForward,−0.4·maxSpeed,maxSpeed)
|
|
226
|
+
v = fNew·vForward + rNew·vLateral
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Recomposition uses the updated heading. A stopped car with zero forward
|
|
230
|
+
speed does not steer; reverse forward speed reverses steering direction.
|
|
231
|
+
Lower grip retains more lateral drift; drag reduces forward speed.
|
|
232
|
+
Negative throttle can brake and then reverse. It is an arcade model,
|
|
233
|
+
without wheelbase geometry, individual tires or weight transfer.
|
|
234
|
+
|
|
235
|
+
**Demo Racing** uses `maxSpeed=0.8·min(W,TH)`, where TH is track height,
|
|
236
|
+
`enginePower=1.1·maxSpeed`, turnRate 210, grip 3.2 and drag 0.8. These numeric
|
|
237
|
+
values have the units in the table above, not an interchangeable percentage.
|
|
238
|
+
No JS loop should rotate the velocity to reproduce this native model.
|
|
239
|
+
|
|
240
|
+
Sources: Android Sprite.updateCar and iOS TGSprite.updateCar, at the same
|
|
241
|
+
links as [Sprite integration](#sprite-integration-and-constant-acceleration).
|
|
242
|
+
|
|
243
|
+
## Direct character control
|
|
244
|
+
|
|
245
|
+
**Demo Topdown** uses speed `S=3.5·TILE`. For cardinal input it assigns
|
|
246
|
+
`v=(moveX,moveY)·S`; for diagonal input each axis uses `S/sqrt(2)` so
|
|
247
|
+
the combined magnitude remains S. Releasing input writes zero velocity:
|
|
248
|
+
there is no ship-like inertia. For a general nonzero input vector d, the
|
|
249
|
+
application formula is `v=S·d/|d|`; distinguish this from analog magnitude
|
|
250
|
+
control, which may intentionally retain the stick's partial travel.
|
|
251
|
+
|
|
252
|
+
The demo dog normalizes its target direction at `1.1S` on a decision timer.
|
|
253
|
+
The timer chooses direction; native velocity integrates movement between
|
|
254
|
+
decisions. [Top-down recipe](recipes-action.md#top-down--zelda).
|
|
255
|
+
|
|
256
|
+
## Jumps, flaps and scrolling runners
|
|
257
|
+
|
|
258
|
+
**Demo** Platformer assigns `vx=direction·RUN_SPEED` and, on permitted
|
|
259
|
+
grounded input, `vy=−JUMP`. Flappy assigns `vy=−0.6H` per tap; it does not
|
|
260
|
+
accumulate upward impulses. Its tilt tween is visual, not aerodynamic lift.
|
|
261
|
+
Skate keeps player x fixed and moves terrain/obstacles at `vx=−0.5W`.
|
|
262
|
+
|
|
263
|
+
**Analytical**, gravity `g>0`, takeoff speed magnitude `J`:
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
timeToApex = J/g
|
|
267
|
+
height = J²/(2g)
|
|
268
|
+
sameLevelFlightTime = 2J/g
|
|
269
|
+
J for desired height h = sqrt(2gh)
|
|
270
|
+
g for height h and apex time T = 2h/T²; J = 2h/T
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
| Demo | g | J | Continuous prediction |
|
|
274
|
+
| --- | --- | --- | --- |
|
|
275
|
+
| Platformer | 2.2H | 0.95H | Apex 0.432 s; rise 0.205H |
|
|
276
|
+
| Skate | 2.4H | 0.9H | Apex 0.375 s; same-level flight 0.75 s |
|
|
277
|
+
| Flappy | 1.9H | 0.6H | Isolated flap apex0.316 s; rise 0.095H |
|
|
278
|
+
| Volley players | 2.2H | 0.85H | Apex 0.386 s; rise 0.164H |
|
|
279
|
+
|
|
280
|
+
Contact geometry, ceilings and discrete integration change actual results.
|
|
281
|
+
`oneWay` allows top landings through otherwise passable platforms; exact
|
|
282
|
+
conditions differ by resolver. Moving solids carry grounded riders using
|
|
283
|
+
their frame delta. Skate's raised scrolling road disables `carryRiders` so
|
|
284
|
+
the player is not dragged off screen. [Action recipes](recipes-action.md).
|
|
285
|
+
|
|
286
|
+
## Landing and impact measurements
|
|
287
|
+
|
|
288
|
+
**Engine** `land` reports a grounding transition after solid response.
|
|
289
|
+
Its payload does not include the original velocity. Live `velocityY` can
|
|
290
|
+
already be zero or reflected, and JS event delivery can be later still.
|
|
291
|
+
Do not classify a hard touchdown as gentle by reading that zero value.
|
|
292
|
+
|
|
293
|
+
For sprite solids, `solidimpact.speed` snapshots relative closing normal
|
|
294
|
+
speed before response, compensated for the frame's own acceleration.
|
|
295
|
+
Set `impactThreshold:0` to include gentle positive impacts and check the
|
|
296
|
+
contact normal/group. It is not full vector speed or an exact post-gravity
|
|
297
|
+
pre-contact sample. A rule involving tangential velocity needs separate
|
|
298
|
+
evidence; solid TileLayer cells do not emit `solidimpact`.
|
|
299
|
+
|
|
300
|
+
The [Lunar Lander recipe](recipes-action.md#lunar-lander) uses a static sprite
|
|
301
|
+
pad and this normal-impact contract. A resting body with no positive impact
|
|
302
|
+
does not receive an invented impact merely because its threshold is0.
|
|
303
|
+
|
|
304
|
+
Sources: Android `Scene.resolveSolids`, `dispatchSolidImpact`, `finishContacts`;
|
|
305
|
+
iOS `TGScene.resolveSolids`, `dispatchSolidImpactFor`, `finishContactsFor`;
|
|
306
|
+
see [event payload](api.md#sprite-events).
|
|
307
|
+
|
|
308
|
+
## Static reflection and arcade ball sports
|
|
309
|
+
|
|
310
|
+
**Engine**, circle against a static solid, outward normal:
|
|
311
|
+
|
|
312
|
+
```text
|
|
313
|
+
e = max(eMover,eSolid)
|
|
314
|
+
vn = v·n
|
|
315
|
+
if vn < 0:
|
|
316
|
+
if e > 0 and −e·vn > 40 px/s: v -= (1+e)·vn·n
|
|
317
|
+
else: v -= vn·n
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
This preserves tangential velocity. The small-rebound cutoff belongs to
|
|
321
|
+
this response, not the equal-mass bilateral impulse below. No contact
|
|
322
|
+
friction or collision-generated spin is implied.
|
|
323
|
+
|
|
324
|
+
**Demo Volley** replaces the ball's velocity on player contact:
|
|
325
|
+
`u=normalize(pBall−pPlayer)`, `S=0.85H`,
|
|
326
|
+
`vx=u.x·S`, `vy=min(u.y·S,−0.45S)`. It guarantees an upward component
|
|
327
|
+
instead of simulating two masses. Scenario walls use native restitution;
|
|
328
|
+
player hits use an arcade rule. Plinko uses gravity 1100, restitution 0.42,
|
|
329
|
+
circle pegs and `push` chips.
|
|
330
|
+
|
|
331
|
+
Sources: [Android Scene.resolveCircleSolids](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Scene.java),
|
|
332
|
+
[iOS TGScene.resolveCircleSolids](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/ios/Classes/TGScene.m).
|
|
333
|
+
|
|
334
|
+
## Equal-mass billiards and circular containment
|
|
335
|
+
|
|
336
|
+
**Engine**, two visible circles, both `push`, each listing the other's group:
|
|
337
|
+
|
|
338
|
+
```text
|
|
339
|
+
n = normalize(centerA−centerB)
|
|
340
|
+
penetration = radiusA+radiusB−distance
|
|
341
|
+
if penetration > slop: pA+=n·penetration/2; pB−=n·penetration/2
|
|
342
|
+
vn = (vA−vB)·n
|
|
343
|
+
e = max(eA,eB)
|
|
344
|
+
if vn < 0:
|
|
345
|
+
j = −(1+e)·vn/2
|
|
346
|
+
vA += j·n; vB -= j·n
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Each pair resolves once. Restitution1 exchanges normal components; tangent
|
|
350
|
+
components remain unchanged. Even differently sized circles are equal-mass
|
|
351
|
+
bodies. Without mutual groups the response falls back to a one-sided solid.
|
|
352
|
+
|
|
353
|
+
Pool uses restitution 0.96, damping 0.62, and a cue velocity increment
|
|
354
|
+
`2600·min(1,tapDistance/(0.55W))` along the tap direction. That limits the
|
|
355
|
+
strike increment, not total speed when a moving cue is hit again. Pockets
|
|
356
|
+
are triggers, not 3D drops.
|
|
357
|
+
|
|
358
|
+
**Analytical**, unequal masses would require
|
|
359
|
+
`j=−(1+e)·vn/(1/mA+1/mB)`, then `vA+=j·n/mA`, `vB−=j·n/mB`.
|
|
360
|
+
That is not the current `push` implementation.
|
|
361
|
+
|
|
362
|
+
**Engine contain**: a ball of radius r inside a container of radius R has
|
|
363
|
+
allowed center distance `R−r`. If exceeded, the correcting normal points
|
|
364
|
+
toward the container center and penetration is `distance−(R−r)`.
|
|
365
|
+
The ball must fit. Drum uses gravity 1400, restitution 0.55, damping 0.35 and
|
|
366
|
+
mutual `push` balls. It mixes impulses/contact response, not a spinning
|
|
367
|
+
rigid drum or fluid simulation. [Shaped-solid patterns](patterns.md#shaped-solids-ramps-round-posts-and-bodies-that-push-back--slopesjs-circlesjs-pooljs-drumjs).
|
|
368
|
+
|
|
369
|
+
## Slopes and contact friction
|
|
370
|
+
|
|
371
|
+
Rotated ramps require `rotatedRect` geometry for the real face normal.
|
|
372
|
+
The normal component is cancelled/reflected while tangent velocity remains.
|
|
373
|
+
**Analytical**, on a fixed frictionless slope at angle α from horizontal,
|
|
374
|
+
downslope acceleration magnitude is `g·abs(sin α)`.
|
|
375
|
+
|
|
376
|
+
Slopes uses gravity 1100, crate restitution 0, ball0.1 and floor/walls0.5.
|
|
377
|
+
Its ball slides; a circle shape does not add rolling torque. Sprite damping
|
|
378
|
+
acts off the slope too and cannot substitute contact-only tangent friction.
|
|
379
|
+
Smooth uphill walking, connected terrain, mass and torque remain outside
|
|
380
|
+
this model. [Limitations](roadmap.md).
|
|
381
|
+
|
|
382
|
+
## Verlet ropes and tethers
|
|
383
|
+
|
|
384
|
+
**Engine**, a free node:
|
|
385
|
+
|
|
386
|
+
```text
|
|
387
|
+
displacement = (pCurrent−pPrevious)·damping
|
|
388
|
+
pPrevious = pCurrent
|
|
389
|
+
pCurrent += displacement + (0,gravity·dt²)
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
For adjacent nodes, `Δ=pB−pA`, `d=|Δ|`, target length L, correction
|
|
393
|
+
`Δ·(d−L)/d` is split between free nodes, or applied fully to the free node
|
|
394
|
+
when the other is pinned. Division has an epsilon guard. `iterations` repeats
|
|
395
|
+
relaxation: more passes stiffen the result at a CPU cost.
|
|
396
|
+
|
|
397
|
+
The first Rope demo uses 14 links of length 0.04H, gravity 1.6H,
|
|
398
|
+
damping 0.985 and 3 constraint passes. Nodes do not automatically receive
|
|
399
|
+
Sprite solid collisions. `tail` plus positive `maxLength` additionally
|
|
400
|
+
clamps head-to-tail distance and removes outward radial velocity. With two
|
|
401
|
+
free sprite ends the correction is split; dragging an end makes the other
|
|
402
|
+
yield. It is a positional tether, not a mass/stiffness Hooke spring.
|
|
403
|
+
|
|
404
|
+
Rope damping is **per update**, not1/s. Before forces and constraints, the
|
|
405
|
+
retained displacement factor over one second at 0.985 would be about 0.635
|
|
406
|
+
at 30 updates,0.404 at 60 and 0.163 at 120. These are not predictions of the
|
|
407
|
+
whole rope's amplitude. An analytical time-normalized alternative would be
|
|
408
|
+
`exp(−c·dt)` or `dRef^(dt/dtRef)`; the engine uses the fixed supplied factor.
|
|
409
|
+
|
|
410
|
+
At zero or negative dt, the current Rope update returns before tether response,
|
|
411
|
+
Verlet displacement, endpoint pinning and constraint relaxation. Existing node
|
|
412
|
+
positions and their history remain unchanged, so positive dt resumes the saved
|
|
413
|
+
motion. A new rope, or a changed segment count, still rebuilds geometry before
|
|
414
|
+
that return so it is drawable and its endpoint is readable while paused.
|
|
415
|
+
|
|
416
|
+
`timeScale=0` now freezes rope simulation while drawing and touch continue.
|
|
417
|
+
A user drag or explicit property write can still move a sprite independently;
|
|
418
|
+
zero scene time does not disable input. `pause()` stops the render loop as well.
|
|
419
|
+
The earlier `3bea2f4` source lacked the early return and could keep relaxing or
|
|
420
|
+
integrating at zero dt; do not apply that historical warning to current builds.
|
|
421
|
+
`tests/test_rope_pause.py` checks node/history/tether state in both native twins
|
|
422
|
+
without a renderer.
|
|
423
|
+
|
|
424
|
+
Sources: [Android Rope.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Rope.java),
|
|
425
|
+
[iOS TGRope.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/ios/Classes/TGRope.m).
|
|
426
|
+
|
|
427
|
+
## Swept movement and periodic worlds
|
|
428
|
+
|
|
429
|
+
**Analytical** per-step movement is `Δp=v·dt`:9600 px/s travels160 px
|
|
430
|
+
at 60 FPS. A final-position overlap can skip a thin wall.
|
|
431
|
+
Swept tests examine the traveled segment; circle pairs have circular sweeps,
|
|
432
|
+
overlap events for a circle/rect or circle/rotatedRect use an expanded box
|
|
433
|
+
with rounded corners, including relative target movement and periodic offsets.
|
|
434
|
+
Rectangle-pair overlap sweeps retain AABBs. Orientation and size are fixed at
|
|
435
|
+
the end-of-frame values: these are translation sweeps, not rotational sweeps.
|
|
436
|
+
Solid blocking has a separate implementation. The solid
|
|
437
|
+
sweep skips `contain`/`push`; those still use discrete responses. It is not
|
|
438
|
+
a general multiple-substep rigid-body simulation.
|
|
439
|
+
|
|
440
|
+
Raycast is an instantaneous query, not a gravity-driven projectile.
|
|
441
|
+
`wrapAround` uses screen edges; `wrapX/wrapShift` loops scrolling copies;
|
|
442
|
+
`worldWrapX` defines a periodic horizontal world with participating sprites.
|
|
443
|
+
**Analytical** normalization for `L=maxX−minX` is
|
|
444
|
+
`x=minX+((x−minX)%L+L)%L`; nearest image displacement is
|
|
445
|
+
`dx−L·round(dx/L)`. Raycast, A*, particles, ropes and skid trails do not
|
|
446
|
+
repeat at the seam. Native path cursors are aligned to the sprite's nearest
|
|
447
|
+
periodic image **before** advancing, preserving signed frame deltas across the
|
|
448
|
+
seam, including steps longer than half a world. This does not make A* itself
|
|
449
|
+
periodic. Rect resting-contact sweeps also defer shallow normal entry to the
|
|
450
|
+
static resolver so tangential sliding survives float-sized contact gaps;
|
|
451
|
+
deep entries and full crossings still sweep. [Worldwrap contract](api.md#physics).
|
|
452
|
+
|
|
453
|
+
## Paths, tweens and navigation
|
|
454
|
+
|
|
455
|
+
**Engine Path** accumulates `distance+=speed·dt`. Within a segment,
|
|
456
|
+
`u=(distance−segmentStart)/segmentLength`,
|
|
457
|
+
`p=pStart+u·(pEnd−pStart)`. Optional rotation is
|
|
458
|
+
`atan2(dx,−dy)·180/π`. A* chooses the waypoints; it does not apply a force.
|
|
459
|
+
Maze/Tilemap use4·TILE px/s. Path motion overrides integrated position;
|
|
460
|
+
a later position tween can override the path again.
|
|
461
|
+
|
|
462
|
+
**Engine Tween** uses `u=clamp(elapsed/duration,0,1)` after its delay,
|
|
463
|
+
then `value=from+(to−from)·Easing(u)`. Cards, puzzles and Basic use
|
|
464
|
+
prescribed destinations/gestures, not conservation of momentum.
|
|
465
|
+
Camera/parallax, blend, text, flip and pixel snapping are presentation;
|
|
466
|
+
they do not change those motion equations. Snapping rounds the drawn anchor,
|
|
467
|
+
not physical position, and cannot eliminate every rotational raster change.
|
|
468
|
+
|
|
469
|
+
Sources: [Path.advance](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Path.java),
|
|
470
|
+
[Tween.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Tween.java).
|
|
471
|
+
|
|
472
|
+
## Rhythm timing
|
|
473
|
+
|
|
474
|
+
**Demo**, `vy=(PAD_Y+NOTE)/1.6`, starting at `y=−NOTE`, gives a 1.6 s
|
|
475
|
+
trip to the pad. GOOD allows 0.08H distance and PERFECT0.03H.
|
|
476
|
+
**Analytical** temporal error is `abs(y−PAD_Y)/abs(vy)`; changing fall speed
|
|
477
|
+
changes the milliseconds represented by a fixed pixel window.
|
|
478
|
+
|
|
479
|
+
At the demo's stated 150 BPM, quarter duration is `60000/150=400 ms`;
|
|
480
|
+
an eighth is200 ms. The400 ms spawn interval and 1.6s travel are one and four
|
|
481
|
+
quarters, respectively. The older `3bea2f4` comments called them eighths, and the old action recipe
|
|
482
|
+
repeated that error. Current source comments and the corrected recipe preserve
|
|
483
|
+
the same timing and identify the quarter-note spacing.
|
|
484
|
+
|
|
485
|
+
This is a comment/recipe error, not evidence of a faulty native beat engine.
|
|
486
|
+
The demo uses `setInterval(spawn,SPAWN_MS)` and independent `music.play()`;
|
|
487
|
+
it has no audio-position-driven scheduling. Exact musical synchronization,
|
|
488
|
+
actual asset tempo and phase would need separate measurement. A comment-only
|
|
489
|
+
upstream patch should not halve SPAWN_MS or replace the motor.
|
|
490
|
+
|
|
491
|
+
Source: [Rhythm demo](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/example/rhythm.js).
|
|
492
|
+
|
|
493
|
+
## Particle integration
|
|
494
|
+
|
|
495
|
+
**Engine** accumulates `rate·dt`, emits the integer part and keeps the
|
|
496
|
+
remainder. Live particles age, receive `vy+=gravity·dt`, then position
|
|
497
|
+
updates with the new velocity. At lifetime they retire. They are effects,
|
|
498
|
+
not Sprite solids or automatic damage sensors. [Particle recipes](recipes-adventure-effects.md#particles).
|
|
499
|
+
|
|
500
|
+
## Time step and frame rate
|
|
501
|
+
|
|
502
|
+
Both renderers derive a variable real dt, cap it at 0.1s and then scale by
|
|
503
|
+
`max(0,timeScale)`. They do not run a fixed-step accumulator with physics
|
|
504
|
+
substeps. `maxFps` is a cap, not a guarantee of exact dt. Differences can
|
|
505
|
+
come from Euler error, rest/rebound thresholds, per-update Rope damping,
|
|
506
|
+
surface-based parameters, input/lifecycle or different installed binaries.
|
|
507
|
+
|
|
508
|
+
GameView timers follow scene time, conventional JS timers do not. Rendering
|
|
509
|
+
and touch continue under timeScale 0; current Rope nodes/constraints also
|
|
510
|
+
freeze. Explicit writes, dragging and solid/attachment resolution still need
|
|
511
|
+
to be considered when a pause design permits interaction.
|
|
512
|
+
|
|
513
|
+
For tuning, capture the build, actual FPS, W/H, timeScale, forces and
|
|
514
|
+
velocities. Check rest-to-powered motion, releases, turns, contacts and pauses
|
|
515
|
+
at 30/60/120 FPS, then validate on the target hardware. Scalar equations and
|
|
516
|
+
source parity are useful evidence, not certificates of identical device feel.
|
|
517
|
+
[Telemetry workflow](debugging-performance.md).
|
|
518
|
+
|
|
519
|
+
Sources: [Android SceneRenderer](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/SceneRenderer.java),
|
|
520
|
+
[iOS TGSceneRenderer](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/ios/Classes/TGSceneRenderer.m).
|
|
521
|
+
|
|
522
|
+
## What needs a different model
|
|
523
|
+
|
|
524
|
+
Assisted spacecraft flight can use deliberate resistance, but it is not
|
|
525
|
+
airplane aerodynamics. A fuller aircraft model needs relative airspeed,
|
|
526
|
+
lift, drag, stall, altitude and bank response. Ships, tires, springs,
|
|
527
|
+
variable masses and collision torque also need separate modeling choices.
|
|
528
|
+
Do not claim those simulations from a sprite's artwork or camera angle.
|
|
529
|
+
See [roadmap.md](roadmap.md) before naming a missing API, and retain the
|
|
530
|
+
distinction between analytical tuning, application rules and native support.
|