@guinetik/gcanvas 1.0.2 → 1.0.4

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.
Files changed (217) hide show
  1. package/dist/gcanvas.es.js +25656 -0
  2. package/dist/gcanvas.es.min.js +1 -0
  3. package/dist/gcanvas.umd.js +1 -0
  4. package/dist/gcanvas.umd.min.js +1 -0
  5. package/package.json +23 -6
  6. package/src/game/objects/index.js +1 -0
  7. package/src/game/objects/spritesheet.js +260 -0
  8. package/src/game/ui/theme.js +6 -0
  9. package/src/io/keys.js +9 -1
  10. package/src/math/boolean.js +481 -0
  11. package/src/math/index.js +1 -0
  12. package/.github/workflows/release.yaml +0 -70
  13. package/.jshintrc +0 -4
  14. package/.vscode/settings.json +0 -22
  15. package/CLAUDE.md +0 -310
  16. package/blackhole.jpg +0 -0
  17. package/demo.png +0 -0
  18. package/demos/CNAME +0 -1
  19. package/demos/animations.html +0 -31
  20. package/demos/basic.html +0 -38
  21. package/demos/baskara.html +0 -31
  22. package/demos/bezier.html +0 -35
  23. package/demos/beziersignature.html +0 -29
  24. package/demos/blackhole.html +0 -28
  25. package/demos/blob.html +0 -35
  26. package/demos/coordinates.html +0 -698
  27. package/demos/cube3d.html +0 -23
  28. package/demos/demos.css +0 -303
  29. package/demos/dino.html +0 -42
  30. package/demos/easing.html +0 -28
  31. package/demos/events.html +0 -195
  32. package/demos/fluent.html +0 -647
  33. package/demos/fluid-simple.html +0 -22
  34. package/demos/fluid.html +0 -37
  35. package/demos/fractals.html +0 -36
  36. package/demos/gameobjects.html +0 -626
  37. package/demos/genart.html +0 -26
  38. package/demos/gendream.html +0 -26
  39. package/demos/group.html +0 -36
  40. package/demos/home.html +0 -587
  41. package/demos/index.html +0 -376
  42. package/demos/isometric.html +0 -34
  43. package/demos/js/animations.js +0 -452
  44. package/demos/js/basic.js +0 -204
  45. package/demos/js/baskara.js +0 -751
  46. package/demos/js/bezier.js +0 -692
  47. package/demos/js/beziersignature.js +0 -241
  48. package/demos/js/blackhole/accretiondisk.obj.js +0 -379
  49. package/demos/js/blackhole/blackhole.obj.js +0 -318
  50. package/demos/js/blackhole/index.js +0 -409
  51. package/demos/js/blackhole/particle.js +0 -56
  52. package/demos/js/blackhole/starfield.obj.js +0 -218
  53. package/demos/js/blob.js +0 -2276
  54. package/demos/js/coordinates.js +0 -840
  55. package/demos/js/cube3d.js +0 -789
  56. package/demos/js/dino.js +0 -1420
  57. package/demos/js/easing.js +0 -477
  58. package/demos/js/fluent.js +0 -183
  59. package/demos/js/fluid-simple.js +0 -253
  60. package/demos/js/fluid.js +0 -527
  61. package/demos/js/fractals.js +0 -931
  62. package/demos/js/fractalworker.js +0 -93
  63. package/demos/js/gameobjects.js +0 -176
  64. package/demos/js/genart.js +0 -268
  65. package/demos/js/gendream.js +0 -209
  66. package/demos/js/group.js +0 -140
  67. package/demos/js/info-toggle.js +0 -25
  68. package/demos/js/isometric.js +0 -863
  69. package/demos/js/kerr.js +0 -1556
  70. package/demos/js/lavalamp.js +0 -590
  71. package/demos/js/layout.js +0 -354
  72. package/demos/js/mondrian.js +0 -285
  73. package/demos/js/opacity.js +0 -275
  74. package/demos/js/painter.js +0 -484
  75. package/demos/js/particles-showcase.js +0 -514
  76. package/demos/js/particles.js +0 -299
  77. package/demos/js/patterns.js +0 -397
  78. package/demos/js/penrose/artifact.js +0 -69
  79. package/demos/js/penrose/blackhole.js +0 -121
  80. package/demos/js/penrose/constants.js +0 -73
  81. package/demos/js/penrose/game.js +0 -943
  82. package/demos/js/penrose/lore.js +0 -278
  83. package/demos/js/penrose/penrosescene.js +0 -892
  84. package/demos/js/penrose/ship.js +0 -216
  85. package/demos/js/penrose/sounds.js +0 -211
  86. package/demos/js/penrose/voidparticle.js +0 -55
  87. package/demos/js/penrose/voidscene.js +0 -258
  88. package/demos/js/penrose/voidship.js +0 -144
  89. package/demos/js/penrose/wormhole.js +0 -46
  90. package/demos/js/pipeline.js +0 -555
  91. package/demos/js/plane3d.js +0 -256
  92. package/demos/js/platformer.js +0 -1579
  93. package/demos/js/scene.js +0 -304
  94. package/demos/js/scenes.js +0 -320
  95. package/demos/js/schrodinger.js +0 -410
  96. package/demos/js/schwarzschild.js +0 -1023
  97. package/demos/js/shapes.js +0 -628
  98. package/demos/js/space/alien.js +0 -171
  99. package/demos/js/space/boom.js +0 -98
  100. package/demos/js/space/boss.js +0 -353
  101. package/demos/js/space/buff.js +0 -73
  102. package/demos/js/space/bullet.js +0 -102
  103. package/demos/js/space/constants.js +0 -85
  104. package/demos/js/space/game.js +0 -1884
  105. package/demos/js/space/hud.js +0 -112
  106. package/demos/js/space/laserbeam.js +0 -179
  107. package/demos/js/space/lightning.js +0 -277
  108. package/demos/js/space/minion.js +0 -192
  109. package/demos/js/space/missile.js +0 -212
  110. package/demos/js/space/player.js +0 -430
  111. package/demos/js/space/powerup.js +0 -90
  112. package/demos/js/space/starfield.js +0 -58
  113. package/demos/js/space/starpower.js +0 -90
  114. package/demos/js/spacetime.js +0 -559
  115. package/demos/js/sphere3d.js +0 -229
  116. package/demos/js/sprite.js +0 -473
  117. package/demos/js/svgtween.js +0 -204
  118. package/demos/js/tde/accretiondisk.js +0 -471
  119. package/demos/js/tde/blackhole.js +0 -219
  120. package/demos/js/tde/blackholescene.js +0 -209
  121. package/demos/js/tde/config.js +0 -59
  122. package/demos/js/tde/index.js +0 -820
  123. package/demos/js/tde/jets.js +0 -290
  124. package/demos/js/tde/lensedstarfield.js +0 -154
  125. package/demos/js/tde/tdestar.js +0 -297
  126. package/demos/js/tde/tidalstream.js +0 -372
  127. package/demos/js/tde_old/blackhole.obj.js +0 -354
  128. package/demos/js/tde_old/debris.obj.js +0 -791
  129. package/demos/js/tde_old/flare.obj.js +0 -239
  130. package/demos/js/tde_old/index.js +0 -448
  131. package/demos/js/tde_old/star.obj.js +0 -812
  132. package/demos/js/tiles.js +0 -312
  133. package/demos/js/tweendemo.js +0 -79
  134. package/demos/js/visibility.js +0 -102
  135. package/demos/kerr.html +0 -28
  136. package/demos/lavalamp.html +0 -27
  137. package/demos/layouts.html +0 -37
  138. package/demos/logo.svg +0 -4
  139. package/demos/loop.html +0 -84
  140. package/demos/mondrian.html +0 -32
  141. package/demos/og_image.png +0 -0
  142. package/demos/opacity.html +0 -36
  143. package/demos/painter.html +0 -39
  144. package/demos/particles-showcase.html +0 -28
  145. package/demos/particles.html +0 -24
  146. package/demos/patterns.html +0 -33
  147. package/demos/penrose-game.html +0 -31
  148. package/demos/pipeline.html +0 -737
  149. package/demos/plane3d.html +0 -24
  150. package/demos/platformer.html +0 -43
  151. package/demos/scene.html +0 -33
  152. package/demos/scenes.html +0 -96
  153. package/demos/schrodinger.html +0 -27
  154. package/demos/schwarzschild.html +0 -27
  155. package/demos/shapes.html +0 -16
  156. package/demos/space.html +0 -85
  157. package/demos/spacetime.html +0 -27
  158. package/demos/sphere3d.html +0 -24
  159. package/demos/sprite.html +0 -18
  160. package/demos/svgtween.html +0 -29
  161. package/demos/tde.html +0 -28
  162. package/demos/tiles.html +0 -28
  163. package/demos/transforms.html +0 -400
  164. package/demos/tween.html +0 -45
  165. package/demos/visibility.html +0 -33
  166. package/docs/README.md +0 -230
  167. package/docs/api/FluidSystem.md +0 -173
  168. package/docs/concepts/architecture-overview.md +0 -204
  169. package/docs/concepts/coordinate-system.md +0 -384
  170. package/docs/concepts/lifecycle.md +0 -255
  171. package/docs/concepts/rendering-pipeline.md +0 -279
  172. package/docs/concepts/shapes-vs-gameobjects.md +0 -187
  173. package/docs/concepts/tde-zorder.md +0 -106
  174. package/docs/concepts/two-layer-architecture.md +0 -229
  175. package/docs/fluid-dynamics.md +0 -99
  176. package/docs/getting-started/first-game.md +0 -354
  177. package/docs/getting-started/hello-world.md +0 -269
  178. package/docs/getting-started/installation.md +0 -175
  179. package/docs/modules/collision/README.md +0 -453
  180. package/docs/modules/fluent/README.md +0 -1075
  181. package/docs/modules/game/README.md +0 -303
  182. package/docs/modules/isometric-camera.md +0 -210
  183. package/docs/modules/isometric.md +0 -275
  184. package/docs/modules/painter/README.md +0 -328
  185. package/docs/modules/particle/README.md +0 -559
  186. package/docs/modules/shapes/README.md +0 -221
  187. package/docs/modules/shapes/base/euclidian.md +0 -123
  188. package/docs/modules/shapes/base/geometry2d.md +0 -204
  189. package/docs/modules/shapes/base/renderable.md +0 -215
  190. package/docs/modules/shapes/base/shape.md +0 -262
  191. package/docs/modules/shapes/base/transformable.md +0 -243
  192. package/docs/modules/shapes/hierarchy.md +0 -218
  193. package/docs/modules/state/README.md +0 -577
  194. package/docs/modules/util/README.md +0 -99
  195. package/docs/modules/util/camera3d.md +0 -412
  196. package/docs/modules/util/scene3d.md +0 -395
  197. package/index.html +0 -17
  198. package/jsdoc.json +0 -50
  199. package/scripts/build-demo.js +0 -69
  200. package/scripts/bundle4llm.js +0 -276
  201. package/scripts/clearconsole.js +0 -48
  202. package/test/math/orbital.test.js +0 -61
  203. package/test/math/tensor.test.js +0 -114
  204. package/test/particle/emitter.test.js +0 -204
  205. package/test/particle/particle-system.test.js +0 -310
  206. package/test/particle/particle.test.js +0 -116
  207. package/test/particle/updaters.test.js +0 -386
  208. package/test/setup.js +0 -120
  209. package/test/shapes/euclidian.test.js +0 -44
  210. package/test/shapes/geometry.test.js +0 -86
  211. package/test/shapes/group.test.js +0 -86
  212. package/test/shapes/rectangle.test.js +0 -64
  213. package/test/shapes/transform.test.js +0 -379
  214. package/test/util/camera3d.test.js +0 -428
  215. package/test/util/scene3d.test.js +0 -352
  216. package/vite.config.js +0 -50
  217. package/vitest.config.js +0 -13
@@ -1,303 +0,0 @@
1
- # Game Module
2
-
3
- > Core game loop, Pipeline, GameObjects, and Scenes.
4
-
5
- ## Overview
6
-
7
- The game module provides the interactive layer of GCanvas. It manages the game loop, object lifecycle, input handling, and scene organization.
8
-
9
- ## Quick Start
10
-
11
- ```js
12
- import { Game, Scene, GameObject, Circle } from '@guinetik/gcanvas';
13
-
14
- class Player extends GameObject {
15
- constructor(game) {
16
- super(game);
17
- this.shape = new Circle(30, { color: 'blue' });
18
- this.enableInteractivity(this.shape);
19
- }
20
-
21
- update(dt) {
22
- if (this.game.input.isKeyDown('ArrowRight')) {
23
- this.shape.x += 200 * dt;
24
- }
25
- }
26
-
27
- render() {
28
- this.shape.draw();
29
- }
30
- }
31
-
32
- class MyGame extends Game {
33
- init() {
34
- super.init();
35
- this.enableFluidSize();
36
-
37
- const scene = new Scene(this);
38
- scene.add(new Player(this));
39
- this.pipeline.add(scene);
40
- }
41
- }
42
-
43
- const game = new MyGame(document.getElementById('canvas'));
44
- game.start();
45
- ```
46
-
47
- ## Core Classes
48
-
49
- | Class | Description |
50
- |-------|-------------|
51
- | **Game** | Main loop, canvas management, input initialization |
52
- | **Pipeline** | Manages object collections, update/render dispatch |
53
- | **GameObject** | Base class for interactive entities |
54
- | **Scene** | Hierarchical container for GameObjects |
55
-
56
- ## Game Class
57
-
58
- The entry point for interactive applications.
59
-
60
- ```js
61
- class MyGame extends Game {
62
- constructor(canvas) {
63
- super(canvas);
64
- this.enableFluidSize(); // Canvas fills viewport
65
- this.backgroundColor = '#1a1a2e';
66
- }
67
-
68
- init() {
69
- super.init(); // Initialize input systems
70
- // Create scenes and objects
71
- }
72
-
73
- update(dt) {
74
- super.update(dt); // Update pipeline
75
- // Custom game logic
76
- }
77
-
78
- render() {
79
- super.render(); // Render pipeline
80
- // Custom rendering
81
- }
82
- }
83
-
84
- const game = new MyGame(canvas);
85
- game.setFPS(60); // Set target FPS
86
- game.start(); // Begin game loop
87
- ```
88
-
89
- ### Key Properties
90
-
91
- | Property | Type | Description |
92
- |----------|------|-------------|
93
- | `canvas` | `HTMLCanvasElement` | The canvas element |
94
- | `ctx` | `CanvasRenderingContext2D` | 2D context |
95
- | `pipeline` | `Pipeline` | Object manager |
96
- | `events` | `EventEmitter` | Event system |
97
- | `width` | `number` | Canvas width |
98
- | `height` | `number` | Canvas height |
99
- | `running` | `boolean` | Is loop running? |
100
- | `dt` | `number` | Last delta time |
101
-
102
- ### Key Methods
103
-
104
- | Method | Description |
105
- |--------|-------------|
106
- | `start()` | Begin the game loop |
107
- | `stop()` | Stop the game loop |
108
- | `init()` | Initialize game (override) |
109
- | `update(dt)` | Update logic (override) |
110
- | `render()` | Render logic (override) |
111
- | `enableFluidSize()` | Auto-resize to window |
112
- | `setFPS(fps)` | Set target frame rate |
113
- | `enablePauseOnBlur(bool)` | Pause when tab loses focus |
114
-
115
- ## Pipeline Class
116
-
117
- Manages collections of objects for update and render.
118
-
119
- ```js
120
- // Add objects
121
- game.pipeline.add(scene);
122
- game.pipeline.add(gameObject);
123
-
124
- // Remove objects
125
- game.pipeline.remove(object);
126
-
127
- // Clear all
128
- game.pipeline.clear();
129
- ```
130
-
131
- Pipeline automatically:
132
- - Calls `update(dt)` on active objects
133
- - Calls `render()` on visible objects
134
- - Sorts by zIndex
135
- - Dispatches input events
136
-
137
- ## GameObject Class
138
-
139
- Base class for all interactive entities.
140
-
141
- ```js
142
- class Enemy extends GameObject {
143
- constructor(game) {
144
- super(game);
145
- this.shape = new Rectangle({ width: 40, height: 40, color: 'red' });
146
- this.enableInteractivity(this.shape);
147
- this.speed = 100;
148
- }
149
-
150
- update(dt) {
151
- this.shape.x += this.speed * dt;
152
- }
153
-
154
- render() {
155
- this.shape.draw();
156
- }
157
-
158
- // Input events
159
- onPointerDown(e) { }
160
- onPointerUp(e) { }
161
- onPointerMove(e) { }
162
- onMouseOver() { }
163
- onMouseOut() { }
164
- }
165
- ```
166
-
167
- ### Lifecycle Methods
168
-
169
- | Method | When Called |
170
- |--------|-------------|
171
- | `update(dt)` | Every frame (if active) |
172
- | `render()` | Every frame (if visible) |
173
- | `destroy()` | When removed from pipeline |
174
-
175
- ### Input Methods
176
-
177
- | Method | Event |
178
- |--------|-------|
179
- | `onPointerDown(e)` | Click/tap start |
180
- | `onPointerUp(e)` | Click/tap end |
181
- | `onPointerMove(e)` | Pointer movement |
182
- | `onMouseOver()` | Hover enter |
183
- | `onMouseOut()` | Hover leave |
184
-
185
- ## Scene Class
186
-
187
- Hierarchical container for GameObjects.
188
-
189
- ```js
190
- const gameScene = new Scene(game);
191
- const uiScene = new Scene(game);
192
-
193
- // Add objects to scenes
194
- gameScene.add(player);
195
- gameScene.add(enemy);
196
-
197
- uiScene.add(healthBar);
198
- uiScene.add(scoreDisplay);
199
-
200
- // Add scenes to pipeline (order matters)
201
- game.pipeline.add(gameScene); // Rendered first
202
- game.pipeline.add(uiScene); // Rendered on top
203
- ```
204
-
205
- Scenes provide:
206
- - Hierarchical organization
207
- - Coordinate spaces
208
- - Z-ordering of children
209
- - Collective transforms
210
-
211
- ## UI Components
212
-
213
- Built on GameObject and Scene:
214
-
215
- | Component | Description |
216
- |-----------|-------------|
217
- | `Button` | Clickable with visual states |
218
- | `ToggleButton` | On/off toggle |
219
- | `Cursor` | Custom cursor |
220
- | `FPSCounter` | FPS display |
221
-
222
- ```js
223
- import { Button, FPSCounter } from '@guinetik/gcanvas';
224
-
225
- const button = new Button(game, 'Click Me', {
226
- x: 400,
227
- y: 300
228
- });
229
-
230
- button.on('click', () => {
231
- console.log('Clicked!');
232
- });
233
-
234
- scene.add(button);
235
- scene.add(new FPSCounter(game, { anchor: 'bottom-right' }));
236
- ```
237
-
238
- ## Input Access
239
-
240
- ```js
241
- // In GameObject or Game
242
- update(dt) {
243
- // Keyboard
244
- if (this.game.input.isKeyDown('ArrowLeft')) { }
245
- if (this.game.input.isKeyDown('Space')) { }
246
- if (this.game.input.isKeyDown('KeyW')) { }
247
-
248
- // Mouse position
249
- const mx = this.game.mouse.x;
250
- const my = this.game.mouse.y;
251
- }
252
- ```
253
-
254
- ## Events
255
-
256
- ```js
257
- // Custom events
258
- game.events.on('player-died', (data) => {
259
- console.log('Player died:', data);
260
- });
261
-
262
- game.events.emit('player-died', { score: 100 });
263
-
264
- // Remove listener
265
- game.events.off('player-died', handler);
266
- ```
267
-
268
- ## Architecture Diagram
269
-
270
- ```
271
- ┌─────────────────────────────────────────────────────────┐
272
- │ Game │
273
- │ ┌────────────────────────────────────────────────────┐ │
274
- │ │ Pipeline │ │
275
- │ │ ┌──────────────────────────────────────────────┐ │ │
276
- │ │ │ Scene │ │ │
277
- │ │ │ ┌────────────┐ ┌────────────┐ │ │ │
278
- │ │ │ │ GameObject │ │ GameObject │ ... │ │ │
279
- │ │ │ │ + Shape │ │ + Shape │ │ │ │
280
- │ │ │ └────────────┘ └────────────┘ │ │ │
281
- │ │ └──────────────────────────────────────────────┘ │ │
282
- │ └────────────────────────────────────────────────────┘ │
283
- │ │ │
284
- │ ▼ │
285
- │ ┌────────────┐ │
286
- │ │ Game Loop │ │
287
- │ │ update(dt) │ │
288
- │ │ render() │ │
289
- │ └────────────┘ │
290
- └─────────────────────────────────────────────────────────┘
291
- ```
292
-
293
- ## Related
294
-
295
- - [Game Lifecycle](../../concepts/lifecycle.md) - Update/render cycle
296
- - [Two-Layer Architecture](../../concepts/two-layer-architecture.md) - Shape vs Game layer
297
- - [First Game Guide](../../getting-started/first-game.md)
298
-
299
- ## See Also
300
-
301
- - [Shapes Module](../shapes/README.md)
302
- - [IO Module](../io/README.md) - Input handling
303
- - [Motion Module](../motion/README.md) - Animation
@@ -1,210 +0,0 @@
1
- # IsometricCamera
2
-
3
- A camera for isometric views with step-based rotation and smooth animation.
4
-
5
- Unlike `Camera3D` which provides free 3D rotation, `IsometricCamera` is designed for isometric games where the view rotates in fixed increments (e.g., 45° or 90°), similar to classic games like SimCity or Age of Empires.
6
-
7
- ## Features
8
-
9
- - **Step-based rotation** - Rotate in fixed increments (default 45°)
10
- - **Smooth animation** - Animated transitions between angles with easing
11
- - **Easy integration** - Works seamlessly with `IsometricScene`
12
- - **Multiple easing options** - Choose from various easing functions
13
- - **Callbacks** - Get notified when rotation starts/ends
14
-
15
- ## Basic Usage
16
-
17
- ```javascript
18
- import { IsometricCamera, IsometricScene } from '@guinetik/gcanvas';
19
-
20
- // Create camera with 90° rotation steps (recommended for isometric)
21
- const camera = new IsometricCamera({
22
- rotationStep: Math.PI / 2, // 90 degrees (default)
23
- animationDuration: 0.5 // 500ms transition
24
- });
25
-
26
- // Create scene with camera attached
27
- const scene = new IsometricScene(game, {
28
- x: game.width / 2,
29
- y: game.height / 2,
30
- camera: camera
31
- });
32
-
33
- // Rotate the view
34
- camera.rotateRight(); // Rotate 45° clockwise
35
- camera.rotateLeft(); // Rotate 45° counter-clockwise
36
- ```
37
-
38
- ## Constructor Options
39
-
40
- | Option | Type | Default | Description |
41
- |--------|------|---------|-------------|
42
- | `angle` | number | `0` | Initial viewing angle in radians |
43
- | `rotationStep` | number | `Math.PI / 2` | Rotation step size (default 90°). Use 90° for proper isometric look. |
44
- | `animationDuration` | number | `0.4` | Transition duration in seconds |
45
- | `easing` | string | `'easeInOutCubic'` | Easing function name |
46
-
47
- > **Note:** 45° rotation steps can cause visual flattening at certain angles due to how isometric projection works. 90° steps are recommended for the best visual results (like SimCity, Age of Empires).
48
-
49
- ## Available Easing Functions
50
-
51
- - `linear` - No easing
52
- - `easeInQuad`, `easeOutQuad`, `easeInOutQuad` - Quadratic
53
- - `easeInCubic`, `easeOutCubic`, `easeInOutCubic` - Cubic
54
- - `easeOutBack` - Overshoot effect
55
-
56
- ## API Reference
57
-
58
- ### Methods
59
-
60
- #### `rotateRight()`
61
- Rotate view clockwise by one step. Returns `this` for chaining.
62
-
63
- ```javascript
64
- camera.rotateRight(); // Rotate 45° right
65
- ```
66
-
67
- #### `rotateLeft()`
68
- Rotate view counter-clockwise by one step. Returns `this` for chaining.
69
-
70
- ```javascript
71
- camera.rotateLeft(); // Rotate 45° left
72
- ```
73
-
74
- #### `rotateTo(angle)`
75
- Rotate to a specific angle (animated).
76
-
77
- ```javascript
78
- camera.rotateTo(Math.PI / 2); // Rotate to 90°
79
- ```
80
-
81
- #### `setAngle(angle)`
82
- Set angle immediately (no animation).
83
-
84
- ```javascript
85
- camera.setAngle(0); // Jump to 0° instantly
86
- ```
87
-
88
- #### `update(dt)`
89
- Update camera animation. Call each frame with delta time in seconds.
90
-
91
- ```javascript
92
- // In your update loop
93
- camera.update(dt);
94
- ```
95
-
96
- > Note: If using with `IsometricScene`, the scene calls this automatically.
97
-
98
- #### `isAnimating()`
99
- Check if camera is currently animating.
100
-
101
- ```javascript
102
- if (!camera.isAnimating()) {
103
- // Safe to trigger another rotation
104
- }
105
- ```
106
-
107
- #### `getAngleDegrees()`
108
- Get current angle in degrees (0-360).
109
-
110
- #### `getNormalizedAngle()`
111
- Get angle in radians, normalized to 0-2π range.
112
-
113
- #### `reset()`
114
- Reset camera to angle 0.
115
-
116
- ### Callbacks
117
-
118
- #### `onRotationStart(callback)`
119
- Set callback for when rotation begins.
120
-
121
- ```javascript
122
- camera.onRotationStart((startAngle, targetAngle) => {
123
- console.log(`Rotating from ${startAngle} to ${targetAngle}`);
124
- });
125
- ```
126
-
127
- #### `onRotationEnd(callback)`
128
- Set callback for when rotation completes.
129
-
130
- ```javascript
131
- camera.onRotationEnd((finalAngle) => {
132
- console.log(`Rotation complete at ${finalAngle}`);
133
- });
134
- ```
135
-
136
- ## Integration with IsometricScene
137
-
138
- When attached to an `IsometricScene`, the camera's angle is automatically applied to the `toIsometric()` projection:
139
-
140
- ```javascript
141
- // Attach camera to scene
142
- scene.setCamera(camera);
143
-
144
- // The scene automatically:
145
- // 1. Updates the camera each frame
146
- // 2. Applies camera rotation to all projections
147
- // 3. Handles depth sorting correctly during rotation
148
- ```
149
-
150
- ### How It Works
151
-
152
- The camera modifies the isometric projection by rotating grid coordinates before projecting:
153
-
154
- ```javascript
155
- // Standard isometric projection
156
- isoX = (x - y) * (tileWidth / 2)
157
- isoY = (x + y) * (tileHeight / 2)
158
-
159
- // With camera rotation
160
- rotatedX = x * cos(angle) - y * sin(angle)
161
- rotatedY = x * sin(angle) + y * cos(angle)
162
- isoX = (rotatedX - rotatedY) * (tileWidth / 2)
163
- isoY = (rotatedX + rotatedY) * (tileHeight / 2)
164
- ```
165
-
166
- ## UI Controls Example
167
-
168
- Add rotation buttons to your game:
169
-
170
- ```javascript
171
- import { Button, Keys } from '@guinetik/gcanvas';
172
-
173
- // Arrow buttons
174
- const leftBtn = new Button(game, {
175
- x: 50, y: game.height - 50,
176
- width: 50, height: 50,
177
- text: "◀",
178
- onClick: () => camera.rotateLeft()
179
- });
180
-
181
- const rightBtn = new Button(game, {
182
- x: 110, y: game.height - 50,
183
- width: 50, height: 50,
184
- text: "▶",
185
- onClick: () => camera.rotateRight()
186
- });
187
-
188
- game.pipeline.add(leftBtn);
189
- game.pipeline.add(rightBtn);
190
-
191
- // Keyboard controls (Q/E)
192
- game.events.on(Keys.Q, () => camera.rotateLeft());
193
- game.events.on(Keys.E, () => camera.rotateRight());
194
- ```
195
-
196
- ## Why 90° Rotation Steps?
197
-
198
- Isometric projection uses the formula:
199
- - `screenX = (x - y) * tileWidth / 2`
200
- - `screenY = (x + y) * tileHeight / 2`
201
-
202
- At 45° rotation angles (like 45°, 135°, 225°, 315°), the `(x + y)` term can become very small, causing the view to appear flat. At 90° increments (0°, 90°, 180°, 270°), the rotation simply swaps axes, maintaining the proper 3D look.
203
-
204
- This is why classic isometric games like SimCity and Age of Empires use 4-direction rotation (90° steps).
205
-
206
- ## See Also
207
-
208
- - [IsometricScene](./isometric.md) - Isometric scene rendering
209
- - [Camera3D](./camera3d.md) - Free 3D rotation camera
210
- - [Button](./button.md) - UI buttons