feat:(game) added a small landing to allow control choice, switch between M&K, K or Gamepad

pull/8/head
Alex 2024-02-13 14:58:41 +01:00
parent 3ef3ec7d25
commit c22c9e9012
17 changed files with 8134 additions and 66 deletions

2885
public/CurvedPath.json Normal file

File diff suppressed because it is too large Load Diff

3605
public/SPLINE.json Normal file

File diff suppressed because it is too large Load Diff

BIN
public/images/gamepad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

BIN
public/images/keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -5,6 +5,7 @@ import { Physics } from '@react-three/rapier'
import { KeyboardControls, Loader, OrbitControls, Preload, Stats } from '@react-three/drei' import { KeyboardControls, Loader, OrbitControls, Preload, Stats } from '@react-three/drei'
import { insertCoin, onPlayerJoin } from 'playroomkit' import { insertCoin, onPlayerJoin } from 'playroomkit'
import { useStore } from "./components/store"; import { useStore } from "./components/store";
import * as THREE from "three";
export const Controls = { export const Controls = {
up: 'up', up: 'up',
@ -37,13 +38,13 @@ function App() {
onPlayerJoin((state) => { onPlayerJoin((state) => {
actions.addPlayer(state); actions.addPlayer(state);
console.log('player joined', state);
actions.setId(state.id); actions.setId(state.id);
console.log(state)
state.onQuit(() => { state.onQuit(() => {
actions.removePlayer(state); actions.removePlayer(state);
console.log('player quit', state);
}); });
}); });
} }
@ -59,7 +60,11 @@ function App() {
shadows shadows
dpr={1} dpr={1}
gl={{ antialias: false, stencil: false, powerPreference: 'high-performance' }} gl={{ antialias: false, stencil: false, powerPreference: 'high-performance' }}
> mode="concurrent"
onCreated={({ gl, camera }) => {
gl.toneMapping = THREE.AgXToneMapping
// gl.setClearColor(new THREE.Color('#020209'))
}}>
<Suspense fallback={null}> <Suspense fallback={null}>
<Preload all /> <Preload all />
<Physics <Physics

View File

@ -4,7 +4,7 @@ import { useStore } from "./components/store";
export const HUD = () => { export const HUD = () => {
const wheel = useRef(); const wheel = useRef();
const [image, setImage] = useState(""); const [image, setImage] = useState("");
const {item} = useStore(); const { item, gameStarted } = useStore();
useEffect(() => { useEffect(() => {
const handleMouseMove = (e) => { const handleMouseMove = (e) => {
@ -44,31 +44,19 @@ export const HUD = () => {
return ( return (
<div className="overlay"> <div className="overlay">
<div className="logo"> {gameStarted && (
<img src="./logo.png" alt="logo" /> <>
</div> <div className="item">
<div className="item"> <div className="borderOut">
<div className="borderOut"> <div className="borderIn">
<div className="borderIn"> <div className="background">
<div className="background"> {image && <img src={image} alt="item" width={90} />}
{image && <img src={image} alt="item" width={90} />} </div>
</div>
</div> </div>
</div> </div>
</div> </>
</div> )}
<div className="wheel">
<img
ref={wheel}
src="./steering_wheel.png"
alt="steering wheel"
className="steering-wheel"
style={{
position: "absolute",
pointerEvents: "none",
transformOrigin: "center",
}}
/>
</div>
</div> </div>
); );
}; };

98
src/Landing.jsx Normal file
View File

@ -0,0 +1,98 @@
import React, { useEffect, useRef, useState } from "react";
import { useStore } from "./components/store";
import gsap from "gsap";
export const Landing = () => {
const { gameStarted, actions } = useStore();
const logo = useRef();
const startButton = useRef();
const homeRef = useRef();
const [setupStatus, setSetupStatus] = useState(0);
const [controlStyle, setControlStyle] = useState("");
useEffect(() => {
const tl = gsap.timeline();
if (setupStatus === 0) {
if (logo.current && startButton.current) {
tl.from(logo.current, {
scale: 122,
opacity: 0,
duration: 0,
ease: "power4.out",
})
.to(logo.current, {
scale: 1,
opacity: 1,
duration: 1.5,
ease: "power4.out",
})
.to(startButton.current, {
opacity: 1,
duration: 3,
delay: 1,
ease: "power4.out",
});
}
}
}, [setupStatus]);
if (gameStarted) {
return null;
}
return (
<>
{setupStatus === 0 && (
<div className="home" ref={homeRef}>
<div className="logo">
<img ref={logo} src="./logo.png" alt="logo" />
</div>
<div className="start" ref={startButton}>
<button className="start-button" onClick={() => setSetupStatus(1)}>
PRESS ENTER TO START
</button>
</div>
</div>
)}
{setupStatus === 1 && (
<div className="home">
<div className="glassy">
<h1>CHOOSE YOUR CONTROL STYLE</h1>
<div className="articles">
<div className={controlStyle === "keyboard" ? "article selected" : "article"} onClick={() =>
setControlStyle("keyboard")}>
<h2>Keyboard</h2>
<img src="./images/keyboard.png" alt="keyboard" />
</div>
<div className={controlStyle === "gamepad" ? "article selected" : "article"} onClick={() =>
setControlStyle("gamepad")}>
<h2>Gamepad</h2>
<img src="./images/gamepad.png" alt="gamepad" />
</div>
<div className={controlStyle === "mouseKeyboard" ? "article selected" : "article"} onClick={() =>
setControlStyle("mouseKeyboard")}>
<h2>Mouse & Keybaord</h2>
<img src="./images/mousekeyboard.png" alt="mouse & keyboard" />
</div>
</div>
<div className={controlStyle != "" ? "submit" : "submit disabled"}>
<button
className={controlStyle != "" ? "submit-button" : "submit-button disabled"}
onClick={() => {
actions.setControls(controlStyle);
actions.setGameStarted(true);
}}
>
CONFIRM
</button>
</div>
</div>
</div>
)}
</>
);
};

View File

@ -7,6 +7,8 @@ import {
} from "@react-three/drei"; } from "@react-three/drei";
import { Ground } from "./Ground"; import { Ground } from "./Ground";
import { PlayerController } from "./PlayerController"; import { PlayerController } from "./PlayerController";
import { PlayerControllerGamepad } from "./PlayerControllerGamepad";
import { PlayerControllerKeyboard } from "./PlayerControllerKeyboard";
import { Paris } from "./models/tracks/Tour_paris_promenade"; import { Paris } from "./models/tracks/Tour_paris_promenade";
import { import {
EffectComposer, EffectComposer,
@ -34,24 +36,40 @@ import {
useMultiplayerState, useMultiplayerState,
} from "playroomkit"; } from "playroomkit";
import { PlayerDummies } from "./PlayerDummies"; import { PlayerDummies } from "./PlayerDummies";
import { useEffect, useState } from "react"; import { useEffect, useState, useRef } from "react";
import { useFrame, useLoader } from "@react-three/fiber"; import { useFrame, useLoader } from "@react-three/fiber";
import { LUTPass, LUTCubeLoader } from 'three-stdlib' import { LUTPass, LUTCubeLoader } from "three-stdlib";
import { useCurvedPathPoints } from "./useCurvedPath";
export const Experience = () => { export const Experience = () => {
const onCollide = (event) => { const onCollide = (event) => {};
console.log(event); const { gameStarted, bananas, shells, players, id, actions, controls } =
}; useStore();
const { bananas, shells, players, id, actions } = useStore();
const [networkBananas, setNetworkBananas] = useMultiplayerState( const [networkBananas, setNetworkBananas] = useMultiplayerState(
"bananas", "bananas",
[] []
); );
const { points, loading, error } = useCurvedPathPoints("./CurvedPath.json");
const [networkShells, setNetworkShells] = useMultiplayerState("shells", []); const [networkShells, setNetworkShells] = useMultiplayerState("shells", []);
const [pointest, setPointest] = useState([]);
const [currentPoint, setCurrentPoint] = useState(0);
useEffect(() => {
if (points) {
//This is adjusted to Paris scale
const scaledPoints = points.map((point) => ({
x: point.x * 50,
y: point.y * 50,
z: point.z * 50,
}));
setPointest(scaledPoints.reverse());
}
}, [points]);
const testing = getState("bananas"); const testing = getState("bananas");
const cam = useRef();
const lookAtTarget = useRef();
// useEffect(() => { // useEffect(() => {
// setNetworkBananas(bananas); // setNetworkBananas(bananas);
// }, [bananas]); // }, [bananas]);
@ -59,34 +77,73 @@ export const Experience = () => {
// useEffect(() => { // useEffect(() => {
// setNetworkShells(shells); // setNetworkShells(shells);
// }, [shells]); // }, [shells]);
const speedFactor = 5;
const { texture } = useLoader(LUTCubeLoader, "./cubicle-99.CUBE");
useFrame((state, delta) => {
if (!gameStarted) {
const camera = cam.current;
const {texture}= useLoader(LUTCubeLoader, "./cubicle-99.CUBE"); if (currentPoint < pointest.length - 1) {
camera.position.lerp(pointest[currentPoint], delta * speedFactor);
lookAtTarget.current.position.lerp(
pointest[currentPoint + 1],
delta * speedFactor
);
camera.lookAt(lookAtTarget.current.position);
if (camera.position.distanceTo(pointest[currentPoint]) < 5) {
setCurrentPoint(currentPoint + 1);
}
} else {
setCurrentPoint(0);
}
}
});
return ( return (
<> <>
{players.map((player) => ( {gameStarted &&
<PlayerController players.map((player) => {
key={player.id} const ControllerComponent =
player={player} controls === "keyboard"
userPlayer={player.id === myPlayer()?.id} ? PlayerControllerKeyboard
setNetworkBananas={setNetworkBananas} : controls === "gamepad"
setNetworkShells={setNetworkShells} ? PlayerControllerGamepad
networkBananas={networkBananas} : PlayerController;
networkShells={networkShells}
/>
))}
{players.map((player) => (
<PlayerDummies
key={player.id}
player={player}
userPlayer={player.id === myPlayer()?.id}
/>
))}
return (
<ControllerComponent
key={player.id}
player={player}
userPlayer={player.id === myPlayer()?.id}
setNetworkBananas={setNetworkBananas}
setNetworkShells={setNetworkShells}
networkBananas={networkBananas}
networkShells={networkShells}
/>
);
})}
{gameStarted &&
players.map((player) => (
<PlayerDummies
key={player.id}
player={player}
userPlayer={player.id === myPlayer()?.id}
/>
))}
{!gameStarted && (
<>
<mesh ref={lookAtTarget}></mesh>
<PerspectiveCamera
ref={cam}
makeDefault
position={[0, 2, 0]}
far={5000}
/>
</>
)}
<Paris position={[0, 0, 0]} /> <Paris position={[0, 0, 0]} />
{/* <Banana onCollide={onCollide} position={[-10, 1.8, -119]} /> */}
{/* <Shell position={[-20, 2, -119]} /> */}
<ItemBox position={[-20, 2.5, -119]} /> <ItemBox position={[-20, 2.5, -119]} />
<Coin position={[-30, 2, -119]} /> <Coin position={[-30, 2, -119]} />
@ -100,7 +157,6 @@ export const Experience = () => {
setNetworkBananas={setNetworkBananas} setNetworkBananas={setNetworkBananas}
networkBananas={networkBananas} networkBananas={networkBananas}
id={banana.id} id={banana.id}
// rotation={banana.rotation}
/> />
))} ))}
{networkShells.map((shell) => ( {networkShells.map((shell) => (
@ -111,11 +167,10 @@ export const Experience = () => {
rotation={shell.rotation} rotation={shell.rotation}
setNetworkShells={setNetworkShells} setNetworkShells={setNetworkShells}
networkShells={networkShells} networkShells={networkShells}
/> />
))} ))}
{/* <directionalLight <directionalLight
position={[10, 50, -30]} position={[10, 50, -30]}
intensity={1} intensity={1}
shadow-bias={-0.0001} shadow-bias={-0.0001}
@ -125,7 +180,7 @@ export const Experience = () => {
shadow-camera-top={300} shadow-camera-top={300}
shadow-camera-bottom={-300} shadow-camera-bottom={-300}
castShadow castShadow
/> */} />
<EffectComposer <EffectComposer
multisampling={0} multisampling={0}

View File

@ -90,7 +90,7 @@ export const PlayerController = ({
const effectiveBoost = useRef(0); const effectiveBoost = useRef(0);
const text = useRef(); const text = useRef();
const { actions, shouldSlowDown, item, bananas, coins, id } = useStore(); const { actions, shouldSlowDown, item, bananas, coins, id, controls } = useStore();
const slowDownDuration = useRef(1500); const slowDownDuration = useRef(1500);
useFrame(({ pointer, clock }, delta) => { useFrame(({ pointer, clock }, delta) => {
@ -576,6 +576,7 @@ export const PlayerController = ({
position={[0, 2, 8]} position={[0, 2, 8]}
fov={50} fov={50}
ref={cam} ref={cam}
far={5000}
/> />
<PositionalAudio <PositionalAudio
ref={engineSound} ref={engineSound}

View File

@ -0,0 +1,623 @@
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 PlayerControllerGamepad = ({
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 } = useStore();
const slowDownDuration = useRef(1500);
const { buttonA, buttonB, RB, LB, joystick, select} = useGamepad();
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 * -joystick[0];
targetXPosition = -camMaxOffset * -joystick[0];
} else if (driftLeft.current && !driftRight.current) {
steeringAngle = currentSteeringSpeed * -(joystick[0] - 1);
targetXPosition = -camMaxOffset * -joystick[0];
} else if (driftRight.current && !driftLeft.current) {
steeringAngle = currentSteeringSpeed * -(joystick[0] + 1);
targetXPosition = -camMaxOffset * -joystick[0];
}
// ACCELERATING
const shouldSlow = actions.getShouldSlowDown();
if (buttonA && currentSpeed < maxSpeed) {
// Accelerate the kart within the maximum speed limit
setCurrentSpeed(
Math.min(currentSpeed + acceleration * delta * 144, maxSpeed)
);
} else if (
buttonA &&
currentSpeed > maxSpeed &&
effectiveBoost.current > 0
) {
setCurrentSpeed(
Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)
);
}
if (buttonA) {
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);
}
}
// REVERSING
if (buttonB && currentSpeed < -maxSpeed) {
setCurrentSpeed(
Math.max(currentSpeed - acceleration * delta * 144, -maxSpeed)
);
}
// DECELERATING
else if (!buttonA && !buttonB) {
if (currentSteeringSpeed > 0) {
setCurrentSteeringSpeed(
Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)
);
} else if (currentSteeringSpeed < 0) {
setCurrentSteeringSpeed(
Math.min(currentSteeringSpeed + 0.00005 * delta * 144, 0)
);
}
setCurrentSpeed(Math.max(currentSpeed - decceleration * delta * 144, 0));
}
// 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 (RB && 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 (!RB) {
jumpIsHeld.current = false;
driftDirection.current = 0;
driftForce.current = 0;
driftLeft.current = false;
driftRight.current = false;
}
// DRIFTING
if (
jumpIsHeld.current &&
currentSteeringSpeed > 0 &&
joystick[0] < -0.1 &&
!driftRight.current
) {
driftLeft.current = true;
}
if (
jumpIsHeld.current &&
currentSteeringSpeed > 0 &&
joystick[0] > 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
// MISC
if (select) {
body.current.setTranslation({ x: 8, y: 2, z: -119 });
body.current.setLinvel({ x: 0, y: 0, z: 0 });
body.current.setAngvel({ x: 0, y: 0, z: 0 });
setCurrentSpeed(0);
setCurrentSteeringSpeed(0);
setIsBoosting(false);
effectiveBoost.current = 0;
setIsOnGround(false);
jumpForce.current = 0;
driftDirection.current = 0;
kart.current.rotation.y = Math.PI / 2;
}
// ITEMS
if (LB && 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 (LB && 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 (LB && 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 ? (
<group>
<RigidBody
ref={body}
colliders={false}
position={[8, 60, -119]}
centerOfMass={[0, -1, 0]}
mass={3}
ccd
name="player"
type={player.id === id ? "dynamic" : "kinematic"}
>
<BallCollider
args={[0.5]}
mass={3}
onCollisionEnter={({ other }) => {
isOnFloor.current = true;
setIsOnGround(true);
}}
onCollisionExit={({ other }) => {
isOnFloor.current = false;
setIsOnGround(false);
}}
/>
</RigidBody>
<group ref={kart} rotation={[0, Math.PI / 2, 0]}>
<group ref={mario}>
<Mario
currentSpeed={currentSpeed}
steeringAngleWheels={steeringAngleWheels}
isBoosting={isBoosting}
shouldLaunch={shouldLaunch}
/>
<CoinParticles coins={coins} />
<ItemParticles item={item} />
<mesh position={[0.6, 0.05, 0.5]} scale={scale}>
<sphereGeometry args={[0.05, 16, 16]} />
<meshStandardMaterial
emissive={turboColor}
toneMapped={false}
emissiveIntensity={100}
transparent
opacity={0.4}
/>
</mesh>
<mesh position={[0.6, 0.05, 0.5]} scale={scale * 10}>
<sphereGeometry args={[0.05, 16, 16]} />
<FakeGlowMaterial
falloff={3}
glowInternalRadius={1}
glowColor={turboColor}
glowSharpness={1}
/>
</mesh>
<mesh position={[-0.6, 0.05, 0.5]} scale={scale}>
<sphereGeometry args={[0.05, 16, 16]} />
<meshStandardMaterial
emissive={turboColor}
toneMapped={false}
emissiveIntensity={100}
transparent
opacity={0.4}
/>
</mesh>
<mesh position={[-0.6, 0.05, 0.5]} scale={scale * 10}>
<sphereGeometry args={[0.05, 16, 16]} />
<FakeGlowMaterial
falloff={3}
glowInternalRadius={1}
glowColor={turboColor}
glowSharpness={1}
/>
</mesh>
{/* <FlameParticles isBoosting={isBoosting} /> */}
<DriftParticlesLeft turboColor={turboColor} scale={scale} />
<DriftParticlesRight turboColor={turboColor} scale={scale} />
<PointParticle
position={[-0.6, 0.05, 0.5]}
png="./particles/circle.png"
turboColor={turboColor}
/>
<PointParticle
position={[0.6, 0.05, 0.5]}
png="./particles/circle.png"
turboColor={turboColor}
/>
<PointParticle
position={[-0.6, 0.05, 0.5]}
png="./particles/star.png"
turboColor={turboColor}
/>
<PointParticle
position={[0.6, 0.05, 0.5]}
png="./particles/star.png"
turboColor={turboColor}
/>
<HitParticles shouldLaunch={shouldLaunch} />
</group>
{/* <ContactShadows frames={1} /> */}
<PerspectiveCamera
makeDefault
position={[0, 2, 8]}
fov={50}
ref={cam}
far={5000}
/>
<PositionalAudio
ref={engineSound}
url="./sounds/engine.wav"
autoplay
loop
distance={1000}
/>
<PositionalAudio
ref={driftSound}
url="./sounds/drifting.mp3"
loop
distance={1000}
/>
<PositionalAudio
ref={driftTwoSound}
url="./sounds/driftingTwo.mp3"
loop
distance={1000}
/>
<PositionalAudio
ref={driftOrangeSound}
url="./sounds/driftOrange.wav"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={driftBlueSound}
url="./sounds/driftBlue.wav"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={driftPurpleSound}
url="./sounds/driftPurple.wav"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={jumpSound}
url="./sounds/jump.mp3"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={landingSound}
url="./sounds/landing.wav"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={turboSound}
url="./sounds/turbo.wav"
loop={false}
distance={1000}
/>
</group>
</group>
) : null;
};

View File

@ -0,0 +1,628 @@
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";
extend(geometry);
export const PlayerControllerKeyboard = ({
player,
userPlayer,
setNetworkBananas,
setNetworkShells,
networkBananas,
networkShells,
}) => {
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 shootPressed = useKeyboardControls((state) => state[Controls.shoot]);
const resetPressed = useKeyboardControls((state) => state[Controls.reset]);
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 } = 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)
);
if (leftPressed && currentSpeed > 0) {
steeringAngle = currentSteeringSpeed;
targetXPosition = -camMaxOffset;
} else if (rightPressed && currentSpeed > 0) {
steeringAngle = -currentSteeringSpeed;
targetXPosition = camMaxOffset;
} else {
steeringAngle = 0;
targetXPosition = 0;
1;
}
// ACCELERATING
const shouldSlow = actions.getShouldSlowDown();
if (upPressed && currentSpeed < maxSpeed) {
// Accelerate the kart within the maximum speed limit
setCurrentSpeed(
Math.min(currentSpeed + acceleration * delta * 144, maxSpeed)
);
} else if (
upPressed &&
currentSpeed > maxSpeed &&
effectiveBoost.current > 0
) {
setCurrentSpeed(
Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)
);
}
if (upPressed) {
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);
}
}
// REVERSING
if (downPressed && currentSpeed < -maxSpeed) {
setCurrentSpeed(
Math.max(currentSpeed - acceleration * delta * 144, -maxSpeed)
);
}
// DECELERATING
else if (!upPressed && !downPressed) {
if (currentSteeringSpeed > 0) {
setCurrentSteeringSpeed(
Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)
);
} else if (currentSteeringSpeed < 0) {
setCurrentSteeringSpeed(
Math.min(currentSteeringSpeed + 0.00005 * delta * 144, 0)
);
}
setCurrentSpeed(Math.max(currentSpeed - decceleration * delta * 144, 0));
}
// 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 (jumpPressed && 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 (!jumpPressed) {
jumpIsHeld.current = false;
driftDirection.current = 0;
driftForce.current = 0;
driftLeft.current = false;
driftRight.current = false;
}
// DRIFTING
if (
jumpIsHeld.current &&
currentSteeringSpeed > 0 &&
leftPressed &&
!driftRight.current
) {
driftLeft.current = true;
}
if (
jumpIsHeld.current &&
currentSteeringSpeed > 0 &&
rightPressed > 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
// MISC
if (resetPressed) {
body.current.setTranslation({ x: 8, y: 2, z: -119 });
body.current.setLinvel({ x: 0, y: 0, z: 0 });
body.current.setAngvel({ x: 0, y: 0, z: 0 });
setCurrentSpeed(0);
setCurrentSteeringSpeed(0);
setIsBoosting(false);
effectiveBoost.current = 0;
setIsOnGround(false);
jumpForce.current = 0;
driftDirection.current = 0;
kart.current.rotation.y = Math.PI / 2;
}
// ITEMS
if (shootPressed && 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 (shootPressed && 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 (shootPressed && 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 ? (
<group>
<RigidBody
ref={body}
colliders={false}
position={[8, 60, -119]}
centerOfMass={[0, -1, 0]}
mass={3}
ccd
name="player"
type={player.id === id ? "dynamic" : "kinematic"}
>
<BallCollider
args={[0.5]}
mass={3}
onCollisionEnter={({ other }) => {
isOnFloor.current = true;
setIsOnGround(true);
}}
onCollisionExit={({ other }) => {
isOnFloor.current = false;
setIsOnGround(false);
}}
/>
</RigidBody>
<group ref={kart} rotation={[0, Math.PI / 2, 0]}>
<group ref={mario}>
<Mario
currentSpeed={currentSpeed}
steeringAngleWheels={steeringAngleWheels}
isBoosting={isBoosting}
shouldLaunch={shouldLaunch}
/>
<CoinParticles coins={coins} />
<ItemParticles item={item} />
<mesh position={[0.6, 0.05, 0.5]} scale={scale}>
<sphereGeometry args={[0.05, 16, 16]} />
<meshStandardMaterial
emissive={turboColor}
toneMapped={false}
emissiveIntensity={100}
transparent
opacity={0.4}
/>
</mesh>
<mesh position={[0.6, 0.05, 0.5]} scale={scale * 10}>
<sphereGeometry args={[0.05, 16, 16]} />
<FakeGlowMaterial
falloff={3}
glowInternalRadius={1}
glowColor={turboColor}
glowSharpness={1}
/>
</mesh>
<mesh position={[-0.6, 0.05, 0.5]} scale={scale}>
<sphereGeometry args={[0.05, 16, 16]} />
<meshStandardMaterial
emissive={turboColor}
toneMapped={false}
emissiveIntensity={100}
transparent
opacity={0.4}
/>
</mesh>
<mesh position={[-0.6, 0.05, 0.5]} scale={scale * 10}>
<sphereGeometry args={[0.05, 16, 16]} />
<FakeGlowMaterial
falloff={3}
glowInternalRadius={1}
glowColor={turboColor}
glowSharpness={1}
/>
</mesh>
{/* <FlameParticles isBoosting={isBoosting} /> */}
<DriftParticlesLeft turboColor={turboColor} scale={scale} />
<DriftParticlesRight turboColor={turboColor} scale={scale} />
<PointParticle
position={[-0.6, 0.05, 0.5]}
png="./particles/circle.png"
turboColor={turboColor}
/>
<PointParticle
position={[0.6, 0.05, 0.5]}
png="./particles/circle.png"
turboColor={turboColor}
/>
<PointParticle
position={[-0.6, 0.05, 0.5]}
png="./particles/star.png"
turboColor={turboColor}
/>
<PointParticle
position={[0.6, 0.05, 0.5]}
png="./particles/star.png"
turboColor={turboColor}
/>
<HitParticles shouldLaunch={shouldLaunch} />
</group>
{/* <ContactShadows frames={1} /> */}
<PerspectiveCamera
makeDefault
position={[0, 2, 8]}
fov={50}
ref={cam}
far={5000}
/>
<PositionalAudio
ref={engineSound}
url="./sounds/engine.wav"
autoplay
loop
distance={1000}
/>
<PositionalAudio
ref={driftSound}
url="./sounds/drifting.mp3"
loop
distance={1000}
/>
<PositionalAudio
ref={driftTwoSound}
url="./sounds/driftingTwo.mp3"
loop
distance={1000}
/>
<PositionalAudio
ref={driftOrangeSound}
url="./sounds/driftOrange.wav"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={driftBlueSound}
url="./sounds/driftBlue.wav"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={driftPurpleSound}
url="./sounds/driftPurple.wav"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={jumpSound}
url="./sounds/jump.mp3"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={landingSound}
url="./sounds/landing.wav"
loop={false}
distance={1000}
/>
<PositionalAudio
ref={turboSound}
url="./sounds/turbo.wav"
loop={false}
distance={1000}
/>
</group>
</group>
) : null;
};

View File

@ -102,7 +102,7 @@ export const PlayerDummies = ( { player, userPlayer }) => {
setShouldLaunch(player.getState("shouldLaunch")); setShouldLaunch(player.getState("shouldLaunch"));
setTurboColor(player.getState("turboColor")); setTurboColor(player.getState("turboColor"));
setScale(player.getState("scale")); setScale(player.getState("scale"));
console.log(player.state.profile.name)

View File

@ -13,6 +13,8 @@ export const items = [
] ]
export const useStore = create((set, get) => ({ export const useStore = create((set, get) => ({
gameStarted: false,
controls: "",
particles1: [], particles1: [],
particles2: [], particles2: [],
bodyPosition: [0, 0, 0], bodyPosition: [0, 0, 0],
@ -139,7 +141,14 @@ export const useStore = create((set, get) => ({
}, },
setId : (id) => { setId : (id) => {
set({id}); set({id});
},
setGameStarted: (gameStarted) => {
set({ gameStarted });
},
setControls: (controls) => {
set({ controls });
} }
}, },
})); }));

View File

@ -0,0 +1,26 @@
import React, { useState, useEffect } from 'react';
import * as THREE from 'three';
export const useCurvedPathPoints = (jsonPath) => {
const [points, setPoints] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const loadPointsFromJson = async () => {
try {
const response = await fetch(jsonPath);
const data = await response.json();
setPoints(data.points.map(point => new THREE.Vector3(point.x, point.y, point.z)));
setLoading(false);
} catch (err) {
setError(err);
setLoading(false);
}
};
loadPointsFromJson();
}, [jsonPath]);
return { points, loading, error };
};

View File

@ -30,7 +30,7 @@ body::-webkit-scrollbar {
} }
} }
.overlay { /* .overlay {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -51,7 +51,7 @@ body::-webkit-scrollbar {
animation: bounce 0.4s infinite cubic-bezier(0.71, 1.94, 0.5, 0.61); animation: bounce 0.4s infinite cubic-bezier(0.71, 1.94, 0.5, 0.61);
} }
} }
*/
.item { .item {
width: 152px; width: 152px;
height: 152px; height: 152px;
@ -120,4 +120,146 @@ body::-webkit-scrollbar {
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
pointer-events: none; pointer-events: none;
} }
.home {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: grid;
place-content: center;
gap: 400px;
z-index: 2;
font-family: "Hanken Grotesk";
.logo {
img {
width: 1200px;
}
}
.glassy {
width: 1280px;
height: 720px;
background: rgba(95, 95, 95, 0.25);
/* box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 ); */
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.18);
color: white;
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
padding: 40px;
.articles {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 80px;
gap: 40px;
.article {
background: rgba(216, 216, 216, 0.25);
width: 300px;
height: 300px;
border-radius: 6px;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
padding: 20px;
transition: all 0.2s ease 0s;
cursor: pointer;
&:hover {
background: rgba(216, 216, 216, 0.5);
}
img {
width: 200px;
filter: drop-shadow(5px 5px 5px #0000008f);
}
}
.article.selected {
background: rgba(216, 216, 216, 0.7);
box-shadow: 0 0 20px 0 rgb(255, 255, 255);
}
}
.submit {
font-weight: 900;
color: rgba(255, 255, 255, 0.795);
font-size: 27px;
transition: all 0.2s ease 0s;
cursor: pointer;
&:hover {
text-shadow: 0 0 40px rgba(255, 255, 255, 1);
color: white;
opacity: 1;
animation: none;
}
button {
all: unset;
display: flex;
justify-content: center;
align-items: center;
display: inline-block;
}
}
}
.start {
opacity: 0;
display: flex;
justify-content: center;
align-items: center;
font-weight: 900;
color: rgba(255, 255, 255, 0.795);
font-size: 60px;
text-shadow: 0 0 10px rgba(0, 0, 0, 1);
background: linear-gradient(
to right,
rgba(54, 54, 54, 0),
rgb(54, 54, 54),
rgba(54, 54, 54, 0)
);
animation: blinking 2s infinite;
transition: all 0.2s ease 0s;
cursor: pointer;
&:hover {
text-shadow: 0 0 40px rgba(255, 255, 255, 1);
color: white;
opacity: 1;
animation: none;
}
button {
all: unset;
display: flex;
justify-content: center;
align-items: center;
display: inline-block;
}
}
}
.disabled{
pointer-events: none;
cursor: not-allowed;
opacity: 0.4;
}
@keyframes blinking {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -3,10 +3,13 @@ import ReactDOM from 'react-dom/client'
import App from './App' import App from './App'
import './index.css' import './index.css'
import { HUD } from './HUD' import { HUD } from './HUD'
import { Landing } from './Landing'
import { useStore } from './components/store'
ReactDOM.createRoot(document.getElementById('root')).render( ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
<HUD /> <HUD />
<Landing />
</React.StrictMode>, </React.StrictMode>,
) )