@energy8platform/game-engine 0.2.0 → 0.3.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/README.md +318 -49
- package/dist/animation.cjs.js +191 -1
- package/dist/animation.cjs.js.map +1 -1
- package/dist/animation.d.ts +117 -1
- package/dist/animation.esm.js +192 -3
- package/dist/animation.esm.js.map +1 -1
- package/dist/audio.cjs.js +66 -16
- package/dist/audio.cjs.js.map +1 -1
- package/dist/audio.d.ts +4 -0
- package/dist/audio.esm.js +66 -16
- package/dist/audio.esm.js.map +1 -1
- package/dist/core.cjs.js +310 -84
- package/dist/core.cjs.js.map +1 -1
- package/dist/core.d.ts +60 -1
- package/dist/core.esm.js +311 -85
- package/dist/core.esm.js.map +1 -1
- package/dist/debug.cjs.js +36 -68
- package/dist/debug.cjs.js.map +1 -1
- package/dist/debug.d.ts +4 -6
- package/dist/debug.esm.js +36 -68
- package/dist/debug.esm.js.map +1 -1
- package/dist/index.cjs.js +1250 -251
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +386 -41
- package/dist/index.esm.js +1250 -254
- package/dist/index.esm.js.map +1 -1
- package/dist/ui.cjs.js +757 -1
- package/dist/ui.cjs.js.map +1 -1
- package/dist/ui.d.ts +208 -2
- package/dist/ui.esm.js +756 -2
- package/dist/ui.esm.js.map +1 -1
- package/dist/vite.cjs.js +65 -68
- package/dist/vite.cjs.js.map +1 -1
- package/dist/vite.d.ts +17 -23
- package/dist/vite.esm.js +66 -68
- package/dist/vite.esm.js.map +1 -1
- package/package.json +4 -5
- package/src/animation/SpriteAnimation.ts +210 -0
- package/src/animation/Tween.ts +27 -1
- package/src/animation/index.ts +2 -0
- package/src/audio/AudioManager.ts +64 -15
- package/src/core/EventEmitter.ts +7 -1
- package/src/core/GameApplication.ts +25 -7
- package/src/core/SceneManager.ts +3 -1
- package/src/debug/DevBridge.ts +49 -80
- package/src/index.ts +6 -0
- package/src/input/InputManager.ts +26 -0
- package/src/loading/CSSPreloader.ts +7 -33
- package/src/loading/LoadingScene.ts +17 -41
- package/src/loading/index.ts +1 -0
- package/src/loading/logo.ts +95 -0
- package/src/types.ts +4 -0
- package/src/ui/BalanceDisplay.ts +14 -0
- package/src/ui/Button.ts +1 -1
- package/src/ui/Layout.ts +364 -0
- package/src/ui/ScrollContainer.ts +557 -0
- package/src/ui/index.ts +4 -0
- package/src/viewport/ViewportManager.ts +2 -0
- package/src/vite/index.ts +83 -83
package/README.md
CHANGED
|
@@ -17,7 +17,21 @@ A universal casino game engine built on [PixiJS v8](https://pixijs.com/) and [@e
|
|
|
17
17
|
- [Viewport & Scaling](#viewport--scaling)
|
|
18
18
|
- [State Machine](#state-machine)
|
|
19
19
|
- [Animation](#animation)
|
|
20
|
+
- [Tween](#tween)
|
|
21
|
+
- [Timeline](#timeline)
|
|
22
|
+
- [SpriteAnimation](#spriteanimation)
|
|
23
|
+
- [Spine Animations](#spine-animations)
|
|
20
24
|
- [UI Components](#ui-components)
|
|
25
|
+
- [Layout](#layout)
|
|
26
|
+
- [ScrollContainer](#scrollcontainer)
|
|
27
|
+
- [Button](#button)
|
|
28
|
+
- [Label](#label)
|
|
29
|
+
- [BalanceDisplay](#balancedisplay)
|
|
30
|
+
- [WinDisplay](#windisplay)
|
|
31
|
+
- [ProgressBar](#progressbar)
|
|
32
|
+
- [Panel](#panel)
|
|
33
|
+
- [Modal](#modal)
|
|
34
|
+
- [Toast](#toast)
|
|
21
35
|
- [Input](#input)
|
|
22
36
|
- [Vite Configuration](#vite-configuration)
|
|
23
37
|
- [DevBridge](#devbridge)
|
|
@@ -83,7 +97,7 @@ async function bootstrap() {
|
|
|
83
97
|
debug: true,
|
|
84
98
|
});
|
|
85
99
|
|
|
86
|
-
game.scenes.register('game', GameScene
|
|
100
|
+
game.scenes.register('game', GameScene);
|
|
87
101
|
|
|
88
102
|
game.on('initialized', () => console.log('Engine initialized'));
|
|
89
103
|
game.on('loaded', () => console.log('Assets loaded'));
|
|
@@ -105,7 +119,7 @@ bootstrap();
|
|
|
105
119
|
| Package | Version | Required |
|
|
106
120
|
| --- | --- | --- |
|
|
107
121
|
| `pixi.js` | `^8.16.0` | Yes |
|
|
108
|
-
| `@energy8platform/game-sdk` | `^2.
|
|
122
|
+
| `@energy8platform/game-sdk` | `^2.6.0` | Yes |
|
|
109
123
|
| `@pixi/sound` | `^6.0.0` | Optional — for audio |
|
|
110
124
|
| `@esotericsoftware/spine-pixi-v8` | `~4.2.0` | Optional — for Spine animations |
|
|
111
125
|
|
|
@@ -118,8 +132,8 @@ import { GameApplication } from '@energy8platform/game-engine'; // full b
|
|
|
118
132
|
import { Scene, SceneManager } from '@energy8platform/game-engine/core';
|
|
119
133
|
import { AssetManager } from '@energy8platform/game-engine/assets';
|
|
120
134
|
import { AudioManager } from '@energy8platform/game-engine/audio';
|
|
121
|
-
import { Button, Label, Modal } from '@energy8platform/game-engine/ui';
|
|
122
|
-
import { Tween, Timeline, Easing } from '@energy8platform/game-engine/animation';
|
|
135
|
+
import { Button, Label, Modal, Layout, ScrollContainer } from '@energy8platform/game-engine/ui';
|
|
136
|
+
import { Tween, Timeline, Easing, SpriteAnimation } from '@energy8platform/game-engine/animation';
|
|
123
137
|
import { DevBridge, FPSOverlay } from '@energy8platform/game-engine/debug';
|
|
124
138
|
import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
125
139
|
```
|
|
@@ -146,7 +160,7 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
|
146
160
|
|
|
147
161
|
1. **CSS Preloader** — an instant HTML/CSS overlay shown while PixiJS initializes (inline SVG logo with a shimmer animation and "Loading..." text).
|
|
148
162
|
2. **PixiJS initialization** — creates `Application`, initializes `ResizeObserver`.
|
|
149
|
-
3. **SDK handshake** — connects to the casino host (or DevBridge in dev mode).
|
|
163
|
+
3. **SDK handshake** — connects to the casino host (or DevBridge in dev mode via shared `MemoryChannel`).
|
|
150
164
|
4. **Canvas Loading Screen** — `LoadingScene` displays the SVG logo with an animated progress bar, `preload` bundle is loaded first.
|
|
151
165
|
5. **Asset loading** — remaining bundles are loaded; the progress bar fills in real time.
|
|
152
166
|
6. **Tap-to-start** — optional screen shown after loading (required on mobile for audio unlock).
|
|
@@ -169,6 +183,10 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
|
169
183
|
| `manifest` | `AssetManifest` | — | Asset manifest |
|
|
170
184
|
| `audio` | `AudioConfig` | — | Audio configuration |
|
|
171
185
|
| `sdk` | `object \| false` | — | SDK options; `false` to disable |
|
|
186
|
+
| `sdk.devMode` | `boolean` | `false` | Use in-memory channel instead of `postMessage` (no iframe needed) |
|
|
187
|
+
| `sdk.parentOrigin` | `string` | — | Expected parent origin for `postMessage` validation |
|
|
188
|
+
| `sdk.timeout` | `number` | — | SDK handshake timeout in ms |
|
|
189
|
+
| `sdk.debug` | `boolean` | — | Enable SDK debug logging |
|
|
172
190
|
| `pixi` | `Partial<ApplicationOptions>` | — | PixiJS pass-through options |
|
|
173
191
|
| `debug` | `boolean` | `false` | Enable FPS overlay |
|
|
174
192
|
|
|
@@ -182,21 +200,21 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
|
182
200
|
| `logoScale` | `number` | `1` | Logo scale factor |
|
|
183
201
|
| `showPercentage` | `boolean` | `true` | Show loading percentage text |
|
|
184
202
|
| `progressTextFormatter` | `(progress: number) => string` | — | Custom progress text formatter |
|
|
185
|
-
| `tapToStart` | `boolean` | `
|
|
203
|
+
| `tapToStart` | `boolean` | `true` | Show "Tap to start" overlay |
|
|
186
204
|
| `tapToStartText` | `string` | `'TAP TO START'` | Tap-to-start label |
|
|
187
|
-
| `minDisplayTime` | `number` | `
|
|
205
|
+
| `minDisplayTime` | `number` | `1500` | Minimum display time (ms) |
|
|
188
206
|
| `cssPreloaderHTML` | `string` | — | Custom HTML for the CSS preloader |
|
|
189
207
|
|
|
190
208
|
### `AudioConfig`
|
|
191
209
|
|
|
192
210
|
| Property | Type | Default | Description |
|
|
193
211
|
| --- | --- | --- | --- |
|
|
194
|
-
| `music` | `number` | `
|
|
212
|
+
| `music` | `number` | `0.7` | Default music volume (0–1) |
|
|
195
213
|
| `sfx` | `number` | `1` | Default SFX volume |
|
|
196
|
-
| `ui` | `number` | `
|
|
197
|
-
| `ambient` | `number` | `
|
|
198
|
-
| `persist` | `boolean` | `
|
|
199
|
-
| `storageKey` | `string` | `'
|
|
214
|
+
| `ui` | `number` | `0.8` | Default UI sounds volume |
|
|
215
|
+
| `ambient` | `number` | `0.5` | Default ambient volume |
|
|
216
|
+
| `persist` | `boolean` | `true` | Save mute state to localStorage |
|
|
217
|
+
| `storageKey` | `string` | `'ge_audio'` | localStorage key prefix |
|
|
200
218
|
|
|
201
219
|
### Enums
|
|
202
220
|
|
|
@@ -261,9 +279,9 @@ export class GameScene extends Scene {
|
|
|
261
279
|
const scenes = game.scenes;
|
|
262
280
|
|
|
263
281
|
// Register scenes
|
|
264
|
-
scenes.register('menu', MenuScene
|
|
265
|
-
scenes.register('game', GameScene
|
|
266
|
-
scenes.register('bonus', BonusScene
|
|
282
|
+
scenes.register('menu', MenuScene);
|
|
283
|
+
scenes.register('game', GameScene);
|
|
284
|
+
scenes.register('bonus', BonusScene);
|
|
267
285
|
|
|
268
286
|
// Navigate (replaces entire stack)
|
|
269
287
|
await scenes.goto('game');
|
|
@@ -400,7 +418,7 @@ const audio = game.audio;
|
|
|
400
418
|
audio.play('click', 'ui');
|
|
401
419
|
audio.play('coin-drop', 'sfx', { volume: 0.8 });
|
|
402
420
|
|
|
403
|
-
// Music with crossfade
|
|
421
|
+
// Music with crossfade (smooth volume transition between tracks)
|
|
404
422
|
audio.playMusic('main-theme', 1000); // 1s crossfade
|
|
405
423
|
audio.stopMusic();
|
|
406
424
|
|
|
@@ -542,13 +560,17 @@ await Tween.from(sprite, { scale: 0 }, 300, Easing.easeOutBack);
|
|
|
542
560
|
// Animate between two sets of values
|
|
543
561
|
await Tween.fromTo(sprite, { x: -100 }, { x: 500 }, 1000, Easing.easeInOutQuad);
|
|
544
562
|
|
|
545
|
-
// Wait (
|
|
563
|
+
// Wait (uses PixiJS Ticker for consistent timing)
|
|
546
564
|
await Tween.delay(1000);
|
|
547
565
|
|
|
548
566
|
// Cancel tweens
|
|
549
567
|
Tween.killTweensOf(sprite);
|
|
550
568
|
Tween.killAll();
|
|
551
569
|
|
|
570
|
+
// Full reset — kill all tweens and remove ticker listener
|
|
571
|
+
// Useful for cleanup between game instances, tests, or hot-reload
|
|
572
|
+
Tween.reset();
|
|
573
|
+
|
|
552
574
|
// Supports nested properties
|
|
553
575
|
await Tween.to(sprite, { 'scale.x': 2, 'position.y': 300 }, 500);
|
|
554
576
|
```
|
|
@@ -592,6 +614,59 @@ All 24 easing functions:
|
|
|
592
614
|
| | | | `easeOutElastic` |
|
|
593
615
|
| | | | `easeInElastic` |
|
|
594
616
|
|
|
617
|
+
### SpriteAnimation
|
|
618
|
+
|
|
619
|
+
Frame-based animation helper wrapping PixiJS `AnimatedSprite`. Cheaper than Spine for simple frame sequences — perfect for coin showers, symbol animations, sparkle trails, and win celebrations.
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
import { SpriteAnimation } from '@energy8platform/game-engine';
|
|
623
|
+
import { Assets } from 'pixi.js';
|
|
624
|
+
|
|
625
|
+
// From an array of textures
|
|
626
|
+
const coinAnim = SpriteAnimation.create(coinTextures, {
|
|
627
|
+
fps: 30,
|
|
628
|
+
loop: true,
|
|
629
|
+
});
|
|
630
|
+
scene.container.addChild(coinAnim);
|
|
631
|
+
|
|
632
|
+
// From a spritesheet using a name prefix
|
|
633
|
+
const sheet = Assets.get('effects');
|
|
634
|
+
const sparkle = SpriteAnimation.fromSpritesheet(sheet, 'sparkle_', {
|
|
635
|
+
fps: 24,
|
|
636
|
+
loop: true,
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
// From a numbered range (e.g., 'explosion_00' to 'explosion_24')
|
|
640
|
+
const explosion = SpriteAnimation.fromRange(sheet, 'explosion_{i}', 0, 24, {
|
|
641
|
+
fps: 60,
|
|
642
|
+
loop: false,
|
|
643
|
+
onComplete: () => explosion.destroy(),
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// From pre-loaded texture aliases
|
|
647
|
+
const anim = SpriteAnimation.fromAliases(
|
|
648
|
+
['frame_0', 'frame_1', 'frame_2', 'frame_3'],
|
|
649
|
+
{ fps: 12 },
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
// Fire-and-forget: play once and auto-destroy
|
|
653
|
+
const { sprite, finished } = SpriteAnimation.playOnce(coinTextures, {
|
|
654
|
+
fps: 30,
|
|
655
|
+
});
|
|
656
|
+
scene.container.addChild(sprite);
|
|
657
|
+
await finished; // resolves when animation completes
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
#### SpriteAnimationConfig
|
|
661
|
+
|
|
662
|
+
| Property | Type | Default | Description |
|
|
663
|
+
| --- | --- | --- | --- |
|
|
664
|
+
| `fps` | `number` | `24` | Frames per second |
|
|
665
|
+
| `loop` | `boolean` | `true` | Whether to loop |
|
|
666
|
+
| `autoPlay` | `boolean` | `true` | Start playing immediately |
|
|
667
|
+
| `onComplete` | `() => void` | — | Callback when animation completes (non-looping) |
|
|
668
|
+
| `anchor` | `number \| { x, y }` | `0.5` | Anchor point |
|
|
669
|
+
|
|
595
670
|
### Spine Animations
|
|
596
671
|
|
|
597
672
|
If `@esotericsoftware/spine-pixi-v8` is installed:
|
|
@@ -621,6 +696,136 @@ console.log(SpineHelper.getAnimationNames(spine));
|
|
|
621
696
|
|
|
622
697
|
## UI Components
|
|
623
698
|
|
|
699
|
+
### Layout
|
|
700
|
+
|
|
701
|
+
Responsive layout container that automatically arranges children. Supports horizontal, vertical, grid, and wrap modes with alignment, padding, gap, anchor positioning, and viewport breakpoints.
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
import { Layout } from '@energy8platform/game-engine';
|
|
705
|
+
|
|
706
|
+
// Horizontal toolbar anchored to bottom-center
|
|
707
|
+
const toolbar = new Layout({
|
|
708
|
+
direction: 'horizontal',
|
|
709
|
+
gap: 20,
|
|
710
|
+
alignment: 'center',
|
|
711
|
+
anchor: 'bottom-center',
|
|
712
|
+
padding: 16,
|
|
713
|
+
breakpoints: {
|
|
714
|
+
768: { direction: 'vertical', gap: 10 },
|
|
715
|
+
},
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
toolbar.addItem(spinButton);
|
|
719
|
+
toolbar.addItem(betLabel);
|
|
720
|
+
toolbar.addItem(balanceDisplay);
|
|
721
|
+
scene.container.addChild(toolbar);
|
|
722
|
+
|
|
723
|
+
// Update position on resize
|
|
724
|
+
toolbar.updateViewport(width, height);
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
// Grid layout for a symbol paytable
|
|
729
|
+
const grid = new Layout({
|
|
730
|
+
direction: 'grid',
|
|
731
|
+
columns: 3,
|
|
732
|
+
gap: 16,
|
|
733
|
+
alignment: 'center',
|
|
734
|
+
anchor: 'center',
|
|
735
|
+
padding: [20, 40, 20, 40],
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
symbols.forEach((sym) => grid.addItem(sym));
|
|
739
|
+
grid.updateViewport(viewWidth, viewHeight);
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
```typescript
|
|
743
|
+
// Wrap layout — items flow and wrap to next line
|
|
744
|
+
const tags = new Layout({
|
|
745
|
+
direction: 'wrap',
|
|
746
|
+
gap: 8,
|
|
747
|
+
maxWidth: 600,
|
|
748
|
+
});
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### LayoutConfig
|
|
752
|
+
|
|
753
|
+
| Property | Type | Default | Description |
|
|
754
|
+
| --- | --- | --- | --- |
|
|
755
|
+
| `direction` | `'horizontal' \| 'vertical' \| 'grid' \| 'wrap'` | `'vertical'` | Layout direction |
|
|
756
|
+
| `gap` | `number` | `0` | Gap between children (px) |
|
|
757
|
+
| `padding` | `number \| [top, right, bottom, left]` | `0` | Inner padding |
|
|
758
|
+
| `alignment` | `'start' \| 'center' \| 'end' \| 'stretch'` | `'start'` | Cross-axis alignment |
|
|
759
|
+
| `anchor` | `LayoutAnchor` | `'top-left'` | Position relative to viewport |
|
|
760
|
+
| `columns` | `number` | `2` | Column count (grid mode only) |
|
|
761
|
+
| `maxWidth` | `number` | `Infinity` | Max width before wrapping (wrap mode) |
|
|
762
|
+
| `autoLayout` | `boolean` | `true` | Auto-recalculate on add/remove |
|
|
763
|
+
| `breakpoints` | `Record<number, Partial<LayoutConfig>>` | — | Override config per viewport width |
|
|
764
|
+
|
|
765
|
+
**Anchor values:** `top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`, `bottom-left`, `bottom-center`, `bottom-right`
|
|
766
|
+
|
|
767
|
+
### ScrollContainer
|
|
768
|
+
|
|
769
|
+
Scrollable container with touch/drag, mouse wheel, inertia deceleration, elastic overscroll bounce, snap-to-item, and auto-hiding scrollbars. Ideal for paytables, settings, bet history, and any content that doesn't fit on screen.
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
import { ScrollContainer } from '@energy8platform/game-engine';
|
|
773
|
+
|
|
774
|
+
const scroll = new ScrollContainer({
|
|
775
|
+
width: 600,
|
|
776
|
+
height: 400,
|
|
777
|
+
direction: 'vertical',
|
|
778
|
+
showScrollbar: true,
|
|
779
|
+
elasticity: 0.3,
|
|
780
|
+
inertia: 0.92,
|
|
781
|
+
borderRadius: 12,
|
|
782
|
+
backgroundColor: 0x1a1a2e,
|
|
783
|
+
backgroundAlpha: 0.8,
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
// Build scrollable content
|
|
787
|
+
const list = new Container();
|
|
788
|
+
for (let i = 0; i < 50; i++) {
|
|
789
|
+
const row = createRow(i);
|
|
790
|
+
row.y = i * 40;
|
|
791
|
+
list.addChild(row);
|
|
792
|
+
}
|
|
793
|
+
scroll.setContent(list);
|
|
794
|
+
|
|
795
|
+
scene.container.addChild(scroll);
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
// Programmatic scrolling
|
|
800
|
+
scroll.scrollTo(0, 200); // scroll to y=200 with animation
|
|
801
|
+
scroll.scrollTo(0, 200, false); // instant jump
|
|
802
|
+
scroll.scrollToItem(5); // snap to item index (when snapSize is set)
|
|
803
|
+
|
|
804
|
+
// Current position
|
|
805
|
+
const { x, y } = scroll.scrollPosition;
|
|
806
|
+
|
|
807
|
+
// Resize viewport
|
|
808
|
+
scroll.resize(newWidth, newHeight);
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
#### ScrollContainerConfig
|
|
812
|
+
|
|
813
|
+
| Property | Type | Default | Description |
|
|
814
|
+
| --- | --- | --- | --- |
|
|
815
|
+
| `width` | `number` | — | Visible viewport width |
|
|
816
|
+
| `height` | `number` | — | Visible viewport height |
|
|
817
|
+
| `direction` | `'vertical' \| 'horizontal' \| 'both'` | `'vertical'` | Scroll direction |
|
|
818
|
+
| `showScrollbar` | `boolean` | `true` | Show scrollbar(s) |
|
|
819
|
+
| `scrollbarWidth` | `number` | `6` | Scrollbar width (px) |
|
|
820
|
+
| `scrollbarColor` | `number` | `0xffffff` | Scrollbar color |
|
|
821
|
+
| `scrollbarAlpha` | `number` | `0.4` | Scrollbar opacity |
|
|
822
|
+
| `elasticity` | `number` | `0.3` | Overscroll bounce (0 = none) |
|
|
823
|
+
| `inertia` | `number` | `0.92` | Deceleration factor (0 = instant stop) |
|
|
824
|
+
| `snapSize` | `number` | `0` | Snap to fixed increments (0 = disabled) |
|
|
825
|
+
| `backgroundColor` | `number` | — | Background color (transparent if omitted) |
|
|
826
|
+
| `backgroundAlpha` | `number` | `1` | Background opacity |
|
|
827
|
+
| `borderRadius` | `number` | `0` | Mask border radius |
|
|
828
|
+
|
|
624
829
|
### Button
|
|
625
830
|
|
|
626
831
|
```typescript
|
|
@@ -677,12 +882,12 @@ Displays player balance with formatting:
|
|
|
677
882
|
import { BalanceDisplay } from '@energy8platform/game-engine';
|
|
678
883
|
|
|
679
884
|
const balance = new BalanceDisplay({
|
|
680
|
-
initialBalance: 10000,
|
|
681
885
|
currency: 'USD',
|
|
886
|
+
animated: true,
|
|
682
887
|
// ... text style options
|
|
683
888
|
});
|
|
684
889
|
|
|
685
|
-
balance.
|
|
890
|
+
balance.setValue(9500); // Animates the balance change
|
|
686
891
|
```
|
|
687
892
|
|
|
688
893
|
### WinDisplay
|
|
@@ -693,10 +898,11 @@ Animated win amount display with countup:
|
|
|
693
898
|
import { WinDisplay } from '@energy8platform/game-engine';
|
|
694
899
|
|
|
695
900
|
const winDisplay = new WinDisplay({
|
|
901
|
+
countupDuration: 2000,
|
|
696
902
|
// ... text style options
|
|
697
903
|
});
|
|
698
904
|
|
|
699
|
-
await winDisplay.
|
|
905
|
+
await winDisplay.showWin(5000); // Show $50.00, countup over 2 seconds
|
|
700
906
|
winDisplay.hide();
|
|
701
907
|
```
|
|
702
908
|
|
|
@@ -709,11 +915,11 @@ const bar = new ProgressBar({
|
|
|
709
915
|
width: 400,
|
|
710
916
|
height: 20,
|
|
711
917
|
fillColor: 0x00ff00,
|
|
712
|
-
|
|
918
|
+
trackColor: 0x333333,
|
|
713
919
|
borderRadius: 10,
|
|
714
920
|
});
|
|
715
921
|
|
|
716
|
-
bar.
|
|
922
|
+
bar.progress = 0.75; // 75%
|
|
717
923
|
```
|
|
718
924
|
|
|
719
925
|
### Panel
|
|
@@ -728,7 +934,7 @@ const panel = new Panel({
|
|
|
728
934
|
height: 400,
|
|
729
935
|
backgroundColor: 0x1a1a2e,
|
|
730
936
|
borderRadius: 16,
|
|
731
|
-
|
|
937
|
+
backgroundAlpha: 0.9,
|
|
732
938
|
});
|
|
733
939
|
```
|
|
734
940
|
|
|
@@ -740,14 +946,13 @@ Full-screen overlay dialog:
|
|
|
740
946
|
import { Modal } from '@energy8platform/game-engine';
|
|
741
947
|
|
|
742
948
|
const modal = new Modal({
|
|
743
|
-
width: 500,
|
|
744
|
-
height: 350,
|
|
745
949
|
overlayAlpha: 0.7,
|
|
746
|
-
|
|
747
|
-
|
|
950
|
+
overlayColor: 0x000000,
|
|
951
|
+
closeOnOverlay: true,
|
|
952
|
+
animationDuration: 300,
|
|
748
953
|
});
|
|
749
954
|
|
|
750
|
-
await modal.show();
|
|
955
|
+
await modal.show(viewWidth, viewHeight);
|
|
751
956
|
await modal.hide();
|
|
752
957
|
```
|
|
753
958
|
|
|
@@ -760,11 +965,9 @@ import { Toast } from '@energy8platform/game-engine';
|
|
|
760
965
|
|
|
761
966
|
const toast = new Toast({
|
|
762
967
|
duration: 2000,
|
|
763
|
-
backgroundColor: 0x333333,
|
|
764
|
-
textStyle: { fill: 0xffffff, fontSize: 20 },
|
|
765
968
|
});
|
|
766
969
|
|
|
767
|
-
await toast.show('Free spins activated!');
|
|
970
|
+
await toast.show('Free spins activated!', 'success');
|
|
768
971
|
```
|
|
769
972
|
|
|
770
973
|
---
|
|
@@ -801,10 +1004,17 @@ if (input.isKeyDown('ArrowLeft')) {
|
|
|
801
1004
|
input.lock();
|
|
802
1005
|
// ... animation plays ...
|
|
803
1006
|
input.unlock();
|
|
1007
|
+
|
|
1008
|
+
// Convert DOM canvas position to game-world coordinates
|
|
1009
|
+
// (accounts for viewport scaling and offset)
|
|
1010
|
+
const worldPos = input.getWorldPosition(canvasX, canvasY);
|
|
1011
|
+
console.log(worldPos.x, worldPos.y);
|
|
804
1012
|
```
|
|
805
1013
|
|
|
806
1014
|
**Events:** `tap`, `press`, `release`, `move`, `swipe`, `keydown`, `keyup`
|
|
807
1015
|
|
|
1016
|
+
> **Note:** The viewport transform for coordinate mapping is wired up automatically by `GameApplication`. Call `getWorldPosition()` when you need to convert raw DOM coordinates to game-world space.
|
|
1017
|
+
|
|
808
1018
|
---
|
|
809
1019
|
|
|
810
1020
|
## Vite Configuration
|
|
@@ -817,28 +1027,31 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
|
817
1027
|
|
|
818
1028
|
export default defineGameConfig({
|
|
819
1029
|
base: '/games/my-slot/',
|
|
820
|
-
outDir: 'dist',
|
|
821
1030
|
devBridge: true, // Auto-inject DevBridge in dev mode
|
|
822
|
-
|
|
823
|
-
'png', 'jpg', 'webp', 'avif',
|
|
824
|
-
'mp3', 'ogg', 'wav',
|
|
825
|
-
'json', 'atlas', 'skel',
|
|
826
|
-
'fnt', 'ttf', 'woff2',
|
|
827
|
-
],
|
|
1031
|
+
devBridgeConfig: './dev.config', // Custom config path (optional)
|
|
828
1032
|
vite: {
|
|
829
1033
|
// Additional Vite config overrides
|
|
830
1034
|
},
|
|
831
1035
|
});
|
|
832
1036
|
```
|
|
833
1037
|
|
|
1038
|
+
### `GameConfig`
|
|
1039
|
+
|
|
1040
|
+
| Property | Type | Default | Description |
|
|
1041
|
+
| --- | --- | --- | --- |
|
|
1042
|
+
| `base` | `string` | `'/'` | Vite `base` path for deployment |
|
|
1043
|
+
| `devBridge` | `boolean` | `false` | Auto-inject DevBridge in dev mode |
|
|
1044
|
+
| `devBridgeConfig` | `string` | `'./dev.config'` | Path to DevBridge config file |
|
|
1045
|
+
| `vite` | `UserConfig` | — | Additional Vite config to merge |
|
|
1046
|
+
|
|
834
1047
|
### What `defineGameConfig` Provides
|
|
835
1048
|
|
|
836
|
-
- **
|
|
1049
|
+
- **Build target: ES2020+** — modern output for all supported casino platforms
|
|
1050
|
+
- **Asset inlining** — files under 8KB are auto-inlined
|
|
1051
|
+
- **PixiJS chunk splitting** — `pixi.js` is extracted into a separate chunk for caching
|
|
837
1052
|
- **DevBridge injection** — automatically available in dev mode via virtual module
|
|
838
|
-
- **
|
|
839
|
-
- **
|
|
840
|
-
- **Gzip-friendly output** — chunking strategy optimized for compression
|
|
841
|
-
- **Base path** — configurable for deployment subpaths
|
|
1053
|
+
- **Dev server** — port 3000, auto-open browser
|
|
1054
|
+
- **Dependency optimization** — `pixi.js` pre-bundled for faster dev starts
|
|
842
1055
|
|
|
843
1056
|
### Custom DevBridge Configuration
|
|
844
1057
|
|
|
@@ -860,13 +1073,15 @@ export default {
|
|
|
860
1073
|
} satisfies DevBridgeConfig;
|
|
861
1074
|
```
|
|
862
1075
|
|
|
863
|
-
This file is auto-imported by the Vite plugin when `devBridge: true`.
|
|
1076
|
+
This file is auto-imported by the Vite plugin when `devBridge: true`. The plugin injects a virtual module (`/@dev-bridge-entry.js`) that starts DevBridge **before** importing your app entry point, ensuring the `MemoryChannel` is ready when the SDK calls `ready()`.
|
|
864
1077
|
|
|
865
1078
|
---
|
|
866
1079
|
|
|
867
1080
|
## DevBridge
|
|
868
1081
|
|
|
869
|
-
`DevBridge` simulates a casino host for local development. It
|
|
1082
|
+
`DevBridge` simulates a casino host for local development. It uses the SDK's `Bridge` class in `devMode`, communicating with `CasinoGameSDK` through a shared in-memory `MemoryChannel` — no `postMessage` or iframe required.
|
|
1083
|
+
|
|
1084
|
+
> **Requires `@energy8platform/game-sdk` >= 2.6.0**
|
|
870
1085
|
|
|
871
1086
|
```typescript
|
|
872
1087
|
import { DevBridge } from '@energy8platform/game-engine/debug';
|
|
@@ -878,7 +1093,10 @@ const bridge = new DevBridge({
|
|
|
878
1093
|
networkDelay: 200,
|
|
879
1094
|
debug: true,
|
|
880
1095
|
gameConfig: {
|
|
1096
|
+
id: 'my-slot',
|
|
1097
|
+
type: 'slot',
|
|
881
1098
|
viewport: { width: 1920, height: 1080 },
|
|
1099
|
+
betLevels: [0.1, 0.2, 0.5, 1, 2, 5, 10],
|
|
882
1100
|
},
|
|
883
1101
|
onPlay: ({ action, bet, roundId }) => {
|
|
884
1102
|
// Return custom play result
|
|
@@ -887,7 +1105,7 @@ const bridge = new DevBridge({
|
|
|
887
1105
|
},
|
|
888
1106
|
});
|
|
889
1107
|
|
|
890
|
-
bridge.start();
|
|
1108
|
+
bridge.start(); // Creates SDK Bridge({ devMode: true }) + registers handlers
|
|
891
1109
|
|
|
892
1110
|
// Update balance programmatically
|
|
893
1111
|
bridge.setBalance(5000);
|
|
@@ -896,6 +1114,8 @@ bridge.setBalance(5000);
|
|
|
896
1114
|
bridge.destroy();
|
|
897
1115
|
```
|
|
898
1116
|
|
|
1117
|
+
When using the Vite plugin with `devBridge: true`, the SDK is automatically configured with `devMode: true` so both sides use the same `MemoryChannel`.
|
|
1118
|
+
|
|
899
1119
|
### Handled Messages
|
|
900
1120
|
|
|
901
1121
|
| Message | Description |
|
|
@@ -922,7 +1142,7 @@ fps.toggle();
|
|
|
922
1142
|
fps.hide();
|
|
923
1143
|
```
|
|
924
1144
|
|
|
925
|
-
When `debug: true` is set in `GameApplicationConfig`, the FPS overlay is
|
|
1145
|
+
When `debug: true` is set in `GameApplicationConfig`, the FPS overlay is created and shown automatically — no manual setup needed.
|
|
926
1146
|
|
|
927
1147
|
The overlay displays:
|
|
928
1148
|
- Average FPS
|
|
@@ -944,6 +1164,7 @@ class GameApplication extends EventEmitter<GameEngineEvents> {
|
|
|
944
1164
|
scenes: SceneManager;
|
|
945
1165
|
assets: AssetManager;
|
|
946
1166
|
audio: AudioManager;
|
|
1167
|
+
input: InputManager;
|
|
947
1168
|
viewport: ViewportManager;
|
|
948
1169
|
sdk: CasinoGameSDK | null;
|
|
949
1170
|
initData: InitData | null;
|
|
@@ -1078,9 +1299,10 @@ class Tween {
|
|
|
1078
1299
|
static to(target: any, props: Record<string, number>, duration: number, easing?: EasingFunction, onUpdate?: (p: number) => void): Promise<void>;
|
|
1079
1300
|
static from(target: any, props: Record<string, number>, duration: number, easing?, onUpdate?): Promise<void>;
|
|
1080
1301
|
static fromTo(target: any, fromProps: Record<string, number>, toProps: Record<string, number>, duration: number, easing?, onUpdate?): Promise<void>;
|
|
1081
|
-
static delay(ms: number): Promise<void>;
|
|
1302
|
+
static delay(ms: number): Promise<void>; // Uses PixiJS Ticker
|
|
1082
1303
|
static killTweensOf(target: any): void;
|
|
1083
1304
|
static killAll(): void;
|
|
1305
|
+
static reset(): void; // Kill all + remove ticker listener
|
|
1084
1306
|
}
|
|
1085
1307
|
```
|
|
1086
1308
|
|
|
@@ -1111,10 +1333,56 @@ class InputManager extends EventEmitter<InputEvents> {
|
|
|
1111
1333
|
lock(): void;
|
|
1112
1334
|
unlock(): void;
|
|
1113
1335
|
isKeyDown(key: string): boolean;
|
|
1336
|
+
setViewportTransform(scale: number, offsetX: number, offsetY: number): void;
|
|
1337
|
+
getWorldPosition(canvasX: number, canvasY: number): { x: number; y: number };
|
|
1114
1338
|
destroy(): void;
|
|
1115
1339
|
}
|
|
1116
1340
|
```
|
|
1117
1341
|
|
|
1342
|
+
### Layout
|
|
1343
|
+
|
|
1344
|
+
```typescript
|
|
1345
|
+
class Layout extends Container {
|
|
1346
|
+
get items(): readonly Container[];
|
|
1347
|
+
|
|
1348
|
+
constructor(config?: LayoutConfig);
|
|
1349
|
+
addItem(child: Container): this;
|
|
1350
|
+
removeItem(child: Container): this;
|
|
1351
|
+
clearItems(): this;
|
|
1352
|
+
updateViewport(width: number, height: number): void;
|
|
1353
|
+
layout(): void;
|
|
1354
|
+
}
|
|
1355
|
+
```
|
|
1356
|
+
|
|
1357
|
+
### ScrollContainer
|
|
1358
|
+
|
|
1359
|
+
```typescript
|
|
1360
|
+
class ScrollContainer extends Container {
|
|
1361
|
+
get content(): Container | null;
|
|
1362
|
+
get scrollPosition(): { x: number; y: number };
|
|
1363
|
+
|
|
1364
|
+
constructor(config: ScrollContainerConfig);
|
|
1365
|
+
setContent(content: Container): void;
|
|
1366
|
+
scrollTo(x: number, y: number, animate?: boolean): void;
|
|
1367
|
+
scrollToItem(index: number): void;
|
|
1368
|
+
resize(width: number, height: number): void;
|
|
1369
|
+
destroy(): void;
|
|
1370
|
+
}
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
### SpriteAnimation
|
|
1374
|
+
|
|
1375
|
+
```typescript
|
|
1376
|
+
class SpriteAnimation {
|
|
1377
|
+
static create(textures: Texture[], config?: SpriteAnimationConfig): AnimatedSprite;
|
|
1378
|
+
static fromSpritesheet(sheet: Spritesheet, prefix: string, config?: SpriteAnimationConfig): AnimatedSprite;
|
|
1379
|
+
static fromRange(sheet: Spritesheet, pattern: string, start: number, end: number, config?: SpriteAnimationConfig): AnimatedSprite;
|
|
1380
|
+
static fromAliases(aliases: string[], config?: SpriteAnimationConfig): AnimatedSprite;
|
|
1381
|
+
static playOnce(textures: Texture[], config?: SpriteAnimationConfig): { sprite: AnimatedSprite; finished: Promise<void> };
|
|
1382
|
+
static getTexturesByPrefix(sheet: Spritesheet, prefix: string): Texture[];
|
|
1383
|
+
}
|
|
1384
|
+
```
|
|
1385
|
+
|
|
1118
1386
|
### EventEmitter
|
|
1119
1387
|
|
|
1120
1388
|
```typescript
|
|
@@ -1122,7 +1390,8 @@ class EventEmitter<TEvents extends {}> {
|
|
|
1122
1390
|
on<K>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
1123
1391
|
once<K>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
1124
1392
|
off<K>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
1125
|
-
|
|
1393
|
+
// Void events can be emitted without a data argument
|
|
1394
|
+
emit<K>(event: K, ...args): void;
|
|
1126
1395
|
removeAllListeners(event?: keyof TEvents): this;
|
|
1127
1396
|
}
|
|
1128
1397
|
```
|