feat:(game) added mobile controls
parent
5cc900ab35
commit
6ed6c0ff27
|
@ -18,6 +18,7 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-gamepad": "^1.0.3",
|
"react-gamepad": "^1.0.3",
|
||||||
|
"react-joystick-component": "^6.2.1",
|
||||||
"three": "^0.160.1",
|
"three": "^0.160.1",
|
||||||
"three-mesh-bvh": "^0.7.0",
|
"three-mesh-bvh": "^0.7.0",
|
||||||
"zustand": "^4.5.0"
|
"zustand": "^4.5.0"
|
||||||
|
@ -2607,6 +2608,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-joystick-component": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-joystick-component/-/react-joystick-component-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-0G5Y5aX4hNuXB3xJCwz6Q+nYQOtC6kprNGKmZxmfoPvhepNYUiid0DbLEGZxmr/UKip3S/LUbcQUobtRCuB8IQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17.0.2",
|
||||||
|
"react-dom": ">=17.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-merge-refs": {
|
"node_modules/react-merge-refs": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-gamepad": "^1.0.3",
|
"react-gamepad": "^1.0.3",
|
||||||
|
"react-joystick-component": "^6.2.1",
|
||||||
"three": "^0.160.1",
|
"three": "^0.160.1",
|
||||||
"three-mesh-bvh": "^0.7.0",
|
"three-mesh-bvh": "^0.7.0",
|
||||||
"zustand": "^4.5.0"
|
"zustand": "^4.5.0"
|
||||||
|
|
|
@ -63,7 +63,6 @@ function App() {
|
||||||
mode="concurrent"
|
mode="concurrent"
|
||||||
onCreated={({ gl, camera }) => {
|
onCreated={({ gl, camera }) => {
|
||||||
gl.toneMapping = THREE.AgXToneMapping
|
gl.toneMapping = THREE.AgXToneMapping
|
||||||
// gl.setClearColor(new THREE.Color('#020209'))
|
|
||||||
}}>
|
}}>
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<Preload all />
|
<Preload all />
|
||||||
|
|
43
src/HUD.jsx
43
src/HUD.jsx
|
@ -1,10 +1,11 @@
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { useStore } from "./components/store";
|
import { useStore } from "./components/store";
|
||||||
|
import { Joystick } from "react-joystick-component";
|
||||||
|
|
||||||
export const HUD = () => {
|
export const HUD = () => {
|
||||||
const wheel = useRef();
|
const wheel = useRef();
|
||||||
const [image, setImage] = useState("");
|
const [image, setImage] = useState("");
|
||||||
const { item, gameStarted } = useStore();
|
const { item, gameStarted, actions } = useStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleMouseMove = (e) => {
|
const handleMouseMove = (e) => {
|
||||||
|
@ -26,6 +27,14 @@ export const HUD = () => {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleMove = (e) => {
|
||||||
|
actions.setJoystickX(e.x);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStop = () => {
|
||||||
|
actions.setJoystickX(0);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
switch (item) {
|
switch (item) {
|
||||||
case "banana":
|
case "banana":
|
||||||
|
@ -55,6 +64,38 @@ export const HUD = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="controls joystick">
|
||||||
|
<Joystick
|
||||||
|
size={100}
|
||||||
|
sticky={false}
|
||||||
|
baseColor="rgba(255, 255, 255, 0.5)"
|
||||||
|
stickColor="rgba(255, 255, 255, 0.5)"
|
||||||
|
move={handleMove}
|
||||||
|
stop={handleStop}
|
||||||
|
></Joystick>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="controls drift"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
actions.setDriftButton(true);
|
||||||
|
}}
|
||||||
|
onMouseUp={(e) => {
|
||||||
|
actions.setDriftButton(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
drift
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="controls itemButton"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
actions.setItemButton(true);
|
||||||
|
}}
|
||||||
|
onMouseUp={(e) => {
|
||||||
|
actions.setItemButton(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
item
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const Landing = () => {
|
||||||
const startButton = useRef();
|
const startButton = useRef();
|
||||||
const homeRef = useRef();
|
const homeRef = useRef();
|
||||||
const [setupStatus, setSetupStatus] = useState(0);
|
const [setupStatus, setSetupStatus] = useState(0);
|
||||||
const [controlStyle, setControlStyle] = useState("");
|
const [controlStyle, setControlStyle] = useState("touch");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tl = gsap.timeline();
|
const tl = gsap.timeline();
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { Ground } from "./Ground";
|
||||||
import { PlayerController } from "./PlayerController";
|
import { PlayerController } from "./PlayerController";
|
||||||
import { PlayerControllerGamepad } from "./PlayerControllerGamepad";
|
import { PlayerControllerGamepad } from "./PlayerControllerGamepad";
|
||||||
import { PlayerControllerKeyboard } from "./PlayerControllerKeyboard";
|
import { PlayerControllerKeyboard } from "./PlayerControllerKeyboard";
|
||||||
|
import { PlayerControllerTouch } from "./PlayerControllerTouch";
|
||||||
import { Paris } from "./models/tracks/Tour_paris_promenade";
|
import { Paris } from "./models/tracks/Tour_paris_promenade";
|
||||||
import {
|
import {
|
||||||
EffectComposer,
|
EffectComposer,
|
||||||
|
@ -109,6 +110,8 @@ export const Experience = () => {
|
||||||
? PlayerControllerKeyboard
|
? PlayerControllerKeyboard
|
||||||
: controls === "gamepad"
|
: controls === "gamepad"
|
||||||
? PlayerControllerGamepad
|
? PlayerControllerGamepad
|
||||||
|
: controls === "touch"
|
||||||
|
? PlayerControllerTouch
|
||||||
: PlayerController;
|
: PlayerController;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -0,0 +1,586 @@
|
||||||
|
import { Controls } from "../App";
|
||||||
|
import { BallCollider, RigidBody, useRapier, vec3 } from "@react-three/rapier";
|
||||||
|
import {
|
||||||
|
useKeyboardControls,
|
||||||
|
PerspectiveCamera,
|
||||||
|
PositionalAudio,
|
||||||
|
} from "@react-three/drei";
|
||||||
|
import { useFrame, useThree, extend } from "@react-three/fiber";
|
||||||
|
import { useRef, useState, useEffect, useCallback } from "react";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
|
import { Mario } from "./models/characters/Mario_kart";
|
||||||
|
import { DriftParticlesLeft } from "./Particles/drifts/DriftParticlesLeft";
|
||||||
|
import { DriftParticlesRight } from "./Particles/drifts/DriftParticlesRight";
|
||||||
|
|
||||||
|
import { PointParticle } from "./Particles/drifts/PointParticle";
|
||||||
|
|
||||||
|
import { FlameParticles } from "./Particles/flames/FlameParticles";
|
||||||
|
import { useStore } from "./store";
|
||||||
|
import { Cylinder } from "@react-three/drei";
|
||||||
|
import FakeGlowMaterial from "./ShaderMaterials/FakeGlow/FakeGlowMaterial";
|
||||||
|
import { HitParticles } from "./Particles/hits/HitParticles";
|
||||||
|
import { CoinParticles } from "./Particles/coins/CoinParticles";
|
||||||
|
import { ItemParticles } from "./Particles/items/ItemParticles";
|
||||||
|
import { geometry } from "maath";
|
||||||
|
import { useGamepad } from "./useGamepad";
|
||||||
|
extend(geometry);
|
||||||
|
|
||||||
|
export const PlayerControllerTouch = ({
|
||||||
|
player,
|
||||||
|
userPlayer,
|
||||||
|
setNetworkBananas,
|
||||||
|
setNetworkShells,
|
||||||
|
networkBananas,
|
||||||
|
networkShells,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const [isOnGround, setIsOnGround] = useState(false);
|
||||||
|
const body = useRef();
|
||||||
|
const kart = useRef();
|
||||||
|
const cam = useRef();
|
||||||
|
const initialSpeed = 0;
|
||||||
|
const maxSpeed = 30;
|
||||||
|
const boostSpeed = 50;
|
||||||
|
const acceleration = 0.1;
|
||||||
|
const decceleration = 0.2;
|
||||||
|
const damping = -0.1;
|
||||||
|
const MaxSteeringSpeed = 0.01;
|
||||||
|
const [currentSteeringSpeed, setCurrentSteeringSpeed] = useState(0);
|
||||||
|
const [currentSpeed, setCurrentSpeed] = useState(initialSpeed);
|
||||||
|
const camMaxOffset = 1;
|
||||||
|
let steeringAngle = 0;
|
||||||
|
const isOnFloor = useRef(false);
|
||||||
|
const jumpForce = useRef(0);
|
||||||
|
const jumpIsHeld = useRef(false);
|
||||||
|
const driftDirection = useRef(0);
|
||||||
|
const driftLeft = useRef(false);
|
||||||
|
const driftRight = useRef(false);
|
||||||
|
const driftForce = useRef(0);
|
||||||
|
const mario = useRef();
|
||||||
|
const accumulatedDriftPower = useRef(0);
|
||||||
|
const blueTurboThreshold = 10;
|
||||||
|
const orangeTurboThreshold = 30;
|
||||||
|
const purpleTurboThreshold = 60;
|
||||||
|
const [turboColor, setTurboColor] = useState(0xffffff);
|
||||||
|
const boostDuration = useRef(0);
|
||||||
|
const [isBoosting, setIsBoosting] = useState(false);
|
||||||
|
let targetXPosition = 0;
|
||||||
|
let targetZPosition = 8;
|
||||||
|
const [steeringAngleWheels, setSteeringAngleWheels] = useState(0);
|
||||||
|
const engineSound = useRef();
|
||||||
|
const driftSound = useRef();
|
||||||
|
const driftTwoSound = useRef();
|
||||||
|
const driftOrangeSound = useRef();
|
||||||
|
const driftPurpleSound = useRef();
|
||||||
|
const driftBlueSound = useRef();
|
||||||
|
const jumpSound = useRef();
|
||||||
|
const landingSound = useRef();
|
||||||
|
const turboSound = useRef();
|
||||||
|
const [scale, setScale] = useState(0);
|
||||||
|
const raycaster = new THREE.Raycaster();
|
||||||
|
const downDirection = new THREE.Vector3(0, -1, 0);
|
||||||
|
const [shouldLaunch, setShouldLaunch] = useState(false);
|
||||||
|
const effectiveBoost = useRef(0);
|
||||||
|
const text = useRef();
|
||||||
|
|
||||||
|
const { actions, shouldSlowDown, item, bananas, coins, id, controls, joystickX, driftButton, itemButton } = useStore();
|
||||||
|
const slowDownDuration = useRef(1500);
|
||||||
|
|
||||||
|
useFrame(({ pointer, clock }, delta) => {
|
||||||
|
if (player.id !== id) return;
|
||||||
|
const time = clock.getElapsedTime();
|
||||||
|
if (!body.current && !mario.current) return;
|
||||||
|
engineSound.current.setVolume(currentSpeed / 300 + 0.2);
|
||||||
|
engineSound.current.setPlaybackRate(currentSpeed / 10 + 0.1);
|
||||||
|
jumpSound.current.setPlaybackRate(1.5);
|
||||||
|
jumpSound.current.setVolume(0.5);
|
||||||
|
driftSound.current.setVolume(0.2);
|
||||||
|
|
||||||
|
driftBlueSound.current.setVolume(0.5);
|
||||||
|
driftOrangeSound.current.setVolume(0.6);
|
||||||
|
driftPurpleSound.current.setVolume(0.7);
|
||||||
|
// HANDLING AND STEERING
|
||||||
|
const kartRotation =
|
||||||
|
kart.current.rotation.y - driftDirection.current * driftForce.current;
|
||||||
|
const forwardDirection = new THREE.Vector3(
|
||||||
|
-Math.sin(kartRotation),
|
||||||
|
0,
|
||||||
|
-Math.cos(kartRotation)
|
||||||
|
);
|
||||||
|
|
||||||
|
// mouse steering
|
||||||
|
|
||||||
|
if (!driftLeft.current && !driftRight.current) {
|
||||||
|
steeringAngle = currentSteeringSpeed * -joystickX;
|
||||||
|
targetXPosition = -camMaxOffset * -joystickX;
|
||||||
|
} else if (driftLeft.current && !driftRight.current) {
|
||||||
|
steeringAngle = currentSteeringSpeed * -(joystickX - 1);
|
||||||
|
targetXPosition = -camMaxOffset * -joystickX;
|
||||||
|
} else if (driftRight.current && !driftLeft.current) {
|
||||||
|
steeringAngle = currentSteeringSpeed * -(joystickX + 1);
|
||||||
|
targetXPosition = -camMaxOffset * -joystickX;
|
||||||
|
}
|
||||||
|
// ACCELERATING
|
||||||
|
const shouldSlow = actions.getShouldSlowDown();
|
||||||
|
|
||||||
|
if ( currentSpeed < maxSpeed) {
|
||||||
|
// Accelerate the kart within the maximum speed limit
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.min(currentSpeed + acceleration * delta * 144, maxSpeed)
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
|
||||||
|
currentSpeed > maxSpeed &&
|
||||||
|
effectiveBoost.current > 0
|
||||||
|
) {
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (currentSteeringSpeed < MaxSteeringSpeed) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.min(
|
||||||
|
currentSteeringSpeed + 0.0001 * delta * 144,
|
||||||
|
MaxSteeringSpeed
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (shouldSlow) {
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - decceleration * 2 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
setCurrentSteeringSpeed(0);
|
||||||
|
slowDownDuration.current -= 1500 * delta;
|
||||||
|
setShouldLaunch(true);
|
||||||
|
if (slowDownDuration.current <= 1) {
|
||||||
|
actions.setShouldSlowDown(false);
|
||||||
|
slowDownDuration.current = 1500;
|
||||||
|
setShouldLaunch(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the kart's rotation based on the steering angle
|
||||||
|
kart.current.rotation.y += steeringAngle * delta * 144;
|
||||||
|
|
||||||
|
// Apply damping to simulate slowdown when no keys are pressed
|
||||||
|
body.current.applyImpulse(
|
||||||
|
{
|
||||||
|
x: -body.current.linvel().x * (1 - damping) * delta * 144,
|
||||||
|
y: 0,
|
||||||
|
z: -body.current.linvel().z * (1 - damping) * delta * 144,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const bodyPosition = body.current.translation();
|
||||||
|
kart.current.position.set(
|
||||||
|
bodyPosition.x,
|
||||||
|
bodyPosition.y - 0.5,
|
||||||
|
bodyPosition.z
|
||||||
|
);
|
||||||
|
|
||||||
|
// JUMPING
|
||||||
|
if (driftButton && isOnGround && !jumpIsHeld.current) {
|
||||||
|
jumpForce.current += 10;
|
||||||
|
isOnFloor.current = false;
|
||||||
|
jumpIsHeld.current = true;
|
||||||
|
jumpSound.current.play();
|
||||||
|
setIsOnGround(false);
|
||||||
|
|
||||||
|
if (jumpSound.current.isPlaying) {
|
||||||
|
jumpSound.current.stop();
|
||||||
|
jumpSound.current.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOnFloor.current && jumpForce.current > 0) {
|
||||||
|
landingSound.current.play();
|
||||||
|
}
|
||||||
|
if (!isOnGround && jumpForce.current > 0) {
|
||||||
|
jumpForce.current -= 1 * delta * 144;
|
||||||
|
}
|
||||||
|
if (!driftButton) {
|
||||||
|
jumpIsHeld.current = false;
|
||||||
|
driftDirection.current = 0;
|
||||||
|
driftForce.current = 0;
|
||||||
|
driftLeft.current = false;
|
||||||
|
driftRight.current = false;
|
||||||
|
}
|
||||||
|
// DRIFTING
|
||||||
|
if (
|
||||||
|
jumpIsHeld.current &&
|
||||||
|
currentSteeringSpeed > 0 &&
|
||||||
|
joystickX < -0.1 &&
|
||||||
|
!driftRight.current
|
||||||
|
) {
|
||||||
|
driftLeft.current = true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
jumpIsHeld.current &&
|
||||||
|
currentSteeringSpeed > 0 &&
|
||||||
|
joystickX > 0.1 &&
|
||||||
|
!driftLeft.current
|
||||||
|
) {
|
||||||
|
driftRight.current = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jumpIsHeld.current && !driftLeft.current && !driftRight.current) {
|
||||||
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
mario.current.rotation.y,
|
||||||
|
0,
|
||||||
|
0.0001 * delta * 144
|
||||||
|
);
|
||||||
|
setTurboColor(0xffffff);
|
||||||
|
accumulatedDriftPower.current = 0;
|
||||||
|
driftSound.current.stop();
|
||||||
|
driftTwoSound.current.stop();
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driftLeft.current) {
|
||||||
|
driftDirection.current = 1;
|
||||||
|
driftForce.current = 0.4;
|
||||||
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
mario.current.rotation.y,
|
||||||
|
steeringAngle * 25 + 0.4,
|
||||||
|
0.05 * delta * 144
|
||||||
|
);
|
||||||
|
accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144;
|
||||||
|
}
|
||||||
|
if (driftRight.current) {
|
||||||
|
driftDirection.current = -1;
|
||||||
|
driftForce.current = 0.4;
|
||||||
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
mario.current.rotation.y,
|
||||||
|
-(-steeringAngle * 25 + 0.4),
|
||||||
|
0.05 * delta * 144
|
||||||
|
);
|
||||||
|
accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144;
|
||||||
|
}
|
||||||
|
if (!driftLeft.current && !driftRight.current) {
|
||||||
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
mario.current.rotation.y,
|
||||||
|
steeringAngle * 30,
|
||||||
|
0.05 * delta * 144
|
||||||
|
);
|
||||||
|
setScale(0);
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > blueTurboThreshold) {
|
||||||
|
setTurboColor(0x00ffff);
|
||||||
|
boostDuration.current = 50;
|
||||||
|
driftBlueSound.current.play();
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > orangeTurboThreshold) {
|
||||||
|
setTurboColor(0xffcf00);
|
||||||
|
boostDuration.current = 100;
|
||||||
|
driftBlueSound.current.stop();
|
||||||
|
driftOrangeSound.current.play();
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > purpleTurboThreshold) {
|
||||||
|
setTurboColor(0xff00ff);
|
||||||
|
boostDuration.current = 250;
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driftLeft.current || driftRight.current) {
|
||||||
|
const oscillation = Math.sin(time * 1000) * 0.1;
|
||||||
|
const vibration = oscillation + 0.9;
|
||||||
|
if (turboColor === 0xffffff) {
|
||||||
|
setScale(vibration * 0.8);
|
||||||
|
} else {
|
||||||
|
setScale(vibration);
|
||||||
|
}
|
||||||
|
if (isOnFloor.current && !driftSound.current.isPlaying) {
|
||||||
|
driftSound.current.play();
|
||||||
|
driftTwoSound.current.play();
|
||||||
|
landingSound.current.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// RELEASING DRIFT
|
||||||
|
|
||||||
|
if (boostDuration.current > 1 && !jumpIsHeld.current) {
|
||||||
|
setIsBoosting(true);
|
||||||
|
effectiveBoost.current = boostDuration.current;
|
||||||
|
boostDuration.current = 0;
|
||||||
|
} else if (effectiveBoost.current <= 1) {
|
||||||
|
targetZPosition = 8;
|
||||||
|
setIsBoosting(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBoosting && effectiveBoost.current > 1) {
|
||||||
|
setCurrentSpeed(boostSpeed);
|
||||||
|
effectiveBoost.current -= 1 * delta * 144;
|
||||||
|
targetZPosition = 10;
|
||||||
|
if (!turboSound.current.isPlaying) turboSound.current.play();
|
||||||
|
driftTwoSound.current.play();
|
||||||
|
driftBlueSound.current.stop();
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.stop();
|
||||||
|
} else if (effectiveBoost.current <= 1) {
|
||||||
|
setIsBoosting(false);
|
||||||
|
targetZPosition = 8;
|
||||||
|
turboSound.current.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAMERA WORK
|
||||||
|
|
||||||
|
cam.current.updateMatrixWorld();
|
||||||
|
|
||||||
|
cam.current.position.x = THREE.MathUtils.lerp(
|
||||||
|
cam.current.position.x,
|
||||||
|
targetXPosition,
|
||||||
|
0.01 * delta * 144
|
||||||
|
);
|
||||||
|
|
||||||
|
cam.current.position.z = THREE.MathUtils.lerp(
|
||||||
|
cam.current.position.z,
|
||||||
|
targetZPosition,
|
||||||
|
0.01 * delta * 144
|
||||||
|
);
|
||||||
|
|
||||||
|
body.current.applyImpulse(
|
||||||
|
{
|
||||||
|
x: forwardDirection.x * currentSpeed * delta * 144,
|
||||||
|
y: 0 + jumpForce.current * delta * 144,
|
||||||
|
z: forwardDirection.z * currentSpeed * delta * 144,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the kart's rotation based on the steering angle
|
||||||
|
setSteeringAngleWheels(steeringAngle * 25);
|
||||||
|
|
||||||
|
// SOUND WORK
|
||||||
|
|
||||||
|
|
||||||
|
// ITEMS
|
||||||
|
|
||||||
|
if (itemButton && item === "banana") {
|
||||||
|
const distanceBehind = 2;
|
||||||
|
const scaledBackwardDirection =
|
||||||
|
forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
...vec3(body.current.translation())
|
||||||
|
);
|
||||||
|
|
||||||
|
const bananaPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newBanana = {
|
||||||
|
id: Math.random() + "-" + +new Date(),
|
||||||
|
position: bananaPosition,
|
||||||
|
player: true,
|
||||||
|
};
|
||||||
|
setNetworkBananas([...networkBananas, newBanana]);
|
||||||
|
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemButton && item === "shell") {
|
||||||
|
const distanceBehind = -2;
|
||||||
|
const scaledBackwardDirection =
|
||||||
|
forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
body.current.translation().x,
|
||||||
|
body.current.translation().y,
|
||||||
|
body.current.translation().z
|
||||||
|
);
|
||||||
|
|
||||||
|
const shellPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newShell = {
|
||||||
|
id: Math.random() + "-" + +new Date(),
|
||||||
|
position: shellPosition,
|
||||||
|
player: true,
|
||||||
|
rotation: kartRotation,
|
||||||
|
};
|
||||||
|
setNetworkShells([...networkShells, newShell]);
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemButton && item === "mushroom") {
|
||||||
|
setIsBoosting(true);
|
||||||
|
effectiveBoost.current = 300;
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
player.setState("position", body.current.translation());
|
||||||
|
player.setState("rotation", kartRotation + mario.current.rotation.y);
|
||||||
|
player.setState("isBoosting", isBoosting);
|
||||||
|
player.setState("shouldLaunch", shouldLaunch);
|
||||||
|
player.setState("turboColor", turboColor);
|
||||||
|
player.setState("scale", scale);
|
||||||
|
player.setState("bananas", bananas);
|
||||||
|
});
|
||||||
|
|
||||||
|
return player.id === id ? (
|
||||||
|
<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;
|
||||||
|
};
|
|
@ -14,7 +14,7 @@ export const items = [
|
||||||
|
|
||||||
export const useStore = create((set, get) => ({
|
export const useStore = create((set, get) => ({
|
||||||
gameStarted: false,
|
gameStarted: false,
|
||||||
controls: "",
|
controls: "touch",
|
||||||
particles1: [],
|
particles1: [],
|
||||||
particles2: [],
|
particles2: [],
|
||||||
bodyPosition: [0, 0, 0],
|
bodyPosition: [0, 0, 0],
|
||||||
|
@ -29,6 +29,9 @@ export const useStore = create((set, get) => ({
|
||||||
coins : 0,
|
coins : 0,
|
||||||
players : [],
|
players : [],
|
||||||
id : "",
|
id : "",
|
||||||
|
joystickX: 0,
|
||||||
|
driftButton: false,
|
||||||
|
itemButton: false,
|
||||||
addPastPosition: (position) => {
|
addPastPosition: (position) => {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
pastPositions: [position, ...state.pastPositions.slice(0, 499)],
|
pastPositions: [position, ...state.pastPositions.slice(0, 499)],
|
||||||
|
@ -147,8 +150,16 @@ export const useStore = create((set, get) => ({
|
||||||
},
|
},
|
||||||
setControls: (controls) => {
|
setControls: (controls) => {
|
||||||
set({ controls });
|
set({ controls });
|
||||||
}
|
},
|
||||||
|
setJoystickX: (joystickX) => {
|
||||||
|
set({ joystickX });
|
||||||
|
},
|
||||||
|
setDriftButton: (driftButton) => {
|
||||||
|
set({ driftButton });
|
||||||
|
},
|
||||||
|
setItemButton: (itemButton) => {
|
||||||
|
set({ itemButton });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Hanken+Grotesk:ital,wght@0,100..900;1,100..900&display=swap');
|
@import url("https://fonts.googleapis.com/css2?family=Hanken+Grotesk:ital,wght@0,100..900;1,100..900&display=swap");
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
@ -87,8 +87,8 @@ body::-webkit-scrollbar {
|
||||||
border-box;
|
border-box;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
border-radius: 50em;
|
border-radius: 50em;
|
||||||
.background{
|
.background {
|
||||||
background-image: url('./images/scanline.jpg');
|
background-image: url("./images/scanline.jpg");
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -97,12 +97,37 @@ body::-webkit-scrollbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.joystick {
|
||||||
|
left: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drift {
|
||||||
|
right: 150px;
|
||||||
|
font-family: "Hanken Grotesk";
|
||||||
|
border-radius: 100px;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
height: 66.6667px;
|
||||||
|
width: 66.6667px;
|
||||||
|
border: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
touch-action: none;
|
||||||
|
color: white;
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes bounce {
|
@keyframes bounce {
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
|
@ -113,15 +138,14 @@ body::-webkit-scrollbar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotation{
|
.annotation {
|
||||||
display:flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
background:none;
|
background: none;
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home {
|
.home {
|
||||||
|
@ -248,11 +272,10 @@ body::-webkit-scrollbar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled{
|
.disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
|
||||||
}
|
}
|
||||||
@keyframes blinking {
|
@keyframes blinking {
|
||||||
0% {
|
0% {
|
||||||
|
@ -265,3 +288,20 @@ body::-webkit-scrollbar {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.controls.itemButton{
|
||||||
|
|
||||||
|
right: 250px;
|
||||||
|
font-family: "Hanken Grotesk";
|
||||||
|
border-radius: 100px;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
height: 66.6667px;
|
||||||
|
width: 66.6667px;
|
||||||
|
border: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
touch-action: none;
|
||||||
|
color: white;
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue