@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.
- package/.github/workflows/release.yaml +70 -0
- package/.jshintrc +4 -0
- package/.vscode/settings.json +22 -0
- package/CLAUDE.md +310 -0
- package/blackhole.jpg +0 -0
- package/demo.png +0 -0
- package/demos/CNAME +1 -0
- package/demos/animations.html +31 -0
- package/demos/basic.html +38 -0
- package/demos/baskara.html +31 -0
- package/demos/bezier.html +35 -0
- package/demos/beziersignature.html +29 -0
- package/demos/blackhole.html +28 -0
- package/demos/blob.html +35 -0
- package/demos/demos.css +289 -0
- package/demos/easing.html +28 -0
- package/demos/events.html +195 -0
- package/demos/fluent.html +647 -0
- package/demos/fractals.html +36 -0
- package/demos/genart.html +26 -0
- package/demos/gendream.html +26 -0
- package/demos/group.html +36 -0
- package/demos/home.html +587 -0
- package/demos/index.html +364 -0
- package/demos/isometric.html +34 -0
- package/demos/js/animations.js +452 -0
- package/demos/js/basic.js +204 -0
- package/demos/js/baskara.js +751 -0
- package/demos/js/bezier.js +692 -0
- package/demos/js/beziersignature.js +241 -0
- package/demos/js/blackhole/accretiondisk.obj.js +379 -0
- package/demos/js/blackhole/blackhole.obj.js +318 -0
- package/demos/js/blackhole/index.js +409 -0
- package/demos/js/blackhole/particle.js +56 -0
- package/demos/js/blackhole/starfield.obj.js +218 -0
- package/demos/js/blob.js +2263 -0
- package/demos/js/easing.js +477 -0
- package/demos/js/fluent.js +183 -0
- package/demos/js/fractals.js +931 -0
- package/demos/js/fractalworker.js +93 -0
- package/demos/js/genart.js +268 -0
- package/demos/js/gendream.js +209 -0
- package/demos/js/group.js +140 -0
- package/demos/js/info-toggle.js +25 -0
- package/demos/js/isometric.js +863 -0
- package/demos/js/kerr.js +1556 -0
- package/demos/js/lavalamp.js +590 -0
- package/demos/js/layout.js +354 -0
- package/demos/js/mondrian.js +285 -0
- package/demos/js/opacity.js +275 -0
- package/demos/js/painter.js +484 -0
- package/demos/js/particles-showcase.js +514 -0
- package/demos/js/particles.js +299 -0
- package/demos/js/patterns.js +397 -0
- package/demos/js/penrose/artifact.js +69 -0
- package/demos/js/penrose/blackhole.js +121 -0
- package/demos/js/penrose/constants.js +73 -0
- package/demos/js/penrose/game.js +943 -0
- package/demos/js/penrose/lore.js +278 -0
- package/demos/js/penrose/penrosescene.js +892 -0
- package/demos/js/penrose/ship.js +216 -0
- package/demos/js/penrose/sounds.js +211 -0
- package/demos/js/penrose/voidparticle.js +55 -0
- package/demos/js/penrose/voidscene.js +258 -0
- package/demos/js/penrose/voidship.js +144 -0
- package/demos/js/penrose/wormhole.js +46 -0
- package/demos/js/pipeline.js +555 -0
- package/demos/js/scene.js +304 -0
- package/demos/js/scenes.js +320 -0
- package/demos/js/schrodinger.js +410 -0
- package/demos/js/schwarzschild.js +1023 -0
- package/demos/js/shapes.js +628 -0
- package/demos/js/space/alien.js +171 -0
- package/demos/js/space/boom.js +98 -0
- package/demos/js/space/boss.js +353 -0
- package/demos/js/space/buff.js +73 -0
- package/demos/js/space/bullet.js +102 -0
- package/demos/js/space/constants.js +85 -0
- package/demos/js/space/game.js +1884 -0
- package/demos/js/space/hud.js +112 -0
- package/demos/js/space/laserbeam.js +179 -0
- package/demos/js/space/lightning.js +277 -0
- package/demos/js/space/minion.js +192 -0
- package/demos/js/space/missile.js +212 -0
- package/demos/js/space/player.js +430 -0
- package/demos/js/space/powerup.js +90 -0
- package/demos/js/space/starfield.js +58 -0
- package/demos/js/space/starpower.js +90 -0
- package/demos/js/spacetime.js +559 -0
- package/demos/js/svgtween.js +204 -0
- package/demos/js/tde/accretiondisk.js +418 -0
- package/demos/js/tde/blackhole.js +219 -0
- package/demos/js/tde/blackholescene.js +209 -0
- package/demos/js/tde/config.js +59 -0
- package/demos/js/tde/index.js +695 -0
- package/demos/js/tde/jets.js +290 -0
- package/demos/js/tde/lensedstarfield.js +147 -0
- package/demos/js/tde/tdestar.js +317 -0
- package/demos/js/tde/tidalstream.js +356 -0
- package/demos/js/tde_old/blackhole.obj.js +354 -0
- package/demos/js/tde_old/debris.obj.js +791 -0
- package/demos/js/tde_old/flare.obj.js +239 -0
- package/demos/js/tde_old/index.js +448 -0
- package/demos/js/tde_old/star.obj.js +812 -0
- package/demos/js/tiles.js +312 -0
- package/demos/js/tweendemo.js +79 -0
- package/demos/js/visibility.js +102 -0
- package/demos/kerr.html +28 -0
- package/demos/lavalamp.html +27 -0
- package/demos/layouts.html +37 -0
- package/demos/logo.svg +4 -0
- package/demos/loop.html +84 -0
- package/demos/mondrian.html +32 -0
- package/demos/og_image.png +0 -0
- package/demos/opacity.html +36 -0
- package/demos/painter.html +39 -0
- package/demos/particles-showcase.html +28 -0
- package/demos/particles.html +24 -0
- package/demos/patterns.html +33 -0
- package/demos/penrose-game.html +31 -0
- package/demos/pipeline.html +737 -0
- package/demos/scene.html +33 -0
- package/demos/scenes.html +96 -0
- package/demos/schrodinger.html +27 -0
- package/demos/schwarzschild.html +27 -0
- package/demos/shapes.html +16 -0
- package/demos/space.html +85 -0
- package/demos/spacetime.html +27 -0
- package/demos/svgtween.html +29 -0
- package/demos/tde.html +28 -0
- package/demos/tiles.html +28 -0
- package/demos/transforms.html +400 -0
- package/demos/tween.html +45 -0
- package/demos/visibility.html +33 -0
- package/disk_example.png +0 -0
- package/docs/README.md +222 -0
- package/docs/concepts/architecture-overview.md +204 -0
- package/docs/concepts/lifecycle.md +255 -0
- package/docs/concepts/rendering-pipeline.md +279 -0
- package/docs/concepts/tde-zorder.md +106 -0
- package/docs/concepts/two-layer-architecture.md +229 -0
- package/docs/getting-started/first-game.md +354 -0
- package/docs/getting-started/hello-world.md +269 -0
- package/docs/getting-started/installation.md +157 -0
- package/docs/modules/collision/README.md +453 -0
- package/docs/modules/fluent/README.md +1075 -0
- package/docs/modules/game/README.md +303 -0
- package/docs/modules/isometric-camera.md +210 -0
- package/docs/modules/isometric.md +275 -0
- package/docs/modules/painter/README.md +328 -0
- package/docs/modules/particle/README.md +559 -0
- package/docs/modules/shapes/README.md +221 -0
- package/docs/modules/shapes/base/euclidian.md +123 -0
- package/docs/modules/shapes/base/geometry2d.md +204 -0
- package/docs/modules/shapes/base/renderable.md +215 -0
- package/docs/modules/shapes/base/shape.md +262 -0
- package/docs/modules/shapes/base/transformable.md +243 -0
- package/docs/modules/shapes/hierarchy.md +218 -0
- package/docs/modules/state/README.md +577 -0
- package/docs/modules/util/README.md +99 -0
- package/docs/modules/util/camera3d.md +412 -0
- package/docs/modules/util/scene3d.md +395 -0
- package/index.html +17 -0
- package/jsdoc.json +50 -0
- package/package.json +55 -0
- package/readme.md +599 -0
- package/scripts/build-demo.js +69 -0
- package/scripts/bundle4llm.js +276 -0
- package/scripts/clearconsole.js +48 -0
- package/src/collision/collision-system.js +332 -0
- package/src/collision/collision.js +303 -0
- package/src/collision/index.js +10 -0
- package/src/fluent/fluent-game.js +430 -0
- package/src/fluent/fluent-go.js +1060 -0
- package/src/fluent/fluent-layer.js +152 -0
- package/src/fluent/fluent-scene.js +291 -0
- package/src/fluent/index.js +98 -0
- package/src/fluent/sketch.js +380 -0
- package/src/game/game.js +467 -0
- package/src/game/index.js +49 -0
- package/src/game/objects/go.js +220 -0
- package/src/game/objects/imagego.js +30 -0
- package/src/game/objects/index.js +54 -0
- package/src/game/objects/isometric-scene.js +260 -0
- package/src/game/objects/layoutscene.js +549 -0
- package/src/game/objects/scene.js +175 -0
- package/src/game/objects/scene3d.js +118 -0
- package/src/game/objects/text.js +221 -0
- package/src/game/objects/wrapper.js +232 -0
- package/src/game/pipeline.js +243 -0
- package/src/game/ui/button.js +396 -0
- package/src/game/ui/cursor.js +93 -0
- package/src/game/ui/fps.js +91 -0
- package/src/game/ui/index.js +5 -0
- package/src/game/ui/togglebutton.js +93 -0
- package/src/game/ui/tooltip.js +249 -0
- package/src/index.js +25 -0
- package/src/io/events.js +20 -0
- package/src/io/index.js +86 -0
- package/src/io/input.js +70 -0
- package/src/io/keys.js +152 -0
- package/src/io/mouse.js +61 -0
- package/src/io/touch.js +39 -0
- package/src/logger/debugtab.js +138 -0
- package/src/logger/index.js +3 -0
- package/src/logger/loggable.js +47 -0
- package/src/logger/logger.js +113 -0
- package/src/math/complex.js +37 -0
- package/src/math/constants.js +1 -0
- package/src/math/fractal.js +1271 -0
- package/src/math/gr.js +201 -0
- package/src/math/heat.js +202 -0
- package/src/math/index.js +12 -0
- package/src/math/noise.js +433 -0
- package/src/math/orbital.js +191 -0
- package/src/math/patterns.js +1339 -0
- package/src/math/penrose.js +259 -0
- package/src/math/quantum.js +115 -0
- package/src/math/random.js +195 -0
- package/src/math/tensor.js +1009 -0
- package/src/mixins/anchor.js +131 -0
- package/src/mixins/draggable.js +72 -0
- package/src/mixins/index.js +2 -0
- package/src/motion/bezier.js +132 -0
- package/src/motion/bounce.js +58 -0
- package/src/motion/easing.js +349 -0
- package/src/motion/float.js +130 -0
- package/src/motion/follow.js +125 -0
- package/src/motion/hop.js +52 -0
- package/src/motion/index.js +82 -0
- package/src/motion/motion.js +1124 -0
- package/src/motion/orbit.js +49 -0
- package/src/motion/oscillate.js +39 -0
- package/src/motion/parabolic.js +141 -0
- package/src/motion/patrol.js +147 -0
- package/src/motion/pendulum.js +48 -0
- package/src/motion/pulse.js +88 -0
- package/src/motion/shake.js +83 -0
- package/src/motion/spiral.js +144 -0
- package/src/motion/spring.js +150 -0
- package/src/motion/swing.js +47 -0
- package/src/motion/tween.js +92 -0
- package/src/motion/tweenetik.js +139 -0
- package/src/motion/waypoint.js +210 -0
- package/src/painter/index.js +8 -0
- package/src/painter/painter.colors.js +331 -0
- package/src/painter/painter.effects.js +230 -0
- package/src/painter/painter.img.js +229 -0
- package/src/painter/painter.js +295 -0
- package/src/painter/painter.lines.js +189 -0
- package/src/painter/painter.opacity.js +41 -0
- package/src/painter/painter.shapes.js +277 -0
- package/src/painter/painter.text.js +273 -0
- package/src/particle/emitter.js +124 -0
- package/src/particle/index.js +11 -0
- package/src/particle/particle-system.js +322 -0
- package/src/particle/particle.js +71 -0
- package/src/particle/updaters.js +170 -0
- package/src/shapes/arc.js +43 -0
- package/src/shapes/arrow.js +33 -0
- package/src/shapes/bezier.js +42 -0
- package/src/shapes/circle.js +62 -0
- package/src/shapes/clouds.js +56 -0
- package/src/shapes/cone.js +219 -0
- package/src/shapes/cross.js +70 -0
- package/src/shapes/cube.js +244 -0
- package/src/shapes/cylinder.js +254 -0
- package/src/shapes/diamond.js +48 -0
- package/src/shapes/euclidian.js +111 -0
- package/src/shapes/figure.js +115 -0
- package/src/shapes/geometry.js +220 -0
- package/src/shapes/group.js +375 -0
- package/src/shapes/heart.js +42 -0
- package/src/shapes/hexagon.js +26 -0
- package/src/shapes/image.js +192 -0
- package/src/shapes/index.js +111 -0
- package/src/shapes/line.js +29 -0
- package/src/shapes/pattern.js +90 -0
- package/src/shapes/pin.js +44 -0
- package/src/shapes/poly.js +31 -0
- package/src/shapes/prism.js +226 -0
- package/src/shapes/rect.js +35 -0
- package/src/shapes/renderable.js +333 -0
- package/src/shapes/ring.js +26 -0
- package/src/shapes/roundrect.js +95 -0
- package/src/shapes/shape.js +117 -0
- package/src/shapes/slice.js +26 -0
- package/src/shapes/sphere.js +314 -0
- package/src/shapes/sphere3d.js +537 -0
- package/src/shapes/square.js +15 -0
- package/src/shapes/star.js +99 -0
- package/src/shapes/svg.js +408 -0
- package/src/shapes/text.js +553 -0
- package/src/shapes/traceable.js +83 -0
- package/src/shapes/transform.js +357 -0
- package/src/shapes/transformable.js +172 -0
- package/src/shapes/triangle.js +26 -0
- package/src/sound/index.js +17 -0
- package/src/sound/sound.js +473 -0
- package/src/sound/synth.analyzer.js +149 -0
- package/src/sound/synth.effects.js +207 -0
- package/src/sound/synth.envelope.js +59 -0
- package/src/sound/synth.js +229 -0
- package/src/sound/synth.musical.js +160 -0
- package/src/sound/synth.noise.js +85 -0
- package/src/sound/synth.oscillators.js +293 -0
- package/src/state/index.js +10 -0
- package/src/state/state-machine.js +371 -0
- package/src/util/camera3d.js +438 -0
- package/src/util/index.js +6 -0
- package/src/util/isometric-camera.js +235 -0
- package/src/util/layout.js +317 -0
- package/src/util/position.js +147 -0
- package/src/util/tasks.js +47 -0
- package/src/util/zindex.js +287 -0
- package/src/webgl/index.js +9 -0
- package/src/webgl/shaders/sphere-shaders.js +994 -0
- package/src/webgl/webgl-renderer.js +388 -0
- package/tde.png +0 -0
- package/test/math/orbital.test.js +61 -0
- package/test/math/tensor.test.js +114 -0
- package/test/particle/emitter.test.js +204 -0
- package/test/particle/particle-system.test.js +310 -0
- package/test/particle/particle.test.js +116 -0
- package/test/particle/updaters.test.js +386 -0
- package/test/setup.js +120 -0
- package/test/shapes/euclidian.test.js +44 -0
- package/test/shapes/geometry.test.js +86 -0
- package/test/shapes/group.test.js +86 -0
- package/test/shapes/rectangle.test.js +64 -0
- package/test/shapes/transform.test.js +379 -0
- package/test/util/camera3d.test.js +428 -0
- package/test/util/scene3d.test.js +352 -0
- package/types/collision.d.ts +249 -0
- package/types/common.d.ts +155 -0
- package/types/game.d.ts +497 -0
- package/types/index.d.ts +309 -0
- package/types/io.d.ts +188 -0
- package/types/logger.d.ts +127 -0
- package/types/math.d.ts +268 -0
- package/types/mixins.d.ts +92 -0
- package/types/motion.d.ts +678 -0
- package/types/painter.d.ts +378 -0
- package/types/shapes.d.ts +864 -0
- package/types/sound.d.ts +672 -0
- package/types/state.d.ts +251 -0
- package/types/util.d.ts +253 -0
- package/vite.config.js +50 -0
- package/vitest.config.js +13 -0
package/src/game/game.js
ADDED
|
@@ -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";
|