Merge pull request #1 from Lunakepio/instancedMeshParticles

Instanced mesh particles
gamepadSupport
Lunakepio 2024-01-26 04:40:06 +01:00 committed by GitHub
commit bfccc00f4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 474 additions and 375 deletions

View File

@ -43,7 +43,7 @@ function App() {
<KeyboardControls map={map}> <KeyboardControls map={map}>
<Experience /> <Experience />
</KeyboardControls> </KeyboardControls>
{/* <Stats /> */} <Stats />
</Physics> </Physics>
</Suspense> </Suspense>
</Canvas> </Canvas>

View File

@ -3,23 +3,23 @@ import { Particles3 } from "./Particles3";
export const DriftParticlesLeft = ({turboColor,scale, ...props}) => { export const DriftParticlesLeft = ({turboColor,scale, ...props}) => {
if(scale < 0.8) { // if(scale < 0.8) {
return null; // return null;
} // }
return ( return (
<group {...props}> <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} />
<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} /> */}
</group> </group>
) )

View File

@ -3,24 +3,13 @@ import { Particles4 } from "./Particles4";
export const DriftParticlesRight = ({turboColor,scale, ...props}) => { export const DriftParticlesRight = ({turboColor,scale, ...props}) => {
if(scale < 0.8) { // if(scale < 0.8) {
return null; // return null;
} // }
return ( return (
<group {...props}> <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} />
<Particles2 turboColor={turboColor} scale={scale} />
<Particles4 turboColor={turboColor} scale={scale} />
</group> </group>
) )

View File

@ -6,11 +6,13 @@ import { Track } from './models/Spafrancorchamps-REALISTIC'
import { Paris } from './models/Tour_paris_promenade' import { Paris } from './models/Tour_paris_promenade'
import { EffectComposer, N8AO, Bloom, DepthOfField, TiltShift2, HueSaturation, SMAA, ChromaticAberration, Vignette } from '@react-three/postprocessing' import { EffectComposer, N8AO, Bloom, DepthOfField, TiltShift2, HueSaturation, SMAA, ChromaticAberration, Vignette } from '@react-three/postprocessing'
import { PlayerControllerAgain } from './PlayerControllerAgain' import { PlayerControllerAgain } from './PlayerControllerAgain'
import { Skid } from './Skid'
export const Experience = () => { export const Experience = () => {
return ( return (
<> <>
<PlayerController /> <PlayerController />
<Skid />
{/* <PlayerControllerAgain /> */} {/* <PlayerControllerAgain /> */}
<Ground position={[0, 0, 0]} /> <Ground position={[0, 0, 0]} />
<Environment <Environment

View File

@ -1,22 +1,21 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useRef, useMemo } from "react";
import { useLoader, useFrame } from "@react-three/fiber"; import { useLoader, useFrame } from "@react-three/fiber";
import * as THREE from "three"; import * as THREE from "three";
export const FlameParticle = ({ position, png, turboColor, delay = 0 }) => { export const FlameParticle = ({ position, png, isBoosting, delay = 0 }) => {
const texture = useLoader(THREE.TextureLoader, png); const texture = useLoader(THREE.TextureLoader, png);
const pointsRef = useRef(); const pointsRef = useRef();
const [size, setSize] = useState(1); const initialized = useRef(false);
const [opacity, setOpacity] = useState(1);
const [initialized, setInitialized] = useState(false);
useEffect(() => { // Initialize after delay
useMemo(() => {
const timer = setTimeout(() => { const timer = setTimeout(() => {
setInitialized(true); initialized.current = true;
}, delay); }, delay);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [delay]); }, [delay]);
const points = React.useMemo(() => { const points = useMemo(() => {
const geom = new THREE.BufferGeometry(); const geom = new THREE.BufferGeometry();
geom.setAttribute( geom.setAttribute(
"position", "position",
@ -26,29 +25,41 @@ export const FlameParticle = ({ position, png, turboColor, delay = 0 }) => {
}, [position]); }, [position]);
useFrame(({clock}, delta) => { useFrame(({clock}, delta) => {
if (!initialized) return; if (!initialized.current) return;
pointsRef.current.position.y += 0.03 * delta * 144; const pointsCurrent = pointsRef.current;
pointsRef.current.position.z += 0.06 * delta * 144;
if(pointsRef.current.position.y > 0.4) { if (isBoosting) {
pointsRef.current.position.y = 0; // Update logic when boosting
pointsRef.current.position.z = 0; pointsCurrent.position.y += 0.03 * delta * 144;
setOpacity(1); pointsCurrent.position.z += 0.06 * delta * 144;
}
if(opacity > 0) { if(pointsCurrent.position.y > 0.4) {
setOpacity((opacity) => opacity - 0.05 * delta * 144); pointsCurrent.position.y = 0;
pointsCurrent.position.z = 0;
pointsCurrent.material.opacity = 1;
}
if(pointsCurrent.material.opacity > 0) {
pointsCurrent.material.opacity -= 0.05 * delta * 144;
}
} else {
// Reset position and opacity when not boosting
pointsCurrent.position.y = 0;
pointsCurrent.position.z = 0;
pointsCurrent.material.opacity = 0;
} }
}); });
return ( return (
<points ref={pointsRef} geometry={points}> <points ref={pointsRef} geometry={points}>
<pointsMaterial <pointsMaterial
size={size} size={1}
alphaMap={texture} alphaMap={texture}
transparent={true} transparent={true}
depthWrite={false} depthWrite={false}
color={turboColor} color={0x000000}
opacity={opacity} opacity={1}
toneMapped={false} toneMapped={false}
/> />
</points> </points>

View File

@ -2,9 +2,7 @@ import { FlameParticle } from "./FlameParticle";
export const FlameParticles = ({ isBoosting }) => { export const FlameParticles = ({ isBoosting }) => {
if (!isBoosting) {
return null;
}
return ( return (
<group> <group>
{/* bottom left */} {/* bottom left */}
@ -12,37 +10,37 @@ export const FlameParticles = ({ isBoosting }) => {
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={0} delay={0}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={100} delay={100}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={200} delay={200}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={300} delay={300}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={400} delay={400}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={500} delay={500}
/> />
</group> </group>
@ -52,37 +50,37 @@ export const FlameParticles = ({ isBoosting }) => {
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={0} delay={0}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={100} delay={100}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={200} delay={200}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={300} delay={300}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={400} delay={400}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={500} delay={500}
/> />
</group> </group>
@ -91,37 +89,37 @@ export const FlameParticles = ({ isBoosting }) => {
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={0} delay={0}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={100} delay={100}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={200} delay={200}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={300} delay={300}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={400} delay={400}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={500} delay={500}
/> />
</group> </group>
@ -129,37 +127,37 @@ export const FlameParticles = ({ isBoosting }) => {
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={0} delay={0}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={100} delay={100}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={200} delay={200}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={300} delay={300}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_02.png" png="./fire_02.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={400} delay={400}
/> />
<FlameParticle <FlameParticle
position={[0, 0, 0]} position={[0, 0, 0]}
png="./fire_01.png" png="./fire_01.png"
turboColor={0xfea347} isBoosting={isBoosting}
delay={500} delay={500}
/> />
</group> </group>

View File

@ -1,55 +1,57 @@
import { useRef } from "react"; import { useRef, useMemo } from "react";
import { useFrame } from "@react-three/fiber"; import { useFrame } from "@react-three/fiber";
import * as THREE from 'three'; import * as THREE from 'three';
export const Particles1 = ({ turboColor, scale, ...props }) => { export const Particles1 = ({ turboColor, scale, numParticles = 50, ...props }) => {
const ref = useRef(); const instancedMeshRef = useRef();
const velocity = useRef({ const particlesData = useMemo(() => {
x: -Math.random() * 0.05, return new Array(numParticles).fill().map(() => ({
y: Math.random() * 0.05, position: new THREE.Vector3(-0.6, 0.05, 0.5),
z: Math.random() * 0.02, velocity: new THREE.Vector3(-Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.02),
}); gravity: -0.003
const gravity = -0.003; }));
}, [numParticles]);
useFrame((state, delta) => { useFrame((state, delta) => {
let position = ref.current.position; if (!instancedMeshRef.current) {
let velocityVector = new THREE.Vector3(velocity.current.x, velocity.current.y, velocity.current.z); return;
// Adjust gravity and velocity based on delta
velocity.current.y += gravity * delta * 144; // Multiply by 144 to scale for 144 FPS
// Scale velocity changes by delta
position.x += velocity.current.x * delta * 144;
position.y += velocity.current.y * delta * 144;
position.z += velocity.current.z * delta * 144;
if (!velocityVector.equals(new THREE.Vector3(0, 0, 0))) {
const alignmentQuaternion = new THREE.Quaternion();
alignmentQuaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), velocityVector.normalize());
ref.current.quaternion.slerp(alignmentQuaternion, 0.1);
} }
if (position.y < 0.05) { // Manage visibility directly in the animation loop
position.set(-0.6, 0.05, 0.5); instancedMeshRef.current.visible = scale >= 0.8;
velocity.current = {
x: -Math.random() * 0.05, if (scale < 0.8) {
y: Math.random() * 0.05, return;
z: Math.random() * 0.02,
};
} }
ref.current.position.set(position.x, position.y, position.z); const deltaScaled = delta * 144; // Scale for 144 FPS
particlesData.forEach((particle, i) => {
particle.velocity.y += particle.gravity * deltaScaled;
particle.position.x += particle.velocity.x * deltaScaled;
particle.position.y += particle.velocity.y * deltaScaled;
particle.position.z += particle.velocity.z * deltaScaled;
if (particle.position.y < 0.05) {
particle.position.set(-0.6, 0.05, 0.5);
particle.velocity.set(-Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.02);
}
const matrix = new THREE.Matrix4();
matrix.setPosition(particle.position);
instancedMeshRef.current.setMatrixAt(i, matrix);
});
instancedMeshRef.current.instanceMatrix.needsUpdate = true;
}); });
return ( return (
<mesh ref={ref} position={[-0.6, 0.05, 0.5]} scale={[scale, scale * 5, scale]}> <instancedMesh ref={instancedMeshRef} args={[null, null, numParticles]} visible={scale >= 0.8}>
<sphereGeometry args={[0.01, 16, 16]} /> <sphereGeometry args={[0.01, 16, 16]} />
<meshStandardMaterial <meshStandardMaterial
emissive={turboColor} emissive={turboColor}
toneMapped={false} toneMapped={false}
emissiveIntensity={5} emissiveIntensity={5}
/> />
</mesh> </instancedMesh>
); );
}; };

View File

@ -1,55 +1,57 @@
import { useRef } from "react"; import { useRef, useMemo } from "react";
import { useFrame } from "@react-three/fiber"; import { useFrame } from "@react-three/fiber";
import * as THREE from 'three'; import * as THREE from 'three';
export const Particles2 = ({ turboColor, scale, ...props }) => { export const Particles2 = ({ turboColor, scale, numParticles = 50, ...props }) => {
const ref = useRef(); const instancedMeshRef = useRef();
const velocity = useRef({ const particlesData = useMemo(() => {
x: Math.random() * 0.05, return new Array(numParticles).fill().map(() => ({
y: Math.random() * 0.05, position: new THREE.Vector3(0.6, 0.05, 0.5),
z: Math.random() * 0.02, velocity: new THREE.Vector3(Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.02),
}); gravity: -0.003
const gravity = -0.003; }));
}, [numParticles]);
useFrame((state, delta) => { useFrame((state, delta) => {
let position = ref.current.position; if (!instancedMeshRef.current) {
let velocityVector = new THREE.Vector3(velocity.current.x, velocity.current.y, velocity.current.z); return;
// Adjust gravity and velocity based on delta
velocity.current.y += gravity * delta * 144; // Multiply by 144 to scale for 144 FPS
// Scale velocity changes by delta
position.x += velocity.current.x * delta * 144;
position.y += velocity.current.y * delta * 144;
position.z += velocity.current.z * delta * 144;
if (!velocityVector.equals(new THREE.Vector3(0, 0, 0))) {
const alignmentQuaternion = new THREE.Quaternion();
alignmentQuaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), velocityVector.normalize());
ref.current.quaternion.slerp(alignmentQuaternion, 0.1);
} }
if (position.y < 0.05) { // Manage visibility directly in the animation loop
position.set(0.6, 0.05, 0.5); instancedMeshRef.current.visible = scale >= 0.8;
velocity.current = {
x: Math.random() * 0.05, if (scale < 0.8) {
y: Math.random() * 0.05, return;
z: Math.random() * 0.02,
};
} }
ref.current.position.set(position.x, position.y, position.z); const deltaScaled = delta * 144; // Scale for 144 FPS
particlesData.forEach((particle, i) => {
particle.velocity.y += particle.gravity * deltaScaled;
particle.position.x += particle.velocity.x * deltaScaled;
particle.position.y += particle.velocity.y * deltaScaled;
particle.position.z += particle.velocity.z * deltaScaled;
if (particle.position.y < 0.05) {
particle.position.set(0.6, 0.05, 0.5);
particle.velocity.set(Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.02);
}
const matrix = new THREE.Matrix4();
matrix.setPosition(particle.position);
instancedMeshRef.current.setMatrixAt(i, matrix);
});
instancedMeshRef.current.instanceMatrix.needsUpdate = true;
}); });
return ( return (
<mesh ref={ref} position={[0.6, 0.05, 0.5]} scale={[scale, scale * 5, scale]}> <instancedMesh ref={instancedMeshRef} args={[null, null, numParticles]} visible={scale >= 0.8}>
<sphereGeometry args={[0.01, 16, 16]} /> <sphereGeometry args={[0.01, 16, 16]} />
<meshStandardMaterial <meshStandardMaterial
emissive={turboColor} emissive={turboColor}
toneMapped={false} toneMapped={false}
emissiveIntensity={5} emissiveIntensity={5}
/> />
</mesh> </instancedMesh>
); );
}; };

View File

@ -1,254 +1,328 @@
import { Controls } from '../App' import { Controls } from "../App";
import { BallCollider, RigidBody, useRapier } from '@react-three/rapier' import { BallCollider, RigidBody, useRapier, vec3 } from "@react-three/rapier";
import { useKeyboardControls, PerspectiveCamera, ContactShadows, Sphere, OrbitControls, Trail, PositionalAudio } from '@react-three/drei' import {
import { useFrame, useThree } from '@react-three/fiber' useKeyboardControls,
import { useRef, useState, useEffect, useCallback } from 'react' PerspectiveCamera,
import * as THREE from 'three' ContactShadows,
import { Model } from './models/Racing_kart' Sphere,
import { FrontRightWheel } from './models/Front_Right_Wheel' OrbitControls,
import { FrontLeftWheel } from './models/Front_Left_Wheel' Trail,
import { RearWheels } from './models/Rear_wheels' PositionalAudio,
import gsap from 'gsap' } from "@react-three/drei";
import { Mario } from './models/Mario_kart' import { useFrame, useThree } from "@react-three/fiber";
import { Particles1 } from './Particles1' import { useRef, useState, useEffect, useCallback } from "react";
import { DriftParticlesLeft } from './DriftParticlesLeft' import * as THREE from "three";
import { DriftParticlesRight } from './DriftParticlesRight' import { Model } from "./models/Racing_kart";
import FakeGlowMaterial from './FakeGlow/FakeGlowMaterial' import { FrontRightWheel } from "./models/Front_Right_Wheel";
import { PointParticle } from './PointParticle' import { FrontLeftWheel } from "./models/Front_Left_Wheel";
import { FlameParticle } from './FlameParticle' import { RearWheels } from "./models/Rear_wheels";
import { FlameParticles } from './FlameParticles' import gsap from "gsap";
import { Mario } from "./models/Mario_kart";
import { Particles1 } from "./Particles1";
import { DriftParticlesLeft } from "./DriftParticlesLeft";
import { DriftParticlesRight } from "./DriftParticlesRight";
import FakeGlowMaterial from "./FakeGlow/FakeGlowMaterial";
import { PointParticle } from "./PointParticle";
import { FlameParticle } from "./FlameParticle";
import { FlameParticles } from "./FlameParticles";
import { useStore } from "./store";
import { Cylinder } from "@react-three/drei";
export const PlayerController = () => { export const PlayerController = () => {
const upPressed = useKeyboardControls((state) => state[Controls.up]) const upPressed = useKeyboardControls((state) => state[Controls.up]);
const downPressed = useKeyboardControls((state) => state[Controls.down]) const downPressed = useKeyboardControls((state) => state[Controls.down]);
const leftPressed = useKeyboardControls((state) => state[Controls.left]) const leftPressed = useKeyboardControls((state) => state[Controls.left]);
const rightPressed = useKeyboardControls((state) => state[Controls.right]) const rightPressed = useKeyboardControls((state) => state[Controls.right]);
const jumpPressed = useKeyboardControls((state) => state[Controls.jump]) const jumpPressed = useKeyboardControls((state) => state[Controls.jump]);
const [isOnGround, setIsOnGround] = useState(false) const [isOnGround, setIsOnGround] = useState(false);
const body = useRef() const body = useRef();
const kart = useRef() const kart = useRef();
const cam = useRef() const cam = useRef();
const initialSpeed = 0 const initialSpeed = 0;
const maxSpeed = 30 const maxSpeed = 30;
const boostSpeed = 50 const boostSpeed = 50;
const acceleration = 0.1 const acceleration = 0.1;
const decceleration = 0.2 const decceleration = 0.2;
const damping = -0.1 const damping = -0.1;
const MaxSteeringSpeed = 0.01 const MaxSteeringSpeed = 0.01;
const [currentSteeringSpeed, setCurrentSteeringSpeed] = useState(0) const [currentSteeringSpeed, setCurrentSteeringSpeed] = useState(0);
const [currentSpeed, setCurrentSpeed] = useState(initialSpeed) const [currentSpeed, setCurrentSpeed] = useState(initialSpeed);
const camMaxOffset = 1 const camMaxOffset = 1;
let steeringAngle = 0 let steeringAngle = 0;
const isOnFloor = useRef(false) const isOnFloor = useRef(false);
const jumpForce = useRef(0) const jumpForce = useRef(0);
const jumpIsHeld = useRef(false) const jumpIsHeld = useRef(false);
const driftDirection = useRef(0) const driftDirection = useRef(0);
const driftLeft = useRef(false) const driftLeft = useRef(false);
const driftRight = useRef(false) const driftRight = useRef(false);
const driftForce = useRef(0) const driftForce = useRef(0);
const mario = useRef() const mario = useRef();
const accumulatedDriftPower = useRef(0) const accumulatedDriftPower = useRef(0);
const blueTurboThreshold = 10 const blueTurboThreshold = 10;
const orangeTurboThreshold = 30 const orangeTurboThreshold = 30;
const purpleTurboThreshold = 60 const purpleTurboThreshold = 60;
const [turboColor, setTurboColor] = useState(0xffffff) const [turboColor, setTurboColor] = useState(0xffffff);
const boostDuration = useRef(0) const boostDuration = useRef(0);
const [isBoosting, setIsBoosting] = useState(false) const [isBoosting, setIsBoosting] = useState(false);
let targetXPosition = 0 let targetXPosition = 0;
let targetZPosition = 8 let targetZPosition = 8;
const [steeringAngleWheels, setSteeringAngleWheels] = useState(0) const [steeringAngleWheels, setSteeringAngleWheels] = useState(0);
const engineSound = useRef() const engineSound = useRef();
const [scale, setScale] = useState(0) const [scale, setScale] = useState(0);
const { actions, addPastPosition } = useStore();
useFrame(({ pointer, clock }, delta) => { useFrame(({ pointer, clock }, delta) => {
const time = clock.getElapsedTime() const time = clock.getElapsedTime();
if (!body.current && !mario.current) return if (!body.current && !mario.current) return;
// HANDLING AND STEERING // HANDLING AND STEERING
const kartRotation = kart.current.rotation.y - driftDirection.current * driftForce.current const kartRotation =
const forwardDirection = new THREE.Vector3(-Math.sin(kartRotation), 0, -Math.cos(kartRotation)) kart.current.rotation.y - driftDirection.current * driftForce.current;
const forwardDirection = new THREE.Vector3(
-Math.sin(kartRotation),
0,
-Math.cos(kartRotation)
);
actions.setBodyPosition(body.current.translation());
actions.setBodyRotation(kart.current.rotation);
if (leftPressed && currentSpeed > 0) { if (leftPressed && currentSpeed > 0) {
steeringAngle = currentSteeringSpeed steeringAngle = currentSteeringSpeed;
targetXPosition = -camMaxOffset targetXPosition = -camMaxOffset;
} else if (rightPressed && currentSpeed > 0) { } else if (rightPressed && currentSpeed > 0) {
steeringAngle = -currentSteeringSpeed steeringAngle = -currentSteeringSpeed;
targetXPosition = camMaxOffset targetXPosition = camMaxOffset;
} else { } else {
steeringAngle = 0 steeringAngle = 0;
targetXPosition = 0 targetXPosition = 0;
1 1;
} }
// mouse steering // mouse steering
if (!driftLeft.current && !driftRight.current) { if (!driftLeft.current && !driftRight.current) {
steeringAngle = currentSteeringSpeed * -pointer.x steeringAngle = currentSteeringSpeed * -pointer.x;
targetXPosition = -camMaxOffset * -pointer.x targetXPosition = -camMaxOffset * -pointer.x;
} else if (driftLeft.current && !driftRight.current) { } else if (driftLeft.current && !driftRight.current) {
steeringAngle = currentSteeringSpeed * -(pointer.x - 0.5) steeringAngle = currentSteeringSpeed * -(pointer.x - 0.5);
targetXPosition = -camMaxOffset * -pointer.x targetXPosition = -camMaxOffset * -pointer.x;
} else if (driftRight.current && !driftLeft.current) { } else if (driftRight.current && !driftLeft.current) {
steeringAngle = currentSteeringSpeed * -(pointer.x + 0.5) steeringAngle = currentSteeringSpeed * -(pointer.x + 0.5);
targetXPosition = -camMaxOffset * -pointer.x targetXPosition = -camMaxOffset * -pointer.x;
} }
// ACCELERATING // ACCELERATING
if (upPressed && currentSpeed < maxSpeed) { if (upPressed && currentSpeed < maxSpeed) {
// Accelerate the kart within the maximum speed limit // Accelerate the kart within the maximum speed limit
setCurrentSpeed(Math.min(currentSpeed + acceleration * delta * 144, maxSpeed)) setCurrentSpeed(
} else if (upPressed && currentSpeed > maxSpeed && boostDuration.current > 0) { Math.min(currentSpeed + acceleration * delta * 144, maxSpeed)
setCurrentSpeed(Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)) );
} else if (
upPressed &&
currentSpeed > maxSpeed &&
boostDuration.current > 0
) {
setCurrentSpeed(
Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)
);
} }
if (upPressed) { if (upPressed) {
if (currentSteeringSpeed < MaxSteeringSpeed) { if (currentSteeringSpeed < MaxSteeringSpeed) {
setCurrentSteeringSpeed(Math.min(currentSteeringSpeed + 0.0001 * delta * 144, MaxSteeringSpeed)) setCurrentSteeringSpeed(
Math.min(
currentSteeringSpeed + 0.0001 * delta * 144,
MaxSteeringSpeed
)
);
} }
} }
// REVERSING // REVERSING
if (downPressed && currentSpeed < -maxSpeed) { if (downPressed && currentSpeed < -maxSpeed) {
setCurrentSpeed(Math.max(currentSpeed - acceleration * delta * 144, -maxSpeed)) setCurrentSpeed(
Math.max(currentSpeed - acceleration * delta * 144, -maxSpeed)
);
} }
// DECELERATING // DECELERATING
else if (!upPressed && !downPressed) { else if (!upPressed && !downPressed) {
if (currentSteeringSpeed > 0) { if (currentSteeringSpeed > 0) {
setCurrentSteeringSpeed(Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)) setCurrentSteeringSpeed(
Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)
);
} else if (currentSteeringSpeed < 0) { } else if (currentSteeringSpeed < 0) {
setCurrentSteeringSpeed(Math.min(currentSteeringSpeed + 0.00005 * delta * 144, 0)) setCurrentSteeringSpeed(
Math.min(currentSteeringSpeed + 0.00005 * delta * 144, 0)
);
} }
setCurrentSpeed(Math.max(currentSpeed - decceleration * delta * 144, 0)) setCurrentSpeed(Math.max(currentSpeed - decceleration * delta * 144, 0));
} }
// Update the kart's rotation based on the steering angle // Update the kart's rotation based on the steering angle
kart.current.rotation.y += steeringAngle * delta * 144 kart.current.rotation.y += steeringAngle * delta * 144;
// Apply damping to simulate slowdown when no keys are pressed // Apply damping to simulate slowdown when no keys are pressed
body.current.applyImpulse( body.current.applyImpulse(
{ {
x: -body.current.linvel().x * (1 - damping) * delta * 144, x: -body.current.linvel().x * (1 - damping) * delta * 144,
y: 0, y: 0,
z: -body.current.linvel().z * (1 - damping) * delta * 144 z: -body.current.linvel().z * (1 - damping) * delta * 144,
}, },
true true
) );
const bodyPosition = body.current.translation() const bodyPosition = body.current.translation();
kart.current.position.set(bodyPosition.x, bodyPosition.y - 0.5, bodyPosition.z) kart.current.position.set(
bodyPosition.x,
bodyPosition.y - 0.5,
bodyPosition.z
);
// JUMPING // JUMPING
if (jumpPressed && isOnFloor.current && !jumpIsHeld.current) { if (jumpPressed && isOnFloor.current && !jumpIsHeld.current) {
jumpForce.current += 10 jumpForce.current += 10;
isOnFloor.current = false isOnFloor.current = false;
jumpIsHeld.current = true jumpIsHeld.current = true;
} }
if (!isOnFloor.current && jumpForce.current > 0) { if (!isOnFloor.current && jumpForce.current > 0) {
jumpForce.current -= 1 * delta * 144 jumpForce.current -= 1 * delta * 144;
} }
if (!jumpPressed) { if (!jumpPressed) {
jumpIsHeld.current = false jumpIsHeld.current = false;
driftDirection.current = 0 driftDirection.current = 0;
driftForce.current = 0 driftForce.current = 0;
driftLeft.current = false driftLeft.current = false;
driftRight.current = false driftRight.current = false;
} }
// DRIFTING // DRIFTING
if (jumpIsHeld.current && currentSteeringSpeed > 0 && pointer.x < -0.1 && !driftRight.current) { if (
driftLeft.current = true jumpIsHeld.current &&
currentSteeringSpeed > 0 &&
pointer.x < -0.1 &&
!driftRight.current
) {
driftLeft.current = true;
} }
if (jumpIsHeld.current && currentSteeringSpeed > 0 && pointer.x > 0.1 && !driftLeft.current) { if (
driftRight.current = true jumpIsHeld.current &&
currentSteeringSpeed > 0 &&
pointer.x > 0.1 &&
!driftLeft.current
) {
driftRight.current = true;
} }
if (!jumpIsHeld.current && !driftLeft.current && !driftRight.current) { if (!jumpIsHeld.current && !driftLeft.current && !driftRight.current) {
mario.current.rotation.y = THREE.MathUtils.lerp(mario.current.rotation.y, 0, 0.0001 * delta * 144) mario.current.rotation.y = THREE.MathUtils.lerp(
setTurboColor(0xffffff) mario.current.rotation.y,
accumulatedDriftPower.current = 0 0,
0.0001 * delta * 144
);
setTurboColor(0xffffff);
accumulatedDriftPower.current = 0;
} }
if (driftLeft.current) { if (driftLeft.current) {
driftDirection.current = 1 driftDirection.current = 1;
driftForce.current = 0.4 driftForce.current = 0.4;
mario.current.rotation.y = THREE.MathUtils.lerp(mario.current.rotation.y, steeringAngle * 50 + 0.5, 0.05 * delta * 144) mario.current.rotation.y = THREE.MathUtils.lerp(
accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144 mario.current.rotation.y,
steeringAngle * 50 + 0.5,
0.05 * delta * 144
);
accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144;
} }
if (driftRight.current) { if (driftRight.current) {
driftDirection.current = -1 driftDirection.current = -1;
driftForce.current = 0.4 driftForce.current = 0.4;
mario.current.rotation.y = THREE.MathUtils.lerp(mario.current.rotation.y, -(-steeringAngle * 50 + 0.5), 0.05 * delta * 144) mario.current.rotation.y = THREE.MathUtils.lerp(
accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144 mario.current.rotation.y,
-(-steeringAngle * 50 + 0.5),
0.05 * delta * 144
);
accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144;
} }
if (!driftLeft.current && !driftRight.current) { if (!driftLeft.current && !driftRight.current) {
mario.current.rotation.y = THREE.MathUtils.lerp(mario.current.rotation.y, steeringAngle * 30, 0.05 * delta * 144) mario.current.rotation.y = THREE.MathUtils.lerp(
setScale(0) mario.current.rotation.y,
steeringAngle * 30,
0.05 * delta * 144
);
setScale(0);
} }
if (accumulatedDriftPower.current > blueTurboThreshold) { if (accumulatedDriftPower.current > blueTurboThreshold) {
setTurboColor(0x00ffff) setTurboColor(0x00ffff);
boostDuration.current = 50 boostDuration.current = 50;
} }
if (accumulatedDriftPower.current > orangeTurboThreshold) { if (accumulatedDriftPower.current > orangeTurboThreshold) {
setTurboColor(0xffcf00) setTurboColor(0xffcf00);
boostDuration.current = 100 boostDuration.current = 100;
} }
if (accumulatedDriftPower.current > purpleTurboThreshold) { if (accumulatedDriftPower.current > purpleTurboThreshold) {
setTurboColor(0xff00ff) setTurboColor(0xff00ff);
boostDuration.current = 250 boostDuration.current = 250;
} }
if (driftLeft.current || driftRight.current) { if (driftLeft.current || driftRight.current) {
const oscillation = Math.sin(time * 1000) * 0.1 const oscillation = Math.sin(time * 1000) * 0.1;
const vibration = oscillation+ 0.9 const vibration = oscillation + 0.9;
if (turboColor === 0xffffff) { if (turboColor === 0xffffff) {
setScale(vibration * 0.8) setScale(vibration * 0.8);
} else { } else {
setScale(vibration) setScale(vibration);
} }
} }
// RELEASING DRIFT // RELEASING DRIFT
if (boostDuration.current > 1 && !jumpIsHeld.current) { if (boostDuration.current > 1 && !jumpIsHeld.current) {
setIsBoosting(true) setIsBoosting(true);
} else if (boostDuration.current <= 1) { } else if (boostDuration.current <= 1) {
targetZPosition = 8 targetZPosition = 8;
setIsBoosting(false) setIsBoosting(false);
} }
if(isBoosting && boostDuration.current > 1){ if (isBoosting && boostDuration.current > 1) {
setCurrentSpeed(boostSpeed) setCurrentSpeed(boostSpeed);
boostDuration.current -= 1 * delta * 144 boostDuration.current -= 1 * delta * 144;
targetZPosition = 10 targetZPosition = 10;
} else if (boostDuration.current <= 1) { } else if (boostDuration.current <= 1) {
setIsBoosting(false) setIsBoosting(false);
targetZPosition = 8 targetZPosition = 8;
} }
// CAMERA WORK // CAMERA WORK
cam.current.updateMatrixWorld() cam.current.updateMatrixWorld();
cam.current.position.x = THREE.MathUtils.lerp(cam.current.position.x, targetXPosition, 0.01 * delta * 144) cam.current.position.x = THREE.MathUtils.lerp(
cam.current.position.x,
targetXPosition,
0.01 * delta * 144
);
cam.current.position.z = THREE.MathUtils.lerp(cam.current.position.z, targetZPosition, 0.01 * delta * 144) cam.current.position.z = THREE.MathUtils.lerp(
cam.current.position.z,
targetZPosition,
0.01 * delta * 144
);
body.current.applyImpulse( body.current.applyImpulse(
{ {
x: forwardDirection.x * currentSpeed * delta * 144, x: forwardDirection.x * currentSpeed * delta * 144,
y: 0 + jumpForce.current * delta * 144, y: 0 + jumpForce.current * delta * 144,
z: forwardDirection.z * currentSpeed * delta * 144 z: forwardDirection.z * currentSpeed * delta * 144,
}, },
true true
) );
// Update the kart's rotation based on the steering angle // Update the kart's rotation based on the steering angle
setSteeringAngleWheels(steeringAngle * 25) setSteeringAngleWheels(steeringAngle * 25);
// SOUND WORK // SOUND WORK
// console.lowg(body.current.translation()) // console.lowg(body.current.translation())
}) });
return ( return (
<group> <group>
@ -264,38 +338,30 @@ export const PlayerController = () => {
args={[0.5]} args={[0.5]}
mass={3} mass={3}
onCollisionEnter={(event) => { onCollisionEnter={(event) => {
isOnFloor.current = true isOnFloor.current = true;
}} }}
// onCollisionExit={(event) => { // onCollisionExit={(event) => {
// isOnFloor.current = false // isOnFloor.current = false
// }} // }}
/> />
</RigidBody> </RigidBody>
<group <group ref={kart} rotation={[0, Math.PI / 2, 0]}>
ref={kart}
rotation={[0, Math.PI / 2, 0]}
>
<group ref={mario}> <group ref={mario}>
<Mario <Mario
currentSpeed={currentSpeed} currentSpeed={currentSpeed}
steeringAngleWheels={steeringAngleWheels} steeringAngleWheels={steeringAngleWheels}
isBoosting={isBoosting} isBoosting={isBoosting}
/> />
<pointLight {/* <pointLight
position={[0.6, 0.05, 0.5]} position={[0.6, 0.05, 0.5]}
intensity={scale} intensity={scale}
color={turboColor} color={turboColor}
distance={1} distance={1}
/> /> */}
<mesh <mesh position={[0.6, 0.05, 0.5]} scale={scale}>
position={[0.6, 0.05, 0.5]} <sphereGeometry args={[0.05, 16, 16]} />
scale={scale}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial <meshStandardMaterial
emissive={turboColor} emissive={turboColor}
toneMapped={false} toneMapped={false}
@ -304,17 +370,14 @@ export const PlayerController = () => {
opacity={0.4} opacity={0.4}
/> />
</mesh> </mesh>
<pointLight {/* <pointLight
position={[-0.6, 0.05, 0.5]} position={[-0.6, 0.05, 0.5]}
intensity={scale} intensity={scale}
color={turboColor} color={turboColor}
distance={1} distance={1}
/> /> */}
<mesh <mesh position={[-0.6, 0.05, 0.5]} scale={scale}>
position={[-0.6, 0.05, 0.5]} <sphereGeometry args={[0.05, 16, 16]} />
scale={scale}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial <meshStandardMaterial
emissive={turboColor} emissive={turboColor}
toneMapped={false} toneMapped={false}
@ -323,34 +386,36 @@ export const PlayerController = () => {
opacity={0.4} opacity={0.4}
/> />
</mesh> </mesh>
{/* <Cylinder
args={[0.1, 0, 1, 128, 64, true]}
position={[-0.6, 0.05, 0.5]}
rotation={[Math.PI / 3, 0 , 0]}
>
<meshStandardMaterial side={THREE.DoubleSide} />
</Cylinder> */}
{/* <Flame/> */} {/* <Flame/> */}
<FlameParticles isBoosting={isBoosting} /> <FlameParticles isBoosting={isBoosting} />
<DriftParticlesLeft <DriftParticlesLeft turboColor={turboColor} scale={scale} />
turboColor={turboColor} <DriftParticlesRight turboColor={turboColor} scale={scale} />
scale={scale}
/>
<DriftParticlesRight
turboColor={turboColor}
scale={scale}
/>
<PointParticle <PointParticle
position={[-0.6, 0.05, 0.5]} position={[-0.6, 0.05, 0.5]}
png='./circle.png' png="./circle.png"
turboColor={turboColor} turboColor={turboColor}
/> />
<PointParticle <PointParticle
position={[0.6, 0.05, 0.5]} position={[0.6, 0.05, 0.5]}
png='./circle.png' png="./circle.png"
turboColor={turboColor} turboColor={turboColor}
/> />
<PointParticle <PointParticle
position={[-0.6, 0.05, 0.5]} position={[-0.6, 0.05, 0.5]}
png='./star.png' png="./star.png"
turboColor={turboColor} turboColor={turboColor}
/> />
<PointParticle <PointParticle
position={[0.6, 0.05, 0.5]} position={[0.6, 0.05, 0.5]}
png='./star.png' png="./star.png"
turboColor={turboColor} turboColor={turboColor}
/> />
</group> </group>
@ -365,5 +430,5 @@ export const PlayerController = () => {
{/* <PositionalAudio ref={engineSound} url="./sounds/engine.wav" autoplay loop distance={10}/> */} {/* <PositionalAudio ref={engineSound} url="./sounds/engine.wav" autoplay loop distance={10}/> */}
</group> </group>
</group> </group>
) );
} };

View File

@ -1,25 +1,22 @@
import { Euler, Object3D, Vector3, Matrix4 } from 'three'; import { Euler, Object3D, BackSide, Vector3 } from "three";
import { useRef, useLayoutEffect } from 'react'; import { useRef, useLayoutEffect } from "react";
import { useFrame } from '@react-three/fiber'; import { useFrame } from "@react-three/fiber";
import { useStore } from "./store";
import { getState, mutation, useStore } from '../store';
const e = new Euler();
const m = new Matrix4();
const o = new Object3D(); const o = new Object3D();
const v = new Vector3();
export function Skid({ count = 500, opacity = 0.5, size = 0.4 }) { export function Skid({ count = 500, opacity = 1, size = 0.4 }) {
const ref = useRef(null); const ref = useRef(null);
const [chassisBody, wheels] = useStore((state) => [state.chassisBody, state.wheels]); const [bodyPosition, bodyRotation] = useStore((state) => [
state.bodyPosition,
state.bodyRotation,
]);
let brake;
let index = 0; let index = 0;
useFrame(() => { useFrame(() => {
brake = getState().controls.brake; // console.log(bodyPosition, bodyRotation);
if (chassisBody.current && wheels[2].current && wheels[3].current && brake && mutation.speed > 10) { if (ref.current && bodyPosition && bodyRotation !== undefined) {
e.setFromRotationMatrix(m.extractRotation(chassisBody.current.matrix)); setItemAt(ref.current, bodyPosition, bodyRotation, index++);
setItemAt(ref.current, e, wheels[2].current, index++);
if (index === count) index = 0; if (index === count) index = 0;
} }
}); });
@ -36,14 +33,34 @@ export function Skid({ count = 500, opacity = 0.5, size = 0.4 }) {
return ( return (
<instancedMesh ref={ref} args={[undefined, undefined, count]}> <instancedMesh ref={ref} args={[undefined, undefined, count]}>
<planeGeometry args={[size, size * 2]} /> <planeGeometry args={[size, size * 2]} />
<meshBasicMaterial color={0x4d4d4d} transparent opacity={opacity} depthWrite={false} /> <meshBasicMaterial
color={0x000000}
transparent
opacity={opacity}
depthWrite={false}
side={BackSide}
/>
</instancedMesh> </instancedMesh>
); );
} }
function setItemAt(instances, rotation, wheel, index) { function setItemAt(instances, bodyPosition, bodyRotation, index) {
o.position.copy(wheel.getWorldPosition(v)); // Calculate the backward offset
o.rotation.copy(rotation); const backwardOffset = 0.5; // Adjust this value as needed
const forwardDirection = new Vector3(
-Math.sin(bodyRotation),
0,
-Math.cos(bodyRotation)
);
const backwardPosition = forwardDirection
.multiplyScalar(-backwardOffset)
.add(bodyPosition);
// Apply the offset to position the skid marks behind the body
console.log(bodyPosition);
o.position.copy(bodyPosition.x, bodyPosition.y + 2, bodyPosition.z);
o.rotation.set(0, bodyRotation, 0);
o.scale.setScalar(1); o.scale.setScalar(1);
o.updateMatrix(); o.updateMatrix();
instances.setMatrixAt(index, o.matrix); instances.setMatrixAt(index, o.matrix);

View File

@ -26,9 +26,9 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
rearWheels.current.rotation.x += rotation rearWheels.current.rotation.x += rotation
frontWheels.current.rotation.y = steeringAngleWheels frontWheels.current.rotation.y = steeringAngleWheels
if (isBoosting){ if (isBoosting){
setScale(Math.min(scale + 0.1 * 144 * delta, 1)) setScale(Math.min(scale + 0.05 * 144 * delta, 1))
} else { } else {
setScale(Math.max(scale - 0.1 * 144 * delta, 0)) setScale(Math.max(scale - 0.03 * 144 * delta, 0))
} }
}) })
return ( return (

View File

@ -1,35 +1,48 @@
import { create } from "zustand"; import { create } from "zustand";
export const useStore = create((set, get) => ({ export const useStore = create((set, get) => ({
return : { particles1: [],
particles1: [], particles2: [],
particles2: [], bodyPosition: [0, 0, 0],
bodyPosition: [0, 0, 0], bodyRotation: [0, 0, 0],
bodyRotation: [0, 0, 0], pastPositions: [],
actions : { addPastPosition: (position) => {
addParticle1: (particle) => { set((state) => ({
set((state) => ({ pastPositions: [position, ...state.pastPositions.slice(0, 499)],
particles1: [...state.particles1, particle], }));
})); },
}, actions: {
removeParticle1: (particle) => { addParticle1: (particle) => {
set((state) => ({ set((state) => ({
particles1: state.particles1.filter((p) => p.id !== particle.id), particles1: [...state.particles1, particle],
})); }));
},
addParticle2: (particle) => {
set((state) => ({
particles2: [...state.particles2, particle],
}));
},
removeParticle2: (particle) => {
set((state) => ({
particles2: state.particles2.filter((p) => p.id !== particle.id),
}));
},
}, },
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),
}));
},
setBodyPosition: (position) => {
set({ bodyPosition: position });
},
setBodyRotation: (rotation) => {
set({ bodyRotation: rotation });
},
getBodyPosition: () => {
return get().bodyPosition;
},
getBodyRotation: () => {
return get().bodyRotation;
},
},
})); }));