diff --git a/public/particles/smokes/smoke_01.png b/public/particles/smokes/smoke_01.png new file mode 100644 index 0000000..1cd418e Binary files /dev/null and b/public/particles/smokes/smoke_01.png differ diff --git a/public/particles/smokes/smoke_02.png b/public/particles/smokes/smoke_02.png new file mode 100644 index 0000000..d866368 Binary files /dev/null and b/public/particles/smokes/smoke_02.png differ diff --git a/public/particles/smokes/smoke_03.png b/public/particles/smokes/smoke_03.png new file mode 100644 index 0000000..909717a Binary files /dev/null and b/public/particles/smokes/smoke_03.png differ diff --git a/public/particles/smokes/smoke_04.png b/public/particles/smokes/smoke_04.png new file mode 100644 index 0000000..74a2695 Binary files /dev/null and b/public/particles/smokes/smoke_04.png differ diff --git a/public/particles/smokes/smoke_05.png b/public/particles/smokes/smoke_05.png new file mode 100644 index 0000000..0ac46dc Binary files /dev/null and b/public/particles/smokes/smoke_05.png differ diff --git a/public/particles/smokes/smoke_06.png b/public/particles/smokes/smoke_06.png new file mode 100644 index 0000000..300c5fa Binary files /dev/null and b/public/particles/smokes/smoke_06.png differ diff --git a/public/particles/smokes/smoke_07.png b/public/particles/smokes/smoke_07.png new file mode 100644 index 0000000..fb78c0f Binary files /dev/null and b/public/particles/smokes/smoke_07.png differ diff --git a/public/particles/smokes/smoke_08.png b/public/particles/smokes/smoke_08.png new file mode 100644 index 0000000..feec49e Binary files /dev/null and b/public/particles/smokes/smoke_08.png differ diff --git a/src/App.jsx b/src/App.jsx index 6a7b1a5..3201e8a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -38,7 +38,7 @@ function App() { const { actions } = useStore(); const start = async () => { - await insertCoin(); + await insertCoin({ skipLobby: true}); onPlayerJoin((state) => { actions.addPlayer(state); diff --git a/src/components/Dust.jsx b/src/components/Dust.jsx new file mode 100644 index 0000000..a0c21cb --- /dev/null +++ b/src/components/Dust.jsx @@ -0,0 +1,86 @@ +import { Euler, Object3D, Vector3, Matrix4, DoubleSide, Quaternion, TextureLoader, BackSide } from 'three' +import { useRef, useLayoutEffect } from 'react' +import { useFrame, useLoader } from '@react-three/fiber' +import { vec3 } from '@react-three/rapier' + + +import { useStore } from './store' + + +const e = new Euler() +const m = new Matrix4() +const o = new Object3D() +const v = new Vector3() +const q = new Quaternion() + + + +export function Dust({ count = 500, opacity = 0.1, size = 0.6 }) { + const smoke01 = useLoader(TextureLoader, './particles/smokes/smoke_01.png'); + const smoke02 = useLoader(TextureLoader, './particles/smokes/smoke_02.png'); + const smoke03 = useLoader(TextureLoader, './particles/smokes/smoke_03.png'); + const smoke04 = useLoader(TextureLoader, './particles/smokes/smoke_04.png'); + const smoke05 = useLoader(TextureLoader, './particles/smokes/smoke_05.png'); + const smoke06 = useLoader(TextureLoader, './particles/smokes/smoke_06.png'); + const smoke07 = useLoader(TextureLoader, './particles/smokes/smoke_07.png'); + const smoke08 = useLoader(TextureLoader, './particles/smokes/smoke_08.png'); + + const texture = [smoke01, smoke02, smoke03, smoke04, smoke05, smoke06, smoke07, smoke08] + const ref = useRef(null); + const { leftWheel, rightWheel } = useStore(); + let index = 0 + let time = 0 + let i = 0 + useFrame((state,delta ) => { + const rotation = leftWheel.kartRotation; + if (state.clock.getElapsedTime() - time > 0.02 && leftWheel && rightWheel && ref.current && leftWheel.isSpinning) { + time = state.clock.getElapsedTime() + setItemAt(ref.current, rotation, leftWheel, index++); + setItemAt(ref.current, rotation, rightWheel, index++); + + if (index === count) index = 0 + } else { + // Shrink old one + for (i = 0; i < count; i++) { + const direction = new Vector3(Math.sin(time * 6 + i * 10) , 2, 0); + ref.current.getMatrixAt(i, m) + m.decompose(o.position, q, v) + o.scale.setScalar(Math.max(0, v.x - 0.005)) + o.position.addScaledVector(direction, 0.01) + o.updateMatrix() + ref.current.setMatrixAt(i, o.matrix) + ref.current.instanceMatrix.needsUpdate = true + } + } + }) + + useLayoutEffect(() => { + if(ref.current){ + ref.current.geometry.rotateY(-Math.PI / 2) + return () => { + ref.current.geometry.rotateY(Math.PI / 2) + } + } + }) + + return ( + + + + + ) +} + +function setItemAt(instances, rotation, body, index) { + const randomOffset = (Math.random() - 0.5) * 0.3 ; + const pos = body.getWorldPosition(v); + o.rotation.set(0, rotation + Math.PI / 2, 0); + pos.x += randomOffset + // pos.y += randomOffset + pos.z += randomOffset + o.position.copy(pos); + o.scale.setScalar(1) + o.updateMatrix() + instances.setMatrixAt(index, o.matrix) + instances.instanceMatrix.needsUpdate = true +} diff --git a/src/components/Experience.jsx b/src/components/Experience.jsx index 511e0cc..13d734e 100644 --- a/src/components/Experience.jsx +++ b/src/components/Experience.jsx @@ -42,6 +42,8 @@ import { useFrame, useLoader } from "@react-three/fiber"; import { LUTPass, LUTCubeLoader } from "three-stdlib"; import { useCurvedPathPoints } from "./useCurvedPath"; import { ParisBis } from "./models/tracks/Paris-bis"; +import { Skid } from "./Skid"; +import { Dust } from "./Dust"; export const Experience = () => { const onCollide = (event) => {}; @@ -151,10 +153,11 @@ export const Experience = () => { + + - {networkBananas.map((banana) => ( { + if (leftWheel.current && rightWheel.current && body.current) { + actions.setLeftWheel(leftWheel.current); + actions.setRightWheel(rightWheel.current); + } + }, [body.current]); useFrame(({ pointer, clock }, delta) => { if (player.id !== id) return; const time = clock.getElapsedTime(); @@ -115,11 +124,13 @@ export const PlayerController = ({ 0, -Math.cos(kartRotation) ); - + if (escPressed) { actions.setGameStarted(false); } - + if(kartRotation){ + leftWheel.current.kartRotation = kartRotation; + } if (leftPressed && currentSpeed > 0) { steeringAngle = currentSteeringSpeed; targetXPosition = -camMaxOffset; @@ -179,6 +190,8 @@ export const PlayerController = ({ } } if (shouldSlow) { + rightWheel.current.isSpinning = true; + setCurrentSpeed( Math.max(currentSpeed - decceleration * 2 * delta * 144, 0) ); @@ -186,6 +199,7 @@ export const PlayerController = ({ slowDownDuration.current -= 1500 * delta; setShouldLaunch(true); if (slowDownDuration.current <= 1) { + rightWheel.current.isSpinning = false; actions.setShouldSlowDown(false); slowDownDuration.current = 1500; setShouldLaunch(false); @@ -311,7 +325,11 @@ export const PlayerController = ({ steeringAngle * 25 + 0.4, 0.05 * delta * 144 ); - accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144; + if (isOnFloor.current) { + leftWheel.current.isSpinning = true; + accumulatedDriftPower.current += + 0.1 * (steeringAngle + 1) * delta * 144; + } } if (driftRight.current) { driftDirection.current = -1; @@ -321,7 +339,11 @@ export const PlayerController = ({ -(-steeringAngle * 25 + 0.4), 0.05 * delta * 144 ); - accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144; + if(isOnFloor.current){ + leftWheel.current.isSpinning = true; + accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144; + + } } if (!driftLeft.current && !driftRight.current) { mario.current.rotation.y = THREE.MathUtils.lerp( @@ -330,6 +352,11 @@ export const PlayerController = ({ 0.05 * delta * 144 ); setScale(0); + if((pointer.x > 0.8 || pointer.x < -0.8) && currentSpeed > 20 && isOnFloor.current){ + leftWheel.current.isSpinning = true; + } else { + leftWheel.current.isSpinning = false; + } } if (accumulatedDriftPower.current > blueTurboThreshold) { setTurboColor(0x00ffff); @@ -559,6 +586,8 @@ export const PlayerController = ({ opacity={0.4} /> + + */} - + { - const [isOnGround, setIsOnGround] = useState(false); const body = useRef(); const kart = useRef(); @@ -84,14 +82,28 @@ export const PlayerControllerGamepad = ({ const effectiveBoost = useRef(0); const text = useRef(); - const { actions, shouldSlowDown, item, bananas, coins, id, controls } = useStore(); + const { actions, shouldSlowDown, item, bananas, coins, id, controls } = + useStore(); const slowDownDuration = useRef(1500); const { buttonA, buttonB, RB, LB, joystick, select, start } = useGamepad(); + const skidRotation = useRef(0); + + const rightWheel = useRef(); + const leftWheel = useRef(); + const isDrifting = useRef(false); + useEffect(() => { + if (leftWheel.current && rightWheel.current && body.current) { + actions.setLeftWheel(leftWheel.current); + actions.setRightWheel(rightWheel.current); + } + }, [body.current]); + useFrame(({ pointer, clock }, delta) => { if (player.id !== id) return; const time = clock.getElapsedTime(); if (!body.current && !mario.current) return; + isDrifting.current = driftLeft.current || driftRight.current; engineSound.current.setVolume(currentSpeed / 300 + 0.2); engineSound.current.setPlaybackRate(currentSpeed / 10 + 0.1); jumpSound.current.setPlaybackRate(1.5); @@ -113,8 +125,7 @@ export const PlayerControllerGamepad = ({ if (start) { actions.setGameStarted(false); } - - // mouse steering + leftWheel.current.kartRotation = kartRotation ; if (!driftLeft.current && !driftRight.current) { steeringAngle = currentSteeringSpeed * -joystick[0]; @@ -155,6 +166,7 @@ export const PlayerControllerGamepad = ({ } } if (shouldSlow) { + rightWheel.current.isSpinning = true; setCurrentSpeed( Math.max(currentSpeed - decceleration * 2 * delta * 144, 0) ); @@ -162,6 +174,7 @@ export const PlayerControllerGamepad = ({ slowDownDuration.current -= 1500 * delta; setShouldLaunch(true); if (slowDownDuration.current <= 1) { + rightWheel.current.isSpinning = false; actions.setShouldSlowDown(false); slowDownDuration.current = 1500; setShouldLaunch(false); @@ -285,9 +298,13 @@ export const PlayerControllerGamepad = ({ steeringAngle * 25 + 0.4, 0.05 * delta * 144 ); - accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144; + if(isOnFloor.current){ + leftWheel.current.isSpinning = true; + accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144; + + } } - if (driftRight.current) { + if (driftRight.current ) { driftDirection.current = -1; driftForce.current = 0.4; mario.current.rotation.y = THREE.MathUtils.lerp( @@ -295,7 +312,11 @@ export const PlayerControllerGamepad = ({ -(-steeringAngle * 25 + 0.4), 0.05 * delta * 144 ); - accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144; + if(isOnFloor.current){ + leftWheel.current.isSpinning = true; + accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144; + + } } if (!driftLeft.current && !driftRight.current) { mario.current.rotation.y = THREE.MathUtils.lerp( @@ -304,6 +325,12 @@ export const PlayerControllerGamepad = ({ 0.05 * delta * 144 ); setScale(0); + + if((joystick[0] > 0.8 || joystick[0] < -0.8) && currentSpeed > 20 && isOnFloor.current){ + leftWheel.current.isSpinning = true; + } else { + leftWheel.current.isSpinning = false; + } } if (accumulatedDriftPower.current > blueTurboThreshold) { setTurboColor(0x00ffff); @@ -373,6 +400,7 @@ export const PlayerControllerGamepad = ({ 0.01 * delta * 144 ); + cam.current.position.z = THREE.MathUtils.lerp( cam.current.position.z, targetZPosition, @@ -466,6 +494,8 @@ export const PlayerControllerGamepad = ({ player.setState("turboColor", turboColor); player.setState("scale", scale); player.setState("bananas", bananas); + + skidRotation.current = kartRotation + mario.current.rotation.y; }); return player.id === id ? ( @@ -504,7 +534,7 @@ export const PlayerControllerGamepad = ({ /> - + + + {/* */} - { + if (leftWheel.current && rightWheel.current && body.current) { + actions.setLeftWheel(leftWheel.current); + actions.setRightWheel(rightWheel.current); + } + }, [body.current]); + useFrame(({ pointer, clock }, delta) => { if (player.id !== id) return; const time = clock.getElapsedTime(); @@ -116,7 +127,7 @@ export const PlayerControllerKeyboard = ({ 0, -Math.cos(kartRotation) ); - + leftWheel.current.kartRotation = kartRotation; if (escPressed) { actions.setGameStarted(false); } @@ -168,6 +179,7 @@ export const PlayerControllerKeyboard = ({ } } if (shouldSlow) { + rightWheel.current.isSpinning = true; setCurrentSpeed( Math.max(currentSpeed - decceleration * 2 * delta * 144, 0) ); @@ -175,6 +187,7 @@ export const PlayerControllerKeyboard = ({ slowDownDuration.current -= 1500 * delta; setShouldLaunch(true); if (slowDownDuration.current <= 1) { + rightWheel.current.isSpinning = false; actions.setShouldSlowDown(false); slowDownDuration.current = 1500; setShouldLaunch(false); @@ -300,7 +313,11 @@ export const PlayerControllerKeyboard = ({ steeringAngle * 25 + 0.4, 0.05 * delta * 144 ); - accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144; + if (isOnFloor.current) { + leftWheel.current.isSpinning = true; + accumulatedDriftPower.current += + 0.1 * (steeringAngle + 1) * delta * 144; + } } if (driftRight.current) { driftDirection.current = -1; @@ -310,7 +327,11 @@ export const PlayerControllerKeyboard = ({ -(-steeringAngle * 25 + 0.4), 0.05 * delta * 144 ); - accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144; + if (isOnFloor.current) { + leftWheel.current.isSpinning = true; + accumulatedDriftPower.current += + 0.1 * (-steeringAngle + 1) * delta * 144; + } } if (!driftLeft.current && !driftRight.current) { mario.current.rotation.y = THREE.MathUtils.lerp( @@ -319,6 +340,11 @@ export const PlayerControllerKeyboard = ({ 0.05 * delta * 144 ); setScale(0); + if((leftPressed || rightPressed) && currentSpeed > 20 && isOnFloor.current){ + leftWheel.current.isSpinning = true; + } else { + leftWheel.current.isSpinning = false; + } } if (accumulatedDriftPower.current > blueTurboThreshold) { setTurboColor(0x00ffff); @@ -529,6 +555,8 @@ export const PlayerControllerKeyboard = ({ opacity={0.4} /> + + + */} - + { + if (leftWheel.current && rightWheel.current && body.current) { + actions.setLeftWheel(leftWheel.current); + actions.setRightWheel(rightWheel.current); + } + }, [body.current]); useFrame(({ pointer, clock }, delta) => { if (player.id !== id) return; @@ -108,7 +117,7 @@ export const PlayerControllerTouch = ({ 0, -Math.cos(kartRotation) ); - + leftWheel.current.kartRotation = kartRotation; if (menuButton) { actions.setGameStarted(false); } @@ -153,6 +162,8 @@ export const PlayerControllerTouch = ({ ); } if (shouldSlow) { + rightWheel.current.isSpinning = true; + setCurrentSpeed( Math.max(currentSpeed - decceleration * 2 * delta * 144, 0) ); @@ -160,6 +171,8 @@ export const PlayerControllerTouch = ({ slowDownDuration.current -= 1500 * delta; setShouldLaunch(true); if (slowDownDuration.current <= 1) { + rightWheel.current.isSpinning = false; + actions.setShouldSlowDown(false); slowDownDuration.current = 1500; setShouldLaunch(false); @@ -252,7 +265,11 @@ export const PlayerControllerTouch = ({ steeringAngle * 25 + 0.4, 0.05 * delta * 144 ); - accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144; + if(isOnFloor.current){ + leftWheel.current.isSpinning = true; + accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144; + + } } if (driftRight.current) { driftDirection.current = -1; @@ -262,7 +279,11 @@ export const PlayerControllerTouch = ({ -(-steeringAngle * 25 + 0.4), 0.05 * delta * 144 ); - accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144; + if(isOnFloor.current){ + leftWheel.current.isSpinning = true; + accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144; + + } } if (!driftLeft.current && !driftRight.current) { mario.current.rotation.y = THREE.MathUtils.lerp( @@ -271,6 +292,11 @@ export const PlayerControllerTouch = ({ 0.05 * delta * 144 ); setScale(0); + if((joystickX > 0.8 || joystickX < -0.8) && currentSpeed > 20 && isOnFloor.current){ + leftWheel.current.isSpinning = true; + } else { + leftWheel.current.isSpinning = false; + } } if (accumulatedDriftPower.current > blueTurboThreshold) { setTurboColor(0x00ffff); @@ -466,6 +492,8 @@ export const PlayerControllerTouch = ({ opacity={0.4} /> + + { + const rotation = leftWheel.kartRotation; + if (leftWheel && rightWheel && ref.current && (leftWheel.isSpinning || rightWheel.isSpinning)) { + setItemAt(ref.current, rotation, leftWheel, index++); + setItemAt(ref.current, rotation, rightWheel, index++); + + if (index === count) index = 0 + } + }) + + useLayoutEffect(() => { + if(ref.current){ + ref.current.geometry.rotateX(-Math.PI / 2) + return () => { + ref.current.geometry.rotateX(Math.PI / 2) + } + } + }) + + return ( + + + + + ) +} + +function setItemAt(instances, rotation, body, index) { + o.position.copy(body.getWorldPosition(v)); + o.rotation.set(0, rotation, 0); + o.scale.setScalar(1) + o.updateMatrix() + instances.setMatrixAt(index, o.matrix) + instances.instanceMatrix.needsUpdate = true +} diff --git a/src/components/store.jsx b/src/components/store.jsx index dd0d05f..6be91ad 100644 --- a/src/components/store.jsx +++ b/src/components/store.jsx @@ -13,12 +13,13 @@ export const items = [ ] export const useStore = create((set, get) => ({ - gameStarted: false, - controls: "", + gameStarted: true, + controls: "gamepad", particles1: [], particles2: [], - bodyPosition: [0, 0, 0], - bodyRotation: [0, 0, 0], + leftWheel: null, + rightWheel: null, + bodyRotation: null, pastPositions: [], shouldSlowdown: false, bananas: [], @@ -28,11 +29,13 @@ export const useStore = create((set, get) => ({ skids: [], coins : 0, players : [], + body: null, id : "", joystickX: 0, driftButton: false, itemButton: false, menuButton: false, + isDrifting: false, addPastPosition: (position) => { set((state) => ({ pastPositions: [position, ...state.pastPositions.slice(0, 499)], @@ -63,7 +66,7 @@ export const useStore = create((set, get) => ({ set({ bodyPosition: position }); }, setBodyRotation: (rotation) => { - set({ bodyRotation: rotation }); + set({ rotation }); }, getBodyPosition: () => { return get().bodyPosition; @@ -164,6 +167,21 @@ export const useStore = create((set, get) => ({ setMenuButton: (menuButton) => { set({ menuButton }); }, + setBody: (body) => { + set({ body }); + }, + setLeftWheel: (leftWheel) => { + set({ leftWheel }); + }, + setRightWheel: (rightWheel) => { + set({ rightWheel }); + }, + setIsDrifting: (isDrifting) => { + set({ isDrifting }); + }, + getIsDrifting: () => { + return get().isDrifting; + }, }, }));