@energy8platform/game-engine 0.4.0 → 0.6.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
@@ -36,6 +36,8 @@ A universal casino game engine built on [PixiJS v8](https://pixijs.com/) and [@e
36
36
  - [Vite Configuration](#vite-configuration)
37
37
  - [DevBridge](#devbridge)
38
38
  - [Debug](#debug)
39
+ - [Flexbox-First Layout](#flexbox-first-layout)
40
+ - [Using with React (`@pixi/react`)](#using-with-react-pixireact)
39
41
  - [API Reference](#api-reference)
40
42
  - [License](#license)
41
43
 
@@ -51,9 +53,12 @@ npm init -y
51
53
  # Install dependencies
52
54
  npm install pixi.js @energy8platform/game-sdk @energy8platform/game-engine
53
55
 
54
- # Install UI layout dependencies (optional — needed for Layout, Panel, Toast, ScrollContainer)
56
+ # Install UI layout dependencies (optional — needed for Layout, Panel, ScrollContainer)
55
57
  npm install @pixi/ui @pixi/layout yoga-layout
56
58
 
59
+ # (Optional) React integration
60
+ npm install @pixi/react react react-dom
61
+
57
62
  # (Optional) install spine and audio support
58
63
  npm install @pixi/sound @esotericsoftware/spine-pixi-v8
59
64
  ```
@@ -124,10 +129,11 @@ bootstrap();
124
129
  | `pixi.js` | `^8.16.0` | Yes |
125
130
  | `@energy8platform/game-sdk` | `^2.6.0` | Yes |
126
131
  | `@pixi/ui` | `^2.3.0` | Optional — for Button, ScrollContainer, ProgressBar |
127
- | `@pixi/layout` | `^3.2.0` | Optional — for Layout, Panel, Toast (Yoga flexbox) |
132
+ | `@pixi/layout` | `^3.2.0` | Optional — for Layout, Panel (Yoga flexbox) |
128
133
  | `yoga-layout` | `^3.0.0` | Optional — peer dep of `@pixi/layout` |
129
134
  | `@pixi/sound` | `^6.0.0` | Optional — for audio |
130
135
  | `@esotericsoftware/spine-pixi-v8` | `~4.2.0` | Optional — for Spine animations |
136
+ | `@pixi/react` | `^8.0.0` | Optional — for React integration (see [Using with React](#using-with-react-pixireact)) |
131
137
 
132
138
  ### Sub-path Exports
133
139
 
@@ -707,6 +713,22 @@ console.log(SpineHelper.getAnimationNames(spine));
707
713
  > ```bash
708
714
  > npm install @pixi/ui @pixi/layout yoga-layout
709
715
  > ```
716
+ >
717
+ > **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`.
718
+ >
719
+ > **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:
720
+ >
721
+ > ```typescript
722
+ > // Engine-wrapped components
723
+ > import { Button, Panel, Layout, ScrollContainer } from '@energy8platform/game-engine/ui';
724
+ >
725
+ > // Raw @pixi/ui components (not wrapped by the engine)
726
+ > import { Slider, CheckBox, Input, Select, RadioGroup } from '@pixi/ui';
727
+ >
728
+ > // Raw @pixi/layout components
729
+ > import { LayoutContainer, LayoutView } from '@pixi/layout/components';
730
+ > import type { LayoutStyles } from '@pixi/layout';
731
+ > ```
710
732
 
711
733
  ### Layout
712
734
 
@@ -776,7 +798,7 @@ const tags = new Layout({
776
798
 
777
799
  **Anchor values:** `top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`, `bottom-left`, `bottom-center`, `bottom-right`
778
800
 
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.
801
+ > **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`.
780
802
 
781
803
  ### ScrollContainer
782
804
 
@@ -988,7 +1010,7 @@ await modal.hide();
988
1010
 
989
1011
  ### Toast
990
1012
 
991
- Brief notification messages. Uses `@pixi/layout` `LayoutContainer` for auto-sized background.
1013
+ Brief notification messages with animated appearance/dismissal.
992
1014
 
993
1015
  ```typescript
994
1016
  import { Toast } from '@energy8platform/game-engine';
@@ -1009,6 +1031,262 @@ await toast.dismiss();
1009
1031
 
1010
1032
  ---
1011
1033
 
1034
+ ### Using Raw `@pixi/ui` and `@pixi/layout` Components
1035
+
1036
+ The engine wraps the most common components, but both libraries offer much more. Here are examples of using raw components alongside the engine:
1037
+
1038
+ ```typescript
1039
+ import { Slider, CheckBox, Input, Select } from '@pixi/ui';
1040
+ import { LayoutContainer } from '@pixi/layout/components';
1041
+ import type { LayoutStyles } from '@pixi/layout';
1042
+
1043
+ // Slider (not wrapped by the engine)
1044
+ const volumeSlider = new Slider({
1045
+ bg: bgSprite,
1046
+ fill: fillSprite,
1047
+ slider: handleSprite,
1048
+ min: 0,
1049
+ max: 100,
1050
+ value: 50,
1051
+ });
1052
+
1053
+ // CheckBox
1054
+ const muteCheck = new CheckBox({
1055
+ style: {
1056
+ unchecked: uncheckedView,
1057
+ checked: checkedView,
1058
+ },
1059
+ });
1060
+
1061
+ // LayoutContainer with flexbox styles
1062
+ const row = new LayoutContainer();
1063
+ row.layout = {
1064
+ flexDirection: 'row',
1065
+ gap: 12,
1066
+ alignItems: 'center',
1067
+ padding: 16,
1068
+ } satisfies LayoutStyles;
1069
+ row.addChild(volumeSlider, muteCheck);
1070
+ ```
1071
+
1072
+ > **Tip:** Any container created after importing `@energy8platform/game-engine/ui` can use the `container.layout = { ... }` mixin — the `@pixi/layout` side-effect is automatically activated.
1073
+
1074
+ ---
1075
+
1076
+ ## Flexbox-First Layout
1077
+
1078
+ > **Best practice:** Use `@pixi/layout` flexbox instead of manual pixel positioning. Flexbox adapts to screen sizes, handles RTL, and eliminates fragile `x = width / 2 - 100` math.
1079
+
1080
+ ### ❌ Avoid: Manual Pixel Positioning
1081
+
1082
+ ```typescript
1083
+ // Fragile — breaks when screen size, text length, or element sizes change
1084
+ onResize(width: number, height: number) {
1085
+ this.title.x = width / 2;
1086
+ this.title.y = 80;
1087
+ this.balance.x = width / 2;
1088
+ this.balance.y = 220;
1089
+ this.spinButton.x = width / 2;
1090
+ this.spinButton.y = height - 120;
1091
+ }
1092
+ ```
1093
+
1094
+ ### ✅ Prefer: Flexbox Layout
1095
+
1096
+ ```typescript
1097
+ import { Layout } from '@energy8platform/game-engine/ui';
1098
+ import { LayoutContainer } from '@pixi/layout/components';
1099
+ import type { LayoutStyles } from '@pixi/layout';
1100
+
1101
+ // Root layout — fills the screen, stacks children vertically
1102
+ const root = new LayoutContainer();
1103
+ root.layout = {
1104
+ width: '100%',
1105
+ height: '100%',
1106
+ flexDirection: 'column',
1107
+ alignItems: 'center',
1108
+ justifyContent: 'space-between',
1109
+ padding: 40,
1110
+ } satisfies LayoutStyles;
1111
+
1112
+ // Header area
1113
+ const header = new LayoutContainer();
1114
+ header.layout = {
1115
+ flexDirection: 'column',
1116
+ alignItems: 'center',
1117
+ gap: 12,
1118
+ };
1119
+ header.addChild(title, subtitle, balance);
1120
+
1121
+ // Center area — expands to fill available space
1122
+ const center = new LayoutContainer();
1123
+ center.layout = {
1124
+ flexGrow: 1,
1125
+ alignItems: 'center',
1126
+ justifyContent: 'center',
1127
+ };
1128
+ center.addChild(winDisplay);
1129
+
1130
+ // Footer toolbar
1131
+ const footer = new Layout({
1132
+ direction: 'horizontal',
1133
+ gap: 20,
1134
+ alignment: 'center',
1135
+ });
1136
+ footer.addItem(betLabel);
1137
+ footer.addItem(spinButton);
1138
+
1139
+ root.addChild(header, center, footer);
1140
+ scene.container.addChild(root);
1141
+ ```
1142
+
1143
+ ### When to Use Each Approach
1144
+
1145
+ | Approach | Use When |
1146
+ |---|---|
1147
+ | Engine `Layout` | Toolbar-style rows/columns with anchor positioning and breakpoints |
1148
+ | `LayoutContainer` (raw) | Full flexbox control — `flexGrow`, `justifyContent`, percentage sizes |
1149
+ | `container.layout = { ... }` (mixin) | Adding flex styles to any existing PixiJS container |
1150
+ | Manual pixel positioning | Exact artistic placement (e.g. particle emitters, spine anchors) |
1151
+
1152
+ ### Nested Flexbox Example
1153
+
1154
+ ```typescript
1155
+ // settings-panel.ts — responsive settings UI
1156
+ import { LayoutContainer } from '@pixi/layout/components';
1157
+ import { Slider, CheckBox } from '@pixi/ui';
1158
+ import { Panel, Label } from '@energy8platform/game-engine/ui';
1159
+
1160
+ const panel = new Panel({
1161
+ width: 500,
1162
+ height: 400,
1163
+ backgroundColor: 0x1a1a2e,
1164
+ borderRadius: 16,
1165
+ padding: 24,
1166
+ });
1167
+
1168
+ // Each row: label on left, control on right
1169
+ function settingsRow(labelText: string, control: Container): LayoutContainer {
1170
+ const row = new LayoutContainer();
1171
+ row.layout = {
1172
+ flexDirection: 'row',
1173
+ justifyContent: 'space-between',
1174
+ alignItems: 'center',
1175
+ width: '100%',
1176
+ height: 48,
1177
+ };
1178
+ row.addChild(new Label({ text: labelText }), control);
1179
+ return row;
1180
+ }
1181
+
1182
+ panel.content.addChild(
1183
+ settingsRow('Music Volume', musicSlider),
1184
+ settingsRow('SFX Volume', sfxSlider),
1185
+ settingsRow('Mute', muteCheckbox),
1186
+ );
1187
+ ```
1188
+
1189
+ ---
1190
+
1191
+ ## Using with React (`@pixi/react`)
1192
+
1193
+ For React-based projects, [`@pixi/react`](https://github.com/pixijs/pixi-react) provides declarative JSX components for PixiJS. The engine is framework-agnostic — `@pixi/react` is **not** a dependency, but works seamlessly alongside it.
1194
+
1195
+ ### Installation
1196
+
1197
+ ```bash
1198
+ npm install @pixi/react react react-dom
1199
+ ```
1200
+
1201
+ ### Wrapping the Engine in React
1202
+
1203
+ ```tsx
1204
+ import { Application, extend } from '@pixi/react';
1205
+ import { Container, Graphics, Text } from 'pixi.js';
1206
+ import { GameApplication, Scene } from '@energy8platform/game-engine';
1207
+
1208
+ // Register PixiJS components for JSX
1209
+ extend({ Container, Graphics, Text });
1210
+
1211
+ function GameWrapper() {
1212
+ return (
1213
+ <Application
1214
+ width={1920}
1215
+ height={1080}
1216
+ background={0x0f0f23}
1217
+ >
1218
+ <GameContent />
1219
+ </Application>
1220
+ );
1221
+ }
1222
+ ```
1223
+
1224
+ ### Using Engine Components in JSX
1225
+
1226
+ Engine components (`Button`, `Panel`, `Layout`, etc.) are PixiJS `Container` subclasses — use them with `@pixi/react`'s `extend`:
1227
+
1228
+ ```tsx
1229
+ import { extend, useTick } from '@pixi/react';
1230
+ import { Button, Label, Panel, ProgressBar } from '@energy8platform/game-engine/ui';
1231
+
1232
+ // Register engine components for JSX
1233
+ extend({ Button, Label, Panel, ProgressBar });
1234
+
1235
+ function GameHUD({ balance, bet }: { balance: number; bet: number }) {
1236
+ return (
1237
+ <container>
1238
+ <panel
1239
+ width={400}
1240
+ height={80}
1241
+ backgroundColor={0x1a1a2e}
1242
+ borderRadius={12}
1243
+ padding={16}
1244
+ >
1245
+ <label text={`Balance: $${balance.toFixed(2)}`} />
1246
+ <label text={`Bet: $${bet.toFixed(2)}`} />
1247
+ </panel>
1248
+ </container>
1249
+ );
1250
+ }
1251
+ ```
1252
+
1253
+ ### Combining Flexbox with React
1254
+
1255
+ ```tsx
1256
+ import { extend } from '@pixi/react';
1257
+ import { LayoutContainer } from '@pixi/layout/components';
1258
+
1259
+ extend({ LayoutContainer });
1260
+
1261
+ function FlexRow({ children }: { children: React.ReactNode }) {
1262
+ return (
1263
+ <layoutContainer
1264
+ layout={{
1265
+ flexDirection: 'row',
1266
+ gap: 16,
1267
+ alignItems: 'center',
1268
+ justifyContent: 'center',
1269
+ }}
1270
+ >
1271
+ {children}
1272
+ </layoutContainer>
1273
+ );
1274
+ }
1275
+
1276
+ function Toolbar() {
1277
+ return (
1278
+ <FlexRow>
1279
+ <button text="SPIN" width={180} height={60} />
1280
+ <label text="BET: $1.00" />
1281
+ </FlexRow>
1282
+ );
1283
+ }
1284
+ ```
1285
+
1286
+ > **Note:** `@pixi/react` is entirely optional. The engine works without React. Choose whichever approach fits your team and project.
1287
+
1288
+ ---
1289
+
1012
1290
  ## Input
1013
1291
 
1014
1292
  `InputManager` provides unified touch/mouse/keyboard handling with gesture detection:
@@ -1455,7 +1733,7 @@ class ProgressBar extends Container {
1455
1733
 
1456
1734
  ### Toast
1457
1735
 
1458
- > Uses `@pixi/layout` LayoutContainer for auto-sized background. Supports info / success / warning / error types.
1736
+ > Lightweight toast using `Graphics` for the background. Supports info / success / warning / error types.
1459
1737
 
1460
1738
  ```typescript
1461
1739
  class Toast extends Container {
package/dist/index.cjs.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var pixi_js = require('pixi.js');
4
4
  var gameSdk = require('@energy8platform/game-sdk');
5
- require('@pixi/layout');
6
5
  var ui = require('@pixi/ui');
7
6
  var components = require('@pixi/layout/components');
8
7
 
@@ -3569,8 +3568,6 @@ const TOAST_COLORS = {
3569
3568
  /**
3570
3569
  * Toast notification component for displaying transient messages.
3571
3570
  *
3572
- * Uses `@pixi/layout` LayoutContainer for auto-sized background.
3573
- *
3574
3571
  * @example
3575
3572
  * ```ts
3576
3573
  * const toast = new Toast();
@@ -3589,7 +3586,7 @@ class Toast extends pixi_js.Container {
3589
3586
  duration: config.duration ?? 3000,
3590
3587
  bottomOffset: config.bottomOffset ?? 60,
3591
3588
  };
3592
- this._bg = new components.LayoutContainer();
3589
+ this._bg = new pixi_js.Graphics();
3593
3590
  this.addChild(this._bg);
3594
3591
  this._text = new pixi_js.Text({
3595
3592
  text: '',
@@ -3615,16 +3612,10 @@ class Toast extends pixi_js.Container {
3615
3612
  const width = Math.max(200, this._text.width + padding * 2);
3616
3613
  const height = 44;
3617
3614
  const radius = 8;
3618
- // Style the background
3619
- this._bg.layout = {
3620
- width,
3621
- height,
3622
- borderRadius: radius,
3623
- backgroundColor: TOAST_COLORS[type],
3624
- };
3625
- // Center the bg around origin
3626
- this._bg.x = -width / 2;
3627
- this._bg.y = -height / 2;
3615
+ // Draw the background
3616
+ this._bg.clear();
3617
+ this._bg.roundRect(-width / 2, -height / 2, width, height, radius);
3618
+ this._bg.fill(TOAST_COLORS[type]);
3628
3619
  // Position
3629
3620
  if (viewWidth && viewHeight) {
3630
3621
  this.x = viewWidth / 2;
@@ -3811,12 +3802,18 @@ class Layout extends pixi_js.Container {
3811
3802
  applyGridChildWidth(child) {
3812
3803
  const effective = this.resolveConfig();
3813
3804
  const columns = effective.columns ?? this._layoutConfig.columns;
3814
- const pct = `${(100 / columns).toFixed(2)}%`;
3805
+ const gap = effective.gap ?? this._layoutConfig.gap;
3806
+ // Account for gaps between columns: total gap space = gap * (columns - 1)
3807
+ // Each column gets: (100% - total_gap) / columns
3808
+ // We use flexBasis + flexGrow to let Yoga handle the math when gap > 0
3809
+ const styles = gap > 0
3810
+ ? { flexBasis: 0, flexGrow: 1, flexShrink: 1, maxWidth: `${(100 / columns).toFixed(2)}%` }
3811
+ : { width: `${(100 / columns).toFixed(2)}%` };
3815
3812
  if (child._layout) {
3816
- child._layout.setStyle({ width: pct });
3813
+ child._layout.setStyle(styles);
3817
3814
  }
3818
3815
  else {
3819
- child.layout = { width: pct };
3816
+ child.layout = styles;
3820
3817
  }
3821
3818
  }
3822
3819
  applyAnchor() {
@@ -4109,22 +4106,6 @@ class DevBridge {
4109
4106
  }
4110
4107
  }
4111
4108
 
4112
- Object.defineProperty(exports, "ButtonContainer", {
4113
- enumerable: true,
4114
- get: function () { return ui.ButtonContainer; }
4115
- });
4116
- Object.defineProperty(exports, "FancyButton", {
4117
- enumerable: true,
4118
- get: function () { return ui.FancyButton; }
4119
- });
4120
- Object.defineProperty(exports, "ScrollBox", {
4121
- enumerable: true,
4122
- get: function () { return ui.ScrollBox; }
4123
- });
4124
- Object.defineProperty(exports, "LayoutContainer", {
4125
- enumerable: true,
4126
- get: function () { return components.LayoutContainer; }
4127
- });
4128
4109
  exports.AssetManager = AssetManager;
4129
4110
  exports.AudioManager = AudioManager;
4130
4111
  exports.BalanceDisplay = BalanceDisplay;