@energy8platform/game-engine 0.2.1 → 0.4.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 +400 -35
- 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 +307 -85
- package/dist/core.cjs.js.map +1 -1
- package/dist/core.d.ts +60 -1
- package/dist/core.esm.js +308 -86
- 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 +997 -475
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +356 -79
- package/dist/index.esm.js +983 -478
- package/dist/index.esm.js.map +1 -1
- package/dist/ui.cjs.js +816 -529
- package/dist/ui.cjs.js.map +1 -1
- package/dist/ui.d.ts +179 -41
- package/dist/ui.esm.js +798 -531
- package/dist/ui.esm.js.map +1 -1
- package/dist/vite.cjs.js +85 -68
- package/dist/vite.cjs.js.map +1 -1
- package/dist/vite.d.ts +17 -23
- package/dist/vite.esm.js +86 -68
- package/dist/vite.esm.js.map +1 -1
- package/package.json +19 -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 +19 -7
- package/src/core/SceneManager.ts +3 -1
- package/src/debug/DevBridge.ts +49 -80
- package/src/index.ts +22 -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 +12 -1
- package/src/ui/Button.ts +71 -130
- package/src/ui/Layout.ts +286 -0
- package/src/ui/Modal.ts +6 -5
- package/src/ui/Panel.ts +52 -55
- package/src/ui/ProgressBar.ts +52 -57
- package/src/ui/ScrollContainer.ts +126 -0
- package/src/ui/Toast.ts +19 -13
- package/src/ui/index.ts +17 -0
- package/src/viewport/ViewportManager.ts +2 -0
- package/src/vite/index.ts +103 -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)
|
|
@@ -37,6 +51,9 @@ npm init -y
|
|
|
37
51
|
# Install dependencies
|
|
38
52
|
npm install pixi.js @energy8platform/game-sdk @energy8platform/game-engine
|
|
39
53
|
|
|
54
|
+
# Install UI layout dependencies (optional — needed for Layout, Panel, Toast, ScrollContainer)
|
|
55
|
+
npm install @pixi/ui @pixi/layout yoga-layout
|
|
56
|
+
|
|
40
57
|
# (Optional) install spine and audio support
|
|
41
58
|
npm install @pixi/sound @esotericsoftware/spine-pixi-v8
|
|
42
59
|
```
|
|
@@ -83,7 +100,7 @@ async function bootstrap() {
|
|
|
83
100
|
debug: true,
|
|
84
101
|
});
|
|
85
102
|
|
|
86
|
-
game.scenes.register('game', GameScene
|
|
103
|
+
game.scenes.register('game', GameScene);
|
|
87
104
|
|
|
88
105
|
game.on('initialized', () => console.log('Engine initialized'));
|
|
89
106
|
game.on('loaded', () => console.log('Assets loaded'));
|
|
@@ -105,7 +122,10 @@ bootstrap();
|
|
|
105
122
|
| Package | Version | Required |
|
|
106
123
|
| --- | --- | --- |
|
|
107
124
|
| `pixi.js` | `^8.16.0` | Yes |
|
|
108
|
-
| `@energy8platform/game-sdk` | `^2.
|
|
125
|
+
| `@energy8platform/game-sdk` | `^2.6.0` | Yes |
|
|
126
|
+
| `@pixi/ui` | `^2.3.0` | Optional — for Button, ScrollContainer, ProgressBar |
|
|
127
|
+
| `@pixi/layout` | `^3.2.0` | Optional — for Layout, Panel, Toast (Yoga flexbox) |
|
|
128
|
+
| `yoga-layout` | `^3.0.0` | Optional — peer dep of `@pixi/layout` |
|
|
109
129
|
| `@pixi/sound` | `^6.0.0` | Optional — for audio |
|
|
110
130
|
| `@esotericsoftware/spine-pixi-v8` | `~4.2.0` | Optional — for Spine animations |
|
|
111
131
|
|
|
@@ -118,8 +138,8 @@ import { GameApplication } from '@energy8platform/game-engine'; // full b
|
|
|
118
138
|
import { Scene, SceneManager } from '@energy8platform/game-engine/core';
|
|
119
139
|
import { AssetManager } from '@energy8platform/game-engine/assets';
|
|
120
140
|
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';
|
|
141
|
+
import { Button, Label, Modal, Layout, ScrollContainer, Panel, Toast } from '@energy8platform/game-engine/ui';
|
|
142
|
+
import { Tween, Timeline, Easing, SpriteAnimation } from '@energy8platform/game-engine/animation';
|
|
123
143
|
import { DevBridge, FPSOverlay } from '@energy8platform/game-engine/debug';
|
|
124
144
|
import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
125
145
|
```
|
|
@@ -146,7 +166,7 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
|
146
166
|
|
|
147
167
|
1. **CSS Preloader** — an instant HTML/CSS overlay shown while PixiJS initializes (inline SVG logo with a shimmer animation and "Loading..." text).
|
|
148
168
|
2. **PixiJS initialization** — creates `Application`, initializes `ResizeObserver`.
|
|
149
|
-
3. **SDK handshake** — connects to the casino host (or DevBridge in dev mode).
|
|
169
|
+
3. **SDK handshake** — connects to the casino host (or DevBridge in dev mode via shared `MemoryChannel`).
|
|
150
170
|
4. **Canvas Loading Screen** — `LoadingScene` displays the SVG logo with an animated progress bar, `preload` bundle is loaded first.
|
|
151
171
|
5. **Asset loading** — remaining bundles are loaded; the progress bar fills in real time.
|
|
152
172
|
6. **Tap-to-start** — optional screen shown after loading (required on mobile for audio unlock).
|
|
@@ -169,6 +189,10 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
|
169
189
|
| `manifest` | `AssetManifest` | — | Asset manifest |
|
|
170
190
|
| `audio` | `AudioConfig` | — | Audio configuration |
|
|
171
191
|
| `sdk` | `object \| false` | — | SDK options; `false` to disable |
|
|
192
|
+
| `sdk.devMode` | `boolean` | `false` | Use in-memory channel instead of `postMessage` (no iframe needed) |
|
|
193
|
+
| `sdk.parentOrigin` | `string` | — | Expected parent origin for `postMessage` validation |
|
|
194
|
+
| `sdk.timeout` | `number` | — | SDK handshake timeout in ms |
|
|
195
|
+
| `sdk.debug` | `boolean` | — | Enable SDK debug logging |
|
|
172
196
|
| `pixi` | `Partial<ApplicationOptions>` | — | PixiJS pass-through options |
|
|
173
197
|
| `debug` | `boolean` | `false` | Enable FPS overlay |
|
|
174
198
|
|
|
@@ -261,9 +285,9 @@ export class GameScene extends Scene {
|
|
|
261
285
|
const scenes = game.scenes;
|
|
262
286
|
|
|
263
287
|
// Register scenes
|
|
264
|
-
scenes.register('menu', MenuScene
|
|
265
|
-
scenes.register('game', GameScene
|
|
266
|
-
scenes.register('bonus', BonusScene
|
|
288
|
+
scenes.register('menu', MenuScene);
|
|
289
|
+
scenes.register('game', GameScene);
|
|
290
|
+
scenes.register('bonus', BonusScene);
|
|
267
291
|
|
|
268
292
|
// Navigate (replaces entire stack)
|
|
269
293
|
await scenes.goto('game');
|
|
@@ -400,7 +424,7 @@ const audio = game.audio;
|
|
|
400
424
|
audio.play('click', 'ui');
|
|
401
425
|
audio.play('coin-drop', 'sfx', { volume: 0.8 });
|
|
402
426
|
|
|
403
|
-
// Music with crossfade
|
|
427
|
+
// Music with crossfade (smooth volume transition between tracks)
|
|
404
428
|
audio.playMusic('main-theme', 1000); // 1s crossfade
|
|
405
429
|
audio.stopMusic();
|
|
406
430
|
|
|
@@ -542,13 +566,17 @@ await Tween.from(sprite, { scale: 0 }, 300, Easing.easeOutBack);
|
|
|
542
566
|
// Animate between two sets of values
|
|
543
567
|
await Tween.fromTo(sprite, { x: -100 }, { x: 500 }, 1000, Easing.easeInOutQuad);
|
|
544
568
|
|
|
545
|
-
// Wait (
|
|
569
|
+
// Wait (uses PixiJS Ticker for consistent timing)
|
|
546
570
|
await Tween.delay(1000);
|
|
547
571
|
|
|
548
572
|
// Cancel tweens
|
|
549
573
|
Tween.killTweensOf(sprite);
|
|
550
574
|
Tween.killAll();
|
|
551
575
|
|
|
576
|
+
// Full reset — kill all tweens and remove ticker listener
|
|
577
|
+
// Useful for cleanup between game instances, tests, or hot-reload
|
|
578
|
+
Tween.reset();
|
|
579
|
+
|
|
552
580
|
// Supports nested properties
|
|
553
581
|
await Tween.to(sprite, { 'scale.x': 2, 'position.y': 300 }, 500);
|
|
554
582
|
```
|
|
@@ -592,6 +620,59 @@ All 24 easing functions:
|
|
|
592
620
|
| | | | `easeOutElastic` |
|
|
593
621
|
| | | | `easeInElastic` |
|
|
594
622
|
|
|
623
|
+
### SpriteAnimation
|
|
624
|
+
|
|
625
|
+
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.
|
|
626
|
+
|
|
627
|
+
```typescript
|
|
628
|
+
import { SpriteAnimation } from '@energy8platform/game-engine';
|
|
629
|
+
import { Assets } from 'pixi.js';
|
|
630
|
+
|
|
631
|
+
// From an array of textures
|
|
632
|
+
const coinAnim = SpriteAnimation.create(coinTextures, {
|
|
633
|
+
fps: 30,
|
|
634
|
+
loop: true,
|
|
635
|
+
});
|
|
636
|
+
scene.container.addChild(coinAnim);
|
|
637
|
+
|
|
638
|
+
// From a spritesheet using a name prefix
|
|
639
|
+
const sheet = Assets.get('effects');
|
|
640
|
+
const sparkle = SpriteAnimation.fromSpritesheet(sheet, 'sparkle_', {
|
|
641
|
+
fps: 24,
|
|
642
|
+
loop: true,
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// From a numbered range (e.g., 'explosion_00' to 'explosion_24')
|
|
646
|
+
const explosion = SpriteAnimation.fromRange(sheet, 'explosion_{i}', 0, 24, {
|
|
647
|
+
fps: 60,
|
|
648
|
+
loop: false,
|
|
649
|
+
onComplete: () => explosion.destroy(),
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// From pre-loaded texture aliases
|
|
653
|
+
const anim = SpriteAnimation.fromAliases(
|
|
654
|
+
['frame_0', 'frame_1', 'frame_2', 'frame_3'],
|
|
655
|
+
{ fps: 12 },
|
|
656
|
+
);
|
|
657
|
+
|
|
658
|
+
// Fire-and-forget: play once and auto-destroy
|
|
659
|
+
const { sprite, finished } = SpriteAnimation.playOnce(coinTextures, {
|
|
660
|
+
fps: 30,
|
|
661
|
+
});
|
|
662
|
+
scene.container.addChild(sprite);
|
|
663
|
+
await finished; // resolves when animation completes
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
#### SpriteAnimationConfig
|
|
667
|
+
|
|
668
|
+
| Property | Type | Default | Description |
|
|
669
|
+
| --- | --- | --- | --- |
|
|
670
|
+
| `fps` | `number` | `24` | Frames per second |
|
|
671
|
+
| `loop` | `boolean` | `true` | Whether to loop |
|
|
672
|
+
| `autoPlay` | `boolean` | `true` | Start playing immediately |
|
|
673
|
+
| `onComplete` | `() => void` | — | Callback when animation completes (non-looping) |
|
|
674
|
+
| `anchor` | `number \| { x, y }` | `0.5` | Anchor point |
|
|
675
|
+
|
|
595
676
|
### Spine Animations
|
|
596
677
|
|
|
597
678
|
If `@esotericsoftware/spine-pixi-v8` is installed:
|
|
@@ -621,8 +702,140 @@ console.log(SpineHelper.getAnimationNames(spine));
|
|
|
621
702
|
|
|
622
703
|
## UI Components
|
|
623
704
|
|
|
705
|
+
> UI components are built on top of [`@pixi/ui`](https://github.com/pixijs/ui) and [`@pixi/layout`](https://github.com/pixijs/layout) (Yoga flexbox). Install them as peer dependencies:
|
|
706
|
+
>
|
|
707
|
+
> ```bash
|
|
708
|
+
> npm install @pixi/ui @pixi/layout yoga-layout
|
|
709
|
+
> ```
|
|
710
|
+
|
|
711
|
+
### Layout
|
|
712
|
+
|
|
713
|
+
Responsive layout container powered by `@pixi/layout` (Yoga flexbox engine). Supports horizontal, vertical, grid, and wrap modes with alignment, padding, gap, anchor positioning, and viewport breakpoints.
|
|
714
|
+
|
|
715
|
+
```typescript
|
|
716
|
+
import { Layout } from '@energy8platform/game-engine';
|
|
717
|
+
|
|
718
|
+
// Horizontal toolbar anchored to bottom-center
|
|
719
|
+
const toolbar = new Layout({
|
|
720
|
+
direction: 'horizontal',
|
|
721
|
+
gap: 20,
|
|
722
|
+
alignment: 'center',
|
|
723
|
+
anchor: 'bottom-center',
|
|
724
|
+
padding: 16,
|
|
725
|
+
breakpoints: {
|
|
726
|
+
768: { direction: 'vertical', gap: 10 },
|
|
727
|
+
},
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
toolbar.addItem(spinButton);
|
|
731
|
+
toolbar.addItem(betLabel);
|
|
732
|
+
toolbar.addItem(balanceDisplay);
|
|
733
|
+
scene.container.addChild(toolbar);
|
|
734
|
+
|
|
735
|
+
// Update position on resize
|
|
736
|
+
toolbar.updateViewport(width, height);
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
```typescript
|
|
740
|
+
// Grid layout for a symbol paytable
|
|
741
|
+
const grid = new Layout({
|
|
742
|
+
direction: 'grid',
|
|
743
|
+
columns: 3,
|
|
744
|
+
gap: 16,
|
|
745
|
+
alignment: 'center',
|
|
746
|
+
anchor: 'center',
|
|
747
|
+
padding: [20, 40, 20, 40],
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
symbols.forEach((sym) => grid.addItem(sym));
|
|
751
|
+
grid.updateViewport(viewWidth, viewHeight);
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
// Wrap layout — items flow and wrap to next line
|
|
756
|
+
const tags = new Layout({
|
|
757
|
+
direction: 'wrap',
|
|
758
|
+
gap: 8,
|
|
759
|
+
maxWidth: 600,
|
|
760
|
+
});
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
#### LayoutConfig
|
|
764
|
+
|
|
765
|
+
| Property | Type | Default | Description |
|
|
766
|
+
| --- | --- | --- | --- |
|
|
767
|
+
| `direction` | `'horizontal' \| 'vertical' \| 'grid' \| 'wrap'` | `'vertical'` | Layout direction |
|
|
768
|
+
| `gap` | `number` | `0` | Gap between children (px) |
|
|
769
|
+
| `padding` | `number \| [top, right, bottom, left]` | `0` | Inner padding |
|
|
770
|
+
| `alignment` | `'start' \| 'center' \| 'end' \| 'stretch'` | `'start'` | Cross-axis alignment |
|
|
771
|
+
| `anchor` | `LayoutAnchor` | `'top-left'` | Position relative to viewport |
|
|
772
|
+
| `columns` | `number` | `2` | Column count (grid mode only) |
|
|
773
|
+
| `maxWidth` | `number` | `Infinity` | Max width before wrapping (wrap mode) |
|
|
774
|
+
| `autoLayout` | `boolean` | `true` | Auto-recalculate on add/remove |
|
|
775
|
+
| `breakpoints` | `Record<number, Partial<LayoutConfig>>` | — | Override config per viewport width |
|
|
776
|
+
|
|
777
|
+
**Anchor values:** `top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`, `bottom-left`, `bottom-center`, `bottom-right`
|
|
778
|
+
|
|
779
|
+
> **Under the hood**: Layout maps `direction` → Yoga `flexDirection`/`flexWrap`, `alignment` → `alignItems`, and uses `@pixi/layout`'s `container.layout = { ... }` mixin. Grid mode assigns percentage widths to children.
|
|
780
|
+
|
|
781
|
+
### ScrollContainer
|
|
782
|
+
|
|
783
|
+
Scrollable container powered by `@pixi/ui` ScrollBox. Provides touch/drag scrolling, mouse wheel support, inertia, and dynamic rendering optimization for off-screen items.
|
|
784
|
+
|
|
785
|
+
```typescript
|
|
786
|
+
import { ScrollContainer } from '@energy8platform/game-engine';
|
|
787
|
+
|
|
788
|
+
const scroll = new ScrollContainer({
|
|
789
|
+
width: 600,
|
|
790
|
+
height: 400,
|
|
791
|
+
direction: 'vertical',
|
|
792
|
+
elementsMargin: 8,
|
|
793
|
+
borderRadius: 12,
|
|
794
|
+
backgroundColor: 0x1a1a2e,
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
// Add items directly
|
|
798
|
+
for (let i = 0; i < 50; i++) {
|
|
799
|
+
scroll.addItem(createRow(i));
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
scene.container.addChild(scroll);
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
// Or set content from a Container
|
|
807
|
+
const list = new Container();
|
|
808
|
+
items.forEach((item) => list.addChild(item));
|
|
809
|
+
scroll.setContent(list);
|
|
810
|
+
|
|
811
|
+
// Scroll to a specific item index
|
|
812
|
+
scroll.scrollToItem(5);
|
|
813
|
+
|
|
814
|
+
// Current position
|
|
815
|
+
const { x, y } = scroll.scrollPosition;
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
#### ScrollContainerConfig
|
|
819
|
+
|
|
820
|
+
| Property | Type | Default | Description |
|
|
821
|
+
| --- | --- | --- | --- |
|
|
822
|
+
| `width` | `number` | — | Visible viewport width |
|
|
823
|
+
| `height` | `number` | — | Visible viewport height |
|
|
824
|
+
| `direction` | `'vertical' \| 'horizontal' \| 'both'` | `'vertical'` | Scroll direction |
|
|
825
|
+
| `backgroundColor` | `ColorSource` | — | Background color (transparent if omitted) |
|
|
826
|
+
| `borderRadius` | `number` | `0` | Mask border radius |
|
|
827
|
+
| `elementsMargin` | `number` | `0` | Gap between items |
|
|
828
|
+
| `padding` | `number` | `0` | Content padding |
|
|
829
|
+
| `disableDynamicRendering` | `boolean` | `false` | Render all items even when off-screen |
|
|
830
|
+
| `disableEasing` | `boolean` | `false` | Disable scroll inertia/easing |
|
|
831
|
+
| `globalScroll` | `boolean` | `true` | Scroll even when mouse is not over the component |
|
|
832
|
+
|
|
833
|
+
> **Note:** ScrollContainer extends `@pixi/ui` `ScrollBox`. All `ScrollBox` methods and options are available.
|
|
834
|
+
|
|
624
835
|
### Button
|
|
625
836
|
|
|
837
|
+
Powered by `@pixi/ui` `FancyButton`. Supports both texture-based and Graphics-based rendering with per-state views, press animation, and built-in text.
|
|
838
|
+
|
|
626
839
|
```typescript
|
|
627
840
|
import { Button } from '@energy8platform/game-engine';
|
|
628
841
|
|
|
@@ -631,23 +844,25 @@ const spinBtn = new Button({
|
|
|
631
844
|
height: 60,
|
|
632
845
|
borderRadius: 12,
|
|
633
846
|
colors: {
|
|
634
|
-
|
|
847
|
+
default: 0xffd700,
|
|
635
848
|
hover: 0xffe44d,
|
|
636
849
|
pressed: 0xccac00,
|
|
637
850
|
disabled: 0x666666,
|
|
638
851
|
},
|
|
639
852
|
pressScale: 0.95,
|
|
640
853
|
animationDuration: 100,
|
|
854
|
+
text: 'SPIN',
|
|
641
855
|
});
|
|
642
856
|
|
|
643
|
-
|
|
857
|
+
// Connect press event (Signal-based)
|
|
858
|
+
spinBtn.onPress.connect(() => {
|
|
644
859
|
console.log('Spin!');
|
|
645
|
-
};
|
|
860
|
+
});
|
|
646
861
|
|
|
647
862
|
// Or use texture-based states
|
|
648
863
|
const btn = new Button({
|
|
649
864
|
textures: {
|
|
650
|
-
|
|
865
|
+
default: 'btn-default',
|
|
651
866
|
hover: 'btn-hover',
|
|
652
867
|
pressed: 'btn-pressed',
|
|
653
868
|
disabled: 'btn-disabled',
|
|
@@ -658,6 +873,8 @@ btn.enable();
|
|
|
658
873
|
btn.disable();
|
|
659
874
|
```
|
|
660
875
|
|
|
876
|
+
> **Breaking change:** Button states renamed from `normal` to `default`. The `onTap` callback is replaced by `onPress.connect()` (typed-signals Signal).
|
|
877
|
+
|
|
661
878
|
### Label
|
|
662
879
|
|
|
663
880
|
```typescript
|
|
@@ -703,6 +920,8 @@ winDisplay.hide();
|
|
|
703
920
|
|
|
704
921
|
### ProgressBar
|
|
705
922
|
|
|
923
|
+
Horizontal progress bar powered by `@pixi/ui` ProgressBar. Supports animated smooth fill.
|
|
924
|
+
|
|
706
925
|
```typescript
|
|
707
926
|
import { ProgressBar } from '@energy8platform/game-engine';
|
|
708
927
|
|
|
@@ -712,24 +931,40 @@ const bar = new ProgressBar({
|
|
|
712
931
|
fillColor: 0x00ff00,
|
|
713
932
|
trackColor: 0x333333,
|
|
714
933
|
borderRadius: 10,
|
|
934
|
+
animated: true,
|
|
715
935
|
});
|
|
716
936
|
|
|
717
937
|
bar.progress = 0.75; // 75%
|
|
938
|
+
|
|
939
|
+
// Call each frame for smooth animation
|
|
940
|
+
bar.update(dt);
|
|
718
941
|
```
|
|
719
942
|
|
|
720
943
|
### Panel
|
|
721
944
|
|
|
722
|
-
|
|
945
|
+
Background panel powered by `@pixi/layout` `LayoutContainer`. Supports both Graphics-based (color + border) and 9-slice sprite backgrounds. Children added to `content` participate in flexbox layout.
|
|
723
946
|
|
|
724
947
|
```typescript
|
|
725
948
|
import { Panel } from '@energy8platform/game-engine';
|
|
726
949
|
|
|
950
|
+
// Simple colored panel
|
|
727
951
|
const panel = new Panel({
|
|
728
952
|
width: 600,
|
|
729
953
|
height: 400,
|
|
730
954
|
backgroundColor: 0x1a1a2e,
|
|
731
955
|
borderRadius: 16,
|
|
732
956
|
backgroundAlpha: 0.9,
|
|
957
|
+
padding: 16,
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
panel.content.addChild(myText);
|
|
961
|
+
|
|
962
|
+
// 9-slice panel (texture-based)
|
|
963
|
+
const nineSlicePanel = new Panel({
|
|
964
|
+
nineSliceTexture: 'panel-bg',
|
|
965
|
+
nineSliceBorders: [20, 20, 20, 20],
|
|
966
|
+
width: 400,
|
|
967
|
+
height: 300,
|
|
733
968
|
});
|
|
734
969
|
```
|
|
735
970
|
|
|
@@ -753,18 +988,25 @@ await modal.hide();
|
|
|
753
988
|
|
|
754
989
|
### Toast
|
|
755
990
|
|
|
756
|
-
Brief notification messages
|
|
991
|
+
Brief notification messages. Uses `@pixi/layout` `LayoutContainer` for auto-sized background.
|
|
757
992
|
|
|
758
993
|
```typescript
|
|
759
994
|
import { Toast } from '@energy8platform/game-engine';
|
|
760
995
|
|
|
761
996
|
const toast = new Toast({
|
|
762
997
|
duration: 2000,
|
|
998
|
+
bottomOffset: 60,
|
|
763
999
|
});
|
|
764
1000
|
|
|
765
|
-
|
|
1001
|
+
// Show with type and viewport size
|
|
1002
|
+
await toast.show('Free spins activated!', 'success', viewWidth, viewHeight);
|
|
1003
|
+
|
|
1004
|
+
// Dismiss manually
|
|
1005
|
+
await toast.dismiss();
|
|
766
1006
|
```
|
|
767
1007
|
|
|
1008
|
+
**Toast types:** `info`, `success`, `warning`, `error` — each with a distinct color.
|
|
1009
|
+
|
|
768
1010
|
---
|
|
769
1011
|
|
|
770
1012
|
## Input
|
|
@@ -799,10 +1041,17 @@ if (input.isKeyDown('ArrowLeft')) {
|
|
|
799
1041
|
input.lock();
|
|
800
1042
|
// ... animation plays ...
|
|
801
1043
|
input.unlock();
|
|
1044
|
+
|
|
1045
|
+
// Convert DOM canvas position to game-world coordinates
|
|
1046
|
+
// (accounts for viewport scaling and offset)
|
|
1047
|
+
const worldPos = input.getWorldPosition(canvasX, canvasY);
|
|
1048
|
+
console.log(worldPos.x, worldPos.y);
|
|
802
1049
|
```
|
|
803
1050
|
|
|
804
1051
|
**Events:** `tap`, `press`, `release`, `move`, `swipe`, `keydown`, `keyup`
|
|
805
1052
|
|
|
1053
|
+
> **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.
|
|
1054
|
+
|
|
806
1055
|
---
|
|
807
1056
|
|
|
808
1057
|
## Vite Configuration
|
|
@@ -815,28 +1064,32 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
|
|
|
815
1064
|
|
|
816
1065
|
export default defineGameConfig({
|
|
817
1066
|
base: '/games/my-slot/',
|
|
818
|
-
outDir: 'dist',
|
|
819
1067
|
devBridge: true, // Auto-inject DevBridge in dev mode
|
|
820
|
-
|
|
821
|
-
'png', 'jpg', 'webp', 'avif',
|
|
822
|
-
'mp3', 'ogg', 'wav',
|
|
823
|
-
'json', 'atlas', 'skel',
|
|
824
|
-
'fnt', 'ttf', 'woff2',
|
|
825
|
-
],
|
|
1068
|
+
devBridgeConfig: './dev.config', // Custom config path (optional)
|
|
826
1069
|
vite: {
|
|
827
1070
|
// Additional Vite config overrides
|
|
828
1071
|
},
|
|
829
1072
|
});
|
|
830
1073
|
```
|
|
831
1074
|
|
|
1075
|
+
### `GameConfig`
|
|
1076
|
+
|
|
1077
|
+
| Property | Type | Default | Description |
|
|
1078
|
+
| --- | --- | --- | --- |
|
|
1079
|
+
| `base` | `string` | `'/'` | Vite `base` path for deployment |
|
|
1080
|
+
| `devBridge` | `boolean` | `false` | Auto-inject DevBridge in dev mode |
|
|
1081
|
+
| `devBridgeConfig` | `string` | `'./dev.config'` | Path to DevBridge config file |
|
|
1082
|
+
| `vite` | `UserConfig` | — | Additional Vite config to merge |
|
|
1083
|
+
|
|
832
1084
|
### What `defineGameConfig` Provides
|
|
833
1085
|
|
|
834
|
-
- **
|
|
1086
|
+
- **Build target: ESNext** — required for `yoga-layout` WASM (uses top-level `await`)
|
|
1087
|
+
- **Asset inlining** — files under 8KB are auto-inlined
|
|
1088
|
+
- **PixiJS chunk splitting** — `pixi.js` is extracted into a separate chunk for caching
|
|
835
1089
|
- **DevBridge injection** — automatically available in dev mode via virtual module
|
|
836
|
-
- **
|
|
837
|
-
- **
|
|
838
|
-
- **
|
|
839
|
-
- **Base path** — configurable for deployment subpaths
|
|
1090
|
+
- **Dev server** — port 3000, auto-open browser
|
|
1091
|
+
- **Dependency deduplication** — `resolve.dedupe` ensures a single copy of `pixi.js`, `@pixi/layout`, `@pixi/ui`, and `yoga-layout` across all packages (prevents registration issues when used as a linked dependency)
|
|
1092
|
+
- **Dependency optimization** — `pixi.js`, `@pixi/layout`, `@pixi/layout/components`, `@pixi/ui`, and `yoga-layout/load` are pre-bundled for faster dev starts; `yoga-layout` main entry is excluded from pre-bundling because it contains a top-level `await` incompatible with esbuild's default target
|
|
840
1093
|
|
|
841
1094
|
### Custom DevBridge Configuration
|
|
842
1095
|
|
|
@@ -858,13 +1111,15 @@ export default {
|
|
|
858
1111
|
} satisfies DevBridgeConfig;
|
|
859
1112
|
```
|
|
860
1113
|
|
|
861
|
-
This file is auto-imported by the Vite plugin when `devBridge: true`.
|
|
1114
|
+
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()`.
|
|
862
1115
|
|
|
863
1116
|
---
|
|
864
1117
|
|
|
865
1118
|
## DevBridge
|
|
866
1119
|
|
|
867
|
-
`DevBridge` simulates a casino host for local development. It
|
|
1120
|
+
`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.
|
|
1121
|
+
|
|
1122
|
+
> **Requires `@energy8platform/game-sdk` >= 2.6.0**
|
|
868
1123
|
|
|
869
1124
|
```typescript
|
|
870
1125
|
import { DevBridge } from '@energy8platform/game-engine/debug';
|
|
@@ -876,7 +1131,10 @@ const bridge = new DevBridge({
|
|
|
876
1131
|
networkDelay: 200,
|
|
877
1132
|
debug: true,
|
|
878
1133
|
gameConfig: {
|
|
1134
|
+
id: 'my-slot',
|
|
1135
|
+
type: 'slot',
|
|
879
1136
|
viewport: { width: 1920, height: 1080 },
|
|
1137
|
+
betLevels: [0.1, 0.2, 0.5, 1, 2, 5, 10],
|
|
880
1138
|
},
|
|
881
1139
|
onPlay: ({ action, bet, roundId }) => {
|
|
882
1140
|
// Return custom play result
|
|
@@ -885,7 +1143,7 @@ const bridge = new DevBridge({
|
|
|
885
1143
|
},
|
|
886
1144
|
});
|
|
887
1145
|
|
|
888
|
-
bridge.start();
|
|
1146
|
+
bridge.start(); // Creates SDK Bridge({ devMode: true }) + registers handlers
|
|
889
1147
|
|
|
890
1148
|
// Update balance programmatically
|
|
891
1149
|
bridge.setBalance(5000);
|
|
@@ -894,6 +1152,8 @@ bridge.setBalance(5000);
|
|
|
894
1152
|
bridge.destroy();
|
|
895
1153
|
```
|
|
896
1154
|
|
|
1155
|
+
When using the Vite plugin with `devBridge: true`, the SDK is automatically configured with `devMode: true` so both sides use the same `MemoryChannel`.
|
|
1156
|
+
|
|
897
1157
|
### Handled Messages
|
|
898
1158
|
|
|
899
1159
|
| Message | Description |
|
|
@@ -920,7 +1180,7 @@ fps.toggle();
|
|
|
920
1180
|
fps.hide();
|
|
921
1181
|
```
|
|
922
1182
|
|
|
923
|
-
When `debug: true` is set in `GameApplicationConfig`, the FPS overlay is
|
|
1183
|
+
When `debug: true` is set in `GameApplicationConfig`, the FPS overlay is created and shown automatically — no manual setup needed.
|
|
924
1184
|
|
|
925
1185
|
The overlay displays:
|
|
926
1186
|
- Average FPS
|
|
@@ -1077,9 +1337,10 @@ class Tween {
|
|
|
1077
1337
|
static to(target: any, props: Record<string, number>, duration: number, easing?: EasingFunction, onUpdate?: (p: number) => void): Promise<void>;
|
|
1078
1338
|
static from(target: any, props: Record<string, number>, duration: number, easing?, onUpdate?): Promise<void>;
|
|
1079
1339
|
static fromTo(target: any, fromProps: Record<string, number>, toProps: Record<string, number>, duration: number, easing?, onUpdate?): Promise<void>;
|
|
1080
|
-
static delay(ms: number): Promise<void>;
|
|
1340
|
+
static delay(ms: number): Promise<void>; // Uses PixiJS Ticker
|
|
1081
1341
|
static killTweensOf(target: any): void;
|
|
1082
1342
|
static killAll(): void;
|
|
1343
|
+
static reset(): void; // Kill all + remove ticker listener
|
|
1083
1344
|
}
|
|
1084
1345
|
```
|
|
1085
1346
|
|
|
@@ -1110,10 +1371,113 @@ class InputManager extends EventEmitter<InputEvents> {
|
|
|
1110
1371
|
lock(): void;
|
|
1111
1372
|
unlock(): void;
|
|
1112
1373
|
isKeyDown(key: string): boolean;
|
|
1374
|
+
setViewportTransform(scale: number, offsetX: number, offsetY: number): void;
|
|
1375
|
+
getWorldPosition(canvasX: number, canvasY: number): { x: number; y: number };
|
|
1113
1376
|
destroy(): void;
|
|
1114
1377
|
}
|
|
1115
1378
|
```
|
|
1116
1379
|
|
|
1380
|
+
### Layout
|
|
1381
|
+
|
|
1382
|
+
> Powered by `@pixi/layout` (Yoga flexbox). Uses the `@pixi/layout` mixin on a standard `Container`.
|
|
1383
|
+
|
|
1384
|
+
```typescript
|
|
1385
|
+
class Layout extends Container {
|
|
1386
|
+
get items(): readonly Container[];
|
|
1387
|
+
|
|
1388
|
+
constructor(config?: LayoutConfig);
|
|
1389
|
+
addItem(child: Container): this;
|
|
1390
|
+
removeItem(child: Container): this;
|
|
1391
|
+
clearItems(): this;
|
|
1392
|
+
updateViewport(width: number, height: number): void;
|
|
1393
|
+
}
|
|
1394
|
+
```
|
|
1395
|
+
|
|
1396
|
+
### ScrollContainer
|
|
1397
|
+
|
|
1398
|
+
> Extends `@pixi/ui` ScrollBox — inherits touch/drag scrolling, mouse wheel, inertia, and dynamic rendering.
|
|
1399
|
+
|
|
1400
|
+
```typescript
|
|
1401
|
+
class ScrollContainer extends ScrollBox {
|
|
1402
|
+
get scrollPosition(): { x: number; y: number };
|
|
1403
|
+
|
|
1404
|
+
constructor(config: ScrollContainerConfig);
|
|
1405
|
+
setContent(content: Container): void;
|
|
1406
|
+
addItem(...items: Container[]): Container;
|
|
1407
|
+
scrollToItem(index: number): void;
|
|
1408
|
+
}
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
### Button
|
|
1412
|
+
|
|
1413
|
+
> Extends `@pixi/ui` FancyButton — per-state views, press animation, and text.
|
|
1414
|
+
|
|
1415
|
+
```typescript
|
|
1416
|
+
class Button extends FancyButton {
|
|
1417
|
+
get disabled(): boolean;
|
|
1418
|
+
|
|
1419
|
+
constructor(config?: ButtonConfig);
|
|
1420
|
+
enable(): void;
|
|
1421
|
+
disable(): void;
|
|
1422
|
+
|
|
1423
|
+
// Inherited from FancyButton
|
|
1424
|
+
onPress: Signal; // btn.onPress.connect(() => { … })
|
|
1425
|
+
enabled: boolean;
|
|
1426
|
+
}
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
### Panel
|
|
1430
|
+
|
|
1431
|
+
> Extends `@pixi/layout/components` LayoutContainer — flex-layout background panel with optional 9-slice texture.
|
|
1432
|
+
|
|
1433
|
+
```typescript
|
|
1434
|
+
class Panel extends LayoutContainer {
|
|
1435
|
+
get content(): Container; // children added here participate in flexbox layout
|
|
1436
|
+
|
|
1437
|
+
constructor(config?: PanelConfig);
|
|
1438
|
+
setSize(width: number, height: number): void;
|
|
1439
|
+
}
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
### ProgressBar
|
|
1443
|
+
|
|
1444
|
+
> Wraps `@pixi/ui` ProgressBar — optional smooth animated fill via per-frame `update()`.
|
|
1445
|
+
|
|
1446
|
+
```typescript
|
|
1447
|
+
class ProgressBar extends Container {
|
|
1448
|
+
get progress(): number;
|
|
1449
|
+
set progress(value: number); // 0..1
|
|
1450
|
+
|
|
1451
|
+
constructor(config?: ProgressBarConfig);
|
|
1452
|
+
update(dt: number): void; // call each frame when animated: true
|
|
1453
|
+
}
|
|
1454
|
+
```
|
|
1455
|
+
|
|
1456
|
+
### Toast
|
|
1457
|
+
|
|
1458
|
+
> Uses `@pixi/layout` LayoutContainer for auto-sized background. Supports info / success / warning / error types.
|
|
1459
|
+
|
|
1460
|
+
```typescript
|
|
1461
|
+
class Toast extends Container {
|
|
1462
|
+
constructor(config?: ToastConfig);
|
|
1463
|
+
async show(message: string, type?: ToastType, viewWidth?: number, viewHeight?: number): Promise<void>;
|
|
1464
|
+
async dismiss(): Promise<void>;
|
|
1465
|
+
}
|
|
1466
|
+
```
|
|
1467
|
+
|
|
1468
|
+
### SpriteAnimation
|
|
1469
|
+
|
|
1470
|
+
```typescript
|
|
1471
|
+
class SpriteAnimation {
|
|
1472
|
+
static create(textures: Texture[], config?: SpriteAnimationConfig): AnimatedSprite;
|
|
1473
|
+
static fromSpritesheet(sheet: Spritesheet, prefix: string, config?: SpriteAnimationConfig): AnimatedSprite;
|
|
1474
|
+
static fromRange(sheet: Spritesheet, pattern: string, start: number, end: number, config?: SpriteAnimationConfig): AnimatedSprite;
|
|
1475
|
+
static fromAliases(aliases: string[], config?: SpriteAnimationConfig): AnimatedSprite;
|
|
1476
|
+
static playOnce(textures: Texture[], config?: SpriteAnimationConfig): { sprite: AnimatedSprite; finished: Promise<void> };
|
|
1477
|
+
static getTexturesByPrefix(sheet: Spritesheet, prefix: string): Texture[];
|
|
1478
|
+
}
|
|
1479
|
+
```
|
|
1480
|
+
|
|
1117
1481
|
### EventEmitter
|
|
1118
1482
|
|
|
1119
1483
|
```typescript
|
|
@@ -1121,7 +1485,8 @@ class EventEmitter<TEvents extends {}> {
|
|
|
1121
1485
|
on<K>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
1122
1486
|
once<K>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
1123
1487
|
off<K>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
1124
|
-
|
|
1488
|
+
// Void events can be emitted without a data argument
|
|
1489
|
+
emit<K>(event: K, ...args): void;
|
|
1125
1490
|
removeAllListeners(event?: keyof TEvents): this;
|
|
1126
1491
|
}
|
|
1127
1492
|
```
|