@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.
Files changed (63) hide show
  1. package/README.md +400 -35
  2. package/dist/animation.cjs.js +191 -1
  3. package/dist/animation.cjs.js.map +1 -1
  4. package/dist/animation.d.ts +117 -1
  5. package/dist/animation.esm.js +192 -3
  6. package/dist/animation.esm.js.map +1 -1
  7. package/dist/audio.cjs.js +66 -16
  8. package/dist/audio.cjs.js.map +1 -1
  9. package/dist/audio.d.ts +4 -0
  10. package/dist/audio.esm.js +66 -16
  11. package/dist/audio.esm.js.map +1 -1
  12. package/dist/core.cjs.js +307 -85
  13. package/dist/core.cjs.js.map +1 -1
  14. package/dist/core.d.ts +60 -1
  15. package/dist/core.esm.js +308 -86
  16. package/dist/core.esm.js.map +1 -1
  17. package/dist/debug.cjs.js +36 -68
  18. package/dist/debug.cjs.js.map +1 -1
  19. package/dist/debug.d.ts +4 -6
  20. package/dist/debug.esm.js +36 -68
  21. package/dist/debug.esm.js.map +1 -1
  22. package/dist/index.cjs.js +997 -475
  23. package/dist/index.cjs.js.map +1 -1
  24. package/dist/index.d.ts +356 -79
  25. package/dist/index.esm.js +983 -478
  26. package/dist/index.esm.js.map +1 -1
  27. package/dist/ui.cjs.js +816 -529
  28. package/dist/ui.cjs.js.map +1 -1
  29. package/dist/ui.d.ts +179 -41
  30. package/dist/ui.esm.js +798 -531
  31. package/dist/ui.esm.js.map +1 -1
  32. package/dist/vite.cjs.js +85 -68
  33. package/dist/vite.cjs.js.map +1 -1
  34. package/dist/vite.d.ts +17 -23
  35. package/dist/vite.esm.js +86 -68
  36. package/dist/vite.esm.js.map +1 -1
  37. package/package.json +19 -5
  38. package/src/animation/SpriteAnimation.ts +210 -0
  39. package/src/animation/Tween.ts +27 -1
  40. package/src/animation/index.ts +2 -0
  41. package/src/audio/AudioManager.ts +64 -15
  42. package/src/core/EventEmitter.ts +7 -1
  43. package/src/core/GameApplication.ts +19 -7
  44. package/src/core/SceneManager.ts +3 -1
  45. package/src/debug/DevBridge.ts +49 -80
  46. package/src/index.ts +22 -0
  47. package/src/input/InputManager.ts +26 -0
  48. package/src/loading/CSSPreloader.ts +7 -33
  49. package/src/loading/LoadingScene.ts +17 -41
  50. package/src/loading/index.ts +1 -0
  51. package/src/loading/logo.ts +95 -0
  52. package/src/types.ts +4 -0
  53. package/src/ui/BalanceDisplay.ts +12 -1
  54. package/src/ui/Button.ts +71 -130
  55. package/src/ui/Layout.ts +286 -0
  56. package/src/ui/Modal.ts +6 -5
  57. package/src/ui/Panel.ts +52 -55
  58. package/src/ui/ProgressBar.ts +52 -57
  59. package/src/ui/ScrollContainer.ts +126 -0
  60. package/src/ui/Toast.ts +19 -13
  61. package/src/ui/index.ts +17 -0
  62. package/src/viewport/ViewportManager.ts +2 -0
  63. 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 as any);
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.5.0` | Yes |
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 as any);
265
- scenes.register('game', GameScene as any);
266
- scenes.register('bonus', BonusScene as any);
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 (useful in timelines or sequences)
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
- normal: 0xffd700,
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
- spinBtn.onTap = () => {
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
- normal: 'btn-normal',
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
- Container with a background:
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
- await toast.show('Free spins activated!', 'success');
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
- assetExtensions: [
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
- - **PixiJS optimizations** — tree-shaking, proper externals handling
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
- - **Asset handling** — preconfigured loader patterns for game assets
837
- - **HTML minification** — optimized production builds
838
- - **Gzip-friendly output** — chunking strategy optimized for compression
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 intercepts `postMessage` calls from the SDK and responds with mock data.
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 enabled automatically.
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
- emit<K>(event: K, data: TEvents[K]): void;
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
  ```