diff --git a/src/App.jsx b/src/App.jsx index ca5c783..56dc97f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,55 +1,53 @@ -import { Canvas } from "@react-three/fiber"; -import { Experience } from "./components/Experience"; -import { Suspense, useMemo } from "react"; -import { Physics } from "@react-three/rapier"; -import { - KeyboardControls, - Loader, - OrbitControls, - Preload, - Stats, -} from "@react-three/drei"; +import { Canvas } from '@react-three/fiber' +import { Experience } from './components/Experience' +import { Suspense, useMemo } from 'react' +import { Physics } from '@react-three/rapier' +import { KeyboardControls, Loader, OrbitControls, Preload, Stats } from '@react-three/drei' export const Controls = { - up: "up", - down: "down", - left: "left", - right: "right", - boost: "boost", - shoot: "shoot", - slow: "slow", - reset: "reset", -}; + up: 'up', + down: 'down', + left: 'left', + right: 'right', + boost: 'boost', + shoot: 'shoot', + slow: 'slow', + reset: 'reset' +} function App() { - - const map = useMemo( () => [ - { name: Controls.up, keys: ["KeyW", "ArrowUp"] }, - { name: Controls.down, keys: ["KeyS", "ArrowDown"] }, - { name: Controls.left, keys: ["KeyA", "ArrowLeft"] }, - { name: Controls.right, keys: ["KeyD", "ArrowRight"] }, - { name: Controls.jump, keys: ["Space"] }, - { name: Controls.slow, keys: ["Shift"] }, - { name: Controls.shoot, keys: ["KeyE", "Click"] }, - { name: Controls.reset, keys: ["KeyR"] }, + { name: Controls.up, keys: ['KeyW', 'ArrowUp'] }, + { name: Controls.down, keys: ['KeyS', 'ArrowDown'] }, + { name: Controls.left, keys: ['KeyA', 'ArrowLeft'] }, + { name: Controls.right, keys: ['KeyD', 'ArrowRight'] }, + { name: Controls.jump, keys: ['Space'] }, + { name: Controls.slow, keys: ['Shift'] }, + { name: Controls.shoot, keys: ['KeyE', 'Click'] }, + { name: Controls.reset, keys: ['KeyR'] } ], [] - ); + ) return ( - + - - + + - - {/* */} + + {/* */} - - ); + ) } -export default App; +export default App diff --git a/src/components/Experience.jsx b/src/components/Experience.jsx index 49391b6..0d548c7 100644 --- a/src/components/Experience.jsx +++ b/src/components/Experience.jsx @@ -1,47 +1,58 @@ -import { - Environment, - OrbitControls, - PerspectiveCamera, - Lightformer -} from "@react-three/drei"; -import { Ground } from "./Ground"; -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, SMAA} from '@react-three/postprocessing' +import { Environment, OrbitControls, PerspectiveCamera, Lightformer } from '@react-three/drei' +import { Ground } from './Ground' +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, SMAA } from '@react-three/postprocessing' export const Experience = () => { return ( <> - + - position={[10, 50,-30]} - intensity={1} - shadow-bias={-0.0001} - shadow-mapSize={[4096, 4096]} - shadow-camera-left={-300} - shadow-camera-right={300} - shadow-camera-top={300} - shadow-camera-bottom={-3000} - castShadow - /> - {/* */} {/* */} - - + + {/* */} - - + + {/* */} - - + + - ); -}; + ) +} diff --git a/src/components/FakeGlow/FakeGlowMaterial.jsx b/src/components/FakeGlow/FakeGlowMaterial.jsx new file mode 100644 index 0000000..3483756 --- /dev/null +++ b/src/components/FakeGlow/FakeGlowMaterial.jsx @@ -0,0 +1,82 @@ +/** + * FakeGlow material component by Anderson Mancini - Dec 2024. + */ + +import React, { useRef, useMemo } from 'react' +import { shaderMaterial } from '@react-three/drei' +import { extend, useFrame } from '@react-three/fiber' +import { Color, BackSide, AdditiveBlending } from 'three' + +export default function FakeGlowMaterial({ glowCenter = 0.05, edgeIntensity = 6.0, glowColor = '#00ff00', glowOpacity = 1.0, fresnelOpacity = 0.5, fresnelAmount = 0.5, wireframe = false }) { + const FakeGlowMaterial = useMemo(() => { + return shaderMaterial( + { + time: 0, + glowCenter: glowCenter, + edgeIntensity: edgeIntensity, + glowColor: new Color(glowColor), + glowOpacity: glowOpacity, + fresnelOpacity: fresnelOpacity, + fresnelAmount: fresnelAmount + }, + /*GLSL */ + ` + uniform float glowCenter; + uniform float edgeIntensity; + varying float intensity; + varying vec3 vNormel; + varying vec3 vNormal; + uniform float fresnelOpacity; + uniform float fresnelAmount; + + void main() { + vec3 viewVector = normalize(vec3(cameraPosition-position)); + float fresnelEffect = dot(vNormel, vNormal) * ( fresnelOpacity); + fresnelEffect = clamp(fresnelAmount - fresnelEffect, 0.5, fresnelOpacity); + vec3 vNormal = normalize( normalMatrix * normal ); + vec3 vNormel = normalize( normalMatrix * viewVector ); + intensity = pow( glowCenter - dot(vNormal, vNormel), edgeIntensity / fresnelEffect ); + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`, + /*GLSL */ + ` + uniform vec3 glowColor; + varying float intensity; + uniform float glowOpacity; + uniform float fresnelOpacity; + uniform float fresnelAmount; + varying vec3 vNormel; + varying vec3 vNormal; + uniform float glowCenter; + uniform float edgeIntensity; + + void main() + { + vec3 glow = glowColor * intensity; + float fresnelEffect = - dot(vNormel, vNormal); + fresnelEffect = clamp(fresnelEffect, 0.1, 1.0); + gl_FragColor = vec4( clamp(glow, 0., 1.0), clamp(glowOpacity - intensity * fresnelEffect, 0., 1.0 )); + }` + ) + }, [glowCenter, edgeIntensity, glowColor, glowOpacity, fresnelOpacity, fresnelAmount]) + + extend({ FakeGlowMaterial }) + + useFrame((state, delta) => { + ref.current.time += delta + }) + + const ref = useRef() + + return ( + + ) +} diff --git a/src/components/PlayerController.jsx b/src/components/PlayerController.jsx index b2e71a6..1d3a423 100644 --- a/src/components/PlayerController.jsx +++ b/src/components/PlayerController.jsx @@ -1,294 +1,246 @@ -import { Controls } from "../App"; -import { RigidBody, useRapier } from "@react-three/rapier"; -import { - useKeyboardControls, - PerspectiveCamera, - ContactShadows, -} from "@react-three/drei"; -import { useFrame, useThree } from "@react-three/fiber"; -import { useRef, useState, useEffect, useCallback } from "react"; -import * as THREE from "three"; -import { Model } from "./models/Racing_kart"; -import { FrontRightWheel } from "./models/Front_Right_Wheel"; -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"; +import { Controls } from '../App' +import { RigidBody, useRapier } from '@react-three/rapier' +import { useKeyboardControls, PerspectiveCamera, ContactShadows, Sphere } from '@react-three/drei' +import { useFrame, useThree } from '@react-three/fiber' +import { useRef, useState, useEffect, useCallback } from 'react' +import * as THREE from 'three' +import { Model } from './models/Racing_kart' +import { FrontRightWheel } from './models/Front_Right_Wheel' +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' +import FakeGlowMaterial from './FakeGlow/FakeGlowMaterial' export const PlayerController = () => { - const upPressed = useKeyboardControls((state) => state[Controls.up]); - const downPressed = useKeyboardControls((state) => state[Controls.down]); - const leftPressed = useKeyboardControls((state) => state[Controls.left]); - const rightPressed = useKeyboardControls((state) => state[Controls.right]); - const jumpPressed = useKeyboardControls((state) => state[Controls.jump]); - 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.001; - const MaxSteeringSpeed = 0.01; - const [currentSteeringSpeed, setCurrentSteeringSpeed] = useState(0); - const [currentSpeed, setCurrentSpeed] = useState(initialSpeed); - const camMaxOffset = 0.5; - 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 = useRef(false); - let targetXPosition = 0; - let targetZPosition = 8; - const [steeringAngleWheels, setSteeringAngleWheels] = useState(0); + const upPressed = useKeyboardControls((state) => state[Controls.up]) + const downPressed = useKeyboardControls((state) => state[Controls.down]) + const leftPressed = useKeyboardControls((state) => state[Controls.left]) + const rightPressed = useKeyboardControls((state) => state[Controls.right]) + const jumpPressed = useKeyboardControls((state) => state[Controls.jump]) + 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.001 + const MaxSteeringSpeed = 0.01 + const [currentSteeringSpeed, setCurrentSteeringSpeed] = useState(0) + const [currentSpeed, setCurrentSpeed] = useState(initialSpeed) + const camMaxOffset = 0.5 + 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 = useRef(false) + let targetXPosition = 0 + let targetZPosition = 8 + const [steeringAngleWheels, setSteeringAngleWheels] = useState(0) - const [scale, setScale] = useState(0); + const [scale, setScale] = useState(0) useFrame(({ pointer, clock }) => { - const time = clock.getElapsedTime(); - if (!body.current && !mario.current) return; + const time = clock.getElapsedTime() + if (!body.current && !mario.current) return // 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) - ); + const kartRotation = kart.current.rotation.y - driftDirection.current * driftForce.current + const forwardDirection = new THREE.Vector3(-Math.sin(kartRotation), 0, -Math.cos(kartRotation)) if (leftPressed && currentSpeed > 0) { - steeringAngle = currentSteeringSpeed; - targetXPosition = -camMaxOffset; + steeringAngle = currentSteeringSpeed + targetXPosition = -camMaxOffset } else if (rightPressed && currentSpeed > 0) { - steeringAngle = -currentSteeringSpeed; - targetXPosition = camMaxOffset; + steeringAngle = -currentSteeringSpeed + targetXPosition = camMaxOffset } else { - steeringAngle = 0; - targetXPosition = 0; - 1; + steeringAngle = 0 + targetXPosition = 0 + 1 } // mouse steering - steeringAngle = currentSteeringSpeed * -pointer.x; - targetXPosition = -camMaxOffset * -pointer.x; + steeringAngle = currentSteeringSpeed * -pointer.x + targetXPosition = -camMaxOffset * -pointer.x // ACCELERATING 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 - ) { - setCurrentSpeed(Math.max(currentSpeed - decceleration, maxSpeed)); + setCurrentSpeed(Math.min(currentSpeed + acceleration, maxSpeed)) + } else if (upPressed && currentSpeed > maxSpeed && boostDuration.current > 0) { + setCurrentSpeed(Math.max(currentSpeed - decceleration, maxSpeed)) } if (upPressed) { if (currentSteeringSpeed < MaxSteeringSpeed) { - setCurrentSteeringSpeed( - Math.min(currentSteeringSpeed + 0.0001, MaxSteeringSpeed) - ); + setCurrentSteeringSpeed(Math.min(currentSteeringSpeed + 0.0001, MaxSteeringSpeed)) } } // REVERSING if (downPressed && currentSpeed < -maxSpeed) { - setCurrentSpeed(Math.max(currentSpeed - acceleration, -maxSpeed)); + setCurrentSpeed(Math.max(currentSpeed - acceleration, -maxSpeed)) } // DECELERATING else if (!upPressed && !downPressed) { if (currentSteeringSpeed > 0) { - setCurrentSteeringSpeed(Math.max(currentSteeringSpeed - 0.00005, 0)); + setCurrentSteeringSpeed(Math.max(currentSteeringSpeed - 0.00005, 0)) } else if (currentSteeringSpeed < 0) { - setCurrentSteeringSpeed(Math.min(currentSteeringSpeed + 0.00005, 0)); + setCurrentSteeringSpeed(Math.min(currentSteeringSpeed + 0.00005, 0)) } - setCurrentSpeed(Math.max(currentSpeed - decceleration, 0)); + setCurrentSpeed(Math.max(currentSpeed - decceleration, 0)) } // Update the kart's rotation based on the steering angle - kart.current.rotation.y += steeringAngle; + kart.current.rotation.y += steeringAngle // Apply damping to simulate slowdown when no keys are pressed body.current.applyImpulse( { x: -body.current.linvel().x * (1 - damping), y: 0, - z: -body.current.linvel().z * (1 - damping), + z: -body.current.linvel().z * (1 - damping) }, true - ); - const bodyPosition = body.current.translation(); - kart.current.position.set( - bodyPosition.x, - bodyPosition.y - 1, - bodyPosition.z - ); + ) + const bodyPosition = body.current.translation() + kart.current.position.set(bodyPosition.x, bodyPosition.y - 1, bodyPosition.z) // JUMPING if (jumpPressed && isOnFloor.current && !jumpIsHeld.current) { - jumpForce.current += 11; - isOnFloor.current = false; - jumpIsHeld.current = true; + jumpForce.current += 11 + isOnFloor.current = false + jumpIsHeld.current = true } if (!isOnFloor.current && jumpForce.current > 0) { - jumpForce.current -= 1; + jumpForce.current -= 1 } if (!jumpPressed) { - jumpIsHeld.current = false; - driftDirection.current = 0; - driftForce.current = 0; - driftLeft.current = false; - driftRight.current = false; + jumpIsHeld.current = false + driftDirection.current = 0 + driftForce.current = 0 + driftLeft.current = false + driftRight.current = false } // DRIFTING - if ( - jumpIsHeld.current && - currentSteeringSpeed > 0 && - pointer.x < -0.24 && - !driftRight.current - ) { - driftLeft.current = true; + if (jumpIsHeld.current && currentSteeringSpeed > 0 && pointer.x < -0.24 && !driftRight.current) { + driftLeft.current = true } - if ( - jumpIsHeld.current && - currentSteeringSpeed > 0 && - pointer.x > 0.24 && - !driftLeft.current - ) { - driftRight.current = true; + if (jumpIsHeld.current && currentSteeringSpeed > 0 && pointer.x > 0.24 && !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.001 - ); - setTurboColor(0xffffff); - accumulatedDriftPower.current = 0; + mario.current.rotation.y = THREE.MathUtils.lerp(mario.current.rotation.y, 0, 0.001) + setTurboColor(0xffffff) + accumulatedDriftPower.current = 0 } if (driftLeft.current) { - driftDirection.current = 1; - driftForce.current = 0.4; - mario.current.rotation.y = THREE.MathUtils.lerp( - mario.current.rotation.y, - steeringAngle * 50 + 0.5, - 0.1 - ); - accumulatedDriftPower.current += 0.1 * (steeringAngle + 1); + driftDirection.current = 1 + driftForce.current = 0.4 + mario.current.rotation.y = THREE.MathUtils.lerp(mario.current.rotation.y, steeringAngle * 50 + 0.5, 0.1) + accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) } if (driftRight.current) { - driftDirection.current = -1; - driftForce.current = 0.4; - mario.current.rotation.y = THREE.MathUtils.lerp( - mario.current.rotation.y, - -(-steeringAngle * 50 + 0.5), - 0.1 - ); - accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1); + driftDirection.current = -1 + driftForce.current = 0.4 + mario.current.rotation.y = THREE.MathUtils.lerp(mario.current.rotation.y, -(-steeringAngle * 50 + 0.5), 0.1) + accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) } if (!driftLeft.current && !driftRight.current) { - mario.current.rotation.y = THREE.MathUtils.lerp( - mario.current.rotation.y, - steeringAngle * 30, - 0.1 - ); - setScale(0); + mario.current.rotation.y = THREE.MathUtils.lerp(mario.current.rotation.y, steeringAngle * 30, 0.1) + setScale(0) } if (accumulatedDriftPower.current > blueTurboThreshold) { - setTurboColor(0x00ffff); - boostDuration.current = 50; + setTurboColor(0x00ffff) + boostDuration.current = 50 } if (accumulatedDriftPower.current > orangeTurboThreshold) { - setTurboColor(0xffcf00); - boostDuration.current = 100; + setTurboColor(0xffcf00) + boostDuration.current = 100 } if (accumulatedDriftPower.current > purpleTurboThreshold) { - setTurboColor(0xff00ff); - boostDuration.current = 250; + setTurboColor(0xff00ff) + boostDuration.current = 250 } 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; - setScale(vibration); + const vibration = oscillation + 0.9 + setScale(vibration) } // RELEASING DRIFT if (boostDuration.current > 1 && !jumpIsHeld.current) { - setCurrentSpeed(boostSpeed); - boostDuration.current -= 1; - targetZPosition = 10; + setCurrentSpeed(boostSpeed) + boostDuration.current -= 1 + targetZPosition = 10 } else if (boostDuration.current <= 1) { - targetZPosition = 8; + targetZPosition = 8 } // CAMERA WORK - cam.current.updateMatrixWorld(); + cam.current.updateMatrixWorld() - cam.current.position.x = THREE.MathUtils.lerp( - cam.current.position.x, - targetXPosition, - 0.01 - ); + cam.current.position.x = THREE.MathUtils.lerp(cam.current.position.x, targetXPosition, 0.01) - cam.current.position.z = THREE.MathUtils.lerp( - cam.current.position.z, - targetZPosition, - 0.01 - ); - cam.current.updateMatrixWorld(); + cam.current.position.z = THREE.MathUtils.lerp(cam.current.position.z, targetZPosition, 0.01) + cam.current.updateMatrixWorld() body.current.applyImpulse( { x: forwardDirection.x * currentSpeed, y: 0 + jumpForce.current, - z: forwardDirection.z * currentSpeed, + z: forwardDirection.z * currentSpeed }, true - ); + ) // Update the kart's rotation based on the steering angle - setSteeringAngleWheels(steeringAngle * 25); - }); + setSteeringAngleWheels(steeringAngle * 25) + }) return ( { - isOnFloor.current = true; + isOnFloor.current = true }} > - + @@ -298,13 +250,25 @@ export const PlayerController = () => { currentSpeed={currentSpeed} steeringAngleWheels={steeringAngleWheels} /> - - + /> */} + + + + { color={turboColor} distance={1} /> - + { opacity={1} /> - - + + + {/* */} @@ -343,5 +317,5 @@ export const PlayerController = () => { /> - ); -}; + ) +}