@energy8platform/game-engine 0.3.0 → 0.5.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 CHANGED
@@ -51,6 +51,9 @@ npm init -y
51
51
  # Install dependencies
52
52
  npm install pixi.js @energy8platform/game-sdk @energy8platform/game-engine
53
53
 
54
+ # Install UI layout dependencies (optional — needed for Layout, Panel, ScrollContainer)
55
+ npm install @pixi/ui @pixi/layout yoga-layout
56
+
54
57
  # (Optional) install spine and audio support
55
58
  npm install @pixi/sound @esotericsoftware/spine-pixi-v8
56
59
  ```
@@ -120,6 +123,9 @@ bootstrap();
120
123
  | --- | --- | --- |
121
124
  | `pixi.js` | `^8.16.0` | Yes |
122
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 (Yoga flexbox) |
128
+ | `yoga-layout` | `^3.0.0` | Optional — peer dep of `@pixi/layout` |
123
129
  | `@pixi/sound` | `^6.0.0` | Optional — for audio |
124
130
  | `@esotericsoftware/spine-pixi-v8` | `~4.2.0` | Optional — for Spine animations |
125
131
 
@@ -132,7 +138,7 @@ import { GameApplication } from '@energy8platform/game-engine'; // full b
132
138
  import { Scene, SceneManager } from '@energy8platform/game-engine/core';
133
139
  import { AssetManager } from '@energy8platform/game-engine/assets';
134
140
  import { AudioManager } from '@energy8platform/game-engine/audio';
135
- import { Button, Label, Modal, Layout, ScrollContainer } from '@energy8platform/game-engine/ui';
141
+ import { Button, Label, Modal, Layout, ScrollContainer, Panel, Toast } from '@energy8platform/game-engine/ui';
136
142
  import { Tween, Timeline, Easing, SpriteAnimation } from '@energy8platform/game-engine/animation';
137
143
  import { DevBridge, FPSOverlay } from '@energy8platform/game-engine/debug';
138
144
  import { defineGameConfig } from '@energy8platform/game-engine/vite';
@@ -696,9 +702,31 @@ console.log(SpineHelper.getAnimationNames(spine));
696
702
 
697
703
  ## UI Components
698
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
+ > **Important:** These packages are optional peer dependencies. Import UI components from the `@energy8platform/game-engine/ui` sub-path to ensure tree-shaking works correctly. The root `@energy8platform/game-engine` barrel re-exports UI components but does **not** activate the `@pixi/layout` mixin — that only happens when importing from `/ui`.
712
+ >
713
+ > **Direct access:** The engine wraps only the most common UI components. For anything else from `@pixi/ui` (e.g. `Slider`, `CheckBox`, `Input`, `Select`, `RadioGroup`, `List`, `DoubleSlider`, `Switcher`) or `@pixi/layout` (e.g. `LayoutContainer`, `LayoutView`, `Trackpad`, layout-aware sprites), import directly from the source package:
714
+ >
715
+ > ```typescript
716
+ > // Engine-wrapped components
717
+ > import { Button, Panel, Layout, ScrollContainer } from '@energy8platform/game-engine/ui';
718
+ >
719
+ > // Raw @pixi/ui components (not wrapped by the engine)
720
+ > import { Slider, CheckBox, Input, Select, RadioGroup } from '@pixi/ui';
721
+ >
722
+ > // Raw @pixi/layout components
723
+ > import { LayoutContainer, LayoutView } from '@pixi/layout/components';
724
+ > import type { LayoutStyles } from '@pixi/layout';
725
+ > ```
726
+
699
727
  ### Layout
700
728
 
701
- Responsive layout container that automatically arranges children. Supports horizontal, vertical, grid, and wrap modes with alignment, padding, gap, anchor positioning, and viewport breakpoints.
729
+ 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.
702
730
 
703
731
  ```typescript
704
732
  import { Layout } from '@energy8platform/game-engine';
@@ -764,9 +792,11 @@ const tags = new Layout({
764
792
 
765
793
  **Anchor values:** `top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`, `bottom-left`, `bottom-center`, `bottom-right`
766
794
 
795
+ > **Under the hood**: Layout maps `direction` → Yoga `flexDirection`/`flexWrap`, `alignment` → `alignItems`, and uses `@pixi/layout`'s `container.layout = { ... }` mixin. Grid mode uses `flexGrow`/`flexBasis` when `gap > 0` to correctly distribute space, and percentage widths when `gap === 0`.
796
+
767
797
  ### ScrollContainer
768
798
 
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.
799
+ Scrollable container powered by `@pixi/ui` ScrollBox. Provides touch/drag scrolling, mouse wheel support, inertia, and dynamic rendering optimization for off-screen items.
770
800
 
771
801
  ```typescript
772
802
  import { ScrollContainer } from '@energy8platform/game-engine';
@@ -775,37 +805,30 @@ const scroll = new ScrollContainer({
775
805
  width: 600,
776
806
  height: 400,
777
807
  direction: 'vertical',
778
- showScrollbar: true,
779
- elasticity: 0.3,
780
- inertia: 0.92,
808
+ elementsMargin: 8,
781
809
  borderRadius: 12,
782
810
  backgroundColor: 0x1a1a2e,
783
- backgroundAlpha: 0.8,
784
811
  });
785
812
 
786
- // Build scrollable content
787
- const list = new Container();
813
+ // Add items directly
788
814
  for (let i = 0; i < 50; i++) {
789
- const row = createRow(i);
790
- row.y = i * 40;
791
- list.addChild(row);
815
+ scroll.addItem(createRow(i));
792
816
  }
793
- scroll.setContent(list);
794
817
 
795
818
  scene.container.addChild(scroll);
796
819
  ```
797
820
 
798
821
  ```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)
822
+ // Or set content from a Container
823
+ const list = new Container();
824
+ items.forEach((item) => list.addChild(item));
825
+ scroll.setContent(list);
826
+
827
+ // Scroll to a specific item index
828
+ scroll.scrollToItem(5);
803
829
 
804
830
  // Current position
805
831
  const { x, y } = scroll.scrollPosition;
806
-
807
- // Resize viewport
808
- scroll.resize(newWidth, newHeight);
809
832
  ```
810
833
 
811
834
  #### ScrollContainerConfig
@@ -815,19 +838,20 @@ scroll.resize(newWidth, newHeight);
815
838
  | `width` | `number` | — | Visible viewport width |
816
839
  | `height` | `number` | — | Visible viewport height |
817
840
  | `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 |
841
+ | `backgroundColor` | `ColorSource` | | Background color (transparent if omitted) |
827
842
  | `borderRadius` | `number` | `0` | Mask border radius |
843
+ | `elementsMargin` | `number` | `0` | Gap between items |
844
+ | `padding` | `number` | `0` | Content padding |
845
+ | `disableDynamicRendering` | `boolean` | `false` | Render all items even when off-screen |
846
+ | `disableEasing` | `boolean` | `false` | Disable scroll inertia/easing |
847
+ | `globalScroll` | `boolean` | `true` | Scroll even when mouse is not over the component |
848
+
849
+ > **Note:** ScrollContainer extends `@pixi/ui` `ScrollBox`. All `ScrollBox` methods and options are available.
828
850
 
829
851
  ### Button
830
852
 
853
+ Powered by `@pixi/ui` `FancyButton`. Supports both texture-based and Graphics-based rendering with per-state views, press animation, and built-in text.
854
+
831
855
  ```typescript
832
856
  import { Button } from '@energy8platform/game-engine';
833
857
 
@@ -836,23 +860,25 @@ const spinBtn = new Button({
836
860
  height: 60,
837
861
  borderRadius: 12,
838
862
  colors: {
839
- normal: 0xffd700,
863
+ default: 0xffd700,
840
864
  hover: 0xffe44d,
841
865
  pressed: 0xccac00,
842
866
  disabled: 0x666666,
843
867
  },
844
868
  pressScale: 0.95,
845
869
  animationDuration: 100,
870
+ text: 'SPIN',
846
871
  });
847
872
 
848
- spinBtn.onTap = () => {
873
+ // Connect press event (Signal-based)
874
+ spinBtn.onPress.connect(() => {
849
875
  console.log('Spin!');
850
- };
876
+ });
851
877
 
852
878
  // Or use texture-based states
853
879
  const btn = new Button({
854
880
  textures: {
855
- normal: 'btn-normal',
881
+ default: 'btn-default',
856
882
  hover: 'btn-hover',
857
883
  pressed: 'btn-pressed',
858
884
  disabled: 'btn-disabled',
@@ -863,6 +889,8 @@ btn.enable();
863
889
  btn.disable();
864
890
  ```
865
891
 
892
+ > **Breaking change:** Button states renamed from `normal` to `default`. The `onTap` callback is replaced by `onPress.connect()` (typed-signals Signal).
893
+
866
894
  ### Label
867
895
 
868
896
  ```typescript
@@ -908,6 +936,8 @@ winDisplay.hide();
908
936
 
909
937
  ### ProgressBar
910
938
 
939
+ Horizontal progress bar powered by `@pixi/ui` ProgressBar. Supports animated smooth fill.
940
+
911
941
  ```typescript
912
942
  import { ProgressBar } from '@energy8platform/game-engine';
913
943
 
@@ -917,24 +947,40 @@ const bar = new ProgressBar({
917
947
  fillColor: 0x00ff00,
918
948
  trackColor: 0x333333,
919
949
  borderRadius: 10,
950
+ animated: true,
920
951
  });
921
952
 
922
953
  bar.progress = 0.75; // 75%
954
+
955
+ // Call each frame for smooth animation
956
+ bar.update(dt);
923
957
  ```
924
958
 
925
959
  ### Panel
926
960
 
927
- Container with a background:
961
+ 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.
928
962
 
929
963
  ```typescript
930
964
  import { Panel } from '@energy8platform/game-engine';
931
965
 
966
+ // Simple colored panel
932
967
  const panel = new Panel({
933
968
  width: 600,
934
969
  height: 400,
935
970
  backgroundColor: 0x1a1a2e,
936
971
  borderRadius: 16,
937
972
  backgroundAlpha: 0.9,
973
+ padding: 16,
974
+ });
975
+
976
+ panel.content.addChild(myText);
977
+
978
+ // 9-slice panel (texture-based)
979
+ const nineSlicePanel = new Panel({
980
+ nineSliceTexture: 'panel-bg',
981
+ nineSliceBorders: [20, 20, 20, 20],
982
+ width: 400,
983
+ height: 300,
938
984
  });
939
985
  ```
940
986
 
@@ -958,18 +1004,67 @@ await modal.hide();
958
1004
 
959
1005
  ### Toast
960
1006
 
961
- Brief notification messages:
1007
+ Brief notification messages with animated appearance/dismissal.
962
1008
 
963
1009
  ```typescript
964
1010
  import { Toast } from '@energy8platform/game-engine';
965
1011
 
966
1012
  const toast = new Toast({
967
1013
  duration: 2000,
1014
+ bottomOffset: 60,
1015
+ });
1016
+
1017
+ // Show with type and viewport size
1018
+ await toast.show('Free spins activated!', 'success', viewWidth, viewHeight);
1019
+
1020
+ // Dismiss manually
1021
+ await toast.dismiss();
1022
+ ```
1023
+
1024
+ **Toast types:** `info`, `success`, `warning`, `error` — each with a distinct color.
1025
+
1026
+ ---
1027
+
1028
+ ### Using Raw `@pixi/ui` and `@pixi/layout` Components
1029
+
1030
+ The engine wraps the most common components, but both libraries offer much more. Here are examples of using raw components alongside the engine:
1031
+
1032
+ ```typescript
1033
+ import { Slider, CheckBox, Input, Select } from '@pixi/ui';
1034
+ import { LayoutContainer } from '@pixi/layout/components';
1035
+ import type { LayoutStyles } from '@pixi/layout';
1036
+
1037
+ // Slider (not wrapped by the engine)
1038
+ const volumeSlider = new Slider({
1039
+ bg: bgSprite,
1040
+ fill: fillSprite,
1041
+ slider: handleSprite,
1042
+ min: 0,
1043
+ max: 100,
1044
+ value: 50,
968
1045
  });
969
1046
 
970
- await toast.show('Free spins activated!', 'success');
1047
+ // CheckBox
1048
+ const muteCheck = new CheckBox({
1049
+ style: {
1050
+ unchecked: uncheckedView,
1051
+ checked: checkedView,
1052
+ },
1053
+ });
1054
+
1055
+ // LayoutContainer with flexbox styles
1056
+ const row = new LayoutContainer();
1057
+ row.layout = {
1058
+ flexDirection: 'row',
1059
+ gap: 12,
1060
+ alignItems: 'center',
1061
+ padding: 16,
1062
+ } satisfies LayoutStyles;
1063
+ row.addChild(volumeSlider, muteCheck);
971
1064
  ```
972
1065
 
1066
+ > **Tip:** Any container created after importing `@energy8platform/game-engine/ui` can use the `container.layout = { ... }` mixin — the `@pixi/layout` side-effect is automatically activated.
1067
+
973
1068
  ---
974
1069
 
975
1070
  ## Input
@@ -1046,12 +1141,13 @@ export default defineGameConfig({
1046
1141
 
1047
1142
  ### What `defineGameConfig` Provides
1048
1143
 
1049
- - **Build target: ES2020+**modern output for all supported casino platforms
1144
+ - **Build target: ESNext**required for `yoga-layout` WASM (uses top-level `await`)
1050
1145
  - **Asset inlining** — files under 8KB are auto-inlined
1051
1146
  - **PixiJS chunk splitting** — `pixi.js` is extracted into a separate chunk for caching
1052
1147
  - **DevBridge injection** — automatically available in dev mode via virtual module
1053
1148
  - **Dev server** — port 3000, auto-open browser
1054
- - **Dependency optimization** — `pixi.js` pre-bundled for faster dev starts
1149
+ - **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)
1150
+ - **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
1055
1151
 
1056
1152
  ### Custom DevBridge Configuration
1057
1153
 
@@ -1341,6 +1437,8 @@ class InputManager extends EventEmitter<InputEvents> {
1341
1437
 
1342
1438
  ### Layout
1343
1439
 
1440
+ > Powered by `@pixi/layout` (Yoga flexbox). Uses the `@pixi/layout` mixin on a standard `Container`.
1441
+
1344
1442
  ```typescript
1345
1443
  class Layout extends Container {
1346
1444
  get items(): readonly Container[];
@@ -1350,23 +1448,78 @@ class Layout extends Container {
1350
1448
  removeItem(child: Container): this;
1351
1449
  clearItems(): this;
1352
1450
  updateViewport(width: number, height: number): void;
1353
- layout(): void;
1354
1451
  }
1355
1452
  ```
1356
1453
 
1357
1454
  ### ScrollContainer
1358
1455
 
1456
+ > Extends `@pixi/ui` ScrollBox — inherits touch/drag scrolling, mouse wheel, inertia, and dynamic rendering.
1457
+
1359
1458
  ```typescript
1360
- class ScrollContainer extends Container {
1361
- get content(): Container | null;
1459
+ class ScrollContainer extends ScrollBox {
1362
1460
  get scrollPosition(): { x: number; y: number };
1363
1461
 
1364
1462
  constructor(config: ScrollContainerConfig);
1365
1463
  setContent(content: Container): void;
1366
- scrollTo(x: number, y: number, animate?: boolean): void;
1464
+ addItem(...items: Container[]): Container;
1367
1465
  scrollToItem(index: number): void;
1368
- resize(width: number, height: number): void;
1369
- destroy(): void;
1466
+ }
1467
+ ```
1468
+
1469
+ ### Button
1470
+
1471
+ > Extends `@pixi/ui` FancyButton — per-state views, press animation, and text.
1472
+
1473
+ ```typescript
1474
+ class Button extends FancyButton {
1475
+ get disabled(): boolean;
1476
+
1477
+ constructor(config?: ButtonConfig);
1478
+ enable(): void;
1479
+ disable(): void;
1480
+
1481
+ // Inherited from FancyButton
1482
+ onPress: Signal; // btn.onPress.connect(() => { … })
1483
+ enabled: boolean;
1484
+ }
1485
+ ```
1486
+
1487
+ ### Panel
1488
+
1489
+ > Extends `@pixi/layout/components` LayoutContainer — flex-layout background panel with optional 9-slice texture.
1490
+
1491
+ ```typescript
1492
+ class Panel extends LayoutContainer {
1493
+ get content(): Container; // children added here participate in flexbox layout
1494
+
1495
+ constructor(config?: PanelConfig);
1496
+ setSize(width: number, height: number): void;
1497
+ }
1498
+ ```
1499
+
1500
+ ### ProgressBar
1501
+
1502
+ > Wraps `@pixi/ui` ProgressBar — optional smooth animated fill via per-frame `update()`.
1503
+
1504
+ ```typescript
1505
+ class ProgressBar extends Container {
1506
+ get progress(): number;
1507
+ set progress(value: number); // 0..1
1508
+
1509
+ constructor(config?: ProgressBarConfig);
1510
+ update(dt: number): void; // call each frame when animated: true
1511
+ }
1512
+ ```
1513
+
1514
+ ### Toast
1515
+
1516
+ > Lightweight toast using `Graphics` for the background. Supports info / success / warning / error types.
1517
+
1518
+ ```typescript
1519
+ class Toast extends Container {
1520
+ constructor(config?: ToastConfig);
1521
+ async show(message: string, type?: ToastType, viewWidth?: number, viewHeight?: number): Promise<void>;
1522
+ async dismiss(): Promise<void>;
1370
1523
  }
1371
1524
  ```
1372
1525
 
package/dist/core.cjs.js CHANGED
@@ -2104,6 +2104,7 @@ class GameApplication extends EventEmitter {
2104
2104
  async initPixi() {
2105
2105
  this.app = new pixi_js.Application();
2106
2106
  const pixiOpts = {
2107
+ preference: 'webgl',
2107
2108
  background: typeof this.config.loading?.backgroundColor === 'number'
2108
2109
  ? this.config.loading.backgroundColor
2109
2110
  : 0x000000,