@thewhateverapp/tile-sdk 0.14.9 → 0.14.11
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/dist/pixi/index.d.ts +25 -4
- package/dist/pixi/index.d.ts.map +1 -1
- package/dist/pixi/index.js +27 -6
- package/dist/react/PixiGame.d.ts +41 -92
- package/dist/react/PixiGame.d.ts.map +1 -1
- package/dist/react/PixiGame.js +123 -114
- package/dist/scene/SceneFromJson.d.ts +1 -1
- package/dist/scene/SceneFromJson.d.ts.map +1 -1
- package/dist/scene/SceneFromJson.js +4 -31
- package/dist/scene/SceneRenderer.d.ts.map +1 -1
- package/dist/scene/SceneRenderer.js +94 -36
- package/dist/scene/entities/EntityGraphics.d.ts +26 -0
- package/dist/scene/entities/EntityGraphics.d.ts.map +1 -0
- package/dist/scene/entities/EntityGraphics.js +226 -0
- package/dist/scene/index.d.ts +2 -2
- package/dist/scene/index.d.ts.map +1 -1
- package/dist/scene/index.js +2 -2
- package/package.json +2 -12
- package/dist/scene/entities/EntityRenderer.d.ts +0 -14
- package/dist/scene/entities/EntityRenderer.d.ts.map +0 -1
- package/dist/scene/entities/EntityRenderer.js +0 -203
package/dist/pixi/index.d.ts
CHANGED
|
@@ -2,21 +2,42 @@
|
|
|
2
2
|
* Pixi.js game components for tile-sdk
|
|
3
3
|
*
|
|
4
4
|
* Import from '@thewhateverapp/tile-sdk/pixi' to use pixi.js features.
|
|
5
|
-
* This
|
|
5
|
+
* This uses pixi.js directly (no @pixi/react) for better stability.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```tsx
|
|
9
|
-
* import { PixiGame,
|
|
9
|
+
* import { PixiGame, usePixiApp, useGameLoop } from '@thewhateverapp/tile-sdk/pixi';
|
|
10
|
+
* import * as PIXI from 'pixi.js';
|
|
10
11
|
*
|
|
11
12
|
* function MyGame() {
|
|
12
13
|
* return (
|
|
13
|
-
* <PixiGame>
|
|
14
|
+
* <PixiGame width={256} height={554}>
|
|
14
15
|
* <GameContent />
|
|
15
16
|
* </PixiGame>
|
|
16
17
|
* );
|
|
17
18
|
* }
|
|
19
|
+
*
|
|
20
|
+
* function GameContent() {
|
|
21
|
+
* const app = usePixiApp();
|
|
22
|
+
*
|
|
23
|
+
* useEffect(() => {
|
|
24
|
+
* if (!app) return;
|
|
25
|
+
* const graphics = new PIXI.Graphics();
|
|
26
|
+
* graphics.beginFill(0xff0000);
|
|
27
|
+
* graphics.drawRect(100, 100, 50, 50);
|
|
28
|
+
* graphics.endFill();
|
|
29
|
+
* app.stage.addChild(graphics);
|
|
30
|
+
* return () => graphics.destroy();
|
|
31
|
+
* }, [app]);
|
|
32
|
+
*
|
|
33
|
+
* useGameLoop((delta) => {
|
|
34
|
+
* // Game logic runs every frame
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* return null;
|
|
38
|
+
* }
|
|
18
39
|
* ```
|
|
19
40
|
*/
|
|
20
|
-
export { PixiGame, useGameLoop, useGameState, useGameInput, Container,
|
|
41
|
+
export { PixiGame, usePixiApp, useApp, useGameLoop, useGameState, useGameInput, Container, Graphics, Text, Sprite, TILE_WIDTH, TILE_HEIGHT, } from '../react/PixiGame.js';
|
|
21
42
|
export type { PixiGameProps } from '../react/PixiGame.js';
|
|
22
43
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/pixi/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pixi/index.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pixi/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EACL,QAAQ,EACR,UAAU,EACV,MAAM,EACN,WAAW,EACX,YAAY,EACZ,YAAY,EAEZ,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,MAAM,EAEN,UAAU,EACV,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/pixi/index.js
CHANGED
|
@@ -3,23 +3,44 @@
|
|
|
3
3
|
* Pixi.js game components for tile-sdk
|
|
4
4
|
*
|
|
5
5
|
* Import from '@thewhateverapp/tile-sdk/pixi' to use pixi.js features.
|
|
6
|
-
* This
|
|
6
|
+
* This uses pixi.js directly (no @pixi/react) for better stability.
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```tsx
|
|
10
|
-
* import { PixiGame,
|
|
10
|
+
* import { PixiGame, usePixiApp, useGameLoop } from '@thewhateverapp/tile-sdk/pixi';
|
|
11
|
+
* import * as PIXI from 'pixi.js';
|
|
11
12
|
*
|
|
12
13
|
* function MyGame() {
|
|
13
14
|
* return (
|
|
14
|
-
* <PixiGame>
|
|
15
|
+
* <PixiGame width={256} height={554}>
|
|
15
16
|
* <GameContent />
|
|
16
17
|
* </PixiGame>
|
|
17
18
|
* );
|
|
18
19
|
* }
|
|
20
|
+
*
|
|
21
|
+
* function GameContent() {
|
|
22
|
+
* const app = usePixiApp();
|
|
23
|
+
*
|
|
24
|
+
* useEffect(() => {
|
|
25
|
+
* if (!app) return;
|
|
26
|
+
* const graphics = new PIXI.Graphics();
|
|
27
|
+
* graphics.beginFill(0xff0000);
|
|
28
|
+
* graphics.drawRect(100, 100, 50, 50);
|
|
29
|
+
* graphics.endFill();
|
|
30
|
+
* app.stage.addChild(graphics);
|
|
31
|
+
* return () => graphics.destroy();
|
|
32
|
+
* }, [app]);
|
|
33
|
+
*
|
|
34
|
+
* useGameLoop((delta) => {
|
|
35
|
+
* // Game logic runs every frame
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* return null;
|
|
39
|
+
* }
|
|
19
40
|
* ```
|
|
20
41
|
*/
|
|
21
|
-
export { PixiGame, useGameLoop, useGameState, useGameInput,
|
|
22
|
-
// Re-exported
|
|
23
|
-
Container,
|
|
42
|
+
export { PixiGame, usePixiApp, useApp, useGameLoop, useGameState, useGameInput,
|
|
43
|
+
// Re-exported PIXI classes
|
|
44
|
+
Container, Graphics, Text, Sprite,
|
|
24
45
|
// Constants
|
|
25
46
|
TILE_WIDTH, TILE_HEIGHT, } from '../react/PixiGame.js';
|
package/dist/react/PixiGame.d.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PixiGame -
|
|
2
|
+
* PixiGame - Direct pixi.js integration without @pixi/react
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* This component handles all of that setup automatically.
|
|
4
|
+
* This component creates a PIXI.Application imperatively and provides
|
|
5
|
+
* it via React context. This avoids react-reconciler issues that plague
|
|
6
|
+
* @pixi/react.
|
|
8
7
|
*
|
|
9
8
|
* @example
|
|
10
9
|
* ```tsx
|
|
11
|
-
* import { PixiGame,
|
|
10
|
+
* import { PixiGame, usePixiApp, useGameLoop } from '@thewhateverapp/tile-sdk/pixi';
|
|
12
11
|
*
|
|
13
12
|
* function MyGame() {
|
|
14
13
|
* return (
|
|
@@ -19,34 +18,51 @@
|
|
|
19
18
|
* }
|
|
20
19
|
*
|
|
21
20
|
* function GameContent() {
|
|
22
|
-
* const
|
|
21
|
+
* const app = usePixiApp();
|
|
22
|
+
* const containerRef = useRef<PIXI.Container | null>(null);
|
|
23
|
+
*
|
|
24
|
+
* useEffect(() => {
|
|
25
|
+
* if (!app) return;
|
|
26
|
+
* const container = new PIXI.Container();
|
|
27
|
+
* app.stage.addChild(container);
|
|
28
|
+
* containerRef.current = container;
|
|
29
|
+
* return () => {
|
|
30
|
+
* app.stage.removeChild(container);
|
|
31
|
+
* container.destroy();
|
|
32
|
+
* };
|
|
33
|
+
* }, [app]);
|
|
23
34
|
*
|
|
24
35
|
* useGameLoop((delta) => {
|
|
25
|
-
* // Game logic here
|
|
26
|
-
* playerRef.current.x += 1 * delta;
|
|
36
|
+
* // Game logic here
|
|
27
37
|
* });
|
|
28
38
|
*
|
|
29
|
-
* return
|
|
30
|
-
* <Container>
|
|
31
|
-
* <Graphics draw={(g) => {
|
|
32
|
-
* g.beginFill(0xff0000);
|
|
33
|
-
* g.drawRect(playerRef.current.x, playerRef.current.y, 32, 32);
|
|
34
|
-
* g.endFill();
|
|
35
|
-
* }} />
|
|
36
|
-
* </Container>
|
|
37
|
-
* );
|
|
39
|
+
* return null; // No React children needed - pixi manages rendering
|
|
38
40
|
* }
|
|
39
41
|
* ```
|
|
40
42
|
*/
|
|
41
|
-
import React, { ReactNode } from 'react';
|
|
42
|
-
import
|
|
43
|
-
export { Container,
|
|
44
|
-
export { useApp };
|
|
43
|
+
import React, { type ReactNode } from 'react';
|
|
44
|
+
import * as PIXI from 'pixi.js';
|
|
45
|
+
export { Container, Graphics, Text, Sprite } from 'pixi.js';
|
|
45
46
|
/**
|
|
46
47
|
* Tile dimensions - standard tile size
|
|
47
48
|
*/
|
|
48
49
|
export declare const TILE_WIDTH = 256;
|
|
49
50
|
export declare const TILE_HEIGHT = 554;
|
|
51
|
+
/**
|
|
52
|
+
* Hook to get the PIXI Application
|
|
53
|
+
*/
|
|
54
|
+
export declare function usePixiApp(): PIXI.Application | null;
|
|
55
|
+
/**
|
|
56
|
+
* Hook to get the PIXI Application (alias for backwards compatibility)
|
|
57
|
+
*/
|
|
58
|
+
export declare function useApp(): PIXI.Application | null;
|
|
59
|
+
/**
|
|
60
|
+
* useGameLoop - Run a callback every frame
|
|
61
|
+
*
|
|
62
|
+
* @param callback - Function called every frame with delta time (in frames, ~1 at 60fps)
|
|
63
|
+
* @param enabled - Whether the loop is active (default: true)
|
|
64
|
+
*/
|
|
65
|
+
export declare function useGameLoop(callback: (delta: number) => void, enabled?: boolean): void;
|
|
50
66
|
export interface PixiGameProps {
|
|
51
67
|
children: ReactNode;
|
|
52
68
|
/** Width in pixels (default: 256 for tile) */
|
|
@@ -64,93 +80,26 @@ export interface PixiGameProps {
|
|
|
64
80
|
/** Whether game is paused */
|
|
65
81
|
paused?: boolean;
|
|
66
82
|
/** Callback when Application is ready */
|
|
67
|
-
onMount?: (app:
|
|
83
|
+
onMount?: (app: PIXI.Application) => void;
|
|
68
84
|
}
|
|
69
85
|
/**
|
|
70
86
|
* PixiGame - Main wrapper component for pixi.js games
|
|
71
87
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* Use pixi components directly:
|
|
76
|
-
* - <Container> - Container for grouping elements
|
|
77
|
-
* - <Sprite texture={...}> - Sprite with texture
|
|
78
|
-
* - <Graphics draw={...}> - Graphics with draw callback
|
|
79
|
-
* - <Text text="..." style={...}> - Text element
|
|
88
|
+
* Creates a PIXI.Application and provides it via context.
|
|
89
|
+
* Children can use usePixiApp() to access the app and create pixi objects.
|
|
80
90
|
*/
|
|
81
91
|
export declare function PixiGame({ children, width, height, background, options, paused, onMount, }: PixiGameProps): React.JSX.Element;
|
|
82
|
-
/**
|
|
83
|
-
* useGameLoop - A more intuitive name for useTick
|
|
84
|
-
*
|
|
85
|
-
* Runs a callback every frame with delta time.
|
|
86
|
-
* Must be used inside a PixiGame component (or any @pixi/react Stage).
|
|
87
|
-
*
|
|
88
|
-
* @param callback - Function called every frame with delta time (in frames, ~1 at 60fps)
|
|
89
|
-
* @param enabled - Whether the loop is active (default: true)
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```tsx
|
|
93
|
-
* function GameContent() {
|
|
94
|
-
* const posRef = useRef({ x: 0, y: 0 });
|
|
95
|
-
*
|
|
96
|
-
* useGameLoop((delta) => {
|
|
97
|
-
* posRef.current.x += 2 * delta;
|
|
98
|
-
* if (posRef.current.x > 256) posRef.current.x = 0;
|
|
99
|
-
* });
|
|
100
|
-
*
|
|
101
|
-
* return <Sprite x={posRef.current.x} y={posRef.current.y} texture={...} />;
|
|
102
|
-
* }
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
export declare function useGameLoop(callback: (delta: number) => void, enabled?: boolean): void;
|
|
106
92
|
/**
|
|
107
93
|
* useGameState - Helper for managing game state with refs
|
|
108
94
|
*
|
|
109
95
|
* Returns a ref and a forceUpdate function for when you need to trigger re-renders.
|
|
110
96
|
* Use refs for continuous game state (position, velocity) to avoid re-render loops.
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* ```tsx
|
|
114
|
-
* function GameContent() {
|
|
115
|
-
* const [state, forceUpdate] = useGameState({
|
|
116
|
-
* playerX: 128,
|
|
117
|
-
* playerY: 400,
|
|
118
|
-
* score: 0,
|
|
119
|
-
* });
|
|
120
|
-
*
|
|
121
|
-
* useGameLoop((delta) => {
|
|
122
|
-
* state.current.playerX += 1 * delta;
|
|
123
|
-
* if (scoreChanged) {
|
|
124
|
-
* forceUpdate(); // Only re-render when score changes
|
|
125
|
-
* }
|
|
126
|
-
* });
|
|
127
|
-
*
|
|
128
|
-
* return <Text text={`Score: ${state.current.score}`} />;
|
|
129
|
-
* }
|
|
130
|
-
* ```
|
|
131
97
|
*/
|
|
132
98
|
export declare function useGameState<T extends object>(initialState: T): [React.MutableRefObject<T>, () => void];
|
|
133
99
|
/**
|
|
134
100
|
* useGameInput - Simple keyboard input hook for games
|
|
135
101
|
*
|
|
136
102
|
* Returns a ref with currently pressed keys.
|
|
137
|
-
* Can be used inside or outside PixiGame component.
|
|
138
|
-
*
|
|
139
|
-
* @example
|
|
140
|
-
* ```tsx
|
|
141
|
-
* function GameContent() {
|
|
142
|
-
* const keys = useGameInput();
|
|
143
|
-
* const posRef = useRef({ x: 128, y: 400 });
|
|
144
|
-
*
|
|
145
|
-
* useGameLoop((delta) => {
|
|
146
|
-
* if (keys.current.ArrowLeft) posRef.current.x -= 5 * delta;
|
|
147
|
-
* if (keys.current.ArrowRight) posRef.current.x += 5 * delta;
|
|
148
|
-
* if (keys.current[' ']) jump(); // Space key
|
|
149
|
-
* });
|
|
150
|
-
*
|
|
151
|
-
* return <Sprite x={posRef.current.x} y={posRef.current.y} texture={...} />;
|
|
152
|
-
* }
|
|
153
|
-
* ```
|
|
154
103
|
*/
|
|
155
104
|
export declare function useGameInput(): React.MutableRefObject<Record<string, boolean>>;
|
|
156
105
|
//# sourceMappingURL=PixiGame.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PixiGame.d.ts","sourceRoot":"","sources":["../../src/react/PixiGame.tsx"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"PixiGame.d.ts","sourceRoot":"","sources":["../../src/react/PixiGame.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,IAAI,MAAM,SAAS,CAAC;AAGhC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,UAAU,MAAM,CAAC;AAC9B,eAAO,MAAM,WAAW,MAAM,CAAC;AAY/B;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAAC,WAAW,GAAG,IAAI,CAMpD;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,IAAI,CAAC,WAAW,GAAG,IAAI,CAEhD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACjC,OAAO,GAAE,OAAc,QAexB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,6BAA6B;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC;CAC3C;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,KAAkB,EAClB,MAAoB,EACpB,UAAqB,EACrB,OAAY,EACZ,MAAc,EACd,OAAO,GACR,EAAE,aAAa,qBAqFf;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,YAAY,EAAE,CAAC,GACd,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CASzC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,oDAwB3B"}
|
package/dist/react/PixiGame.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
/**
|
|
3
|
-
* PixiGame -
|
|
3
|
+
* PixiGame - Direct pixi.js integration without @pixi/react
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* This component handles all of that setup automatically.
|
|
5
|
+
* This component creates a PIXI.Application imperatively and provides
|
|
6
|
+
* it via React context. This avoids react-reconciler issues that plague
|
|
7
|
+
* @pixi/react.
|
|
9
8
|
*
|
|
10
9
|
* @example
|
|
11
10
|
* ```tsx
|
|
12
|
-
* import { PixiGame,
|
|
11
|
+
* import { PixiGame, usePixiApp, useGameLoop } from '@thewhateverapp/tile-sdk/pixi';
|
|
13
12
|
*
|
|
14
13
|
* function MyGame() {
|
|
15
14
|
* return (
|
|
@@ -20,130 +19,157 @@
|
|
|
20
19
|
* }
|
|
21
20
|
*
|
|
22
21
|
* function GameContent() {
|
|
23
|
-
* const
|
|
22
|
+
* const app = usePixiApp();
|
|
23
|
+
* const containerRef = useRef<PIXI.Container | null>(null);
|
|
24
|
+
*
|
|
25
|
+
* useEffect(() => {
|
|
26
|
+
* if (!app) return;
|
|
27
|
+
* const container = new PIXI.Container();
|
|
28
|
+
* app.stage.addChild(container);
|
|
29
|
+
* containerRef.current = container;
|
|
30
|
+
* return () => {
|
|
31
|
+
* app.stage.removeChild(container);
|
|
32
|
+
* container.destroy();
|
|
33
|
+
* };
|
|
34
|
+
* }, [app]);
|
|
24
35
|
*
|
|
25
36
|
* useGameLoop((delta) => {
|
|
26
|
-
* // Game logic here
|
|
27
|
-
* playerRef.current.x += 1 * delta;
|
|
37
|
+
* // Game logic here
|
|
28
38
|
* });
|
|
29
39
|
*
|
|
30
|
-
* return
|
|
31
|
-
* <Container>
|
|
32
|
-
* <Graphics draw={(g) => {
|
|
33
|
-
* g.beginFill(0xff0000);
|
|
34
|
-
* g.drawRect(playerRef.current.x, playerRef.current.y, 32, 32);
|
|
35
|
-
* g.endFill();
|
|
36
|
-
* }} />
|
|
37
|
-
* </Container>
|
|
38
|
-
* );
|
|
40
|
+
* return null; // No React children needed - pixi manages rendering
|
|
39
41
|
* }
|
|
40
42
|
* ```
|
|
41
43
|
*/
|
|
42
|
-
import React, {
|
|
43
|
-
import
|
|
44
|
-
// Re-export
|
|
45
|
-
export { Container,
|
|
46
|
-
// Re-export useApp for advanced use cases
|
|
47
|
-
export { useApp };
|
|
44
|
+
import React, { createContext, useContext, useRef, useEffect, useState, useCallback, } from 'react';
|
|
45
|
+
import * as PIXI from 'pixi.js';
|
|
46
|
+
// Re-export PIXI classes for convenience
|
|
47
|
+
export { Container, Graphics, Text, Sprite } from 'pixi.js';
|
|
48
48
|
/**
|
|
49
49
|
* Tile dimensions - standard tile size
|
|
50
50
|
*/
|
|
51
51
|
export const TILE_WIDTH = 256;
|
|
52
52
|
export const TILE_HEIGHT = 554;
|
|
53
|
+
const PixiContext = createContext(null);
|
|
53
54
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* Provides the Stage/Application context that @pixi/react hooks need.
|
|
57
|
-
* All game content should be rendered as children of this component.
|
|
58
|
-
*
|
|
59
|
-
* Use pixi components directly:
|
|
60
|
-
* - <Container> - Container for grouping elements
|
|
61
|
-
* - <Sprite texture={...}> - Sprite with texture
|
|
62
|
-
* - <Graphics draw={...}> - Graphics with draw callback
|
|
63
|
-
* - <Text text="..." style={...}> - Text element
|
|
55
|
+
* Hook to get the PIXI Application
|
|
64
56
|
*/
|
|
65
|
-
export function
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
};
|
|
72
|
-
return (React.createElement(Stage, { width: width, height: height, options: stageOptions, onMount: onMount },
|
|
73
|
-
React.createElement(GameWrapper, { paused: paused }, children)));
|
|
57
|
+
export function usePixiApp() {
|
|
58
|
+
const context = useContext(PixiContext);
|
|
59
|
+
if (!context) {
|
|
60
|
+
throw new Error('usePixiApp must be used within a PixiGame component');
|
|
61
|
+
}
|
|
62
|
+
return context.app;
|
|
74
63
|
}
|
|
75
64
|
/**
|
|
76
|
-
*
|
|
65
|
+
* Hook to get the PIXI Application (alias for backwards compatibility)
|
|
77
66
|
*/
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
useEffect(() => {
|
|
81
|
-
if (app?.ticker) {
|
|
82
|
-
if (paused) {
|
|
83
|
-
app.ticker.stop();
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
app.ticker.start();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}, [app, paused]);
|
|
90
|
-
return React.createElement(React.Fragment, null, children);
|
|
67
|
+
export function useApp() {
|
|
68
|
+
return usePixiApp();
|
|
91
69
|
}
|
|
92
70
|
/**
|
|
93
|
-
* useGameLoop -
|
|
94
|
-
*
|
|
95
|
-
* Runs a callback every frame with delta time.
|
|
96
|
-
* Must be used inside a PixiGame component (or any @pixi/react Stage).
|
|
71
|
+
* useGameLoop - Run a callback every frame
|
|
97
72
|
*
|
|
98
73
|
* @param callback - Function called every frame with delta time (in frames, ~1 at 60fps)
|
|
99
74
|
* @param enabled - Whether the loop is active (default: true)
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```tsx
|
|
103
|
-
* function GameContent() {
|
|
104
|
-
* const posRef = useRef({ x: 0, y: 0 });
|
|
105
|
-
*
|
|
106
|
-
* useGameLoop((delta) => {
|
|
107
|
-
* posRef.current.x += 2 * delta;
|
|
108
|
-
* if (posRef.current.x > 256) posRef.current.x = 0;
|
|
109
|
-
* });
|
|
110
|
-
*
|
|
111
|
-
* return <Sprite x={posRef.current.x} y={posRef.current.y} texture={...} />;
|
|
112
|
-
* }
|
|
113
|
-
* ```
|
|
114
75
|
*/
|
|
115
76
|
export function useGameLoop(callback, enabled = true) {
|
|
116
|
-
|
|
77
|
+
const context = useContext(PixiContext);
|
|
78
|
+
const callbackRef = useRef(callback);
|
|
79
|
+
callbackRef.current = callback;
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (!context || !enabled)
|
|
82
|
+
return;
|
|
83
|
+
const wrappedCallback = (delta) => {
|
|
84
|
+
callbackRef.current(delta);
|
|
85
|
+
};
|
|
86
|
+
return context.addTickerCallback(wrappedCallback);
|
|
87
|
+
}, [context, enabled]);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* PixiGame - Main wrapper component for pixi.js games
|
|
91
|
+
*
|
|
92
|
+
* Creates a PIXI.Application and provides it via context.
|
|
93
|
+
* Children can use usePixiApp() to access the app and create pixi objects.
|
|
94
|
+
*/
|
|
95
|
+
export function PixiGame({ children, width = TILE_WIDTH, height = TILE_HEIGHT, background = 0x000000, options = {}, paused = false, onMount, }) {
|
|
96
|
+
const containerRef = useRef(null);
|
|
97
|
+
const appRef = useRef(null);
|
|
98
|
+
const tickerCallbacksRef = useRef(new Set());
|
|
99
|
+
const [isReady, setIsReady] = useState(false);
|
|
100
|
+
// Create PIXI Application
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!containerRef.current)
|
|
103
|
+
return;
|
|
104
|
+
const app = new PIXI.Application({
|
|
105
|
+
width,
|
|
106
|
+
height,
|
|
107
|
+
backgroundColor: background,
|
|
108
|
+
antialias: options.antialias ?? true,
|
|
109
|
+
resolution: options.resolution ?? (typeof window !== 'undefined' ? window.devicePixelRatio : 1),
|
|
110
|
+
autoDensity: options.autoDensity ?? true,
|
|
111
|
+
});
|
|
112
|
+
containerRef.current.appendChild(app.view);
|
|
113
|
+
appRef.current = app;
|
|
114
|
+
// Add master ticker callback that calls all registered callbacks
|
|
115
|
+
app.ticker.add((delta) => {
|
|
116
|
+
for (const callback of tickerCallbacksRef.current) {
|
|
117
|
+
callback(delta);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
setIsReady(true);
|
|
121
|
+
onMount?.(app);
|
|
122
|
+
return () => {
|
|
123
|
+
setIsReady(false);
|
|
124
|
+
tickerCallbacksRef.current.clear();
|
|
125
|
+
app.destroy(true, { children: true, texture: true, baseTexture: true });
|
|
126
|
+
appRef.current = null;
|
|
127
|
+
};
|
|
128
|
+
}, []); // Only run once on mount
|
|
129
|
+
// Handle size changes
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (appRef.current) {
|
|
132
|
+
appRef.current.renderer.resize(width, height);
|
|
133
|
+
}
|
|
134
|
+
}, [width, height]);
|
|
135
|
+
// Handle background color changes
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (appRef.current) {
|
|
138
|
+
appRef.current.renderer.background.color = background;
|
|
139
|
+
}
|
|
140
|
+
}, [background]);
|
|
141
|
+
// Handle pause state
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (appRef.current) {
|
|
144
|
+
if (paused) {
|
|
145
|
+
appRef.current.ticker.stop();
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
appRef.current.ticker.start();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}, [paused]);
|
|
152
|
+
// Context value with app and ticker registration
|
|
153
|
+
const contextValue = {
|
|
154
|
+
app: appRef.current,
|
|
155
|
+
addTickerCallback: useCallback((callback) => {
|
|
156
|
+
tickerCallbacksRef.current.add(callback);
|
|
157
|
+
return () => {
|
|
158
|
+
tickerCallbacksRef.current.delete(callback);
|
|
159
|
+
};
|
|
160
|
+
}, []),
|
|
161
|
+
};
|
|
162
|
+
return (React.createElement("div", { ref: containerRef, style: { width, height } }, isReady && (React.createElement(PixiContext.Provider, { value: contextValue }, children))));
|
|
117
163
|
}
|
|
118
164
|
/**
|
|
119
165
|
* useGameState - Helper for managing game state with refs
|
|
120
166
|
*
|
|
121
167
|
* Returns a ref and a forceUpdate function for when you need to trigger re-renders.
|
|
122
168
|
* Use refs for continuous game state (position, velocity) to avoid re-render loops.
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* ```tsx
|
|
126
|
-
* function GameContent() {
|
|
127
|
-
* const [state, forceUpdate] = useGameState({
|
|
128
|
-
* playerX: 128,
|
|
129
|
-
* playerY: 400,
|
|
130
|
-
* score: 0,
|
|
131
|
-
* });
|
|
132
|
-
*
|
|
133
|
-
* useGameLoop((delta) => {
|
|
134
|
-
* state.current.playerX += 1 * delta;
|
|
135
|
-
* if (scoreChanged) {
|
|
136
|
-
* forceUpdate(); // Only re-render when score changes
|
|
137
|
-
* }
|
|
138
|
-
* });
|
|
139
|
-
*
|
|
140
|
-
* return <Text text={`Score: ${state.current.score}`} />;
|
|
141
|
-
* }
|
|
142
|
-
* ```
|
|
143
169
|
*/
|
|
144
170
|
export function useGameState(initialState) {
|
|
145
171
|
const stateRef = useRef(initialState);
|
|
146
|
-
const [, setTick] =
|
|
172
|
+
const [, setTick] = useState(0);
|
|
147
173
|
const forceUpdate = useCallback(() => {
|
|
148
174
|
setTick((t) => t + 1);
|
|
149
175
|
}, []);
|
|
@@ -153,23 +179,6 @@ export function useGameState(initialState) {
|
|
|
153
179
|
* useGameInput - Simple keyboard input hook for games
|
|
154
180
|
*
|
|
155
181
|
* Returns a ref with currently pressed keys.
|
|
156
|
-
* Can be used inside or outside PixiGame component.
|
|
157
|
-
*
|
|
158
|
-
* @example
|
|
159
|
-
* ```tsx
|
|
160
|
-
* function GameContent() {
|
|
161
|
-
* const keys = useGameInput();
|
|
162
|
-
* const posRef = useRef({ x: 128, y: 400 });
|
|
163
|
-
*
|
|
164
|
-
* useGameLoop((delta) => {
|
|
165
|
-
* if (keys.current.ArrowLeft) posRef.current.x -= 5 * delta;
|
|
166
|
-
* if (keys.current.ArrowRight) posRef.current.x += 5 * delta;
|
|
167
|
-
* if (keys.current[' ']) jump(); // Space key
|
|
168
|
-
* });
|
|
169
|
-
*
|
|
170
|
-
* return <Sprite x={posRef.current.x} y={posRef.current.y} texture={...} />;
|
|
171
|
-
* }
|
|
172
|
-
* ```
|
|
173
182
|
*/
|
|
174
183
|
export function useGameInput() {
|
|
175
184
|
const keysRef = useRef({});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SceneFromJson.d.ts","sourceRoot":"","sources":["../../src/scene/SceneFromJson.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"SceneFromJson.d.ts","sourceRoot":"","sources":["../../src/scene/SceneFromJson.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAG/D,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC;IAC1E,4DAA4D;IAC5D,IAAI,EAAE,OAAO,CAAC;IACd,uDAAuD;IACvD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CACtC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,UAAiB,EACjB,OAAO,EACP,SAAkB,EAClB,GAAG,KAAK,EACT,EAAE,kBAAkB,qBAoGpB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useEffect, useMemo, useCallback } from 'react';
|
|
3
3
|
import { validateScene } from '@thewhateverapp/scene-sdk';
|
|
4
|
+
import { SceneRenderer } from './SceneRenderer.js';
|
|
4
5
|
/**
|
|
5
6
|
* SceneFromJson - Renders a scene from a JSON object with validation
|
|
6
7
|
*
|
|
@@ -16,18 +17,8 @@ import { validateScene } from '@thewhateverapp/scene-sdk';
|
|
|
16
17
|
* ```
|
|
17
18
|
*/
|
|
18
19
|
export function SceneFromJson({ json, showErrors = true, onEvent, container = 'tile', ...props }) {
|
|
19
|
-
// SSR detection - only render SceneRenderer on client
|
|
20
|
-
const [isClient, setIsClient] = useState(false);
|
|
21
|
-
const [SceneRenderer, setSceneRenderer] = useState(null);
|
|
22
|
-
// Load SceneRenderer dynamically on client only (pixi.js requires browser APIs)
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
setIsClient(true);
|
|
25
|
-
import('./SceneRenderer.js').then((mod) => {
|
|
26
|
-
setSceneRenderer(() => mod.SceneRenderer);
|
|
27
|
-
});
|
|
28
|
-
}, []);
|
|
29
20
|
// Wrap onEvent to forward to parent window via postMessage
|
|
30
|
-
const wrappedOnEvent =
|
|
21
|
+
const wrappedOnEvent = useCallback((event, data) => {
|
|
31
22
|
// Call user's onEvent handler
|
|
32
23
|
onEvent?.(event, data);
|
|
33
24
|
// Forward to parent window for tile containers to handle
|
|
@@ -72,20 +63,6 @@ export function SceneFromJson({ json, showErrors = true, onEvent, container = 't
|
|
|
72
63
|
}
|
|
73
64
|
}
|
|
74
65
|
}, [validationResult]);
|
|
75
|
-
// Loading placeholder for SSR and dynamic import
|
|
76
|
-
const loadingPlaceholder = (React.createElement("div", { style: {
|
|
77
|
-
width: '100%',
|
|
78
|
-
height: '100%',
|
|
79
|
-
backgroundColor: '#0a0a1a',
|
|
80
|
-
display: 'flex',
|
|
81
|
-
alignItems: 'center',
|
|
82
|
-
justifyContent: 'center',
|
|
83
|
-
} },
|
|
84
|
-
React.createElement("div", { style: {
|
|
85
|
-
color: '#666',
|
|
86
|
-
fontSize: 14,
|
|
87
|
-
fontFamily: 'system-ui, sans-serif',
|
|
88
|
-
} }, "Loading...")));
|
|
89
66
|
// Show errors if validation failed
|
|
90
67
|
if (!validationResult.valid) {
|
|
91
68
|
if (showErrors) {
|
|
@@ -112,13 +89,9 @@ export function SceneFromJson({ json, showErrors = true, onEvent, container = 't
|
|
|
112
89
|
throw new Error(`Scene validation failed: ${validationResult.errors.map((e) => e.message).join(', ')}`);
|
|
113
90
|
}
|
|
114
91
|
// Show warnings in console
|
|
115
|
-
if (validationResult.warnings.length > 0
|
|
92
|
+
if (validationResult.warnings.length > 0) {
|
|
116
93
|
console.warn('Scene validation warnings:', validationResult.warnings);
|
|
117
94
|
}
|
|
118
|
-
// Wait for client-side rendering and dynamic import
|
|
119
|
-
if (!isClient || !SceneRenderer) {
|
|
120
|
-
return containerStyle ? React.createElement("div", { style: containerStyle }, loadingPlaceholder) : loadingPlaceholder;
|
|
121
|
-
}
|
|
122
95
|
const sceneContent = (React.createElement(SceneRenderer, { spec: json, onEvent: wrappedOnEvent, ...props }));
|
|
123
96
|
return containerStyle ? React.createElement("div", { style: containerStyle }, sceneContent) : sceneContent;
|
|
124
97
|
}
|