pixi-fusion 2.0.1 → 2.2.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 (44) hide show
  1. package/dist/src/assets-manager/AssetsManager.js +2 -2
  2. package/dist/src/camera/Camera.js +8 -8
  3. package/dist/src/game-context/Game.context.d.ts +1 -0
  4. package/dist/src/game-context/Game.context.js +17 -13
  5. package/dist/src/game-objects/usePhysicalObject.js +1 -1
  6. package/dist/src/game-objects/usePhysicalObjectFromConfig.d.ts +1 -1
  7. package/dist/src/game-objects/usePhysicalObjectFromConfig.js +1 -1
  8. package/dist/src/game-objects/useWalls.js +2 -2
  9. package/dist/src/hooks/useAnimatedSprite.js +4 -4
  10. package/dist/src/hooks/useCollisionDetection.js +1 -1
  11. package/dist/src/hooks/useObject.js +10 -10
  12. package/dist/src/hooks/useSprite.js +1 -1
  13. package/dist/src/hooks/useTexture.js +1 -1
  14. package/dist/src/hooks/useTilingSprite.js +2 -2
  15. package/dist/src/index.d.ts +0 -1
  16. package/dist/src/index.js +0 -1
  17. package/dist/src/layer/Layer.js +4 -4
  18. package/dist/src/physics/MatterPhysics.context.js +11 -11
  19. package/dist/src/physics/useCollisionEventHandler.js +1 -1
  20. package/dist/src/physics/usePhysicsEngineEventHandler.js +1 -1
  21. package/dist/src/physics/usePhysicsTickerCallback.js +1 -1
  22. package/dist/src/stage/Stage.js +3 -3
  23. package/dist/src/world/World.js +31 -34
  24. package/package.json +27 -14
  25. package/src/assets-manager/AssetsManager.tsx +14 -17
  26. package/src/camera/Camera.tsx +16 -28
  27. package/src/game-context/Game.context.tsx +18 -13
  28. package/src/game-objects/usePhysicalObject.ts +1 -1
  29. package/src/game-objects/usePhysicalObjectFromConfig.ts +2 -2
  30. package/src/game-objects/useWalls.ts +2 -2
  31. package/src/hooks/useAnimatedSprite.ts +4 -4
  32. package/src/hooks/useCollisionDetection.ts +1 -1
  33. package/src/hooks/useObject.ts +10 -10
  34. package/src/hooks/useSprite.ts +1 -1
  35. package/src/hooks/useTexture.ts +1 -1
  36. package/src/hooks/useTilingSprite.ts +2 -2
  37. package/src/index.ts +0 -1
  38. package/src/layer/Layer.tsx +4 -4
  39. package/src/physics/MatterPhysics.context.tsx +23 -17
  40. package/src/physics/useCollisionEventHandler.ts +1 -1
  41. package/src/physics/usePhysicsEngineEventHandler.ts +1 -1
  42. package/src/physics/usePhysicsTickerCallback.ts +1 -1
  43. package/src/stage/Stage.tsx +7 -13
  44. package/src/world/World.tsx +37 -37
package/package.json CHANGED
@@ -5,22 +5,35 @@
5
5
  "url": "https://github.com/laverve/fusion/issues"
6
6
  },
7
7
  "dependencies": {
8
- "@types/matter-js": "^0.19.8",
8
+ "@types/matter-js": "^0.20.2",
9
9
  "matter-js": "^0.20.0",
10
- "pixi.js": "^8.8.0",
11
- "pixi-viewport": "^6.0.3"
10
+ "pixi-viewport": "^6.0.3",
11
+ "pixi.js": "^8.14.0"
12
12
  },
13
13
  "peerDependencies": {
14
- "react": "^19.0.0"
14
+ "react": "^19.x.x"
15
15
  },
16
16
  "devDependencies": {
17
- "@laverve/eslint-utils": "^5.1.11",
18
- "@laverve/test-utils": "^5.1.11",
19
- "@types/react": "^19.0.10",
20
- "husky": "9.1.7",
21
- "lint-staged": "^15.4.3",
22
- "ts-node": "^10.9.1",
23
- "typescript": "^5.7.3"
17
+ "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0",
18
+ "@eslint/js": "9.39.1",
19
+ "@types/react": "^19.2.2",
20
+ "eslint": "^9.39.1",
21
+ "eslint-config-prettier": "^10.1.8",
22
+ "eslint-plugin-import": "^2.32.0",
23
+ "eslint-plugin-jest": "^29.0.1",
24
+ "eslint-plugin-jsonc": "^2.21.0",
25
+ "eslint-plugin-prettier": "^5.5.4",
26
+ "eslint-plugin-react": "^7.37.5",
27
+ "eslint-plugin-react-hooks": "^7.0.1",
28
+ "husky": "^9.1.7",
29
+ "jest": "^30.2.0",
30
+ "jest-watch-typeahead": "^3.0.1",
31
+ "lint-staged": "^16.2.6",
32
+ "react-app-polyfill": "^3.0.0",
33
+ "ts-jest": "^29.4.5",
34
+ "ts-node": "^10.9.2",
35
+ "typescript": "^5.9.3",
36
+ "typescript-eslint": "^8.46.3"
24
37
  },
25
38
  "description": "This module offers a set of common components needed for playing games.",
26
39
  "keywords": [
@@ -50,14 +63,14 @@
50
63
  "url": "https://github.com/laverve/fusion.git"
51
64
  },
52
65
  "scripts": {
53
- "lint": "eslint ./src/",
54
- "lint:fix": "eslint ./src/ --fix",
66
+ "lint": "eslint .",
67
+ "lint:fix": "eslint . --fix",
55
68
  "lint:staged": "lint-staged",
56
69
  "prepare": "husky",
57
70
  "test": "jest --passWithNoTests",
58
71
  "build": "tsc",
59
72
  "build:dev": "tsc -w"
60
73
  },
61
- "version": "2.0.1",
74
+ "version": "2.2.0",
62
75
  "webpack": "./src/index.ts"
63
76
  }
@@ -1,6 +1,6 @@
1
1
  import React, { PropsWithChildren, useCallback, useEffect, useMemo, useState } from "react";
2
- import { Asset, AssetsManagerContext, AssetsManagerContextValue } from "./AssetsManager.context";
3
2
  import { Assets } from "pixi.js";
3
+ import { Asset, AssetsManagerContext, AssetsManagerContextValue } from "./AssetsManager.context";
4
4
 
5
5
  export const AssetsManager: React.FC<PropsWithChildren> = ({ children }) => {
6
6
  const [isFetching, setIsFetching] = useState(false);
@@ -12,22 +12,19 @@ export const AssetsManager: React.FC<PropsWithChildren> = ({ children }) => {
12
12
  Assets.cache.reset();
13
13
  }, []);
14
14
 
15
- const load = useCallback(
16
- async (groupId: string, localAsset: Asset[]) => {
17
- try {
18
- Assets.addBundle(groupId, localAsset);
19
- await Assets.loadBundle(groupId);
20
- setIsFetched(true);
21
- } catch (e) {
22
- setIsError(true);
23
- setIsFetched(false);
24
- setError(e);
25
- } finally {
26
- setIsFetching(false);
27
- }
28
- },
29
- [isFetching, isError, isFetched, error]
30
- );
15
+ const load = useCallback(async (groupId: string, localAsset: Asset[]) => {
16
+ try {
17
+ Assets.addBundle(groupId, localAsset);
18
+ await Assets.loadBundle(groupId);
19
+ setIsFetched(true);
20
+ } catch (e) {
21
+ setIsError(true);
22
+ setIsFetched(false);
23
+ setError(e);
24
+ } finally {
25
+ setIsFetching(false);
26
+ }
27
+ }, []);
31
28
 
32
29
  const unload = useCallback((groupId: string) => {
33
30
  Assets.unloadBundle(groupId);
@@ -32,19 +32,13 @@ export const Camera: React.FC<CameraProps & PropsWithChildren> = ({ children, cl
32
32
  [size, application]
33
33
  );
34
34
 
35
- const addObject = useCallback(
36
- (thing: Container) => {
37
- setThings((oldThings) => [...oldThings, thing]);
38
- },
39
- [things]
40
- );
35
+ const addObject = useCallback((thing: Container) => {
36
+ setThings((oldThings) => [...oldThings, thing]);
37
+ }, []);
41
38
 
42
- const removeObject = useCallback(
43
- (thing: Container) => {
44
- setThings((oldThings) => oldThings.filter(({ uid }) => uid === thing.uid));
45
- },
46
- [application]
47
- );
39
+ const removeObject = useCallback((thing: Container) => {
40
+ setThings((oldThings) => oldThings.filter(({ uid }) => uid === thing.uid));
41
+ }, []);
48
42
 
49
43
  const conextValue = useMemo<StageContextValue>(
50
44
  () => ({
@@ -58,7 +52,7 @@ export const Camera: React.FC<CameraProps & PropsWithChildren> = ({ children, cl
58
52
  if (viewport && clampZoom && application) {
59
53
  viewport.clampZoom(clampZoom);
60
54
  }
61
- }, [clampZoom]);
55
+ }, [clampZoom, application, viewport]);
62
56
 
63
57
  useEffect(() => {
64
58
  if (viewport) {
@@ -69,7 +63,7 @@ export const Camera: React.FC<CameraProps & PropsWithChildren> = ({ children, cl
69
63
  viewport.height = size.height;
70
64
  }
71
65
  }
72
- }, [size, viewport?.uid, application]);
66
+ }, [size, viewport, application]);
73
67
 
74
68
  useEffect(() => {
75
69
  if (!application || !viewport) {
@@ -90,7 +84,7 @@ export const Camera: React.FC<CameraProps & PropsWithChildren> = ({ children, cl
90
84
  removeObjectFromStage(viewport);
91
85
  setIsReady(false);
92
86
  };
93
- }, [viewport?.uid]);
87
+ }, [viewport, addObjectToStage, removeObjectFromStage]);
94
88
 
95
89
  useEffect(() => {
96
90
  if (!viewport || !isReady) {
@@ -122,26 +116,20 @@ export const Camera: React.FC<CameraProps & PropsWithChildren> = ({ children, cl
122
116
  }
123
117
  }, [isReady, viewport, ensureVisibleOptions]);
124
118
 
125
- const followCallback = useCallback(
126
- (container: Container) => {
127
- setFollowContainer(container);
128
- },
129
- [followContainer]
130
- );
119
+ const followCallback = useCallback((container: Container) => {
120
+ setFollowContainer(container);
121
+ }, []);
131
122
 
132
- const ensureVisibleCallback = useCallback(
133
- (options: EnsureVisibleOptions) => {
134
- setEnsureVisibleOptions(options);
135
- },
136
- [followContainer]
137
- );
123
+ const ensureVisibleCallback = useCallback((options: EnsureVisibleOptions) => {
124
+ setEnsureVisibleOptions(options);
125
+ }, []);
138
126
 
139
127
  const value = useMemo(() => {
140
128
  return {
141
129
  follow: followCallback,
142
130
  ensureVisible: ensureVisibleCallback
143
131
  };
144
- }, [followCallback]);
132
+ }, [followCallback, ensureVisibleCallback]);
145
133
 
146
134
  return (
147
135
  <CameraContext.Provider value={value}>
@@ -1,4 +1,4 @@
1
- import React, { PropsWithChildren, createContext, useMemo, useRef, useState } from "react";
1
+ import React, { PropsWithChildren, createContext, useCallback, useMemo, useRef, useState } from "react";
2
2
  import { GameStatus, GameStatusChangeEvent } from "../types";
3
3
 
4
4
  export type GameContextValue = {
@@ -7,6 +7,7 @@ export type GameContextValue = {
7
7
  readonly pause: () => void;
8
8
  readonly stop: () => void;
9
9
  readonly timeout: number;
10
+ readonly gameId: number;
10
11
  readonly status: GameStatus;
11
12
  readonly startTime: number | null;
12
13
  readonly pauseTime: number;
@@ -27,6 +28,7 @@ export const GameContext = createContext<GameContextValue>({
27
28
  start: () => {},
28
29
  stop: () => {},
29
30
  timeout: 0,
31
+ gameId: 0,
30
32
  status: GameStatus.READY,
31
33
  startTime: null,
32
34
  pauseTime: 0,
@@ -45,6 +47,7 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
45
47
  }) => {
46
48
  const timerRef = useRef<NodeJS.Timeout>(null);
47
49
  const timeout = Math.max(0, inputTimeout);
50
+ const [gameId, setGemaId] = useState(0);
48
51
 
49
52
  const [status, setStatus] = useState(GameStatus.READY);
50
53
  const [startTime, setStartTime] = useState<number | null>(null);
@@ -52,7 +55,7 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
52
55
  const [pauseTime, setPauseTime] = useState<number>(0);
53
56
  const [pausedAtTime, setPausedAtTime] = useState<number | null>(null);
54
57
 
55
- const onTimedOut = () => {
58
+ const onTimedOut = useCallback(() => {
56
59
  const newEndTime = +new Date();
57
60
  const newStatus = GameStatus.TIMEDOUT;
58
61
 
@@ -66,9 +69,9 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
66
69
  pausedAtTime: null,
67
70
  pauseTime
68
71
  });
69
- };
72
+ }, [events, pauseTime, startTime]);
70
73
 
71
- const start = () => {
74
+ const start = useCallback(() => {
72
75
  const newStartTime = startTime || +new Date();
73
76
  const newStatus = GameStatus.IN_PROGRESS;
74
77
  const newEndTime = null;
@@ -93,9 +96,9 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
93
96
  pausedAtTime: null,
94
97
  pauseTime: newPauseTime
95
98
  });
96
- };
99
+ }, [events, onTimedOut, pauseTime, pausedAtTime, startTime, timeout]);
97
100
 
98
- const reset = () => {
101
+ const reset = useCallback(() => {
99
102
  const newStartTime = null;
100
103
  const newStatus = GameStatus.READY;
101
104
  const newEndTime = null;
@@ -107,6 +110,7 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
107
110
  setEndTime(newEndTime);
108
111
  setPauseTime(newPauseTime);
109
112
  setPausedAtTime(newPausedAtTime);
113
+ setGemaId((current) => current++);
110
114
 
111
115
  if (timerRef.current) {
112
116
  clearTimeout(timerRef.current);
@@ -119,9 +123,9 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
119
123
  pausedAtTime: newPausedAtTime,
120
124
  pauseTime: newPauseTime
121
125
  });
122
- };
126
+ }, [events]);
123
127
 
124
- const stop = () => {
128
+ const stop = useCallback(() => {
125
129
  const newStatus = GameStatus.COMPLETED;
126
130
  const newEndTime = +new Date();
127
131
 
@@ -138,9 +142,9 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
138
142
  pausedAtTime,
139
143
  pauseTime
140
144
  });
141
- };
145
+ }, [events, pauseTime, pausedAtTime, startTime]);
142
146
 
143
- const pause = () => {
147
+ const pause = useCallback(() => {
144
148
  const newStatus = GameStatus.PAUSED;
145
149
  const newPausedAtTime = +new Date();
146
150
 
@@ -157,7 +161,7 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
157
161
  status: newStatus,
158
162
  pauseTime
159
163
  });
160
- };
164
+ }, [events, pauseTime, startTime]);
161
165
 
162
166
  const contextValue = useMemo(
163
167
  () => ({
@@ -169,9 +173,10 @@ export const GameContextProvider: React.FC<GameContextProviderProps> = ({
169
173
  stop,
170
174
  start,
171
175
  reset,
172
- pause
176
+ pause,
177
+ gameId
173
178
  }),
174
- [timeout, status, startTime, endTime, pauseTime, pausedAtTime]
179
+ [timeout, status, startTime, endTime, pauseTime, pause, reset, start, stop, gameId]
175
180
  );
176
181
 
177
182
  return <GameContext.Provider value={contextValue}>{children}</GameContext.Provider>;
@@ -16,7 +16,7 @@ export const usePhysicalObject = ({ physicalObject }: { physicalObject: Nullable
16
16
  return () => {
17
17
  removeBody(physicalObject);
18
18
  };
19
- }, [physicalObject?.id]);
19
+ }, [physicalObject, addBody, removeBody]);
20
20
 
21
21
  return {
22
22
  physicalObject
@@ -1,8 +1,8 @@
1
1
  import { Body } from "matter-js";
2
2
  import { useMemo } from "react";
3
+ import { Nullable } from "../types";
3
4
  import { GameObjectPhysicalObjectConfig } from "./GameObjectPhysicalObjectConfig";
4
5
  import { usePhysicalObject } from "./usePhysicalObject";
5
- import { Nullable } from "../types";
6
6
 
7
7
  export const usePhysicalObjectFromConfig = <PhysicalObjectType extends Nullable<Body> = Body>({
8
8
  position = { x: 0, y: 0 },
@@ -14,7 +14,7 @@ export const usePhysicalObjectFromConfig = <PhysicalObjectType extends Nullable<
14
14
  }
15
15
 
16
16
  return null;
17
- }, []) as PhysicalObjectType;
17
+ }, [physicalObjectConfig, position]) as PhysicalObjectType;
18
18
 
19
19
  usePhysicalObject({ physicalObject });
20
20
 
@@ -81,7 +81,7 @@ export const useWalls = ({ left = true, right = true, top = true, bottom = true
81
81
  Composite.add(walls, parts);
82
82
 
83
83
  return walls;
84
- }, [width, height]);
84
+ }, [width, height, bottom, center.x, center.y, left, right, top]);
85
85
 
86
86
  useEffect(() => {
87
87
  addBody(body);
@@ -89,5 +89,5 @@ export const useWalls = ({ left = true, right = true, top = true, bottom = true
89
89
  return () => {
90
90
  removeBody(body);
91
91
  };
92
- }, [body.id]);
92
+ }, [body, addBody, removeBody]);
93
93
  };
@@ -33,7 +33,7 @@ export const useAnimatedSprite = ({
33
33
  return sheet;
34
34
  }
35
35
  return null;
36
- }, [spritesheetJSON]);
36
+ }, [spritesheetJSON, isFetched, textures]);
37
37
 
38
38
  const sprite = useMemo(() => {
39
39
  if (spritesheet?.animations?.[animation]) {
@@ -41,7 +41,7 @@ export const useAnimatedSprite = ({
41
41
  }
42
42
 
43
43
  return null;
44
- }, [texture, spritesheetJSON, animation]);
44
+ }, [animation, spritesheet?.animations]);
45
45
 
46
46
  useObject({ object: sprite, ...options });
47
47
 
@@ -49,7 +49,7 @@ export const useAnimatedSprite = ({
49
49
  if (sprite) {
50
50
  sprite.animationSpeed = animationSpeed;
51
51
  }
52
- }, [animationSpeed, sprite?.uid]);
52
+ }, [animationSpeed, sprite]);
53
53
 
54
54
  useEffect(() => {
55
55
  if (sprite) {
@@ -59,7 +59,7 @@ export const useAnimatedSprite = ({
59
59
  sprite.stop();
60
60
  }
61
61
  }
62
- }, [isPlaying, sprite?.uid]);
62
+ }, [isPlaying, sprite]);
63
63
 
64
64
  return sprite;
65
65
  };
@@ -1,6 +1,6 @@
1
1
  import { Container } from "pixi.js";
2
- import { useTickerCallback } from "./useTickerCallback";
3
2
  import { useCallback } from "react";
3
+ import { useTickerCallback } from "./useTickerCallback";
4
4
 
5
5
  type UseCollisionDetectionOptions = {
6
6
  objectA?: Container | null;
@@ -1,6 +1,6 @@
1
1
  import { Sprite, SpriteOptions, TilingSprite } from "pixi.js";
2
- import { useLayerContext } from "./useLayerContext";
3
2
  import { useEffect } from "react";
3
+ import { useLayerContext } from "./useLayerContext";
4
4
 
5
5
  type UseObjectOptions = Omit<SpriteOptions, "texture"> & {
6
6
  object: Sprite | TilingSprite | null;
@@ -29,53 +29,53 @@ export const useObject = ({
29
29
  return () => {
30
30
  removeObject(object);
31
31
  };
32
- }, [object?.uid]);
32
+ }, [object, addObject, removeObject]);
33
33
 
34
34
  useEffect(() => {
35
35
  if (anchor !== undefined && object) {
36
36
  object.anchor = anchor;
37
37
  }
38
- }, [anchor, object?.uid]);
38
+ }, [anchor, object]);
39
39
 
40
40
  useEffect(() => {
41
41
  if (angle !== undefined && object) {
42
42
  object.angle = angle;
43
43
  }
44
- }, [angle, object?.uid]);
44
+ }, [angle, object]);
45
45
 
46
46
  useEffect(() => {
47
47
  if (object && position !== undefined) {
48
48
  object.position = position;
49
49
  }
50
- }, [position, object?.uid]);
50
+ }, [position, object]);
51
51
 
52
52
  useEffect(() => {
53
53
  if (object && skew !== undefined) {
54
54
  object.skew = skew;
55
55
  }
56
- }, [skew, object?.uid]);
56
+ }, [skew, object]);
57
57
 
58
58
  useEffect(() => {
59
59
  if (object && alpha !== undefined) {
60
60
  object.alpha = alpha;
61
61
  }
62
- }, [alpha, object?.uid]);
62
+ }, [alpha, object]);
63
63
 
64
64
  useEffect(() => {
65
65
  if (object && scale !== undefined) {
66
66
  object.scale = scale;
67
67
  }
68
- }, [scale, object?.uid]);
68
+ }, [scale, object]);
69
69
 
70
70
  useEffect(() => {
71
71
  if (object) {
72
72
  object.visible = visible;
73
73
  }
74
- }, [visible, object?.uid]);
74
+ }, [visible, object]);
75
75
 
76
76
  useEffect(() => {
77
77
  if (object && width !== undefined) {
78
78
  object.width = width;
79
79
  }
80
- }, [width, object?.uid]);
80
+ }, [width, object]);
81
81
  };
@@ -25,7 +25,7 @@ export const useSprite = ({ texture = "", ...options }: UseSpriteOptions) => {
25
25
  }
26
26
 
27
27
  return new Sprite({});
28
- }, [texture, frames, isFetched]);
28
+ }, [texture, isFetched]);
29
29
 
30
30
  useObject({ object: sprite, ...options });
31
31
 
@@ -8,7 +8,7 @@ export const useTextures = ({ keys = [] }: { keys?: string[] }) => {
8
8
 
9
9
  const textures = useMemo(() => {
10
10
  return isFetched ? keys.map((key) => getAsset(key) as Texture) : [];
11
- }, [keys, isFetched, isFetching, isError]);
11
+ }, [keys, isFetched, getAsset]);
12
12
 
13
13
  return useMemo(
14
14
  () => ({
@@ -25,7 +25,7 @@ export const useTilingSprite = ({ texture = "", tilePosition, ...options }: UseT
25
25
  }
26
26
 
27
27
  return new TilingSprite({});
28
- }, [texture, frames]);
28
+ }, [texture, isFetched]);
29
29
 
30
30
  useObject({ object: sprite, ...options });
31
31
 
@@ -33,7 +33,7 @@ export const useTilingSprite = ({ texture = "", tilePosition, ...options }: UseT
33
33
  if (tilePosition && sprite) {
34
34
  sprite.tilePosition = tilePosition;
35
35
  }
36
- }, [tilePosition, sprite?.uid]);
36
+ }, [tilePosition, sprite]);
37
37
 
38
38
  return sprite;
39
39
  };
package/src/index.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from "./game-context";
2
- export * from "./assets-manager";
3
2
  export * from "./world";
4
3
  export * from "./camera";
5
4
  export * from "./layer";
@@ -29,20 +29,20 @@ export const Layer: React.FC<PropsWithChildren & LayerOptions> = ({ options, chi
29
29
  return () => {
30
30
  removeObjectFromStaeg(container);
31
31
  };
32
- }, [container?.uid]);
32
+ }, [container, addObjectIntoParent, addObjectIntoStage, removeObjectFromParent, removeObjectFromStaeg]);
33
33
 
34
34
  const addObject = useCallback(
35
35
  (thing: Container) => {
36
36
  container.addChild(thing);
37
37
  },
38
- [container?.uid]
38
+ [container]
39
39
  );
40
40
 
41
41
  const removeObject = useCallback(
42
42
  (thing: Container) => {
43
43
  container.removeChild(thing);
44
44
  },
45
- [container?.uid]
45
+ [container]
46
46
  );
47
47
 
48
48
  const conextValue = useMemo<LayerContextValue>(
@@ -50,7 +50,7 @@ export const Layer: React.FC<PropsWithChildren & LayerOptions> = ({ options, chi
50
50
  addObject,
51
51
  removeObject
52
52
  }),
53
- [container?.uid]
53
+ [addObject, removeObject]
54
54
  );
55
55
 
56
56
  return <LayerContext.Provider value={conextValue}>{children}</LayerContext.Provider>;
@@ -1,4 +1,4 @@
1
- import React, { PropsWithChildren, createContext, useEffect, useMemo, useRef, useState } from "react";
1
+ import React, { PropsWithChildren, createContext, useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import Matter, { Engine, Runner, World } from "matter-js";
3
3
  import { MatterPhysicsConfig, MatterPhysics } from "./types";
4
4
 
@@ -47,31 +47,37 @@ export const MatterPhysicsContextProvider: React.FC<PropsWithChildren & MatterPh
47
47
 
48
48
  const runner = useMemo(() => Runner.create(), []);
49
49
 
50
- const addBody = (body: Matter.Body | Matter.Composite) => {
51
- if (engine?.world) {
52
- World.add(engine?.world, body);
53
- }
54
- };
50
+ const addBody = useCallback(
51
+ (body: Matter.Body | Matter.Composite) => {
52
+ if (engine?.world) {
53
+ World.add(engine?.world, body);
54
+ }
55
+ },
56
+ [engine]
57
+ );
55
58
 
56
- const removeBody = (body: Matter.Body | Matter.Composite) => {
57
- if (engine?.world) {
58
- World.remove(engine?.world, body);
59
- }
60
- };
59
+ const removeBody = useCallback(
60
+ (body: Matter.Body | Matter.Composite) => {
61
+ if (engine?.world) {
62
+ World.remove(engine?.world, body);
63
+ }
64
+ },
65
+ [engine]
66
+ );
61
67
 
62
- const run = () => {
68
+ const run = useCallback(() => {
63
69
  if (!isRunningRef.current) {
64
70
  Runner.run(runner, engine);
65
71
  isRunningRef.current = true;
66
72
  }
67
- };
73
+ }, [runner, engine]);
68
74
 
69
- const stop = () => {
75
+ const stop = useCallback(() => {
70
76
  if (isRunningRef.current) {
71
77
  Runner.stop(runner);
72
78
  isRunningRef.current = false;
73
79
  }
74
- };
80
+ }, [runner]);
75
81
 
76
82
  const conextValue = useMemo(
77
83
  () => ({
@@ -83,7 +89,7 @@ export const MatterPhysicsContextProvider: React.FC<PropsWithChildren & MatterPh
83
89
  addBody,
84
90
  removeBody
85
91
  }),
86
- [runner, engine, localConfig]
92
+ [runner, engine, localConfig, addBody, removeBody, run, stop]
87
93
  );
88
94
 
89
95
  useEffect(() => {
@@ -94,7 +100,7 @@ export const MatterPhysicsContextProvider: React.FC<PropsWithChildren & MatterPh
94
100
  return () => {
95
101
  stop();
96
102
  };
97
- }, [isRunning]);
103
+ }, [isRunning, run, stop]);
98
104
 
99
105
  return <MatterPhysicsContext.Provider value={conextValue}>{children}</MatterPhysicsContext.Provider>;
100
106
  };
@@ -22,5 +22,5 @@ export const useCollisionEventHandler = ({ isEnabled = true, event, callback }:
22
22
  return () => {
23
23
  Events.off(engine, event, callback);
24
24
  };
25
- }, [isEnabled, callback, event]);
25
+ }, [isEnabled, callback, event, engine]);
26
26
  };
@@ -26,5 +26,5 @@ export const usePhysicsEngineEventHandler = ({
26
26
  return () => {
27
27
  Events.off(engine, event, callback);
28
28
  };
29
- }, [isEnabled, event, callback]);
29
+ }, [isEnabled, event, callback, engine]);
30
30
  };
@@ -21,5 +21,5 @@ export const usePhysicsTickerCallback = ({ isEnabled = true, callback }: UsePhys
21
21
  return () => {
22
22
  Events.off(runner, "tick", callback);
23
23
  };
24
- }, [isEnabled, callback]);
24
+ }, [isEnabled, callback, runner]);
25
25
  };