feat(game) : added particle system basics

anderson-mancini
Alex 2024-01-22 14:44:13 +01:00
parent f4f6028a88
commit 59bc38d06e
12 changed files with 394 additions and 27 deletions

76
package-lock.json generated
View File

@ -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
}

View File

@ -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",

View File

@ -44,8 +44,10 @@ function App() {
<KeyboardControls map={map}>
<Experience />
</KeyboardControls>
{/* <Stats /> */}
</Physics>
</Suspense>
</Canvas>
);
}

View File

@ -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>
)
}

View File

@ -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>
)
}

View File

@ -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} />

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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,9 +94,13 @@ 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));
}
}
if (upPressed) {
if (currentSteeringSpeed < MaxSteeringSpeed) {
@ -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);
@ -223,22 +228,21 @@ export const PlayerController = () => {
boostDuration.current = 250;
}
if(driftLeft.current || driftRight.current){
if (driftLeft.current || driftRight.current) {
const oscillation = Math.sin(time * 1000) * 0.1;
const vibration = oscillation + 0.9;
setScale(vibration);
}
// RELEASING DRIFT
// RELEASING DRIFT
if (boostDuration.current > 1 && !jumpIsHeld.current) {
setCurrentSpeed(boostSpeed);
boostDuration.current -= 1;
targetZPosition = 10;
} else if (boostDuration.current <= 1) {
} else if (boostDuration.current <= 1) {
targetZPosition = 8;
}
// CAMERA WORK
@ -249,7 +253,7 @@ export const PlayerController = () => {
targetXPosition,
0.01
);
cam.current.position.z = THREE.MathUtils.lerp(
cam.current.position.z,
targetZPosition,
@ -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}>
<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} /> */}

33
src/components/store.jsx Normal file
View File

@ -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),
}));
},
},
}
}));