@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
|
@@ -0,0 +1,1075 @@
|
|
|
1
|
+
# Fluent Module
|
|
2
|
+
|
|
3
|
+
> Declarative, chainable API for rapid game development.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Fluent module provides a builder-pattern API layer on top of GCanvas's object-oriented architecture. Instead of manually instantiating classes and wiring them together, you chain method calls to declaratively build your game structure.
|
|
8
|
+
|
|
9
|
+
### Traditional API vs Fluent API
|
|
10
|
+
|
|
11
|
+
**Traditional approach:**
|
|
12
|
+
```js
|
|
13
|
+
import { Game, Scene, GameObject, Circle } from 'gcanvas';
|
|
14
|
+
|
|
15
|
+
const canvas = document.getElementById('game');
|
|
16
|
+
const game = new Game(canvas);
|
|
17
|
+
game.init();
|
|
18
|
+
game.backgroundColor = 'black';
|
|
19
|
+
|
|
20
|
+
const scene = new Scene(game);
|
|
21
|
+
scene.name = 'game';
|
|
22
|
+
game.pipeline.add(scene);
|
|
23
|
+
|
|
24
|
+
const player = new GameObject({ x: 400, y: 300 });
|
|
25
|
+
player.game = game;
|
|
26
|
+
const circle = new Circle(30, { color: 'lime' });
|
|
27
|
+
player.setRenderable(circle);
|
|
28
|
+
scene.add(player);
|
|
29
|
+
|
|
30
|
+
game.start();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Fluent approach:**
|
|
34
|
+
```js
|
|
35
|
+
import { gcanvas } from 'gcanvas';
|
|
36
|
+
|
|
37
|
+
gcanvas({ bg: 'black' })
|
|
38
|
+
.scene('game')
|
|
39
|
+
.go({ x: 400, y: 300, name: 'player' })
|
|
40
|
+
.circle({ radius: 30, fill: 'lime' })
|
|
41
|
+
.start();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Two Entry Points
|
|
45
|
+
|
|
46
|
+
| Entry Point | Purpose | Best For |
|
|
47
|
+
|-------------|---------|----------|
|
|
48
|
+
| `gcanvas(options)` | Full game development API | Games, interactive apps, complex scenes |
|
|
49
|
+
| `sketch(w, h, bg)` | Minimal creative coding API | Quick prototypes, generative art, Genuary-style sketches |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
import { gcanvas } from 'gcanvas';
|
|
57
|
+
|
|
58
|
+
// Create a game with a pulsing circle
|
|
59
|
+
gcanvas({ bg: '#1a1a2e' })
|
|
60
|
+
.scene('game')
|
|
61
|
+
.go({ x: 400, y: 300 })
|
|
62
|
+
.circle({ radius: 40, fill: '#00ff88' })
|
|
63
|
+
.pulse({ min: 0.8, max: 1.2, duration: 1 })
|
|
64
|
+
.start();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Entry Points
|
|
70
|
+
|
|
71
|
+
### gcanvas(options)
|
|
72
|
+
|
|
73
|
+
The main entry point for the full fluent API.
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { gcanvas } from 'gcanvas';
|
|
77
|
+
|
|
78
|
+
const game = gcanvas({
|
|
79
|
+
canvas: document.getElementById('game'), // Optional: use existing canvas
|
|
80
|
+
width: 800, // Canvas width (default: 800)
|
|
81
|
+
height: 600, // Canvas height (default: 600)
|
|
82
|
+
bg: 'black', // Background color
|
|
83
|
+
fluid: true, // Enable responsive sizing (default: true)
|
|
84
|
+
fps: 60, // Target FPS (default: 60)
|
|
85
|
+
container: document.body, // Container for auto-created canvas
|
|
86
|
+
pixelRatio: window.devicePixelRatio // Pixel ratio for HiDPI
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Returns:** `FluentGame` instance
|
|
91
|
+
|
|
92
|
+
### sketch(w, h, bg)
|
|
93
|
+
|
|
94
|
+
Ultra-simple mode for quick creative coding prototypes.
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
import { sketch } from 'gcanvas';
|
|
98
|
+
|
|
99
|
+
sketch(800, 600, '#1a1a1a')
|
|
100
|
+
.circle(400, 300, 50, 'lime')
|
|
101
|
+
.update((dt, ctx) => {
|
|
102
|
+
ctx.shapes[0].x += Math.sin(ctx.time) * 2;
|
|
103
|
+
})
|
|
104
|
+
.start();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Parameters:**
|
|
108
|
+
- `w` - Canvas width (default: 800)
|
|
109
|
+
- `h` - Canvas height (default: 600)
|
|
110
|
+
- `bg` - Background color (default: 'black')
|
|
111
|
+
|
|
112
|
+
**Returns:** `SketchAPI` object
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Builder Chain Architecture
|
|
117
|
+
|
|
118
|
+
The fluent API uses a chain of builder classes that wrap the underlying GCanvas classes:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
FluentGame ─────────── Wraps Game
|
|
122
|
+
│
|
|
123
|
+
└── FluentScene ──── Wraps Scene
|
|
124
|
+
│
|
|
125
|
+
└── FluentGO ─── Wraps GameObject
|
|
126
|
+
│
|
|
127
|
+
└── FluentGO (children)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Navigation
|
|
131
|
+
|
|
132
|
+
Each builder provides methods to navigate the chain:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
gcanvas({ bg: 'black' })
|
|
136
|
+
.scene('game') // → FluentScene
|
|
137
|
+
.go({ x: 100, y: 100 }) // → FluentGO
|
|
138
|
+
.circle({ radius: 20 })
|
|
139
|
+
.end() // ← Back to FluentScene
|
|
140
|
+
.go({ x: 200, y: 100 }) // → Another FluentGO
|
|
141
|
+
.rect({ width: 40, height: 40 })
|
|
142
|
+
.scene('ui') // → New FluentScene (auto-navigates up)
|
|
143
|
+
.go({ x: 20, y: 20 })
|
|
144
|
+
.text('Score: 0')
|
|
145
|
+
.start(); // Start the game
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Shared Context
|
|
149
|
+
|
|
150
|
+
All builders share access to:
|
|
151
|
+
- **refs** - Named object references (objects with `name` property)
|
|
152
|
+
- **state** - Shared state object for game data
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## FluentGame API
|
|
157
|
+
|
|
158
|
+
The root builder class wrapping the `Game` instance.
|
|
159
|
+
|
|
160
|
+
### Scene Management
|
|
161
|
+
|
|
162
|
+
#### .scene(name, options)
|
|
163
|
+
|
|
164
|
+
Create or switch to a scene.
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
// Basic scene creation
|
|
168
|
+
.scene('game')
|
|
169
|
+
|
|
170
|
+
// With options
|
|
171
|
+
.scene('game', {
|
|
172
|
+
zIndex: 0, // Render order
|
|
173
|
+
active: true, // Visibility
|
|
174
|
+
onEnter: (ctx) => {}, // Called when scene becomes visible
|
|
175
|
+
onExit: (ctx) => {} // Called when scene hides
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// With custom Scene class
|
|
179
|
+
.scene('game', MyCustomScene)
|
|
180
|
+
.scene('game', MyCustomScene, { zIndex: 0 })
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Signatures:**
|
|
184
|
+
- `scene(name)` - Create/switch to named scene
|
|
185
|
+
- `scene(name, options)` - Create with options
|
|
186
|
+
- `scene(name, CustomSceneClass)` - Custom class
|
|
187
|
+
- `scene(name, CustomSceneClass, options)` - Custom class with options
|
|
188
|
+
- `scene(CustomSceneClass)` - Custom class, name from class name
|
|
189
|
+
- `scene(CustomSceneClass, options)` - Custom class with options
|
|
190
|
+
|
|
191
|
+
#### .inScene(name)
|
|
192
|
+
|
|
193
|
+
Switch to an existing scene without creating it. Throws if scene doesn't exist.
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
.scene('game')
|
|
197
|
+
.go({ name: 'player' })
|
|
198
|
+
.circle({ radius: 20 })
|
|
199
|
+
.scene('ui')
|
|
200
|
+
.go({ name: 'score' })
|
|
201
|
+
.text('0')
|
|
202
|
+
// Later, switch back without recreating
|
|
203
|
+
.inScene('game')
|
|
204
|
+
.go({ name: 'enemy' })
|
|
205
|
+
.rect({ width: 30, height: 30 })
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### .go(options)
|
|
209
|
+
|
|
210
|
+
Shortcut to create a GameObject in the current scene.
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
gcanvas({ bg: 'black' })
|
|
214
|
+
.go({ x: 400, y: 300 }) // Creates 'default' scene automatically
|
|
215
|
+
.circle({ radius: 30 })
|
|
216
|
+
.start();
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Scene Visibility
|
|
220
|
+
|
|
221
|
+
#### .showScene(name)
|
|
222
|
+
|
|
223
|
+
Show a scene and trigger its `onEnter` callback.
|
|
224
|
+
|
|
225
|
+
#### .hideScene(name)
|
|
226
|
+
|
|
227
|
+
Hide a scene and trigger its `onExit` callback.
|
|
228
|
+
|
|
229
|
+
#### .transition(from, to, options)
|
|
230
|
+
|
|
231
|
+
Transition between scenes.
|
|
232
|
+
|
|
233
|
+
```js
|
|
234
|
+
.transition('menu', 'game', {
|
|
235
|
+
fade: 0.5, // Fade duration in seconds
|
|
236
|
+
onComplete: () => {} // Completion callback
|
|
237
|
+
})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### State Management
|
|
241
|
+
|
|
242
|
+
#### .state(initialState)
|
|
243
|
+
|
|
244
|
+
Set initial state values.
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
gcanvas({ bg: 'black' })
|
|
248
|
+
.state({ score: 0, lives: 3, level: 1 })
|
|
249
|
+
.scene('game')
|
|
250
|
+
// ...
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### .getState(key)
|
|
254
|
+
|
|
255
|
+
Get a state value.
|
|
256
|
+
|
|
257
|
+
```js
|
|
258
|
+
const score = game.getState('score');
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### .setState(key, value)
|
|
262
|
+
|
|
263
|
+
Set a state value.
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
game.setState('score', game.getState('score') + 100);
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Events & Lifecycle
|
|
270
|
+
|
|
271
|
+
#### .on(event, handler)
|
|
272
|
+
|
|
273
|
+
Register event handlers.
|
|
274
|
+
|
|
275
|
+
```js
|
|
276
|
+
// Update loop
|
|
277
|
+
.on('update', (dt, ctx) => {
|
|
278
|
+
// Called every frame
|
|
279
|
+
// dt = delta time, ctx = context object
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
// Keyboard events with key filtering
|
|
283
|
+
.on('keydown:space', (ctx, event) => {
|
|
284
|
+
ctx.refs.player.jump();
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
.on('keydown:escape', (ctx) => {
|
|
288
|
+
ctx.showScene('pause');
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
// Raw events
|
|
292
|
+
.on('click', (ctx, event) => {
|
|
293
|
+
console.log('Clicked at', event.x, event.y);
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
.on('keydown', (ctx, event) => {
|
|
297
|
+
console.log('Key pressed:', event.key);
|
|
298
|
+
})
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Event handler context object:**
|
|
302
|
+
```js
|
|
303
|
+
{
|
|
304
|
+
refs, // Named object references
|
|
305
|
+
state, // Shared state object
|
|
306
|
+
scenes, // All scenes (as object)
|
|
307
|
+
game, // Underlying Game instance
|
|
308
|
+
width, // Canvas width
|
|
309
|
+
height, // Canvas height
|
|
310
|
+
showScene, // Helper function
|
|
311
|
+
hideScene, // Helper function
|
|
312
|
+
transition // Helper function
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Plugins & Extensions
|
|
317
|
+
|
|
318
|
+
#### .use(plugin)
|
|
319
|
+
|
|
320
|
+
Use a plugin or composable scene builder function.
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
// Plugin function
|
|
324
|
+
const playerModule = (g) => g
|
|
325
|
+
.inScene('game')
|
|
326
|
+
.go({ x: 400, y: 300, name: 'player' })
|
|
327
|
+
.circle({ radius: 25, fill: 'lime' });
|
|
328
|
+
|
|
329
|
+
// Usage
|
|
330
|
+
gcanvas({ bg: 'black' })
|
|
331
|
+
.scene('game')
|
|
332
|
+
.use(playerModule)
|
|
333
|
+
.start();
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Lifecycle
|
|
337
|
+
|
|
338
|
+
#### .start()
|
|
339
|
+
|
|
340
|
+
Start the game loop.
|
|
341
|
+
|
|
342
|
+
#### .stop()
|
|
343
|
+
|
|
344
|
+
Stop the game loop.
|
|
345
|
+
|
|
346
|
+
#### .restart()
|
|
347
|
+
|
|
348
|
+
Restart the game.
|
|
349
|
+
|
|
350
|
+
### Accessors
|
|
351
|
+
|
|
352
|
+
| Property | Type | Description |
|
|
353
|
+
|----------|------|-------------|
|
|
354
|
+
| `.game` | `Game` | Underlying Game instance |
|
|
355
|
+
| `.refs` | `Object` | Named object references |
|
|
356
|
+
| `.scenes` | `Map<string, Scene>` | All scenes |
|
|
357
|
+
| `.canvas` | `HTMLCanvasElement` | Canvas element |
|
|
358
|
+
| `.width` | `number` | Canvas width |
|
|
359
|
+
| `.height` | `number` | Canvas height |
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## FluentScene API
|
|
364
|
+
|
|
365
|
+
Builder for scene operations.
|
|
366
|
+
|
|
367
|
+
### GameObject Creation
|
|
368
|
+
|
|
369
|
+
#### .go(options)
|
|
370
|
+
|
|
371
|
+
Create a GameObject in this scene.
|
|
372
|
+
|
|
373
|
+
```js
|
|
374
|
+
.scene('game')
|
|
375
|
+
.go({ x: 100, y: 100, name: 'player', visible: true })
|
|
376
|
+
.circle({ radius: 20 })
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Options:**
|
|
380
|
+
- `x`, `y` - Position (default: 0, 0)
|
|
381
|
+
- `name` - Name for refs lookup
|
|
382
|
+
- `visible` - Initial visibility (default: true)
|
|
383
|
+
- Any custom properties passed to GameObject
|
|
384
|
+
|
|
385
|
+
**Signatures:**
|
|
386
|
+
- `go()` - Plain GameObject at origin
|
|
387
|
+
- `go(options)` - Plain GameObject with options
|
|
388
|
+
- `go(CustomClass)` - Custom GameObject class
|
|
389
|
+
- `go(CustomClass, options)` - Custom class with options
|
|
390
|
+
- `go(options, builderFn)` - With builder callback
|
|
391
|
+
- `go(CustomClass, options, builderFn)` - Custom class with builder
|
|
392
|
+
|
|
393
|
+
#### .group(name, builderFn)
|
|
394
|
+
|
|
395
|
+
Create multiple GOs with a builder function.
|
|
396
|
+
|
|
397
|
+
```js
|
|
398
|
+
.scene('game')
|
|
399
|
+
.group('enemies', (api) => {
|
|
400
|
+
for (let i = 0; i < 5; i++) {
|
|
401
|
+
api.go({ x: 100 + i * 80, y: 100 })
|
|
402
|
+
.rect({ width: 30, height: 30, fill: 'red' });
|
|
403
|
+
}
|
|
404
|
+
})
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Layer Management
|
|
408
|
+
|
|
409
|
+
#### .layer(name, zIndex)
|
|
410
|
+
|
|
411
|
+
Create a z-indexed layer within the scene.
|
|
412
|
+
|
|
413
|
+
```js
|
|
414
|
+
.scene('game')
|
|
415
|
+
.layer('background', -10)
|
|
416
|
+
.go({ x: 400, y: 300 })
|
|
417
|
+
.rect({ width: 800, height: 600, fill: '#333' })
|
|
418
|
+
.endLayer()
|
|
419
|
+
.layer('foreground', 10)
|
|
420
|
+
.go({ x: 400, y: 300 })
|
|
421
|
+
.circle({ radius: 30, fill: 'lime' })
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Lifecycle Hooks
|
|
425
|
+
|
|
426
|
+
#### .onEnter(handler)
|
|
427
|
+
|
|
428
|
+
Register scene enter callback.
|
|
429
|
+
|
|
430
|
+
```js
|
|
431
|
+
.scene('game', {
|
|
432
|
+
onEnter: (ctx) => console.log('Entered game scene')
|
|
433
|
+
})
|
|
434
|
+
// Or
|
|
435
|
+
.scene('game')
|
|
436
|
+
.onEnter((ctx) => console.log('Entered game scene'))
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### .onExit(handler)
|
|
440
|
+
|
|
441
|
+
Register scene exit callback.
|
|
442
|
+
|
|
443
|
+
### Navigation
|
|
444
|
+
|
|
445
|
+
| Method | Returns | Description |
|
|
446
|
+
|--------|---------|-------------|
|
|
447
|
+
| `.scene(name, opts)` | `FluentScene` | Switch to another scene |
|
|
448
|
+
| `.end()` | `FluentGame` | Return to game context |
|
|
449
|
+
| `.start()` | `FluentGame` | Start the game |
|
|
450
|
+
| `.stop()` | `FluentGame` | Stop the game |
|
|
451
|
+
| `.on(event, handler)` | `FluentGame` | Register event (delegates to game) |
|
|
452
|
+
| `.use(plugin)` | `FluentGame` | Use plugin (delegates to game) |
|
|
453
|
+
| `.state(obj)` | `FluentGame` | Set state (delegates to game) |
|
|
454
|
+
|
|
455
|
+
### Accessors
|
|
456
|
+
|
|
457
|
+
| Property | Type | Description |
|
|
458
|
+
|----------|------|-------------|
|
|
459
|
+
| `.sceneInstance` | `Scene` | Underlying Scene instance |
|
|
460
|
+
| `.refs` | `Object` | Named object references |
|
|
461
|
+
| `.state` | `Object` | Shared state |
|
|
462
|
+
| `.parent` | `FluentGame` | Parent FluentGame |
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## FluentGO API
|
|
467
|
+
|
|
468
|
+
Builder for GameObject operations.
|
|
469
|
+
|
|
470
|
+
### Shape Methods
|
|
471
|
+
|
|
472
|
+
All shape methods accept an options object and return `FluentGO` for chaining.
|
|
473
|
+
|
|
474
|
+
#### Basic Shapes
|
|
475
|
+
|
|
476
|
+
```js
|
|
477
|
+
.circle({ radius: 30, fill: 'red', stroke: 'white', lineWidth: 2 })
|
|
478
|
+
.rect({ width: 100, height: 50, fill: 'blue' })
|
|
479
|
+
.roundRect({ width: 100, height: 50, radius: 10, fill: 'blue' })
|
|
480
|
+
.square({ size: 50, fill: 'green' })
|
|
481
|
+
.triangle({ size: 40, fill: 'yellow' })
|
|
482
|
+
.line({ x2: 100, y2: 50, stroke: 'white', lineWidth: 2 })
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
#### Complex Shapes
|
|
486
|
+
|
|
487
|
+
```js
|
|
488
|
+
.star({ points: 5, radius: 30, innerRadius: 15, fill: 'gold' })
|
|
489
|
+
.hexagon({ radius: 25, fill: 'purple' })
|
|
490
|
+
.diamond({ width: 40, height: 60, fill: 'cyan' })
|
|
491
|
+
.heart({ size: 30, fill: 'red' })
|
|
492
|
+
.arc({ radius: 40, startAngle: 0, endAngle: Math.PI, fill: 'orange' })
|
|
493
|
+
.ring({ innerRadius: 20, outerRadius: 40, fill: 'teal' })
|
|
494
|
+
.arrow({ width: 60, height: 20, fill: 'white' })
|
|
495
|
+
.cross({ size: 30, thickness: 8, fill: 'red' })
|
|
496
|
+
.pin({ radius: 15, fill: 'red' })
|
|
497
|
+
.cloud({ width: 80, height: 40, fill: 'white' })
|
|
498
|
+
.poly({ points: [[0,-30], [30,30], [-30,30]], fill: 'lime' })
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
#### Special Shapes
|
|
502
|
+
|
|
503
|
+
```js
|
|
504
|
+
.text('Hello World', { font: '24px monospace', fill: 'white' })
|
|
505
|
+
.image('/path/to/image.png', { width: 64, height: 64 })
|
|
506
|
+
.svg('M10 10 L90 10 L50 90 Z', { fill: 'red', scale: 0.5 })
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
#### Generic Shape
|
|
510
|
+
|
|
511
|
+
```js
|
|
512
|
+
.add(CustomShapeClass, { /* options */ })
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Common shape options:**
|
|
516
|
+
- `fill` - Fill color (alias for `color`)
|
|
517
|
+
- `stroke` - Stroke color
|
|
518
|
+
- `lineWidth` - Stroke width
|
|
519
|
+
- `opacity` - Shape opacity (0-1)
|
|
520
|
+
|
|
521
|
+
### Motion Methods
|
|
522
|
+
|
|
523
|
+
Built-in motion presets that animate the GameObject.
|
|
524
|
+
|
|
525
|
+
#### .oscillate(opts)
|
|
526
|
+
|
|
527
|
+
Oscillate a property between min and max values.
|
|
528
|
+
|
|
529
|
+
```js
|
|
530
|
+
.oscillate({
|
|
531
|
+
prop: 'y', // Property to animate (default: 'y')
|
|
532
|
+
min: -50, // Minimum offset (default: -50)
|
|
533
|
+
max: 50, // Maximum offset (default: 50)
|
|
534
|
+
duration: 2 // Duration in seconds (default: 2)
|
|
535
|
+
})
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
#### .pulse(opts)
|
|
539
|
+
|
|
540
|
+
Pulse scale between min and max.
|
|
541
|
+
|
|
542
|
+
```js
|
|
543
|
+
.pulse({
|
|
544
|
+
prop: 'scale', // Property (default: 'scale')
|
|
545
|
+
min: 0.8, // Minimum value (default: 0.8)
|
|
546
|
+
max: 1.2, // Maximum value (default: 1.2)
|
|
547
|
+
duration: 1 // Duration (default: 1)
|
|
548
|
+
})
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
#### .orbit(opts)
|
|
552
|
+
|
|
553
|
+
Orbit around a center point.
|
|
554
|
+
|
|
555
|
+
```js
|
|
556
|
+
.orbit({
|
|
557
|
+
centerX: 400, // Orbit center X (default: base position)
|
|
558
|
+
centerY: 300, // Orbit center Y
|
|
559
|
+
radiusX: 100, // X radius (default: 100)
|
|
560
|
+
radiusY: 100, // Y radius (default: 100)
|
|
561
|
+
duration: 3, // Orbit period (default: 3)
|
|
562
|
+
clockwise: true // Direction (default: true)
|
|
563
|
+
})
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
#### .float(opts)
|
|
567
|
+
|
|
568
|
+
Random wandering motion.
|
|
569
|
+
|
|
570
|
+
```js
|
|
571
|
+
.float({
|
|
572
|
+
radius: 20, // Float radius (default: 20)
|
|
573
|
+
speed: 0.5, // Float speed (default: 0.5)
|
|
574
|
+
randomness: 0.3, // Randomness factor (default: 0.3)
|
|
575
|
+
duration: 5 // Duration (default: 5)
|
|
576
|
+
})
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
#### .shake(opts)
|
|
580
|
+
|
|
581
|
+
Shake effect.
|
|
582
|
+
|
|
583
|
+
```js
|
|
584
|
+
.shake({
|
|
585
|
+
intensity: 5, // Shake intensity (default: 5)
|
|
586
|
+
frequency: 20, // Shake frequency (default: 20)
|
|
587
|
+
decay: 0.9, // Decay factor (default: 0.9)
|
|
588
|
+
duration: 0.5 // Duration (default: 0.5)
|
|
589
|
+
})
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
#### .bounce(opts)
|
|
593
|
+
|
|
594
|
+
Bouncing motion.
|
|
595
|
+
|
|
596
|
+
```js
|
|
597
|
+
.bounce({
|
|
598
|
+
height: 100, // Bounce height (default: 100)
|
|
599
|
+
bounces: 3, // Number of bounces (default: 3)
|
|
600
|
+
duration: 2 // Duration (default: 2)
|
|
601
|
+
})
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
#### .spring(opts)
|
|
605
|
+
|
|
606
|
+
Spring physics motion.
|
|
607
|
+
|
|
608
|
+
#### .spiral(opts)
|
|
609
|
+
|
|
610
|
+
Spiral outward or inward.
|
|
611
|
+
|
|
612
|
+
```js
|
|
613
|
+
.spiral({
|
|
614
|
+
startRadius: 50, // Starting radius (default: 50)
|
|
615
|
+
endRadius: 150, // Ending radius (default: 150)
|
|
616
|
+
revolutions: 3, // Number of revolutions (default: 3)
|
|
617
|
+
duration: 4 // Duration (default: 4)
|
|
618
|
+
})
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
#### .pendulum(opts)
|
|
622
|
+
|
|
623
|
+
Pendulum swing motion.
|
|
624
|
+
|
|
625
|
+
```js
|
|
626
|
+
.pendulum({
|
|
627
|
+
amplitude: 45, // Swing amplitude in degrees (default: 45)
|
|
628
|
+
duration: 2, // Period (default: 2)
|
|
629
|
+
damped: false // Apply damping (default: false)
|
|
630
|
+
})
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
#### .waypoint(opts)
|
|
634
|
+
|
|
635
|
+
Move between waypoints.
|
|
636
|
+
|
|
637
|
+
```js
|
|
638
|
+
.waypoint({
|
|
639
|
+
waypoints: [
|
|
640
|
+
{ x: 100, y: 100 },
|
|
641
|
+
{ x: 300, y: 100 },
|
|
642
|
+
{ x: 300, y: 300 },
|
|
643
|
+
{ x: 100, y: 300 }
|
|
644
|
+
],
|
|
645
|
+
speed: 100, // Movement speed (default: 100)
|
|
646
|
+
waitTime: 0 // Wait time at each point (default: 0)
|
|
647
|
+
})
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
#### .motion(type, opts)
|
|
651
|
+
|
|
652
|
+
Generic motion by type name.
|
|
653
|
+
|
|
654
|
+
```js
|
|
655
|
+
.motion('oscillate', { prop: 'x', min: -30, max: 30 })
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### Tween
|
|
659
|
+
|
|
660
|
+
#### .tween(props, opts)
|
|
661
|
+
|
|
662
|
+
Tween properties over time.
|
|
663
|
+
|
|
664
|
+
```js
|
|
665
|
+
.tween({ x: 500, y: 400, opacity: 0.5 }, {
|
|
666
|
+
duration: 1, // Duration in seconds (default: 1)
|
|
667
|
+
easing: 'easeOutQuad', // Easing function (default: 'easeOutQuad')
|
|
668
|
+
delay: 0, // Delay before starting (default: 0)
|
|
669
|
+
onComplete: () => {} // Completion callback
|
|
670
|
+
})
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### Transform Shortcuts
|
|
674
|
+
|
|
675
|
+
```js
|
|
676
|
+
.pos(x, y) // Set position
|
|
677
|
+
.scale(sx, sy) // Set scale (sy defaults to sx)
|
|
678
|
+
.rotate(degrees) // Set rotation in degrees
|
|
679
|
+
.opacity(value) // Set opacity (0-1)
|
|
680
|
+
.zIndex(value) // Set z-index
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### Child GameObjects
|
|
684
|
+
|
|
685
|
+
#### .child(opts, builderFn)
|
|
686
|
+
|
|
687
|
+
Create a child GameObject.
|
|
688
|
+
|
|
689
|
+
```js
|
|
690
|
+
.go({ x: 400, y: 300, name: 'parent' })
|
|
691
|
+
.circle({ radius: 50, fill: 'blue' })
|
|
692
|
+
.child({ x: 30, y: 0 }) // Position relative to parent
|
|
693
|
+
.circle({ radius: 10, fill: 'red' })
|
|
694
|
+
.end() // Back to parent
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**Signatures:**
|
|
698
|
+
- `child()` - Plain child GO
|
|
699
|
+
- `child(options)` - Plain child with options
|
|
700
|
+
- `child(CustomClass)` - Custom child class
|
|
701
|
+
- `child(CustomClass, options)` - Custom class with options
|
|
702
|
+
- `child(options, builderFn)` - With builder callback
|
|
703
|
+
- `child(CustomClass, options, builderFn)` - Custom class with builder
|
|
704
|
+
|
|
705
|
+
### Events
|
|
706
|
+
|
|
707
|
+
#### .on(event, handler)
|
|
708
|
+
|
|
709
|
+
Register event handler on this GO.
|
|
710
|
+
|
|
711
|
+
```js
|
|
712
|
+
.go({ x: 400, y: 300 })
|
|
713
|
+
.circle({ radius: 30 })
|
|
714
|
+
.on('click', (ctx, event) => {
|
|
715
|
+
console.log('Clicked!', ctx.go);
|
|
716
|
+
})
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
#### .update(fn)
|
|
720
|
+
|
|
721
|
+
Custom update function for this GO.
|
|
722
|
+
|
|
723
|
+
```js
|
|
724
|
+
.go({ x: 400, y: 300 })
|
|
725
|
+
.circle({ radius: 30 })
|
|
726
|
+
.update((dt, ctx) => {
|
|
727
|
+
ctx.go.rotation += dt * 2;
|
|
728
|
+
})
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**Update context:**
|
|
732
|
+
```js
|
|
733
|
+
{
|
|
734
|
+
go, // This GameObject
|
|
735
|
+
shapes, // Shapes added to this GO
|
|
736
|
+
refs, // Named object references
|
|
737
|
+
state // Shared state
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### Navigation
|
|
742
|
+
|
|
743
|
+
| Method | Returns | Description |
|
|
744
|
+
|--------|---------|-------------|
|
|
745
|
+
| `.end()` | Parent context | Navigate back to parent |
|
|
746
|
+
| `.go(opts)` | `FluentGO` | Create sibling GO |
|
|
747
|
+
| `.scene(name, opts)` | `FluentScene` | Switch to scene |
|
|
748
|
+
| `.start()` | `FluentGame` | Start the game |
|
|
749
|
+
|
|
750
|
+
### Accessors
|
|
751
|
+
|
|
752
|
+
| Property | Type | Description |
|
|
753
|
+
|----------|------|-------------|
|
|
754
|
+
| `.goInstance` | `GameObject` | Underlying GameObject |
|
|
755
|
+
| `.shapes` | `Array` | Shapes added to this GO |
|
|
756
|
+
| `.refs` | `Object` | Named object references |
|
|
757
|
+
| `.state` | `Object` | Shared state |
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
## FluentLayer API
|
|
762
|
+
|
|
763
|
+
Builder for z-indexed layers within a scene.
|
|
764
|
+
|
|
765
|
+
### Methods
|
|
766
|
+
|
|
767
|
+
```js
|
|
768
|
+
.layer('foreground', 10)
|
|
769
|
+
.go({ x: 100, y: 100 })
|
|
770
|
+
.circle({ radius: 20 })
|
|
771
|
+
.visible(true) // Set layer visibility
|
|
772
|
+
.opacity(0.8) // Set layer opacity
|
|
773
|
+
.endLayer() // Return to scene
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
| Method | Returns | Description |
|
|
777
|
+
|--------|---------|-------------|
|
|
778
|
+
| `.go(opts, builderFn)` | `FluentGO` or `FluentLayer` | Create GO in layer |
|
|
779
|
+
| `.visible(bool)` | `FluentLayer` | Set layer visibility |
|
|
780
|
+
| `.opacity(value)` | `FluentLayer` | Set layer opacity |
|
|
781
|
+
| `.endLayer()` | `FluentScene` | Return to scene |
|
|
782
|
+
| `.end()` | `FluentScene` | Return to scene |
|
|
783
|
+
| `.scene(name, opts)` | `FluentScene` | Switch to scene |
|
|
784
|
+
| `.start()` | `FluentGame` | Start the game |
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
## Sketch Mode API
|
|
789
|
+
|
|
790
|
+
Ultra-simple API for quick creative coding.
|
|
791
|
+
|
|
792
|
+
### Shapes
|
|
793
|
+
|
|
794
|
+
All shape methods take positional arguments and return the sketch API for chaining.
|
|
795
|
+
|
|
796
|
+
```js
|
|
797
|
+
sketch(800, 600, 'black')
|
|
798
|
+
.circle(x, y, radius, fill)
|
|
799
|
+
.rect(x, y, width, height, fill)
|
|
800
|
+
.square(x, y, size, fill)
|
|
801
|
+
.star(x, y, points, radius, fill)
|
|
802
|
+
.triangle(x, y, size, fill)
|
|
803
|
+
.hexagon(x, y, radius, fill)
|
|
804
|
+
.line(x1, y1, x2, y2, stroke, lineWidth)
|
|
805
|
+
.ring(x, y, innerRadius, outerRadius, fill)
|
|
806
|
+
.text(content, x, y, { fill, font })
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### Bulk Creation
|
|
810
|
+
|
|
811
|
+
#### .grid(cols, rows, spacing, fn)
|
|
812
|
+
|
|
813
|
+
Create a grid of shapes.
|
|
814
|
+
|
|
815
|
+
```js
|
|
816
|
+
sketch(800, 600, 'black')
|
|
817
|
+
.grid(10, 10, 80, (api, x, y, col, row) => {
|
|
818
|
+
api.circle(x, y, 20, `hsl(${(col + row) * 20}, 70%, 60%)`);
|
|
819
|
+
})
|
|
820
|
+
.start();
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
#### .repeat(count, fn)
|
|
824
|
+
|
|
825
|
+
Repeat shape creation.
|
|
826
|
+
|
|
827
|
+
```js
|
|
828
|
+
sketch(800, 600, 'black')
|
|
829
|
+
.repeat(20, (api, i, total) => {
|
|
830
|
+
const angle = (i / total) * Math.PI * 2;
|
|
831
|
+
const x = 400 + Math.cos(angle) * 200;
|
|
832
|
+
const y = 300 + Math.sin(angle) * 200;
|
|
833
|
+
api.circle(x, y, 15, `hsl(${i * 18}, 70%, 60%)`);
|
|
834
|
+
})
|
|
835
|
+
.start();
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
#### .radial(cx, cy, radius, count, fn)
|
|
839
|
+
|
|
840
|
+
Create shapes in a circular pattern.
|
|
841
|
+
|
|
842
|
+
```js
|
|
843
|
+
sketch(800, 600, 'black')
|
|
844
|
+
.radial(400, 300, 150, 12, (api, x, y, angle, i) => {
|
|
845
|
+
api.star(x, y, 5, 20, `hsl(${i * 30}, 70%, 60%)`);
|
|
846
|
+
})
|
|
847
|
+
.start();
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
### Lifecycle
|
|
851
|
+
|
|
852
|
+
#### .setup(fn)
|
|
853
|
+
|
|
854
|
+
Register a setup function called once before start.
|
|
855
|
+
|
|
856
|
+
```js
|
|
857
|
+
.setup((api) => {
|
|
858
|
+
// Initialize shapes
|
|
859
|
+
})
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
#### .update(fn)
|
|
863
|
+
|
|
864
|
+
Register an update function called every frame.
|
|
865
|
+
|
|
866
|
+
```js
|
|
867
|
+
.update((dt, ctx) => {
|
|
868
|
+
// ctx.shapes - array of GameObjects
|
|
869
|
+
// ctx.time - elapsed time in seconds
|
|
870
|
+
// ctx.frame - current frame number
|
|
871
|
+
// ctx.width, ctx.height - canvas dimensions
|
|
872
|
+
// ctx.mouse.x, ctx.mouse.y - mouse position
|
|
873
|
+
// ctx.refs - named objects
|
|
874
|
+
// ctx.game - Game instance
|
|
875
|
+
})
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
#### .start()
|
|
879
|
+
|
|
880
|
+
Start the sketch.
|
|
881
|
+
|
|
882
|
+
### Properties
|
|
883
|
+
|
|
884
|
+
| Property | Type | Description |
|
|
885
|
+
|----------|------|-------------|
|
|
886
|
+
| `.width` | `number` | Canvas width |
|
|
887
|
+
| `.height` | `number` | Canvas height |
|
|
888
|
+
| `.game` | `FluentGame` | Underlying FluentGame (after start) |
|
|
889
|
+
|
|
890
|
+
---
|
|
891
|
+
|
|
892
|
+
## Advanced Patterns
|
|
893
|
+
|
|
894
|
+
### Class Injection
|
|
895
|
+
|
|
896
|
+
Use custom Scene or GameObject classes with the fluent API.
|
|
897
|
+
|
|
898
|
+
```js
|
|
899
|
+
// Custom Scene class
|
|
900
|
+
class GameScene extends Scene {
|
|
901
|
+
init() {
|
|
902
|
+
this.spawnTimer = 0;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
update(dt) {
|
|
906
|
+
super.update(dt);
|
|
907
|
+
this.spawnTimer += dt;
|
|
908
|
+
if (this.spawnTimer > 1) {
|
|
909
|
+
this.spawnEnemy();
|
|
910
|
+
this.spawnTimer = 0;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
spawnEnemy() { /* ... */ }
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Custom GameObject class
|
|
918
|
+
class Player extends GameObject {
|
|
919
|
+
constructor(opts) {
|
|
920
|
+
super(opts);
|
|
921
|
+
this.speed = 200;
|
|
922
|
+
this.health = 100;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
update(dt) {
|
|
926
|
+
super.update(dt);
|
|
927
|
+
// Custom update logic
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// Usage
|
|
932
|
+
gcanvas({ bg: 'black' })
|
|
933
|
+
.scene('game', GameScene)
|
|
934
|
+
.go(Player, { x: 400, y: 500, name: 'player' })
|
|
935
|
+
.circle({ radius: 25, fill: 'lime' })
|
|
936
|
+
.start();
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
### Composable Scene Modules
|
|
940
|
+
|
|
941
|
+
Create reusable scene builder functions.
|
|
942
|
+
|
|
943
|
+
```js
|
|
944
|
+
// scenes/player.js
|
|
945
|
+
export const playerModule = (g) => g
|
|
946
|
+
.inScene('game')
|
|
947
|
+
.go({ x: 400, y: 500, name: 'player' })
|
|
948
|
+
.circle({ radius: 25, fill: '#00ff88' })
|
|
949
|
+
.on('update', (ctx) => {
|
|
950
|
+
// Player update logic
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
// scenes/enemies.js
|
|
954
|
+
export const enemiesModule = (g) => g
|
|
955
|
+
.inScene('game')
|
|
956
|
+
.group('enemies', (api) => {
|
|
957
|
+
for (let i = 0; i < 5; i++) {
|
|
958
|
+
api.go({ x: 100 + i * 150, y: 100, name: `enemy_${i}` })
|
|
959
|
+
.rect({ width: 40, height: 40, fill: 'red' });
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
// scenes/ui.js
|
|
964
|
+
export const uiModule = (g) => g
|
|
965
|
+
.scene('ui', { zIndex: 100 })
|
|
966
|
+
.go({ x: 20, y: 20, name: 'scoreText' })
|
|
967
|
+
.text('SCORE: 0', { font: '20px monospace', fill: 'white' });
|
|
968
|
+
|
|
969
|
+
// main.js
|
|
970
|
+
import { gcanvas } from 'gcanvas';
|
|
971
|
+
import { playerModule } from './scenes/player';
|
|
972
|
+
import { enemiesModule } from './scenes/enemies';
|
|
973
|
+
import { uiModule } from './scenes/ui';
|
|
974
|
+
|
|
975
|
+
gcanvas({ bg: 'black' })
|
|
976
|
+
.scene('game')
|
|
977
|
+
.use(playerModule)
|
|
978
|
+
.use(enemiesModule)
|
|
979
|
+
.use(uiModule)
|
|
980
|
+
.on('update', (dt, ctx) => {
|
|
981
|
+
// Main game loop
|
|
982
|
+
})
|
|
983
|
+
.start();
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
### State Management
|
|
987
|
+
|
|
988
|
+
```js
|
|
989
|
+
gcanvas({ bg: 'black' })
|
|
990
|
+
// Initialize state
|
|
991
|
+
.state({
|
|
992
|
+
score: 0,
|
|
993
|
+
lives: 3,
|
|
994
|
+
level: 1,
|
|
995
|
+
paused: false
|
|
996
|
+
})
|
|
997
|
+
.scene('game')
|
|
998
|
+
.go({ x: 20, y: 20, name: 'scoreText' })
|
|
999
|
+
.text('Score: 0', { fill: 'white' })
|
|
1000
|
+
.update((dt, ctx) => {
|
|
1001
|
+
// Update text based on state
|
|
1002
|
+
const score = ctx.state.score;
|
|
1003
|
+
ctx.shapes[0].text = `Score: ${score}`;
|
|
1004
|
+
})
|
|
1005
|
+
.on('update', (dt, ctx) => {
|
|
1006
|
+
// Increment score
|
|
1007
|
+
ctx.state.score += dt * 10;
|
|
1008
|
+
})
|
|
1009
|
+
.on('keydown:p', (ctx) => {
|
|
1010
|
+
ctx.state.paused = !ctx.state.paused;
|
|
1011
|
+
})
|
|
1012
|
+
.start();
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
---
|
|
1016
|
+
|
|
1017
|
+
## Context Object Reference
|
|
1018
|
+
|
|
1019
|
+
Event handlers receive a context object with the following properties:
|
|
1020
|
+
|
|
1021
|
+
### Game-level context (.on handlers)
|
|
1022
|
+
|
|
1023
|
+
```js
|
|
1024
|
+
{
|
|
1025
|
+
refs, // Named object references (objects with name prop)
|
|
1026
|
+
state, // Shared state object
|
|
1027
|
+
scenes, // All scenes as { name: Scene }
|
|
1028
|
+
game, // Underlying Game instance
|
|
1029
|
+
width, // Canvas width
|
|
1030
|
+
height, // Canvas height
|
|
1031
|
+
showScene, // (name) => void - Show a scene
|
|
1032
|
+
hideScene, // (name) => void - Hide a scene
|
|
1033
|
+
transition // (from, to, opts) => void - Transition scenes
|
|
1034
|
+
}
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
### GO-level context (.update handlers)
|
|
1038
|
+
|
|
1039
|
+
```js
|
|
1040
|
+
{
|
|
1041
|
+
go, // This GameObject instance
|
|
1042
|
+
shapes, // Array of shapes on this GO
|
|
1043
|
+
refs, // Named object references
|
|
1044
|
+
state // Shared state object
|
|
1045
|
+
}
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
### Sketch context (.update handlers)
|
|
1049
|
+
|
|
1050
|
+
```js
|
|
1051
|
+
{
|
|
1052
|
+
shapes, // Array of GameObjects (one per shape)
|
|
1053
|
+
time, // Elapsed time in seconds
|
|
1054
|
+
frame, // Current frame number
|
|
1055
|
+
width, // Canvas width
|
|
1056
|
+
height, // Canvas height
|
|
1057
|
+
mouse, // { x, y } mouse position
|
|
1058
|
+
refs, // Named objects
|
|
1059
|
+
game // Underlying Game instance
|
|
1060
|
+
}
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
---
|
|
1064
|
+
|
|
1065
|
+
## Related
|
|
1066
|
+
|
|
1067
|
+
- [Game Module](../game/README.md) - Game loop and GameObjects
|
|
1068
|
+
- [Shapes Module](../shapes/README.md) - Shape classes and hierarchy
|
|
1069
|
+
- [Motion Module](../motion/README.md) - Animation functions
|
|
1070
|
+
- [Collision Module](../collision/README.md) - Collision detection
|
|
1071
|
+
|
|
1072
|
+
## See Also
|
|
1073
|
+
|
|
1074
|
+
- [Fluent API Demo](../../demos/fluent.html) - Interactive examples
|
|
1075
|
+
- [Space Invaders](../../demos/space.html) - Full game using fluent API
|