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.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/dist/src/assets-manager/AssetsManager.context.d.ts +15 -0
- package/dist/src/assets-manager/AssetsManager.context.js +6 -0
- package/dist/src/assets-manager/AssetsManager.d.ts +2 -0
- package/dist/src/assets-manager/AssetsManager.js +43 -0
- package/dist/src/assets-manager/index.d.ts +3 -0
- package/dist/src/assets-manager/index.js +3 -0
- package/dist/src/assets-manager/useAssetManager.d.ts +1 -0
- package/dist/src/assets-manager/useAssetManager.js +5 -0
- package/dist/src/camera/Camera.context.d.ts +13 -0
- package/dist/src/camera/Camera.context.js +2 -0
- package/dist/src/camera/Camera.d.ts +7 -0
- package/dist/src/camera/Camera.js +99 -0
- package/dist/src/camera/index.d.ts +2 -0
- package/dist/src/camera/index.js +2 -0
- package/dist/src/game-context/Game.context.d.ts +39 -0
- package/dist/src/game-context/Game.context.js +85 -0
- package/dist/src/game-context/index.d.ts +1 -0
- package/dist/src/game-context/index.js +1 -0
- package/dist/src/game-objects/GameObjectPhysicalObjectConfig.d.ts +4 -0
- package/dist/src/game-objects/GameObjectPhysicalObjectConfig.js +1 -0
- package/dist/src/game-objects/index.d.ts +2 -0
- package/dist/src/game-objects/index.js +2 -0
- package/dist/src/game-objects/usePhysicalObject.d.ts +7 -0
- package/dist/src/game-objects/usePhysicalObject.js +17 -0
- package/dist/src/game-objects/usePhysicalObjectFromConfig.d.ts +6 -0
- package/dist/src/game-objects/usePhysicalObjectFromConfig.js +13 -0
- package/dist/src/game-objects/useWalls.d.ts +11 -0
- package/dist/src/game-objects/useWalls.js +59 -0
- package/dist/src/hooks/index.d.ts +12 -0
- package/dist/src/hooks/index.js +12 -0
- package/dist/src/hooks/useAnimatedSprite.d.ts +10 -0
- package/dist/src/hooks/useAnimatedSprite.js +41 -0
- package/dist/src/hooks/useCamera.d.ts +1 -0
- package/dist/src/hooks/useCamera.js +5 -0
- package/dist/src/hooks/useCollisionDetection.d.ts +9 -0
- package/dist/src/hooks/useCollisionDetection.js +21 -0
- package/dist/src/hooks/useGame.d.ts +1 -0
- package/dist/src/hooks/useGame.js +5 -0
- package/dist/src/hooks/useGlobalEventHandler.d.ts +5 -0
- package/dist/src/hooks/useGlobalEventHandler.js +15 -0
- package/dist/src/hooks/useLayerContext.d.ts +1 -0
- package/dist/src/hooks/useLayerContext.js +5 -0
- package/dist/src/hooks/useObject.d.ts +6 -0
- package/dist/src/hooks/useObject.js +54 -0
- package/dist/src/hooks/useSprite.d.ts +6 -0
- package/dist/src/hooks/useSprite.js +21 -0
- package/dist/src/hooks/useStage.d.ts +1 -0
- package/dist/src/hooks/useStage.js +5 -0
- package/dist/src/hooks/useTexture.d.ts +9 -0
- package/dist/src/hooks/useTexture.js +14 -0
- package/dist/src/hooks/useTickerCallback.d.ts +5 -0
- package/dist/src/hooks/useTickerCallback.js +14 -0
- package/dist/src/hooks/useTilingSprite.d.ts +6 -0
- package/dist/src/hooks/useTilingSprite.js +26 -0
- package/dist/src/hooks/useWorld.d.ts +1 -0
- package/dist/src/hooks/useWorld.js +5 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +10 -0
- package/dist/src/layer/Layer.context.d.ts +6 -0
- package/dist/src/layer/Layer.context.js +2 -0
- package/dist/src/layer/Layer.d.ts +6 -0
- package/dist/src/layer/Layer.js +34 -0
- package/dist/src/layer/index.d.ts +1 -0
- package/dist/src/layer/index.js +1 -0
- package/dist/src/physics/MatterPhysics.context.d.ts +8 -0
- package/dist/src/physics/MatterPhysics.context.js +74 -0
- package/dist/src/physics/index.d.ts +4 -0
- package/dist/src/physics/index.js +4 -0
- package/dist/src/physics/types.d.ts +24 -0
- package/dist/src/physics/types.js +1 -0
- package/dist/src/physics/useCollisionEventHandler.d.ts +7 -0
- package/dist/src/physics/useCollisionEventHandler.js +15 -0
- package/dist/src/physics/usePhysicsEngineEventHandler.d.ts +8 -0
- package/dist/src/physics/usePhysicsEngineEventHandler.js +15 -0
- package/dist/src/physics/usePhysicsTickerCallback.d.ts +7 -0
- package/dist/src/physics/usePhysicsTickerCallback.js +15 -0
- package/dist/src/stage/Stage.context.d.ts +6 -0
- package/dist/src/stage/Stage.context.js +2 -0
- package/dist/src/stage/Stage.d.ts +2 -0
- package/dist/src/stage/Stage.js +29 -0
- package/dist/src/stage/index.d.ts +1 -0
- package/dist/src/stage/index.js +1 -0
- package/dist/src/types.d.ts +7 -0
- package/dist/src/types.js +7 -0
- package/dist/src/world/World.context.d.ts +10 -0
- package/dist/src/world/World.context.js +2 -0
- package/dist/src/world/World.d.ts +11 -0
- package/dist/src/world/World.js +66 -0
- package/dist/src/world/index.d.ts +2 -0
- package/dist/src/world/index.js +2 -0
- package/package.json +52 -0
- package/src/assets-manager/AssetsManager.context.tsx +23 -0
- package/src/assets-manager/AssetsManager.tsx +54 -0
- package/src/assets-manager/index.ts +3 -0
- package/src/assets-manager/useAssetManager.ts +7 -0
- package/src/camera/Camera.context.tsx +17 -0
- package/src/camera/Camera.tsx +153 -0
- package/src/camera/index.ts +2 -0
- package/src/game-context/Game.context.tsx +133 -0
- package/src/game-context/index.ts +1 -0
- package/src/game-objects/GameObjectPhysicalObjectConfig.ts +18 -0
- package/src/game-objects/index.ts +2 -0
- package/src/game-objects/usePhysicalObject.ts +24 -0
- package/src/game-objects/usePhysicalObjectFromConfig.ts +22 -0
- package/src/game-objects/useWalls.ts +93 -0
- package/src/hooks/index.ts +12 -0
- package/src/hooks/useAnimatedSprite.ts +65 -0
- package/src/hooks/useCamera.ts +6 -0
- package/src/hooks/useCollisionDetection.ts +35 -0
- package/src/hooks/useGame.ts +6 -0
- package/src/hooks/useGlobalEventHandler.ts +27 -0
- package/src/hooks/useLayerContext.ts +6 -0
- package/src/hooks/useObject.ts +81 -0
- package/src/hooks/useSprite.ts +33 -0
- package/src/hooks/useStage.ts +7 -0
- package/src/hooks/useTexture.ts +22 -0
- package/src/hooks/useTickerCallback.ts +25 -0
- package/src/hooks/useTilingSprite.ts +39 -0
- package/src/hooks/useWorld.ts +7 -0
- package/src/index.ts +10 -0
- package/src/layer/Layer.context.tsx +9 -0
- package/src/layer/Layer.tsx +57 -0
- package/src/layer/index.ts +1 -0
- package/src/physics/MatterPhysics.context.tsx +100 -0
- package/src/physics/index.ts +4 -0
- package/src/physics/types.ts +27 -0
- package/src/physics/useCollisionEventHandler.ts +26 -0
- package/src/physics/usePhysicsEngineEventHandler.ts +30 -0
- package/src/physics/usePhysicsTickerCallback.ts +25 -0
- package/src/stage/Stage.context.tsx +9 -0
- package/src/stage/Stage.tsx +50 -0
- package/src/stage/index.ts +1 -0
- package/src/types.ts +8 -0
- package/src/world/World.context.tsx +13 -0
- package/src/world/World.tsx +93 -0
- 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,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,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,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,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
|
+
};
|
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
|
+
};
|