feat:(game) added a small landing to allow control choice, switch between M&K, K or Gamepad
parent
3ef3ec7d25
commit
c22c9e9012
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 548 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
13
src/App.jsx
13
src/App.jsx
|
@ -5,6 +5,7 @@ import { Physics } from '@react-three/rapier'
|
|||
import { KeyboardControls, Loader, OrbitControls, Preload, Stats } from '@react-three/drei'
|
||||
import { insertCoin, onPlayerJoin } from 'playroomkit'
|
||||
import { useStore } from "./components/store";
|
||||
import * as THREE from "three";
|
||||
|
||||
export const Controls = {
|
||||
up: 'up',
|
||||
|
@ -37,13 +38,13 @@ function App() {
|
|||
|
||||
onPlayerJoin((state) => {
|
||||
actions.addPlayer(state);
|
||||
console.log('player joined', state);
|
||||
|
||||
actions.setId(state.id);
|
||||
console.log(state)
|
||||
|
||||
|
||||
state.onQuit(() => {
|
||||
actions.removePlayer(state);
|
||||
console.log('player quit', state);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -59,7 +60,11 @@ function App() {
|
|||
shadows
|
||||
dpr={1}
|
||||
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}>
|
||||
<Preload all />
|
||||
<Physics
|
||||
|
|
36
src/HUD.jsx
36
src/HUD.jsx
|
@ -4,7 +4,7 @@ import { useStore } from "./components/store";
|
|||
export const HUD = () => {
|
||||
const wheel = useRef();
|
||||
const [image, setImage] = useState("");
|
||||
const {item} = useStore();
|
||||
const { item, gameStarted } = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e) => {
|
||||
|
@ -44,31 +44,19 @@ export const HUD = () => {
|
|||
|
||||
return (
|
||||
<div className="overlay">
|
||||
<div className="logo">
|
||||
<img src="./logo.png" alt="logo" />
|
||||
</div>
|
||||
<div className="item">
|
||||
<div className="borderOut">
|
||||
<div className="borderIn">
|
||||
<div className="background">
|
||||
{image && <img src={image} alt="item" width={90} />}
|
||||
{gameStarted && (
|
||||
<>
|
||||
<div className="item">
|
||||
<div className="borderOut">
|
||||
<div className="borderIn">
|
||||
<div className="background">
|
||||
{image && <img src={image} alt="item" width={90} />}
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
|
||||
);
|
||||
};
|
|
@ -7,6 +7,8 @@ import {
|
|||
} from "@react-three/drei";
|
||||
import { Ground } from "./Ground";
|
||||
import { PlayerController } from "./PlayerController";
|
||||
import { PlayerControllerGamepad } from "./PlayerControllerGamepad";
|
||||
import { PlayerControllerKeyboard } from "./PlayerControllerKeyboard";
|
||||
import { Paris } from "./models/tracks/Tour_paris_promenade";
|
||||
import {
|
||||
EffectComposer,
|
||||
|
@ -34,24 +36,40 @@ import {
|
|||
useMultiplayerState,
|
||||
} from "playroomkit";
|
||||
import { PlayerDummies } from "./PlayerDummies";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
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 = () => {
|
||||
const onCollide = (event) => {
|
||||
console.log(event);
|
||||
};
|
||||
const { bananas, shells, players, id, actions } = useStore();
|
||||
const onCollide = (event) => {};
|
||||
const { gameStarted, bananas, shells, players, id, actions, controls } =
|
||||
useStore();
|
||||
const [networkBananas, setNetworkBananas] = useMultiplayerState(
|
||||
"bananas",
|
||||
[]
|
||||
);
|
||||
|
||||
const { points, loading, error } = useCurvedPathPoints("./CurvedPath.json");
|
||||
|
||||
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 cam = useRef();
|
||||
const lookAtTarget = useRef();
|
||||
// useEffect(() => {
|
||||
// setNetworkBananas(bananas);
|
||||
// }, [bananas]);
|
||||
|
@ -59,34 +77,73 @@ export const Experience = () => {
|
|||
// useEffect(() => {
|
||||
// setNetworkShells(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 (
|
||||
<>
|
||||
{players.map((player) => (
|
||||
<PlayerController
|
||||
key={player.id}
|
||||
player={player}
|
||||
userPlayer={player.id === myPlayer()?.id}
|
||||
setNetworkBananas={setNetworkBananas}
|
||||
setNetworkShells={setNetworkShells}
|
||||
networkBananas={networkBananas}
|
||||
networkShells={networkShells}
|
||||
/>
|
||||
))}
|
||||
|
||||
{players.map((player) => (
|
||||
<PlayerDummies
|
||||
key={player.id}
|
||||
player={player}
|
||||
userPlayer={player.id === myPlayer()?.id}
|
||||
/>
|
||||
))}
|
||||
{gameStarted &&
|
||||
players.map((player) => {
|
||||
const ControllerComponent =
|
||||
controls === "keyboard"
|
||||
? PlayerControllerKeyboard
|
||||
: controls === "gamepad"
|
||||
? PlayerControllerGamepad
|
||||
: PlayerController;
|
||||
|
||||
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]} />
|
||||
{/* <Banana onCollide={onCollide} position={[-10, 1.8, -119]} /> */}
|
||||
{/* <Shell position={[-20, 2, -119]} /> */}
|
||||
|
||||
<ItemBox position={[-20, 2.5, -119]} />
|
||||
<Coin position={[-30, 2, -119]} />
|
||||
|
||||
|
@ -100,7 +157,6 @@ export const Experience = () => {
|
|||
setNetworkBananas={setNetworkBananas}
|
||||
networkBananas={networkBananas}
|
||||
id={banana.id}
|
||||
// rotation={banana.rotation}
|
||||
/>
|
||||
))}
|
||||
{networkShells.map((shell) => (
|
||||
|
@ -111,11 +167,10 @@ export const Experience = () => {
|
|||
rotation={shell.rotation}
|
||||
setNetworkShells={setNetworkShells}
|
||||
networkShells={networkShells}
|
||||
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* <directionalLight
|
||||
<directionalLight
|
||||
position={[10, 50, -30]}
|
||||
intensity={1}
|
||||
shadow-bias={-0.0001}
|
||||
|
@ -125,7 +180,7 @@ export const Experience = () => {
|
|||
shadow-camera-top={300}
|
||||
shadow-camera-bottom={-300}
|
||||
castShadow
|
||||
/> */}
|
||||
/>
|
||||
|
||||
<EffectComposer
|
||||
multisampling={0}
|
||||
|
|
|
@ -90,7 +90,7 @@ export const PlayerController = ({
|
|||
const effectiveBoost = useRef(0);
|
||||
const text = useRef();
|
||||
|
||||
const { actions, shouldSlowDown, item, bananas, coins, id } = useStore();
|
||||
const { actions, shouldSlowDown, item, bananas, coins, id, controls } = useStore();
|
||||
const slowDownDuration = useRef(1500);
|
||||
|
||||
useFrame(({ pointer, clock }, delta) => {
|
||||
|
@ -576,6 +576,7 @@ export const PlayerController = ({
|
|||
position={[0, 2, 8]}
|
||||
fov={50}
|
||||
ref={cam}
|
||||
far={5000}
|
||||
/>
|
||||
<PositionalAudio
|
||||
ref={engineSound}
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -102,7 +102,7 @@ export const PlayerDummies = ( { player, userPlayer }) => {
|
|||
setShouldLaunch(player.getState("shouldLaunch"));
|
||||
setTurboColor(player.getState("turboColor"));
|
||||
setScale(player.getState("scale"));
|
||||
console.log(player.state.profile.name)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ export const items = [
|
|||
]
|
||||
|
||||
export const useStore = create((set, get) => ({
|
||||
gameStarted: false,
|
||||
controls: "",
|
||||
particles1: [],
|
||||
particles2: [],
|
||||
bodyPosition: [0, 0, 0],
|
||||
|
@ -139,7 +141,14 @@ export const useStore = create((set, get) => ({
|
|||
},
|
||||
setId : (id) => {
|
||||
set({id});
|
||||
},
|
||||
setGameStarted: (gameStarted) => {
|
||||
set({ gameStarted });
|
||||
},
|
||||
setControls: (controls) => {
|
||||
set({ controls });
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}));
|
||||
|
|
|
@ -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 };
|
||||
};
|
146
src/index.css
146
src/index.css
|
@ -30,7 +30,7 @@ body::-webkit-scrollbar {
|
|||
}
|
||||
}
|
||||
|
||||
.overlay {
|
||||
/* .overlay {
|
||||
position: absolute;
|
||||
top: 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);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
.item {
|
||||
width: 152px;
|
||||
height: 152px;
|
||||
|
@ -121,3 +121,145 @@ body::-webkit-scrollbar {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,13 @@ import ReactDOM from 'react-dom/client'
|
|||
import App from './App'
|
||||
import './index.css'
|
||||
import { HUD } from './HUD'
|
||||
import { Landing } from './Landing'
|
||||
import { useStore } from './components/store'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
<HUD />
|
||||
<Landing />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue