feat(game) : added particle system basics
parent
f4f6028a88
commit
59bc38d06e
|
@ -16,7 +16,8 @@
|
|||
"leva": "^0.9.35",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"three": "^0.160.0"
|
||||
"three": "^0.160.0",
|
||||
"zustand": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.28",
|
||||
|
@ -972,6 +973,22 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/drei/node_modules/zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber": {
|
||||
"version": "8.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.15.13.tgz",
|
||||
|
@ -1028,6 +1045,22 @@
|
|||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber/node_modules/zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/postprocessing": {
|
||||
"version": "2.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/postprocessing/-/postprocessing-2.15.11.tgz",
|
||||
|
@ -2140,6 +2173,22 @@
|
|||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/leva/node_modules/zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
|
@ -2948,6 +2997,14 @@
|
|||
"react": ">=17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utility-types": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
|
||||
|
@ -3074,16 +3131,27 @@
|
|||
"integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA=="
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.0.tgz",
|
||||
"integrity": "sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=16.8",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
"leva": "^0.9.35",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"three": "^0.160.0"
|
||||
"three": "^0.160.0",
|
||||
"zustand": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.28",
|
||||
|
|
|
@ -44,8 +44,10 @@ function App() {
|
|||
<KeyboardControls map={map}>
|
||||
<Experience />
|
||||
</KeyboardControls>
|
||||
{/* <Stats /> */}
|
||||
</Physics>
|
||||
</Suspense>
|
||||
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { Particles1 } from "./Particles1";
|
||||
import { Particles3 } from "./Particles3";
|
||||
|
||||
export const DriftParticlesLeft = ({turboColor,scale, ...props}) => {
|
||||
|
||||
if(scale < 0.5) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<group {...props}>
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
<Particles1 turboColor={turboColor} scale={scale} />
|
||||
|
||||
<Particles3 turboColor={turboColor} scale={scale} />
|
||||
<Particles3 turboColor={turboColor} scale={scale} />
|
||||
<Particles3 turboColor={turboColor} scale={scale} />
|
||||
|
||||
</group>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import { Particles2 } from "./Particles2";
|
||||
import { Particles4 } from "./Particles4";
|
||||
|
||||
export const DriftParticlesRight = ({turboColor,scale, ...props}) => {
|
||||
|
||||
if(scale < 0.5) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<group {...props}>
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
<Particles2 turboColor={turboColor} scale={scale} />
|
||||
|
||||
<Particles4 turboColor={turboColor} scale={scale} />
|
||||
<Particles4 turboColor={turboColor} scale={scale} />
|
||||
<Particles4 turboColor={turboColor} scale={scale} />
|
||||
|
||||
</group>
|
||||
)
|
||||
}
|
|
@ -9,7 +9,7 @@ import { RigidBody } from "@react-three/rapier";
|
|||
import { PlayerController } from "./PlayerController";
|
||||
import { Track } from "./models/Spafrancorchamps-REALISTIC";
|
||||
import { Paris } from "./models/Tour_paris_promenade";
|
||||
import { EffectComposer, N8AO, Bloom, DepthOfField, TiltShift2, HueSaturation} from '@react-three/postprocessing'
|
||||
import { EffectComposer, N8AO, Bloom, DepthOfField, TiltShift2, HueSaturation, SMAA} from '@react-three/postprocessing'
|
||||
|
||||
export const Experience = () => {
|
||||
return (
|
||||
|
@ -34,7 +34,8 @@ export const Experience = () => {
|
|||
{/* <ambientLight intensity={0.2} /> */}
|
||||
{/* <spotLight position={[10, 20, 10]} angle={0.12} penumbra={1} intensity={1} castShadow shadow-mapSize={1024} /> */}
|
||||
<Paris position={[0, 0, 0]} />
|
||||
<EffectComposer multisampling={0}>
|
||||
<EffectComposer multisampling={4}>
|
||||
<SMAA/>
|
||||
{/* <N8AO distanceFalloff={1} aoRadius={1} intensity={4} /> */}
|
||||
<Bloom luminanceThreshold={0} mipmapBlur luminanceSmoothing={0.01} intensity={0.5} />
|
||||
<DepthOfField target={[0, 0, 13]} focalLength={0.3} bokehScale={15} height={700} />
|
||||
|
|
|
@ -1,2 +1,47 @@
|
|||
export const Particles1 = () => {
|
||||
import { useRef } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
|
||||
export const Particles1 = ({ turboColor, scale, ...props }) => {
|
||||
const ref = useRef();
|
||||
const frame = useRef(0);
|
||||
|
||||
const velocity = useRef({
|
||||
x: -Math.random() * 0.1,
|
||||
y: Math.random() * 0.05,
|
||||
z: Math.random() * 0.05,
|
||||
});
|
||||
const gravity = -0.003;
|
||||
|
||||
useFrame(() => {
|
||||
frame.current += 1;
|
||||
let position = ref.current.position;
|
||||
|
||||
velocity.current.y += gravity;
|
||||
|
||||
position.x += velocity.current.x;
|
||||
position.y += velocity.current.y;
|
||||
position.z += velocity.current.z;
|
||||
|
||||
if (position.y < 0) {
|
||||
position.set(-0.6, 0.05, 0.5);
|
||||
velocity.current = {
|
||||
x: -Math.random() * 0.1,
|
||||
y: Math.random() * 0.05,
|
||||
z: Math.random() * 0.05,
|
||||
};
|
||||
}
|
||||
|
||||
ref.current.position.set(position.x, position.y, position.z);
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={ref} position={[-0.6, 0.05, 0.5]} scale={scale}>
|
||||
<boxGeometry args={[0.05, 0.1, 0.05]} />
|
||||
<meshStandardMaterial
|
||||
emissive={turboColor}
|
||||
toneMapped={false}
|
||||
emissiveIntensity={10}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import { useRef } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
|
||||
export const Particles2 = ({ turboColor, scale, ...props }) => {
|
||||
const ref = useRef();
|
||||
const frame = useRef(0);
|
||||
|
||||
const velocity = useRef({
|
||||
x: Math.random() * 0.1,
|
||||
y: Math.random() * 0.05,
|
||||
z: Math.random() * 0.05,
|
||||
});
|
||||
const gravity = -0.003;
|
||||
|
||||
useFrame(() => {
|
||||
frame.current += 1;
|
||||
let position = ref.current.position;
|
||||
|
||||
velocity.current.y += gravity;
|
||||
|
||||
position.x += velocity.current.x;
|
||||
position.y += velocity.current.y;
|
||||
position.z += velocity.current.z;
|
||||
|
||||
if (position.y < 0) {
|
||||
position.set(0.6, 0.05, 0.5);
|
||||
velocity.current = {
|
||||
x: Math.random() * 0.1,
|
||||
y: Math.random() * 0.05,
|
||||
z: Math.random() * 0.05,
|
||||
};
|
||||
}
|
||||
|
||||
ref.current.position.set(position.x, position.y, position.z);
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={ref} position={[0.6, 0.05, 0.5]} scale={scale}>
|
||||
<boxGeometry args={[0.05, 0.1, 0.05]} />
|
||||
<meshStandardMaterial
|
||||
emissive={turboColor}
|
||||
toneMapped={false}
|
||||
emissiveIntensity={10}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,48 @@
|
|||
import { useRef } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { Trail } from "@react-three/drei";
|
||||
|
||||
export const Particles3 = ({ turboColor, scale, ...props }) => {
|
||||
const ref = useRef();
|
||||
const frame = useRef(0);
|
||||
|
||||
const velocity = useRef({
|
||||
x: -Math.random() * 0.1,
|
||||
y: Math.random() * 0.1,
|
||||
z: Math.random() * 0.05,
|
||||
});
|
||||
const gravity = -0.003;
|
||||
|
||||
useFrame(() => {
|
||||
frame.current += 1;
|
||||
let position = ref.current.position;
|
||||
|
||||
velocity.current.y += gravity;
|
||||
|
||||
position.x += velocity.current.x;
|
||||
position.y += velocity.current.y;
|
||||
position.z += velocity.current.z;
|
||||
|
||||
if (position.y < 0) {
|
||||
position.set(-0.6, 0.05, 0.5);
|
||||
velocity.current = {
|
||||
x: -Math.random() * 0.1,
|
||||
y: Math.random() * 0.1,
|
||||
z: Math.random() * 0.05,
|
||||
};
|
||||
}
|
||||
|
||||
ref.current.position.set(position.x, position.y, position.z);
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={ref} position={[-0.6, 0.05, 0.5]} scale={scale}>
|
||||
<boxGeometry args={[0.1, 0.2, 0.1]} />
|
||||
<meshStandardMaterial
|
||||
emissive={turboColor}
|
||||
toneMapped={false}
|
||||
emissiveIntensity={50}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,48 @@
|
|||
import { useRef } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { Trail } from "@react-three/drei";
|
||||
|
||||
export const Particles4 = ({ turboColor, scale, ...props }) => {
|
||||
const ref = useRef();
|
||||
const frame = useRef(0);
|
||||
|
||||
const velocity = useRef({
|
||||
x: Math.random() * 0.1,
|
||||
y: Math.random() * 0.2,
|
||||
z: Math.random() * 0.05,
|
||||
});
|
||||
const gravity = -0.003;
|
||||
|
||||
useFrame(() => {
|
||||
frame.current += 1;
|
||||
let position = ref.current.position;
|
||||
|
||||
velocity.current.y += gravity;
|
||||
|
||||
position.x += velocity.current.x;
|
||||
position.y += velocity.current.y;
|
||||
position.z += velocity.current.z;
|
||||
|
||||
if (position.y < 0) {
|
||||
position.set(0.6, 0.05, 0.5);
|
||||
velocity.current = {
|
||||
x: Math.random() * 0.1,
|
||||
y: Math.random() * 0.1,
|
||||
z: Math.random() * 0.05,
|
||||
};
|
||||
}
|
||||
|
||||
ref.current.position.set(position.x, position.y, position.z);
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={ref} position={[0.6, 0.05, 0.5]} scale={scale}>
|
||||
<boxGeometry args={[0.1, 0.1, 0.1]} />
|
||||
<meshStandardMaterial
|
||||
emissive={turboColor}
|
||||
toneMapped={false}
|
||||
emissiveIntensity={50}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
};
|
|
@ -14,6 +14,9 @@ import { FrontLeftWheel } from "./models/Front_Left_Wheel";
|
|||
import { RearWheels } from "./models/Rear_wheels";
|
||||
import gsap from "gsap";
|
||||
import { Mario } from "./models/Mario_kart";
|
||||
import { Particles1 } from "./Particles1";
|
||||
import { DriftParticlesLeft } from "./DriftParticlesLeft";
|
||||
import { DriftParticlesRight } from "./DriftParticlesRight";
|
||||
|
||||
export const PlayerController = () => {
|
||||
const upPressed = useKeyboardControls((state) => state[Controls.up]);
|
||||
|
@ -91,7 +94,11 @@ export const PlayerController = () => {
|
|||
if (upPressed && currentSpeed < maxSpeed) {
|
||||
// Accelerate the kart within the maximum speed limit
|
||||
setCurrentSpeed(Math.min(currentSpeed + acceleration, maxSpeed));
|
||||
} else if (upPressed && currentSpeed > maxSpeed && boostDuration.current > 0){
|
||||
} else if (
|
||||
upPressed &&
|
||||
currentSpeed > maxSpeed &&
|
||||
boostDuration.current > 0
|
||||
) {
|
||||
setCurrentSpeed(Math.max(currentSpeed - decceleration, maxSpeed));
|
||||
}
|
||||
|
||||
|
@ -116,7 +123,6 @@ export const PlayerController = () => {
|
|||
setCurrentSpeed(Math.max(currentSpeed - decceleration, 0));
|
||||
}
|
||||
|
||||
|
||||
// Update the kart's rotation based on the steering angle
|
||||
kart.current.rotation.y += steeringAngle;
|
||||
|
||||
|
@ -179,7 +185,6 @@ export const PlayerController = () => {
|
|||
);
|
||||
setTurboColor(0xffffff);
|
||||
accumulatedDriftPower.current = 0;
|
||||
|
||||
}
|
||||
|
||||
if (driftLeft.current) {
|
||||
|
@ -187,7 +192,7 @@ export const PlayerController = () => {
|
|||
driftForce.current = 0.4;
|
||||
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||
mario.current.rotation.y,
|
||||
(steeringAngle * 50 + 0.5),
|
||||
steeringAngle * 50 + 0.5,
|
||||
0.1
|
||||
);
|
||||
accumulatedDriftPower.current += 0.1 * (steeringAngle + 1);
|
||||
|
@ -239,7 +244,6 @@ export const PlayerController = () => {
|
|||
targetZPosition = 8;
|
||||
}
|
||||
|
||||
|
||||
// CAMERA WORK
|
||||
|
||||
cam.current.updateMatrixWorld();
|
||||
|
@ -268,7 +272,6 @@ export const PlayerController = () => {
|
|||
|
||||
// Update the kart's rotation based on the steering angle
|
||||
setSteeringAngleWheels(steeringAngle * 25);
|
||||
console.log(scale)
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -276,7 +279,7 @@ export const PlayerController = () => {
|
|||
<RigidBody
|
||||
ref={body}
|
||||
type="dynamic"
|
||||
colliders="ball"
|
||||
colliders={"ball"}
|
||||
position={[0, 20, 0]}
|
||||
centerOfMass={[0, -1, 0]}
|
||||
onCollisionEnter={(event) => {
|
||||
|
@ -291,29 +294,44 @@ export const PlayerController = () => {
|
|||
|
||||
<group ref={kart}>
|
||||
<group ref={mario}>
|
||||
<Mario currentSpeed={currentSpeed} steeringAngleWheels={steeringAngleWheels}/>
|
||||
<Mario
|
||||
currentSpeed={currentSpeed}
|
||||
steeringAngleWheels={steeringAngleWheels}
|
||||
/>
|
||||
<pointLight
|
||||
position={[0.6, 0.05, 0.5]}
|
||||
intensity={scale}
|
||||
color={turboColor}
|
||||
distance={1}
|
||||
|
||||
/>
|
||||
<mesh position={[0.6, 0.05, 0.5]} scale={scale}>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshStandardMaterial emissive={turboColor} toneMapped={false} emissiveIntensity={2} transparent opacity={1}/>
|
||||
<meshStandardMaterial
|
||||
emissive={turboColor}
|
||||
toneMapped={false}
|
||||
emissiveIntensity={50}
|
||||
transparent
|
||||
opacity={1}
|
||||
/>
|
||||
</mesh>
|
||||
<pointLight
|
||||
position={[-0.6, 0.05, 0.5]}
|
||||
intensity={scale}
|
||||
color={turboColor}
|
||||
distance={1}
|
||||
|
||||
/>
|
||||
<mesh position={[-0.6, 0.05, 0.5]} scale={scale}>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshStandardMaterial emissive={turboColor} toneMapped={false} emissiveIntensity={2} transparent opacity={1}/>
|
||||
<meshStandardMaterial
|
||||
emissive={turboColor}
|
||||
toneMapped={false}
|
||||
emissiveIntensity={50}
|
||||
transparent
|
||||
opacity={1}
|
||||
/>
|
||||
</mesh>
|
||||
<DriftParticlesLeft turboColor={turboColor} scale={scale}/>
|
||||
<DriftParticlesRight turboColor={turboColor} scale={scale}/>
|
||||
</group>
|
||||
|
||||
{/* <ContactShadows frames={1} /> */}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import { create } from "zustand";
|
||||
|
||||
export const useStore = create((set, get) => ({
|
||||
return : {
|
||||
particles1: [],
|
||||
particles2: [],
|
||||
actions : {
|
||||
addParticle1: (particle) => {
|
||||
set((state) => ({
|
||||
particles1: [...state.particles1, particle],
|
||||
}));
|
||||
},
|
||||
removeParticle1: (particle) => {
|
||||
set((state) => ({
|
||||
particles1: state.particles1.filter((p) => p.id !== particle.id),
|
||||
}));
|
||||
},
|
||||
addParticle2: (particle) => {
|
||||
set((state) => ({
|
||||
particles2: [...state.particles2, particle],
|
||||
}));
|
||||
},
|
||||
removeParticle2: (particle) => {
|
||||
set((state) => ({
|
||||
particles2: state.particles2.filter((p) => p.id !== particle.id),
|
||||
}));
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
|
||||
}
|
||||
}));
|
Loading…
Reference in New Issue