@guinetik/gcanvas 1.0.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.
Files changed (349) hide show
  1. package/.github/workflows/release.yaml +70 -0
  2. package/.jshintrc +4 -0
  3. package/.vscode/settings.json +22 -0
  4. package/CLAUDE.md +310 -0
  5. package/blackhole.jpg +0 -0
  6. package/demo.png +0 -0
  7. package/demos/CNAME +1 -0
  8. package/demos/animations.html +31 -0
  9. package/demos/basic.html +38 -0
  10. package/demos/baskara.html +31 -0
  11. package/demos/bezier.html +35 -0
  12. package/demos/beziersignature.html +29 -0
  13. package/demos/blackhole.html +28 -0
  14. package/demos/blob.html +35 -0
  15. package/demos/demos.css +289 -0
  16. package/demos/easing.html +28 -0
  17. package/demos/events.html +195 -0
  18. package/demos/fluent.html +647 -0
  19. package/demos/fractals.html +36 -0
  20. package/demos/genart.html +26 -0
  21. package/demos/gendream.html +26 -0
  22. package/demos/group.html +36 -0
  23. package/demos/home.html +587 -0
  24. package/demos/index.html +364 -0
  25. package/demos/isometric.html +34 -0
  26. package/demos/js/animations.js +452 -0
  27. package/demos/js/basic.js +204 -0
  28. package/demos/js/baskara.js +751 -0
  29. package/demos/js/bezier.js +692 -0
  30. package/demos/js/beziersignature.js +241 -0
  31. package/demos/js/blackhole/accretiondisk.obj.js +379 -0
  32. package/demos/js/blackhole/blackhole.obj.js +318 -0
  33. package/demos/js/blackhole/index.js +409 -0
  34. package/demos/js/blackhole/particle.js +56 -0
  35. package/demos/js/blackhole/starfield.obj.js +218 -0
  36. package/demos/js/blob.js +2263 -0
  37. package/demos/js/easing.js +477 -0
  38. package/demos/js/fluent.js +183 -0
  39. package/demos/js/fractals.js +931 -0
  40. package/demos/js/fractalworker.js +93 -0
  41. package/demos/js/genart.js +268 -0
  42. package/demos/js/gendream.js +209 -0
  43. package/demos/js/group.js +140 -0
  44. package/demos/js/info-toggle.js +25 -0
  45. package/demos/js/isometric.js +863 -0
  46. package/demos/js/kerr.js +1556 -0
  47. package/demos/js/lavalamp.js +590 -0
  48. package/demos/js/layout.js +354 -0
  49. package/demos/js/mondrian.js +285 -0
  50. package/demos/js/opacity.js +275 -0
  51. package/demos/js/painter.js +484 -0
  52. package/demos/js/particles-showcase.js +514 -0
  53. package/demos/js/particles.js +299 -0
  54. package/demos/js/patterns.js +397 -0
  55. package/demos/js/penrose/artifact.js +69 -0
  56. package/demos/js/penrose/blackhole.js +121 -0
  57. package/demos/js/penrose/constants.js +73 -0
  58. package/demos/js/penrose/game.js +943 -0
  59. package/demos/js/penrose/lore.js +278 -0
  60. package/demos/js/penrose/penrosescene.js +892 -0
  61. package/demos/js/penrose/ship.js +216 -0
  62. package/demos/js/penrose/sounds.js +211 -0
  63. package/demos/js/penrose/voidparticle.js +55 -0
  64. package/demos/js/penrose/voidscene.js +258 -0
  65. package/demos/js/penrose/voidship.js +144 -0
  66. package/demos/js/penrose/wormhole.js +46 -0
  67. package/demos/js/pipeline.js +555 -0
  68. package/demos/js/scene.js +304 -0
  69. package/demos/js/scenes.js +320 -0
  70. package/demos/js/schrodinger.js +410 -0
  71. package/demos/js/schwarzschild.js +1023 -0
  72. package/demos/js/shapes.js +628 -0
  73. package/demos/js/space/alien.js +171 -0
  74. package/demos/js/space/boom.js +98 -0
  75. package/demos/js/space/boss.js +353 -0
  76. package/demos/js/space/buff.js +73 -0
  77. package/demos/js/space/bullet.js +102 -0
  78. package/demos/js/space/constants.js +85 -0
  79. package/demos/js/space/game.js +1884 -0
  80. package/demos/js/space/hud.js +112 -0
  81. package/demos/js/space/laserbeam.js +179 -0
  82. package/demos/js/space/lightning.js +277 -0
  83. package/demos/js/space/minion.js +192 -0
  84. package/demos/js/space/missile.js +212 -0
  85. package/demos/js/space/player.js +430 -0
  86. package/demos/js/space/powerup.js +90 -0
  87. package/demos/js/space/starfield.js +58 -0
  88. package/demos/js/space/starpower.js +90 -0
  89. package/demos/js/spacetime.js +559 -0
  90. package/demos/js/svgtween.js +204 -0
  91. package/demos/js/tde/accretiondisk.js +418 -0
  92. package/demos/js/tde/blackhole.js +219 -0
  93. package/demos/js/tde/blackholescene.js +209 -0
  94. package/demos/js/tde/config.js +59 -0
  95. package/demos/js/tde/index.js +695 -0
  96. package/demos/js/tde/jets.js +290 -0
  97. package/demos/js/tde/lensedstarfield.js +147 -0
  98. package/demos/js/tde/tdestar.js +317 -0
  99. package/demos/js/tde/tidalstream.js +356 -0
  100. package/demos/js/tde_old/blackhole.obj.js +354 -0
  101. package/demos/js/tde_old/debris.obj.js +791 -0
  102. package/demos/js/tde_old/flare.obj.js +239 -0
  103. package/demos/js/tde_old/index.js +448 -0
  104. package/demos/js/tde_old/star.obj.js +812 -0
  105. package/demos/js/tiles.js +312 -0
  106. package/demos/js/tweendemo.js +79 -0
  107. package/demos/js/visibility.js +102 -0
  108. package/demos/kerr.html +28 -0
  109. package/demos/lavalamp.html +27 -0
  110. package/demos/layouts.html +37 -0
  111. package/demos/logo.svg +4 -0
  112. package/demos/loop.html +84 -0
  113. package/demos/mondrian.html +32 -0
  114. package/demos/og_image.png +0 -0
  115. package/demos/opacity.html +36 -0
  116. package/demos/painter.html +39 -0
  117. package/demos/particles-showcase.html +28 -0
  118. package/demos/particles.html +24 -0
  119. package/demos/patterns.html +33 -0
  120. package/demos/penrose-game.html +31 -0
  121. package/demos/pipeline.html +737 -0
  122. package/demos/scene.html +33 -0
  123. package/demos/scenes.html +96 -0
  124. package/demos/schrodinger.html +27 -0
  125. package/demos/schwarzschild.html +27 -0
  126. package/demos/shapes.html +16 -0
  127. package/demos/space.html +85 -0
  128. package/demos/spacetime.html +27 -0
  129. package/demos/svgtween.html +29 -0
  130. package/demos/tde.html +28 -0
  131. package/demos/tiles.html +28 -0
  132. package/demos/transforms.html +400 -0
  133. package/demos/tween.html +45 -0
  134. package/demos/visibility.html +33 -0
  135. package/disk_example.png +0 -0
  136. package/docs/README.md +222 -0
  137. package/docs/concepts/architecture-overview.md +204 -0
  138. package/docs/concepts/lifecycle.md +255 -0
  139. package/docs/concepts/rendering-pipeline.md +279 -0
  140. package/docs/concepts/tde-zorder.md +106 -0
  141. package/docs/concepts/two-layer-architecture.md +229 -0
  142. package/docs/getting-started/first-game.md +354 -0
  143. package/docs/getting-started/hello-world.md +269 -0
  144. package/docs/getting-started/installation.md +157 -0
  145. package/docs/modules/collision/README.md +453 -0
  146. package/docs/modules/fluent/README.md +1075 -0
  147. package/docs/modules/game/README.md +303 -0
  148. package/docs/modules/isometric-camera.md +210 -0
  149. package/docs/modules/isometric.md +275 -0
  150. package/docs/modules/painter/README.md +328 -0
  151. package/docs/modules/particle/README.md +559 -0
  152. package/docs/modules/shapes/README.md +221 -0
  153. package/docs/modules/shapes/base/euclidian.md +123 -0
  154. package/docs/modules/shapes/base/geometry2d.md +204 -0
  155. package/docs/modules/shapes/base/renderable.md +215 -0
  156. package/docs/modules/shapes/base/shape.md +262 -0
  157. package/docs/modules/shapes/base/transformable.md +243 -0
  158. package/docs/modules/shapes/hierarchy.md +218 -0
  159. package/docs/modules/state/README.md +577 -0
  160. package/docs/modules/util/README.md +99 -0
  161. package/docs/modules/util/camera3d.md +412 -0
  162. package/docs/modules/util/scene3d.md +395 -0
  163. package/index.html +17 -0
  164. package/jsdoc.json +50 -0
  165. package/package.json +55 -0
  166. package/readme.md +599 -0
  167. package/scripts/build-demo.js +69 -0
  168. package/scripts/bundle4llm.js +276 -0
  169. package/scripts/clearconsole.js +48 -0
  170. package/src/collision/collision-system.js +332 -0
  171. package/src/collision/collision.js +303 -0
  172. package/src/collision/index.js +10 -0
  173. package/src/fluent/fluent-game.js +430 -0
  174. package/src/fluent/fluent-go.js +1060 -0
  175. package/src/fluent/fluent-layer.js +152 -0
  176. package/src/fluent/fluent-scene.js +291 -0
  177. package/src/fluent/index.js +98 -0
  178. package/src/fluent/sketch.js +380 -0
  179. package/src/game/game.js +467 -0
  180. package/src/game/index.js +49 -0
  181. package/src/game/objects/go.js +220 -0
  182. package/src/game/objects/imagego.js +30 -0
  183. package/src/game/objects/index.js +54 -0
  184. package/src/game/objects/isometric-scene.js +260 -0
  185. package/src/game/objects/layoutscene.js +549 -0
  186. package/src/game/objects/scene.js +175 -0
  187. package/src/game/objects/scene3d.js +118 -0
  188. package/src/game/objects/text.js +221 -0
  189. package/src/game/objects/wrapper.js +232 -0
  190. package/src/game/pipeline.js +243 -0
  191. package/src/game/ui/button.js +396 -0
  192. package/src/game/ui/cursor.js +93 -0
  193. package/src/game/ui/fps.js +91 -0
  194. package/src/game/ui/index.js +5 -0
  195. package/src/game/ui/togglebutton.js +93 -0
  196. package/src/game/ui/tooltip.js +249 -0
  197. package/src/index.js +25 -0
  198. package/src/io/events.js +20 -0
  199. package/src/io/index.js +86 -0
  200. package/src/io/input.js +70 -0
  201. package/src/io/keys.js +152 -0
  202. package/src/io/mouse.js +61 -0
  203. package/src/io/touch.js +39 -0
  204. package/src/logger/debugtab.js +138 -0
  205. package/src/logger/index.js +3 -0
  206. package/src/logger/loggable.js +47 -0
  207. package/src/logger/logger.js +113 -0
  208. package/src/math/complex.js +37 -0
  209. package/src/math/constants.js +1 -0
  210. package/src/math/fractal.js +1271 -0
  211. package/src/math/gr.js +201 -0
  212. package/src/math/heat.js +202 -0
  213. package/src/math/index.js +12 -0
  214. package/src/math/noise.js +433 -0
  215. package/src/math/orbital.js +191 -0
  216. package/src/math/patterns.js +1339 -0
  217. package/src/math/penrose.js +259 -0
  218. package/src/math/quantum.js +115 -0
  219. package/src/math/random.js +195 -0
  220. package/src/math/tensor.js +1009 -0
  221. package/src/mixins/anchor.js +131 -0
  222. package/src/mixins/draggable.js +72 -0
  223. package/src/mixins/index.js +2 -0
  224. package/src/motion/bezier.js +132 -0
  225. package/src/motion/bounce.js +58 -0
  226. package/src/motion/easing.js +349 -0
  227. package/src/motion/float.js +130 -0
  228. package/src/motion/follow.js +125 -0
  229. package/src/motion/hop.js +52 -0
  230. package/src/motion/index.js +82 -0
  231. package/src/motion/motion.js +1124 -0
  232. package/src/motion/orbit.js +49 -0
  233. package/src/motion/oscillate.js +39 -0
  234. package/src/motion/parabolic.js +141 -0
  235. package/src/motion/patrol.js +147 -0
  236. package/src/motion/pendulum.js +48 -0
  237. package/src/motion/pulse.js +88 -0
  238. package/src/motion/shake.js +83 -0
  239. package/src/motion/spiral.js +144 -0
  240. package/src/motion/spring.js +150 -0
  241. package/src/motion/swing.js +47 -0
  242. package/src/motion/tween.js +92 -0
  243. package/src/motion/tweenetik.js +139 -0
  244. package/src/motion/waypoint.js +210 -0
  245. package/src/painter/index.js +8 -0
  246. package/src/painter/painter.colors.js +331 -0
  247. package/src/painter/painter.effects.js +230 -0
  248. package/src/painter/painter.img.js +229 -0
  249. package/src/painter/painter.js +295 -0
  250. package/src/painter/painter.lines.js +189 -0
  251. package/src/painter/painter.opacity.js +41 -0
  252. package/src/painter/painter.shapes.js +277 -0
  253. package/src/painter/painter.text.js +273 -0
  254. package/src/particle/emitter.js +124 -0
  255. package/src/particle/index.js +11 -0
  256. package/src/particle/particle-system.js +322 -0
  257. package/src/particle/particle.js +71 -0
  258. package/src/particle/updaters.js +170 -0
  259. package/src/shapes/arc.js +43 -0
  260. package/src/shapes/arrow.js +33 -0
  261. package/src/shapes/bezier.js +42 -0
  262. package/src/shapes/circle.js +62 -0
  263. package/src/shapes/clouds.js +56 -0
  264. package/src/shapes/cone.js +219 -0
  265. package/src/shapes/cross.js +70 -0
  266. package/src/shapes/cube.js +244 -0
  267. package/src/shapes/cylinder.js +254 -0
  268. package/src/shapes/diamond.js +48 -0
  269. package/src/shapes/euclidian.js +111 -0
  270. package/src/shapes/figure.js +115 -0
  271. package/src/shapes/geometry.js +220 -0
  272. package/src/shapes/group.js +375 -0
  273. package/src/shapes/heart.js +42 -0
  274. package/src/shapes/hexagon.js +26 -0
  275. package/src/shapes/image.js +192 -0
  276. package/src/shapes/index.js +111 -0
  277. package/src/shapes/line.js +29 -0
  278. package/src/shapes/pattern.js +90 -0
  279. package/src/shapes/pin.js +44 -0
  280. package/src/shapes/poly.js +31 -0
  281. package/src/shapes/prism.js +226 -0
  282. package/src/shapes/rect.js +35 -0
  283. package/src/shapes/renderable.js +333 -0
  284. package/src/shapes/ring.js +26 -0
  285. package/src/shapes/roundrect.js +95 -0
  286. package/src/shapes/shape.js +117 -0
  287. package/src/shapes/slice.js +26 -0
  288. package/src/shapes/sphere.js +314 -0
  289. package/src/shapes/sphere3d.js +537 -0
  290. package/src/shapes/square.js +15 -0
  291. package/src/shapes/star.js +99 -0
  292. package/src/shapes/svg.js +408 -0
  293. package/src/shapes/text.js +553 -0
  294. package/src/shapes/traceable.js +83 -0
  295. package/src/shapes/transform.js +357 -0
  296. package/src/shapes/transformable.js +172 -0
  297. package/src/shapes/triangle.js +26 -0
  298. package/src/sound/index.js +17 -0
  299. package/src/sound/sound.js +473 -0
  300. package/src/sound/synth.analyzer.js +149 -0
  301. package/src/sound/synth.effects.js +207 -0
  302. package/src/sound/synth.envelope.js +59 -0
  303. package/src/sound/synth.js +229 -0
  304. package/src/sound/synth.musical.js +160 -0
  305. package/src/sound/synth.noise.js +85 -0
  306. package/src/sound/synth.oscillators.js +293 -0
  307. package/src/state/index.js +10 -0
  308. package/src/state/state-machine.js +371 -0
  309. package/src/util/camera3d.js +438 -0
  310. package/src/util/index.js +6 -0
  311. package/src/util/isometric-camera.js +235 -0
  312. package/src/util/layout.js +317 -0
  313. package/src/util/position.js +147 -0
  314. package/src/util/tasks.js +47 -0
  315. package/src/util/zindex.js +287 -0
  316. package/src/webgl/index.js +9 -0
  317. package/src/webgl/shaders/sphere-shaders.js +994 -0
  318. package/src/webgl/webgl-renderer.js +388 -0
  319. package/tde.png +0 -0
  320. package/test/math/orbital.test.js +61 -0
  321. package/test/math/tensor.test.js +114 -0
  322. package/test/particle/emitter.test.js +204 -0
  323. package/test/particle/particle-system.test.js +310 -0
  324. package/test/particle/particle.test.js +116 -0
  325. package/test/particle/updaters.test.js +386 -0
  326. package/test/setup.js +120 -0
  327. package/test/shapes/euclidian.test.js +44 -0
  328. package/test/shapes/geometry.test.js +86 -0
  329. package/test/shapes/group.test.js +86 -0
  330. package/test/shapes/rectangle.test.js +64 -0
  331. package/test/shapes/transform.test.js +379 -0
  332. package/test/util/camera3d.test.js +428 -0
  333. package/test/util/scene3d.test.js +352 -0
  334. package/types/collision.d.ts +249 -0
  335. package/types/common.d.ts +155 -0
  336. package/types/game.d.ts +497 -0
  337. package/types/index.d.ts +309 -0
  338. package/types/io.d.ts +188 -0
  339. package/types/logger.d.ts +127 -0
  340. package/types/math.d.ts +268 -0
  341. package/types/mixins.d.ts +92 -0
  342. package/types/motion.d.ts +678 -0
  343. package/types/painter.d.ts +378 -0
  344. package/types/shapes.d.ts +864 -0
  345. package/types/sound.d.ts +672 -0
  346. package/types/state.d.ts +251 -0
  347. package/types/util.d.ts +253 -0
  348. package/vite.config.js +50 -0
  349. package/vitest.config.js +13 -0
@@ -0,0 +1,467 @@
1
+ /**************************************************************
2
+ * Game.js
3
+ *
4
+ * The core class providing the main game loop (update & render),
5
+ * pipeline management, and input initialization.
6
+ * Intended to be subclassed for your specific game logic.
7
+ ***************************************************************/
8
+ import { Logger } from "../logger/logger.js";
9
+ import { DebugTab } from "../logger/debugtab.js";
10
+ import { Pipeline } from "./pipeline.js";
11
+ import { Painter } from "../painter/painter.js";
12
+ import { EventEmitter } from "../io";
13
+ import { Mouse } from "../io";
14
+ import { Input } from "../io";
15
+ import { Touch } from "../io";
16
+ import { Keys } from "../io";
17
+ import { Cursor } from "./ui/cursor.js";
18
+ import { Tweenetik } from "../motion/tweenetik.js";
19
+
20
+ /**
21
+ * Core Game class. Provides lifecycle management, the update/render loop,
22
+ * and a Pipeline to manage GameObjects.
23
+ *
24
+ * Usage:
25
+ * 1. Subclass Game and override init(), update(dt), and render() if needed.
26
+ * 2. Create a new instance, passing in a <canvas> element.
27
+ * 3. Call .init() and then .start() to begin the game loop.
28
+ */
29
+ export class Game {
30
+ /**
31
+ * Instantiate a new Game.
32
+ * @param {HTMLCanvasElement} canvas - The canvas element to render onto.
33
+ */
34
+ constructor(canvas) {
35
+ /**
36
+ * The main canvas used for rendering.
37
+ * @type {HTMLCanvasElement}
38
+ */
39
+ this.canvas = canvas;
40
+ /**
41
+ * The 2D rendering context.
42
+ * @type {CanvasRenderingContext2D}
43
+ */
44
+ this.ctx = canvas.getContext("2d");
45
+ /**
46
+ * A centralized event emitter for the entire Game.
47
+ * Handles mouse/keyboard/touch input as well as custom events.
48
+ * @type {EventEmitter}
49
+ */
50
+ this.events = new EventEmitter();
51
+ /**
52
+ * The pipeline is a collection of GameObjects that are updated and rendered each frame.
53
+ * @type {Cursor}
54
+ */
55
+ this._cursor = null;
56
+ /**
57
+ * Tracks the timestamp of the previous frame for calculating delta time.
58
+ * @type {number}
59
+ * @private
60
+ */
61
+ this.lastTime = 0;
62
+ /**
63
+ * Tracks the delta time of the last update.
64
+ * @type {number}
65
+ * @private
66
+ */
67
+ this.dt = 0;
68
+ /**
69
+ * Flag indicating if the game loop is currently running.
70
+ * @type {boolean}
71
+ */
72
+ this.running = false;
73
+ /**
74
+ * Keep track of current frame (how many times loop has been called)
75
+ * @type {number}
76
+ */
77
+ this._frame = 0;
78
+ /**
79
+ * The pipeline is a collection of GameObjects that are updated and rendered each frame.
80
+ * @type {Pipeline}
81
+ */
82
+ this.pipeline = new Pipeline(this);
83
+ // Initialize Painter with this game's 2D context.
84
+ Painter.init(this.ctx);
85
+ //
86
+ this.targetFPS = 60; // default target FPS
87
+ this._frameInterval = 1000 / this.targetFPS; // in milliseconds
88
+ this._accumulator = 0; // tracks time overflow
89
+ // Add visibility pause feature
90
+ this._pauseOnBlur = false;
91
+ this._isPaused = false;
92
+ //
93
+ this._init = false;
94
+ //
95
+ this.initLogging();
96
+ }
97
+
98
+ setFPS(fps) {
99
+ this.targetFPS = fps;
100
+ this._frameInterval = 1000 / fps;
101
+ }
102
+
103
+ #prevWidth = 0;
104
+ #prevHeight = 0;
105
+
106
+ /**
107
+ * Hook to set up initial game state, add objects, etc.
108
+ * Called in restart() and can be called manually if desired.
109
+ * Override in subclasses to initialize custom logic or objects.
110
+ */
111
+ init() {
112
+ //
113
+ // Initialize pointer & input subsystems with reference to this game.
114
+ this.initIO();
115
+ this.initMotion();
116
+ //this.setPauseOnBlur(true);
117
+ this._init = true;
118
+ this.logger.log("[Game] Initialized");
119
+ }
120
+
121
+ /**
122
+ * Initialize Mouse events.
123
+ * This is called automatically in the constructor.
124
+ * Override to add custom mouse event handlers, or disable them.
125
+ */
126
+ initMouse() {
127
+ Mouse.init(this);
128
+ }
129
+
130
+ /**
131
+ * Initialize Touch events.
132
+ * This is called automatically in the constructor.
133
+ * Override to add custom touch event handlers, or disable them.
134
+ */
135
+ initTouch() {
136
+ Touch.init(this);
137
+ }
138
+
139
+ /**
140
+ * Initialize Input Events.
141
+ * An Input event is a combined event for mouse and touch that streamlines hover and click interactions.
142
+ * This is called automatically in the constructor.
143
+ * Override to add custom input event handlers, or disable them.
144
+ */
145
+ initInput() {
146
+ Input.init(this);
147
+ }
148
+
149
+ /**
150
+ * Initialize Keyboard events.
151
+ * This is called automatically in the constructor.
152
+ * Override to add custom keyboard event handlers, or disable them.
153
+ */
154
+ initKeyboard() {
155
+ Keys.init(this);
156
+ }
157
+
158
+ /**
159
+ * Initialize I/O Events.
160
+ * This is a convenience method to set up all input systems at once.
161
+ * This is called automatically in the constructor.
162
+ * Override to add custom event handlers, or disable them.
163
+ */
164
+ initIO() {
165
+ this.initMouse();
166
+ this.initTouch();
167
+ this.initInput();
168
+ this.initKeyboard();
169
+ }
170
+
171
+ initMotion() {
172
+ Tweenetik._active = [];
173
+ }
174
+
175
+ initLogging() {
176
+ this.logger = new Logger("Game");
177
+ //Logger.setOutput(DebugTab.getInstance());
178
+ Logger.setOutput(console);
179
+ // Disable all logs initially
180
+ Logger.disableAll();
181
+ Logger.disable();
182
+ // Enable logs for specific classes
183
+ //Logger.enableFor("Renderable");
184
+ //Logger.enableFor("GameObject");
185
+ // Set the global log level
186
+ Logger.setLevel(Logger.INFO);
187
+ this.logger.groupCollapsed("Initializing Game...");
188
+ }
189
+
190
+ enableLogging() {
191
+ Logger.enable();
192
+ }
193
+
194
+ disableLogging() {
195
+ Logger.disableAll();
196
+ Logger.disable();
197
+ }
198
+
199
+ markBoundsDirty() {
200
+ this._boundsDirty = true;
201
+ }
202
+
203
+ get boundsDirty() {
204
+ return this._boundsDirty;
205
+ }
206
+
207
+ set boundsDirty(dirty) {
208
+ this._boundsDirty = dirty;
209
+ }
210
+
211
+ /**
212
+ * Enables automatic resizing of the canvas to either the window or a given container.
213
+ * @param {HTMLElement} [container=window] - Element to observe for resizing. Defaults to window.
214
+ */
215
+ enableFluidSize(container = window) {
216
+ if (container === window) {
217
+ const resizeCanvas = () => {
218
+ this.canvas.width = window.innerWidth;
219
+ this.canvas.height = window.innerHeight;
220
+ if (
221
+ this.#prevWidth !== this.canvas.width ||
222
+ this.#prevHeight !== this.canvas.height
223
+ ) {
224
+ this.markBoundsDirty();
225
+ this.onResize?.();
226
+ }
227
+ this.#prevWidth = this.canvas.width;
228
+ this.#prevHeight = this.canvas.height;
229
+ };
230
+ resizeCanvas(); // set initial size
231
+ window.addEventListener("resize", resizeCanvas);
232
+ this._fluidResizeCleanup = () => {
233
+ window.removeEventListener("resize", resizeCanvas);
234
+ };
235
+ } else {
236
+ // If ResizeObserver is available, use it to track container size changes
237
+ if (!("ResizeObserver" in window)) {
238
+ console.warn("ResizeObserver not supported in this browser.");
239
+ return;
240
+ }
241
+ const resizeCanvas = () => {
242
+ const rect = container.getBoundingClientRect();
243
+ this.canvas.width = rect.width;
244
+ this.canvas.height = rect.height;
245
+ };
246
+ const observer = new ResizeObserver(() => {
247
+ resizeCanvas();
248
+ });
249
+ observer.observe(container);
250
+ resizeCanvas();
251
+
252
+ this._fluidResizeCleanup = () => observer.disconnect();
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Disables fluid resizing of the canvas.
258
+ * If previously set, removes the event listener or observer.
259
+ */
260
+ disableFluidSize() {
261
+ if (this._fluidResizeCleanup) {
262
+ this._fluidResizeCleanup();
263
+ this._fluidResizeCleanup = null;
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Starts the main game loop using requestAnimationFrame.
269
+ * The loop() method is bound so it can be used as a callback.
270
+ */
271
+ start() {
272
+ this.logger.groupCollapsed("[Game] Starting...");
273
+ this.init();
274
+ if (!this._init) {
275
+ throw new Error(
276
+ "Game not initialized. Did you call init()? Remember to call super.init() in your subclass."
277
+ );
278
+ }
279
+ this.running = true;
280
+ this.loop = this.loop.bind(this);
281
+ requestAnimationFrame(this.loop);
282
+ this.logger.log("[Game] Started");
283
+ this.logger.groupEnd();
284
+ }
285
+
286
+ /**
287
+ * Stops the main game loop.
288
+ */
289
+ stop() {
290
+ this.running = false;
291
+ this.logger.log("[Game] Stopped");
292
+ }
293
+
294
+ /**
295
+ * Clears the pipeline, calls init() again, and restarts the game loop.
296
+ * Useful for resetting the game state.
297
+ */
298
+ restart() {
299
+ this.pipeline.clear();
300
+ this.init();
301
+ this.start();
302
+ this.logger.log("[Game] Restarted");
303
+ }
304
+
305
+ /**
306
+ * The main game loop. Called automatically by requestAnimationFrame.
307
+ * @param {DOMHighResTimeStamp} timestamp - The current time at which requestAnimationFrame fired.
308
+ * Used to measure elapsed time between frames.
309
+ * @private
310
+ */
311
+ loop(timestamp) {
312
+ if (!this.running) return;
313
+
314
+ const elapsed = timestamp - this.lastTime; // <--- Real elapsed time
315
+ this.lastTime = timestamp;
316
+ this._accumulator += elapsed;
317
+
318
+ // 🧠 Real FPS from wall time
319
+ this.actualFps = 1000 / elapsed;
320
+
321
+ if (this._accumulator >= this._frameInterval) {
322
+ const dt = this._frameInterval / 1000; // fixed timestep in seconds
323
+ this.dt = dt;
324
+ this._frame++;
325
+ this.logger.groupCollapsed(`Frame #${this._frame}`);
326
+ this.logger.time("render time");
327
+
328
+ this.update(dt);
329
+ this.render();
330
+
331
+ this.logger.timeEnd("render time");
332
+ this.logger.groupEnd();
333
+
334
+ this._accumulator -= this._frameInterval;
335
+ }
336
+ if (this.boundsDirty) {
337
+ this.boundsDirty = false;
338
+ }
339
+ requestAnimationFrame(this.loop);
340
+ }
341
+
342
+ /**
343
+ * Updates the game logic and propagates to the pipeline.
344
+ * @param {number} dt - Delta time in seconds since the last frame.
345
+ * This is used to make movement or animations frame-rate independent.
346
+ */
347
+ update(dt) {
348
+ this.pipeline.update(dt);
349
+ }
350
+
351
+ /**
352
+ * Renders the game by first clearing the canvas, then asking
353
+ * the pipeline to render all GameObjects.
354
+ */
355
+ render() {
356
+ // Ensure Painter uses this game's context (supports multiple canvases)
357
+ Painter.setContext(this.ctx);
358
+ if (this.running) {
359
+ this.clear();
360
+ }
361
+ this.pipeline.render();
362
+ }
363
+
364
+ /**
365
+ * Clears the entire canvas. Called each frame before rendering.
366
+ * Override to customize clear behavior (e.g. keep background images).
367
+ */
368
+ clear() {
369
+ Painter.clear();
370
+ }
371
+
372
+ /**
373
+ * Returns the current width of the canvas.
374
+ * @type {number}
375
+ */
376
+ get width() {
377
+ return this.canvas.width;
378
+ }
379
+
380
+ /**
381
+ * Returns the current height of the canvas.
382
+ * @type {number}
383
+ */
384
+ get height() {
385
+ return this.canvas.height;
386
+ }
387
+
388
+ /**
389
+ * Sets the canvas background color via CSS.
390
+ * @param {string} color - Any valid CSS color string.
391
+ */
392
+ set backgroundColor(color) {
393
+ this.canvas.style.backgroundColor = color;
394
+ }
395
+
396
+ /**
397
+ * Sets the cursor for the game.
398
+ * @param {Cursor} cursor - The cursor to set.
399
+ */
400
+ set cursor(cursor) {
401
+ if (this._cursor) {
402
+ this._cursor.destroy();
403
+ this.pipeline.remove(this._cursor);
404
+ }
405
+ this._cursor = cursor;
406
+ this._cursor.activate();
407
+ // add the cursor to the pipeline
408
+ this.pipeline.add(cursor);
409
+ }
410
+
411
+ /**
412
+ * Returns the current cursor.
413
+ * @returns {Cursor}
414
+ */
415
+ get cursor() {
416
+ return this._cursor;
417
+ }
418
+
419
+ /**
420
+ * Deactivates the current cursor and removes it from the pipeline.
421
+ */
422
+ resetCursor() {
423
+ if (this._cursor) {
424
+ this._cursor.destroy();
425
+ this.pipeline.remove(this._cursor);
426
+ this._cursor = null;
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Enable/disable pausing the game when the tab loses focus
432
+ * @param {boolean} enabled - Whether to pause on blur
433
+ */
434
+ enablePauseOnBlur(enabled) {
435
+ this._pauseOnBlur = enabled;
436
+ if (enabled) {
437
+ window.addEventListener(
438
+ "visibilitychange",
439
+ this._handleVisibilityChange.bind(this),
440
+ false
441
+ );
442
+ } else {
443
+ window.removeEventListener(
444
+ "visibilitychange",
445
+ this._handleVisibilityChange.bind(this),
446
+ false
447
+ );
448
+ }
449
+ }
450
+
451
+ _handleVisibilityChange() {
452
+ this.logger.log("Visibility change detected");
453
+ if (document.hidden) {
454
+ if (this._pauseOnBlur && this.running) {
455
+ this._isPaused = true;
456
+ this.stop();
457
+ this.logger.log("Paused due to tab visibility change");
458
+ }
459
+ } else {
460
+ if (this._isPaused) {
461
+ this._isPaused = false;
462
+ this.start();
463
+ this.logger.log("Resumed after tab visibility change");
464
+ }
465
+ }
466
+ }
467
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @module Game
3
+ * @description Core game engine components for building canvas-based games and interactive applications.
4
+ *
5
+ * This module provides the fundamental building blocks for creating games:
6
+ * - {@link Game}: The main game loop handler and canvas manager
7
+ * - {@link GameObject}: Base class for all entities in the game world
8
+ * - {@link Pipeline}: Management system for game objects and their lifecycle
9
+ * - UI components for user interaction
10
+ * - Pre-built game objects for common use cases
11
+ *
12
+ * @example
13
+ * // Basic game setup
14
+ * import { Game, GameObject } from './core';
15
+ *
16
+ * // Create a custom game by extending the base Game class
17
+ * class MyGame extends Game {
18
+ * init() {
19
+ * super.init();
20
+ * // Add your game initialization code here
21
+ * this.backgroundColor = '#333';
22
+ *
23
+ * // Create and add objects to the pipeline
24
+ * const player = new GameObject(this, {
25
+ * x: this.width / 2,
26
+ * y: this.height / 2,
27
+ * width: 50,
28
+ * height: 50
29
+ * });
30
+ * this.pipeline.add(player);
31
+ * }
32
+ *
33
+ * update(dt) {
34
+ * super.update(dt);
35
+ * // Add your custom game update logic here
36
+ * }
37
+ * }
38
+ *
39
+ * // Start the game
40
+ * const canvas = document.getElementById('game-canvas');
41
+ * const game = new MyGame(canvas);
42
+ * game.init();
43
+ * game.start();
44
+ */
45
+
46
+ export { Game } from "./game.js";
47
+ export { Pipeline } from "./pipeline.js";
48
+ export * from "./objects";
49
+ export * from "./ui";