pixi-fusion 1.0.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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/dist/src/assets-manager/AssetsManager.context.d.ts +15 -0
  4. package/dist/src/assets-manager/AssetsManager.context.js +6 -0
  5. package/dist/src/assets-manager/AssetsManager.d.ts +2 -0
  6. package/dist/src/assets-manager/AssetsManager.js +43 -0
  7. package/dist/src/assets-manager/index.d.ts +3 -0
  8. package/dist/src/assets-manager/index.js +3 -0
  9. package/dist/src/assets-manager/useAssetManager.d.ts +1 -0
  10. package/dist/src/assets-manager/useAssetManager.js +5 -0
  11. package/dist/src/camera/Camera.context.d.ts +13 -0
  12. package/dist/src/camera/Camera.context.js +2 -0
  13. package/dist/src/camera/Camera.d.ts +7 -0
  14. package/dist/src/camera/Camera.js +99 -0
  15. package/dist/src/camera/index.d.ts +2 -0
  16. package/dist/src/camera/index.js +2 -0
  17. package/dist/src/game-context/Game.context.d.ts +39 -0
  18. package/dist/src/game-context/Game.context.js +85 -0
  19. package/dist/src/game-context/index.d.ts +1 -0
  20. package/dist/src/game-context/index.js +1 -0
  21. package/dist/src/game-objects/GameObjectPhysicalObjectConfig.d.ts +4 -0
  22. package/dist/src/game-objects/GameObjectPhysicalObjectConfig.js +1 -0
  23. package/dist/src/game-objects/index.d.ts +2 -0
  24. package/dist/src/game-objects/index.js +2 -0
  25. package/dist/src/game-objects/usePhysicalObject.d.ts +7 -0
  26. package/dist/src/game-objects/usePhysicalObject.js +17 -0
  27. package/dist/src/game-objects/usePhysicalObjectFromConfig.d.ts +6 -0
  28. package/dist/src/game-objects/usePhysicalObjectFromConfig.js +13 -0
  29. package/dist/src/game-objects/useWalls.d.ts +11 -0
  30. package/dist/src/game-objects/useWalls.js +59 -0
  31. package/dist/src/hooks/index.d.ts +12 -0
  32. package/dist/src/hooks/index.js +12 -0
  33. package/dist/src/hooks/useAnimatedSprite.d.ts +10 -0
  34. package/dist/src/hooks/useAnimatedSprite.js +41 -0
  35. package/dist/src/hooks/useCamera.d.ts +1 -0
  36. package/dist/src/hooks/useCamera.js +5 -0
  37. package/dist/src/hooks/useCollisionDetection.d.ts +9 -0
  38. package/dist/src/hooks/useCollisionDetection.js +21 -0
  39. package/dist/src/hooks/useGame.d.ts +1 -0
  40. package/dist/src/hooks/useGame.js +5 -0
  41. package/dist/src/hooks/useGlobalEventHandler.d.ts +5 -0
  42. package/dist/src/hooks/useGlobalEventHandler.js +15 -0
  43. package/dist/src/hooks/useLayerContext.d.ts +1 -0
  44. package/dist/src/hooks/useLayerContext.js +5 -0
  45. package/dist/src/hooks/useObject.d.ts +6 -0
  46. package/dist/src/hooks/useObject.js +54 -0
  47. package/dist/src/hooks/useSprite.d.ts +6 -0
  48. package/dist/src/hooks/useSprite.js +21 -0
  49. package/dist/src/hooks/useStage.d.ts +1 -0
  50. package/dist/src/hooks/useStage.js +5 -0
  51. package/dist/src/hooks/useTexture.d.ts +9 -0
  52. package/dist/src/hooks/useTexture.js +14 -0
  53. package/dist/src/hooks/useTickerCallback.d.ts +5 -0
  54. package/dist/src/hooks/useTickerCallback.js +14 -0
  55. package/dist/src/hooks/useTilingSprite.d.ts +6 -0
  56. package/dist/src/hooks/useTilingSprite.js +26 -0
  57. package/dist/src/hooks/useWorld.d.ts +1 -0
  58. package/dist/src/hooks/useWorld.js +5 -0
  59. package/dist/src/index.d.ts +10 -0
  60. package/dist/src/index.js +10 -0
  61. package/dist/src/layer/Layer.context.d.ts +6 -0
  62. package/dist/src/layer/Layer.context.js +2 -0
  63. package/dist/src/layer/Layer.d.ts +6 -0
  64. package/dist/src/layer/Layer.js +34 -0
  65. package/dist/src/layer/index.d.ts +1 -0
  66. package/dist/src/layer/index.js +1 -0
  67. package/dist/src/physics/MatterPhysics.context.d.ts +8 -0
  68. package/dist/src/physics/MatterPhysics.context.js +74 -0
  69. package/dist/src/physics/index.d.ts +4 -0
  70. package/dist/src/physics/index.js +4 -0
  71. package/dist/src/physics/types.d.ts +24 -0
  72. package/dist/src/physics/types.js +1 -0
  73. package/dist/src/physics/useCollisionEventHandler.d.ts +7 -0
  74. package/dist/src/physics/useCollisionEventHandler.js +15 -0
  75. package/dist/src/physics/usePhysicsEngineEventHandler.d.ts +8 -0
  76. package/dist/src/physics/usePhysicsEngineEventHandler.js +15 -0
  77. package/dist/src/physics/usePhysicsTickerCallback.d.ts +7 -0
  78. package/dist/src/physics/usePhysicsTickerCallback.js +15 -0
  79. package/dist/src/stage/Stage.context.d.ts +6 -0
  80. package/dist/src/stage/Stage.context.js +2 -0
  81. package/dist/src/stage/Stage.d.ts +2 -0
  82. package/dist/src/stage/Stage.js +29 -0
  83. package/dist/src/stage/index.d.ts +1 -0
  84. package/dist/src/stage/index.js +1 -0
  85. package/dist/src/types.d.ts +7 -0
  86. package/dist/src/types.js +7 -0
  87. package/dist/src/world/World.context.d.ts +10 -0
  88. package/dist/src/world/World.context.js +2 -0
  89. package/dist/src/world/World.d.ts +11 -0
  90. package/dist/src/world/World.js +66 -0
  91. package/dist/src/world/index.d.ts +2 -0
  92. package/dist/src/world/index.js +2 -0
  93. package/package.json +52 -0
  94. package/src/assets-manager/AssetsManager.context.tsx +23 -0
  95. package/src/assets-manager/AssetsManager.tsx +54 -0
  96. package/src/assets-manager/index.ts +3 -0
  97. package/src/assets-manager/useAssetManager.ts +7 -0
  98. package/src/camera/Camera.context.tsx +17 -0
  99. package/src/camera/Camera.tsx +153 -0
  100. package/src/camera/index.ts +2 -0
  101. package/src/game-context/Game.context.tsx +133 -0
  102. package/src/game-context/index.ts +1 -0
  103. package/src/game-objects/GameObjectPhysicalObjectConfig.ts +18 -0
  104. package/src/game-objects/index.ts +2 -0
  105. package/src/game-objects/usePhysicalObject.ts +24 -0
  106. package/src/game-objects/usePhysicalObjectFromConfig.ts +22 -0
  107. package/src/game-objects/useWalls.ts +93 -0
  108. package/src/hooks/index.ts +12 -0
  109. package/src/hooks/useAnimatedSprite.ts +65 -0
  110. package/src/hooks/useCamera.ts +6 -0
  111. package/src/hooks/useCollisionDetection.ts +35 -0
  112. package/src/hooks/useGame.ts +6 -0
  113. package/src/hooks/useGlobalEventHandler.ts +27 -0
  114. package/src/hooks/useLayerContext.ts +6 -0
  115. package/src/hooks/useObject.ts +81 -0
  116. package/src/hooks/useSprite.ts +33 -0
  117. package/src/hooks/useStage.ts +7 -0
  118. package/src/hooks/useTexture.ts +22 -0
  119. package/src/hooks/useTickerCallback.ts +25 -0
  120. package/src/hooks/useTilingSprite.ts +39 -0
  121. package/src/hooks/useWorld.ts +7 -0
  122. package/src/index.ts +10 -0
  123. package/src/layer/Layer.context.tsx +9 -0
  124. package/src/layer/Layer.tsx +57 -0
  125. package/src/layer/index.ts +1 -0
  126. package/src/physics/MatterPhysics.context.tsx +100 -0
  127. package/src/physics/index.ts +4 -0
  128. package/src/physics/types.ts +27 -0
  129. package/src/physics/useCollisionEventHandler.ts +26 -0
  130. package/src/physics/usePhysicsEngineEventHandler.ts +30 -0
  131. package/src/physics/usePhysicsTickerCallback.ts +25 -0
  132. package/src/stage/Stage.context.tsx +9 -0
  133. package/src/stage/Stage.tsx +50 -0
  134. package/src/stage/index.ts +1 -0
  135. package/src/types.ts +8 -0
  136. package/src/world/World.context.tsx +13 -0
  137. package/src/world/World.tsx +93 -0
  138. package/src/world/index.ts +2 -0
@@ -0,0 +1,22 @@
1
+ import { Body } from "matter-js";
2
+ import { useMemo } from "react";
3
+ import { GameObjectPhysicalObjectConfig } from "./GameObjectPhysicalObjectConfig";
4
+ import { usePhysicalObject } from "./usePhysicalObject";
5
+ import { Nullable } from "../types";
6
+
7
+ export const usePhysicalObjectFromConfig = <PhysicalObjectType extends Nullable<Body> = Body>({
8
+ position = { x: 0, y: 0 },
9
+ ...physicalObjectConfig
10
+ }: GameObjectPhysicalObjectConfig) => {
11
+ const physicalObject = useMemo(() => {
12
+ if (physicalObjectConfig) {
13
+ return Body.create({ position, ...physicalObjectConfig });
14
+ }
15
+
16
+ return null;
17
+ }, []) as PhysicalObjectType;
18
+
19
+ usePhysicalObject({ physicalObject });
20
+
21
+ return { physicalObject };
22
+ };
@@ -0,0 +1,93 @@
1
+ import { useContext, useEffect, useMemo } from "react";
2
+ import { Bodies, Body, Composite } from "matter-js";
3
+ import { MatterPhysicsContext } from "../physics/MatterPhysics.context";
4
+
5
+ type WallOptions = {
6
+ thikness: number;
7
+ };
8
+
9
+ type UseWallsOptions = {
10
+ left?: Partial<WallOptions> | boolean;
11
+ right?: Partial<WallOptions> | boolean;
12
+ top?: Partial<WallOptions> | boolean;
13
+ bottom?: Partial<WallOptions> | boolean;
14
+ };
15
+
16
+ const DEFAULT_WALL_CONFIG: WallOptions = {
17
+ thikness: 10
18
+ };
19
+
20
+ export const useWalls = ({ left = true, right = true, top = true, bottom = true }: UseWallsOptions = {}) => {
21
+ const { config } = useContext(MatterPhysicsContext);
22
+ const { height, width } = config.world;
23
+ const center = {
24
+ x: width / 2,
25
+ y: height / 2
26
+ };
27
+
28
+ const { addBody, removeBody } = useContext(MatterPhysicsContext);
29
+
30
+ const body = useMemo(() => {
31
+ const walls = Composite.create();
32
+ const parts: Body[] = [];
33
+
34
+ if (left) {
35
+ const leftWallConfig = Object.assign(DEFAULT_WALL_CONFIG, typeof left === "boolean" ? {} : { ...left });
36
+ const leftWallPosition = { x: 0, y: center.y };
37
+ parts.push(
38
+ Bodies.rectangle(leftWallPosition.x, leftWallPosition.y, leftWallConfig.thikness, height, {
39
+ isStatic: true,
40
+ label: "left-wall"
41
+ })
42
+ );
43
+ }
44
+
45
+ if (right) {
46
+ const rightWallConfig = Object.assign(DEFAULT_WALL_CONFIG, typeof right === "boolean" ? {} : { ...right });
47
+ const rightWallPosition = { x: width, y: center.y };
48
+ parts.push(
49
+ Bodies.rectangle(rightWallPosition.x, rightWallPosition.y, rightWallConfig.thikness, height, {
50
+ isStatic: true,
51
+ label: "right-wall"
52
+ })
53
+ );
54
+ }
55
+
56
+ if (top) {
57
+ const topWallConfig = Object.assign(DEFAULT_WALL_CONFIG, typeof top === "boolean" ? {} : { ...top });
58
+ const topWallPosition = { x: center.x, y: 0 };
59
+ parts.push(
60
+ Bodies.rectangle(topWallPosition.x, topWallPosition.y, width, topWallConfig.thikness, {
61
+ isStatic: true,
62
+ label: "top-wall"
63
+ })
64
+ );
65
+ }
66
+
67
+ if (bottom) {
68
+ const bottomWallConfig = Object.assign(
69
+ DEFAULT_WALL_CONFIG,
70
+ typeof bottom === "boolean" ? {} : { ...bottom }
71
+ );
72
+ const bottomWallPosition = { x: center.x, y: height };
73
+ parts.push(
74
+ Bodies.rectangle(bottomWallPosition.x, bottomWallPosition.y, width, bottomWallConfig.thikness, {
75
+ isStatic: true,
76
+ label: "bottom-wall"
77
+ })
78
+ );
79
+ }
80
+
81
+ Composite.add(walls, parts);
82
+
83
+ return walls;
84
+ }, [width, height]);
85
+
86
+ useEffect(() => {
87
+ addBody(body);
88
+
89
+ return () => {
90
+ removeBody(body);
91
+ };
92
+ }, [body.id]);
93
+ };
@@ -0,0 +1,12 @@
1
+ export * from "./useGlobalEventHandler";
2
+ export * from "./useStage";
3
+ export * from "./useTickerCallback";
4
+ export * from "./useWorld";
5
+ export * from "./useLayerContext";
6
+ export * from "./useSprite";
7
+ export * from "./useAnimatedSprite";
8
+ export * from "./useTexture";
9
+ export * from "./useGame";
10
+ export * from "./useTilingSprite";
11
+ export * from "./useCamera";
12
+ export * from "./useCollisionDetection";
@@ -0,0 +1,65 @@
1
+ import { useEffect, useMemo } from "react";
2
+
3
+ import { AnimatedSprite, AnimatedSpriteOptions, Spritesheet, SpritesheetData } from "pixi.js";
4
+ import { useTextures } from "./useTexture";
5
+ import { useObject } from "./useObject";
6
+
7
+ type UseAnimatedSpriteOptions = Omit<AnimatedSpriteOptions, "textures" | "texture"> & {
8
+ texture: string;
9
+ animation: string;
10
+ spritesheet: SpritesheetData;
11
+ animationSpeed: number;
12
+ isPlaying: boolean;
13
+ };
14
+
15
+ export const useAnimatedSprite = ({
16
+ texture,
17
+ spritesheet: spritesheetJSON,
18
+ animation,
19
+ animationSpeed = 1,
20
+ isPlaying,
21
+ ...options
22
+ }: UseAnimatedSpriteOptions) => {
23
+ const textureKeys = useMemo(() => {
24
+ return [...(texture ? [texture] : [])];
25
+ }, [texture]);
26
+
27
+ const { textures, isFetched } = useTextures({ keys: textureKeys });
28
+
29
+ const spritesheet = useMemo(() => {
30
+ if (isFetched) {
31
+ const sheet = new Spritesheet(textures[0], spritesheetJSON);
32
+ sheet.parse();
33
+ return sheet;
34
+ }
35
+ return null;
36
+ }, [spritesheetJSON]);
37
+
38
+ const sprite = useMemo(() => {
39
+ if (spritesheet?.animations?.[animation]) {
40
+ return new AnimatedSprite({ textures: spritesheet?.animations?.[animation] });
41
+ }
42
+
43
+ return null;
44
+ }, [texture, spritesheetJSON, animation]);
45
+
46
+ useObject({ object: sprite, ...options });
47
+
48
+ useEffect(() => {
49
+ if (sprite) {
50
+ sprite.animationSpeed = animationSpeed;
51
+ }
52
+ }, [animationSpeed, sprite?.uid]);
53
+
54
+ useEffect(() => {
55
+ if (sprite) {
56
+ if (isPlaying) {
57
+ sprite.play();
58
+ } else {
59
+ sprite.stop();
60
+ }
61
+ }
62
+ }, [isPlaying, sprite?.uid]);
63
+
64
+ return sprite;
65
+ };
@@ -0,0 +1,6 @@
1
+ import { useContext } from "react";
2
+ import { CameraContext } from "../camera";
3
+
4
+ export const useCamera = () => {
5
+ return useContext(CameraContext);
6
+ };
@@ -0,0 +1,35 @@
1
+ import { Container } from "pixi.js";
2
+ import { useTickerCallback } from "./useTickerCallback";
3
+ import { useCallback } from "react";
4
+
5
+ type UseCollisionDetectionOptions = {
6
+ objectA?: Container | null;
7
+ objectB?: Container | null;
8
+ isEnabled: boolean;
9
+ onCollide: () => void;
10
+ };
11
+
12
+ export const useCollisionDetection = ({ objectA, objectB, onCollide, isEnabled }: UseCollisionDetectionOptions) => {
13
+ const testForAABB = useCallback(() => {
14
+ if (!objectA || !objectB) {
15
+ return;
16
+ }
17
+
18
+ const bounds1 = objectA.getBounds();
19
+ const bounds2 = objectB.getBounds();
20
+
21
+ if (
22
+ bounds1.x < bounds2.x + bounds2.width &&
23
+ bounds1.x + bounds1.width > bounds2.x &&
24
+ bounds1.y < bounds2.y + bounds2.height &&
25
+ bounds1.y + bounds1.height > bounds2.y
26
+ ) {
27
+ onCollide();
28
+ }
29
+ }, [objectA, objectB, onCollide]);
30
+
31
+ useTickerCallback({
32
+ isEnabled: isEnabled,
33
+ callback: testForAABB
34
+ });
35
+ };
@@ -0,0 +1,6 @@
1
+ import { useContext } from "react";
2
+ import { GameContext } from "../game-context";
3
+
4
+ export const useGame = () => {
5
+ return useContext(GameContext);
6
+ };
@@ -0,0 +1,27 @@
1
+ import { useEffect } from "react";
2
+
3
+ export const useGlobalEventHandler = <K extends keyof DocumentEventMap>({
4
+ isEnabled = true,
5
+ event,
6
+ callback
7
+ }: {
8
+ isEnabled?: boolean;
9
+ event: K;
10
+ callback: (e: DocumentEventMap[K]) => unknown;
11
+ }) => {
12
+ useEffect(() => {
13
+ if (!isEnabled) {
14
+ return () => {};
15
+ }
16
+
17
+ const internalCallback = (e: DocumentEventMap[K]) => {
18
+ callback(e);
19
+ };
20
+
21
+ document.addEventListener(event, internalCallback);
22
+
23
+ return () => {
24
+ document.removeEventListener(event, internalCallback);
25
+ };
26
+ }, [isEnabled, event, callback]);
27
+ };
@@ -0,0 +1,6 @@
1
+ import { useContext } from "react";
2
+ import { LayerContext } from "../layer/Layer.context";
3
+
4
+ export const useLayerContext = () => {
5
+ return useContext(LayerContext);
6
+ };
@@ -0,0 +1,81 @@
1
+ import { Sprite, SpriteOptions, TilingSprite } from "pixi.js";
2
+ import { useLayerContext } from "./useLayerContext";
3
+ import { useEffect } from "react";
4
+
5
+ type UseObjectOptions = Omit<SpriteOptions, "texture"> & {
6
+ object: Sprite | TilingSprite | null;
7
+ };
8
+
9
+ export const useObject = ({
10
+ object,
11
+ anchor,
12
+ position,
13
+ skew,
14
+ scale,
15
+ width,
16
+ angle,
17
+ alpha,
18
+ visible = true
19
+ }: UseObjectOptions) => {
20
+ const { addObject, removeObject } = useLayerContext();
21
+
22
+ useEffect(() => {
23
+ if (!object) {
24
+ return;
25
+ }
26
+
27
+ addObject(object);
28
+
29
+ return () => {
30
+ removeObject(object);
31
+ };
32
+ }, [object?.uid]);
33
+
34
+ useEffect(() => {
35
+ if (anchor !== undefined && object) {
36
+ object.anchor = anchor;
37
+ }
38
+ }, [anchor, object?.uid]);
39
+
40
+ useEffect(() => {
41
+ if (angle !== undefined && object) {
42
+ object.angle = angle;
43
+ }
44
+ }, [angle, object?.uid]);
45
+
46
+ useEffect(() => {
47
+ if (object && position !== undefined) {
48
+ object.position = position;
49
+ }
50
+ }, [position, object?.uid]);
51
+
52
+ useEffect(() => {
53
+ if (object && skew !== undefined) {
54
+ object.skew = skew;
55
+ }
56
+ }, [skew, object?.uid]);
57
+
58
+ useEffect(() => {
59
+ if (object && alpha !== undefined) {
60
+ object.alpha = alpha;
61
+ }
62
+ }, [alpha, object?.uid]);
63
+
64
+ useEffect(() => {
65
+ if (object && scale !== undefined) {
66
+ object.scale = scale;
67
+ }
68
+ }, [scale, object?.uid]);
69
+
70
+ useEffect(() => {
71
+ if (object) {
72
+ object.visible = visible;
73
+ }
74
+ }, [visible, object?.uid]);
75
+
76
+ useEffect(() => {
77
+ if (object && width !== undefined) {
78
+ object.width = width;
79
+ }
80
+ }, [width, object?.uid]);
81
+ };
@@ -0,0 +1,33 @@
1
+ import { useMemo } from "react";
2
+
3
+ import { Sprite, SpriteOptions } from "pixi.js";
4
+ import { useTextures } from "./useTexture";
5
+ import { useObject } from "./useObject";
6
+
7
+ type UseSpriteOptions = Omit<SpriteOptions, "texture"> & {
8
+ texture: string;
9
+ };
10
+
11
+ export const useSprite = ({ texture = "", ...options }: UseSpriteOptions) => {
12
+ const textureKeys = useMemo(() => {
13
+ return [...(texture ? [texture] : [])];
14
+ }, [texture]);
15
+
16
+ const { isFetched } = useTextures({ keys: textureKeys });
17
+
18
+ const sprite = useMemo(() => {
19
+ if (!isFetched) {
20
+ return null;
21
+ }
22
+
23
+ if (texture) {
24
+ return Sprite.from(texture);
25
+ }
26
+
27
+ return new Sprite({});
28
+ }, [texture, frames, isFetched]);
29
+
30
+ useObject({ object: sprite, ...options });
31
+
32
+ return sprite;
33
+ };
@@ -0,0 +1,7 @@
1
+ import { useContext } from "react";
2
+
3
+ import { StageContext } from "../stage/Stage.context";
4
+
5
+ export const useStage = () => {
6
+ return useContext(StageContext);
7
+ };
@@ -0,0 +1,22 @@
1
+ import { useMemo } from "react";
2
+
3
+ import { Texture } from "pixi.js";
4
+ import { useAssetManager } from "../assets-manager";
5
+
6
+ export const useTextures = ({ keys = [] }: { keys?: string[] }) => {
7
+ const { getAsset, isFetched, isFetching, isError } = useAssetManager();
8
+
9
+ const textures = useMemo(() => {
10
+ return isFetched ? keys.map((key) => getAsset(key) as Texture) : [];
11
+ }, [keys, isFetched, isFetching, isError]);
12
+
13
+ return useMemo(
14
+ () => ({
15
+ textures: textures,
16
+ isFetched,
17
+ isFetching,
18
+ isError
19
+ }),
20
+ [textures, isFetched, isError, isFetching]
21
+ );
22
+ };
@@ -0,0 +1,25 @@
1
+ import { useEffect } from "react";
2
+ import { TickerCallback } from "pixi.js";
3
+
4
+ import { useWorld } from "../hooks";
5
+
6
+ export const useTickerCallback = <T = unknown>({
7
+ isEnabled = true,
8
+ callback
9
+ }: {
10
+ isEnabled?: boolean;
11
+ callback: TickerCallback<T>;
12
+ }) => {
13
+ const { application, isInitialized } = useWorld();
14
+
15
+ useEffect(() => {
16
+ if (!isInitialized || !isEnabled) {
17
+ return;
18
+ }
19
+
20
+ application.ticker.add(callback);
21
+ return () => {
22
+ application.ticker.remove(callback);
23
+ };
24
+ }, [isEnabled, application, isInitialized, callback]);
25
+ };
@@ -0,0 +1,39 @@
1
+ import { useEffect, useMemo } from "react";
2
+
3
+ import { TilingSpriteOptions, TilingSprite } from "pixi.js";
4
+ import { useTextures } from "./useTexture";
5
+ import { useObject } from "./useObject";
6
+
7
+ type UseTilingSpriteOptions = Omit<TilingSpriteOptions, "texture"> & {
8
+ texture: string;
9
+ };
10
+
11
+ export const useTilingSprite = ({ texture = "", tilePosition, ...options }: UseTilingSpriteOptions) => {
12
+ const textureKeys = useMemo(() => {
13
+ return [...(texture ? [texture] : [])];
14
+ }, [texture]);
15
+
16
+ const { isFetched } = useTextures({ keys: textureKeys });
17
+
18
+ const sprite = useMemo(() => {
19
+ if (!isFetched) {
20
+ return null;
21
+ }
22
+
23
+ if (texture) {
24
+ return TilingSprite.from(texture);
25
+ }
26
+
27
+ return new TilingSprite({});
28
+ }, [texture, frames]);
29
+
30
+ useObject({ object: sprite, ...options });
31
+
32
+ useEffect(() => {
33
+ if (tilePosition && sprite) {
34
+ sprite.tilePosition = tilePosition;
35
+ }
36
+ }, [tilePosition, sprite?.uid]);
37
+
38
+ return sprite;
39
+ };
@@ -0,0 +1,7 @@
1
+ import { useContext } from "react";
2
+
3
+ import { WorldContext } from "../world/World.context";
4
+
5
+ export const useWorld = () => {
6
+ return useContext(WorldContext);
7
+ };
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ export * from "./game-context";
2
+ export * from "./assets-manager";
3
+ export * from "./world";
4
+ export * from "./camera";
5
+ export * from "./layer";
6
+ export * from "./assets-manager";
7
+ export * from "./game-objects";
8
+ export * from "./physics";
9
+ export * from "./types";
10
+ export * from "./hooks";
@@ -0,0 +1,9 @@
1
+ import { createContext } from "react";
2
+ import { Container } from "pixi.js";
3
+
4
+ export type LayerContextValue = {
5
+ addObject: (body: Container) => void;
6
+ removeObject: (body: Container) => void;
7
+ };
8
+
9
+ export const LayerContext = createContext<LayerContextValue>({} as unknown as LayerContextValue);
@@ -0,0 +1,57 @@
1
+ import React, { PropsWithChildren, useCallback, useContext, useEffect, useMemo } from "react";
2
+ import { Container, ContainerOptions } from "pixi.js";
3
+ import { StageContext } from "../stage/Stage.context";
4
+ import { LayerContext, LayerContextValue } from "./Layer.context";
5
+
6
+ export type LayerOptions = {
7
+ options?: Omit<ContainerOptions, "parent">;
8
+ };
9
+
10
+ export const Layer: React.FC<PropsWithChildren & LayerOptions> = ({ options, children }) => {
11
+ const { addObject: addObjectIntoStage, removeObject: removeObjectFromStaeg } = useContext(StageContext);
12
+ const { addObject: addObjectIntoParent, removeObject: removeObjectFromParent } = useContext(LayerContext);
13
+
14
+ const container = useMemo(() => {
15
+ return new Container(options);
16
+ }, [options]);
17
+
18
+ useEffect(() => {
19
+ if (addObjectIntoParent) {
20
+ addObjectIntoParent(container);
21
+
22
+ return () => {
23
+ removeObjectFromParent(container);
24
+ };
25
+ }
26
+
27
+ addObjectIntoStage(container);
28
+
29
+ return () => {
30
+ removeObjectFromStaeg(container);
31
+ };
32
+ }, [container?.uid]);
33
+
34
+ const addObject = useCallback(
35
+ (thing: Container) => {
36
+ container.addChild(thing);
37
+ },
38
+ [container?.uid]
39
+ );
40
+
41
+ const removeObject = useCallback(
42
+ (thing: Container) => {
43
+ container.removeChild(thing);
44
+ },
45
+ [container?.uid]
46
+ );
47
+
48
+ const conextValue = useMemo<LayerContextValue>(
49
+ () => ({
50
+ addObject,
51
+ removeObject
52
+ }),
53
+ [container?.uid]
54
+ );
55
+
56
+ return <LayerContext.Provider value={conextValue}>{children}</LayerContext.Provider>;
57
+ };
@@ -0,0 +1 @@
1
+ export * from "./Layer";
@@ -0,0 +1,100 @@
1
+ import React, { PropsWithChildren, createContext, useEffect, useMemo, useRef, useState } from "react";
2
+ import Matter, { Engine, Runner, World } from "matter-js";
3
+ import { MatterPhysicsConfig, MatterPhysics } from "./types";
4
+
5
+ export type MatterPhysicsContextProviderOptions = {
6
+ isRunning?: boolean;
7
+ config: MatterPhysicsConfig;
8
+ };
9
+
10
+ export const MatterPhysicsContext = createContext<MatterPhysics>({
11
+ config: {
12
+ world: {
13
+ width: 480,
14
+ height: 320
15
+ }
16
+ },
17
+ engine: Engine.create(),
18
+ runner: Runner.create(),
19
+ run: () => {},
20
+ stop: () => {},
21
+ addBody: () => {},
22
+ removeBody: () => {}
23
+ });
24
+
25
+ export const MatterPhysicsContextProvider: React.FC<PropsWithChildren & MatterPhysicsContextProviderOptions> = ({
26
+ isRunning = false,
27
+ children,
28
+ config = { world: { width: 480, height: 320 } }
29
+ }) => {
30
+ const isRunningRef = useRef(false);
31
+ const [localConfig] = useState(config);
32
+
33
+ const world = useMemo(() => {
34
+ return World.create({
35
+ bounds: {
36
+ min: { x: 0, y: 0 },
37
+ max: { x: config.world.width, y: config.world.height }
38
+ }
39
+ });
40
+ }, [config.world]);
41
+
42
+ const engine = useMemo(() => {
43
+ return Engine.create({
44
+ world
45
+ });
46
+ }, [world]);
47
+
48
+ const runner = useMemo(() => Runner.create(), []);
49
+
50
+ const addBody = (body: Matter.Body | Matter.Composite) => {
51
+ if (engine?.world) {
52
+ World.add(engine?.world, body);
53
+ }
54
+ };
55
+
56
+ const removeBody = (body: Matter.Body | Matter.Composite) => {
57
+ if (engine?.world) {
58
+ World.remove(engine?.world, body);
59
+ }
60
+ };
61
+
62
+ const run = () => {
63
+ if (!isRunningRef.current) {
64
+ Runner.run(runner, engine);
65
+ isRunningRef.current = true;
66
+ }
67
+ };
68
+
69
+ const stop = () => {
70
+ if (isRunningRef.current) {
71
+ Runner.stop(runner);
72
+ isRunningRef.current = false;
73
+ }
74
+ };
75
+
76
+ const conextValue = useMemo(
77
+ () => ({
78
+ config: localConfig,
79
+ runner,
80
+ engine,
81
+ run,
82
+ stop,
83
+ addBody,
84
+ removeBody
85
+ }),
86
+ [runner, engine, localConfig]
87
+ );
88
+
89
+ useEffect(() => {
90
+ if (isRunning) {
91
+ run();
92
+ }
93
+
94
+ return () => {
95
+ stop();
96
+ };
97
+ }, [isRunning]);
98
+
99
+ return <MatterPhysicsContext.Provider value={conextValue}>{children}</MatterPhysicsContext.Provider>;
100
+ };