import { Controls } from "../App"; import { BallCollider, RigidBody, useRapier, vec3 } from "@react-three/rapier"; import { useKeyboardControls, PerspectiveCamera, PositionalAudio, } from "@react-three/drei"; import { useFrame, useThree, extend } from "@react-three/fiber"; import { useRef, useState, useEffect, useCallback } from "react"; import * as THREE from "three"; import { Mario } from "./models/characters/Mario_kart"; import { DriftParticlesLeft } from "./Particles/drifts/DriftParticlesLeft"; import { DriftParticlesRight } from "./Particles/drifts/DriftParticlesRight"; import { PointParticle } from "./Particles/drifts/PointParticle"; import { FlameParticles } from "./Particles/flames/FlameParticles"; import { useStore } from "./store"; import { Cylinder } from "@react-three/drei"; import FakeGlowMaterial from "./ShaderMaterials/FakeGlow/FakeGlowMaterial"; import { HitParticles } from "./Particles/hits/HitParticles"; import { CoinParticles } from "./Particles/coins/CoinParticles"; import { ItemParticles } from "./Particles/items/ItemParticles"; import { geometry } from "maath"; import { useGamepad } from "./useGamepad"; extend(geometry); export const PlayerControllerTouch = ({ player, userPlayer, setNetworkBananas, setNetworkShells, networkBananas, networkShells, }) => { const [isOnGround, setIsOnGround] = useState(false); const body = useRef(); const kart = useRef(); const cam = useRef(); const initialSpeed = 0; const maxSpeed = 30; const boostSpeed = 50; const acceleration = 0.1; const decceleration = 0.2; const damping = -0.1; const MaxSteeringSpeed = 0.01; const [currentSteeringSpeed, setCurrentSteeringSpeed] = useState(0); const [currentSpeed, setCurrentSpeed] = useState(initialSpeed); const camMaxOffset = 1; let steeringAngle = 0; const isOnFloor = useRef(false); const jumpForce = useRef(0); const jumpIsHeld = useRef(false); const driftDirection = useRef(0); const driftLeft = useRef(false); const driftRight = useRef(false); const driftForce = useRef(0); const mario = useRef(); const accumulatedDriftPower = useRef(0); const blueTurboThreshold = 10; const orangeTurboThreshold = 30; const purpleTurboThreshold = 60; const [turboColor, setTurboColor] = useState(0xffffff); const boostDuration = useRef(0); const [isBoosting, setIsBoosting] = useState(false); let targetXPosition = 0; let targetZPosition = 8; const [steeringAngleWheels, setSteeringAngleWheels] = useState(0); const engineSound = useRef(); const driftSound = useRef(); const driftTwoSound = useRef(); const driftOrangeSound = useRef(); const driftPurpleSound = useRef(); const driftBlueSound = useRef(); const jumpSound = useRef(); const landingSound = useRef(); const turboSound = useRef(); const [scale, setScale] = useState(0); const raycaster = new THREE.Raycaster(); const downDirection = new THREE.Vector3(0, -1, 0); const [shouldLaunch, setShouldLaunch] = useState(false); const effectiveBoost = useRef(0); const text = useRef(); const { actions, shouldSlowDown, item, bananas, coins, id, controls, joystickX, driftButton, itemButton } = useStore(); const slowDownDuration = useRef(1500); useFrame(({ pointer, clock }, delta) => { if (player.id !== id) return; const time = clock.getElapsedTime(); if (!body.current && !mario.current) return; engineSound.current.setVolume(currentSpeed / 300 + 0.2); engineSound.current.setPlaybackRate(currentSpeed / 10 + 0.1); jumpSound.current.setPlaybackRate(1.5); jumpSound.current.setVolume(0.5); driftSound.current.setVolume(0.2); driftBlueSound.current.setVolume(0.5); driftOrangeSound.current.setVolume(0.6); driftPurpleSound.current.setVolume(0.7); // HANDLING AND STEERING const kartRotation = kart.current.rotation.y - driftDirection.current * driftForce.current; const forwardDirection = new THREE.Vector3( -Math.sin(kartRotation), 0, -Math.cos(kartRotation) ); // mouse steering if (!driftLeft.current && !driftRight.current) { steeringAngle = currentSteeringSpeed * -joystickX; targetXPosition = -camMaxOffset * -joystickX; } else if (driftLeft.current && !driftRight.current) { steeringAngle = currentSteeringSpeed * -(joystickX - 1); targetXPosition = -camMaxOffset * -joystickX; } else if (driftRight.current && !driftLeft.current) { steeringAngle = currentSteeringSpeed * -(joystickX + 1); targetXPosition = -camMaxOffset * -joystickX; } // ACCELERATING const shouldSlow = actions.getShouldSlowDown(); if ( currentSpeed < maxSpeed) { // Accelerate the kart within the maximum speed limit setCurrentSpeed( Math.min(currentSpeed + acceleration * delta * 144, maxSpeed) ); } else if ( currentSpeed > maxSpeed && effectiveBoost.current > 0 ) { setCurrentSpeed( Math.max(currentSpeed - decceleration * delta * 144, maxSpeed) ); } if (currentSteeringSpeed < MaxSteeringSpeed) { setCurrentSteeringSpeed( Math.min( currentSteeringSpeed + 0.0001 * delta * 144, MaxSteeringSpeed ) ); } if (shouldSlow) { setCurrentSpeed( Math.max(currentSpeed - decceleration * 2 * delta * 144, 0) ); setCurrentSteeringSpeed(0); slowDownDuration.current -= 1500 * delta; setShouldLaunch(true); if (slowDownDuration.current <= 1) { actions.setShouldSlowDown(false); slowDownDuration.current = 1500; setShouldLaunch(false); } } // Update the kart's rotation based on the steering angle kart.current.rotation.y += steeringAngle * delta * 144; // Apply damping to simulate slowdown when no keys are pressed body.current.applyImpulse( { x: -body.current.linvel().x * (1 - damping) * delta * 144, y: 0, z: -body.current.linvel().z * (1 - damping) * delta * 144, }, true ); const bodyPosition = body.current.translation(); kart.current.position.set( bodyPosition.x, bodyPosition.y - 0.5, bodyPosition.z ); // JUMPING if (driftButton && isOnGround && !jumpIsHeld.current) { jumpForce.current += 10; isOnFloor.current = false; jumpIsHeld.current = true; jumpSound.current.play(); setIsOnGround(false); if (jumpSound.current.isPlaying) { jumpSound.current.stop(); jumpSound.current.play(); } } if (isOnFloor.current && jumpForce.current > 0) { landingSound.current.play(); } if (!isOnGround && jumpForce.current > 0) { jumpForce.current -= 1 * delta * 144; } if (!driftButton) { jumpIsHeld.current = false; driftDirection.current = 0; driftForce.current = 0; driftLeft.current = false; driftRight.current = false; } // DRIFTING if ( jumpIsHeld.current && currentSteeringSpeed > 0 && joystickX < -0.1 && !driftRight.current ) { driftLeft.current = true; } if ( jumpIsHeld.current && currentSteeringSpeed > 0 && joystickX > 0.1 && !driftLeft.current ) { driftRight.current = true; } if (!jumpIsHeld.current && !driftLeft.current && !driftRight.current) { mario.current.rotation.y = THREE.MathUtils.lerp( mario.current.rotation.y, 0, 0.0001 * delta * 144 ); setTurboColor(0xffffff); accumulatedDriftPower.current = 0; driftSound.current.stop(); driftTwoSound.current.stop(); driftOrangeSound.current.stop(); driftPurpleSound.current.stop(); } if (driftLeft.current) { driftDirection.current = 1; driftForce.current = 0.4; mario.current.rotation.y = THREE.MathUtils.lerp( mario.current.rotation.y, steeringAngle * 25 + 0.4, 0.05 * delta * 144 ); accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144; } if (driftRight.current) { driftDirection.current = -1; driftForce.current = 0.4; mario.current.rotation.y = THREE.MathUtils.lerp( mario.current.rotation.y, -(-steeringAngle * 25 + 0.4), 0.05 * delta * 144 ); accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144; } if (!driftLeft.current && !driftRight.current) { mario.current.rotation.y = THREE.MathUtils.lerp( mario.current.rotation.y, steeringAngle * 30, 0.05 * delta * 144 ); setScale(0); } if (accumulatedDriftPower.current > blueTurboThreshold) { setTurboColor(0x00ffff); boostDuration.current = 50; driftBlueSound.current.play(); } if (accumulatedDriftPower.current > orangeTurboThreshold) { setTurboColor(0xffcf00); boostDuration.current = 100; driftBlueSound.current.stop(); driftOrangeSound.current.play(); } if (accumulatedDriftPower.current > purpleTurboThreshold) { setTurboColor(0xff00ff); boostDuration.current = 250; driftOrangeSound.current.stop(); driftPurpleSound.current.play(); } if (driftLeft.current || driftRight.current) { const oscillation = Math.sin(time * 1000) * 0.1; const vibration = oscillation + 0.9; if (turboColor === 0xffffff) { setScale(vibration * 0.8); } else { setScale(vibration); } if (isOnFloor.current && !driftSound.current.isPlaying) { driftSound.current.play(); driftTwoSound.current.play(); landingSound.current.play(); } } // RELEASING DRIFT if (boostDuration.current > 1 && !jumpIsHeld.current) { setIsBoosting(true); effectiveBoost.current = boostDuration.current; boostDuration.current = 0; } else if (effectiveBoost.current <= 1) { targetZPosition = 8; setIsBoosting(false); } if (isBoosting && effectiveBoost.current > 1) { setCurrentSpeed(boostSpeed); effectiveBoost.current -= 1 * delta * 144; targetZPosition = 10; if (!turboSound.current.isPlaying) turboSound.current.play(); driftTwoSound.current.play(); driftBlueSound.current.stop(); driftOrangeSound.current.stop(); driftPurpleSound.current.stop(); } else if (effectiveBoost.current <= 1) { setIsBoosting(false); targetZPosition = 8; turboSound.current.stop(); } // CAMERA WORK cam.current.updateMatrixWorld(); 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 ); body.current.applyImpulse( { x: forwardDirection.x * currentSpeed * delta * 144, y: 0 + jumpForce.current * delta * 144, z: forwardDirection.z * currentSpeed * delta * 144, }, true ); // Update the kart's rotation based on the steering angle setSteeringAngleWheels(steeringAngle * 25); // SOUND WORK // ITEMS if (itemButton && item === "banana") { const distanceBehind = 2; const scaledBackwardDirection = forwardDirection.multiplyScalar(distanceBehind); const kartPosition = new THREE.Vector3( ...vec3(body.current.translation()) ); const bananaPosition = kartPosition.sub(scaledBackwardDirection); const newBanana = { id: Math.random() + "-" + +new Date(), position: bananaPosition, player: true, }; setNetworkBananas([...networkBananas, newBanana]); actions.useItem(); } if (itemButton && item === "shell") { const distanceBehind = -2; const scaledBackwardDirection = forwardDirection.multiplyScalar(distanceBehind); const kartPosition = new THREE.Vector3( body.current.translation().x, body.current.translation().y, body.current.translation().z ); const shellPosition = kartPosition.sub(scaledBackwardDirection); const newShell = { id: Math.random() + "-" + +new Date(), position: shellPosition, player: true, rotation: kartRotation, }; setNetworkShells([...networkShells, newShell]); actions.useItem(); } if (itemButton && item === "mushroom") { setIsBoosting(true); effectiveBoost.current = 300; actions.useItem(); } player.setState("position", body.current.translation()); player.setState("rotation", kartRotation + mario.current.rotation.y); player.setState("isBoosting", isBoosting); player.setState("shouldLaunch", shouldLaunch); player.setState("turboColor", turboColor); player.setState("scale", scale); player.setState("bananas", bananas); }); return player.id === id ? ( { isOnFloor.current = true; setIsOnGround(true); }} onCollisionExit={({ other }) => { isOnFloor.current = false; setIsOnGround(false); }} /> {/* */} {/* */} ) : null; };