added item system
|
@ -18,6 +18,7 @@
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-gamepad": "^1.0.3",
|
"react-gamepad": "^1.0.3",
|
||||||
"three": "^0.160.1",
|
"three": "^0.160.1",
|
||||||
|
"three-mesh-bvh": "^0.7.0",
|
||||||
"zustand": "^4.5.0"
|
"zustand": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-gamepad": "^1.0.3",
|
"react-gamepad": "^1.0.3",
|
||||||
"three": "^0.160.1",
|
"three": "^0.160.1",
|
||||||
|
"three-mesh-bvh": "^0.7.0",
|
||||||
"zustand": "^4.5.0"
|
"zustand": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 9.9 KiB |
|
@ -39,6 +39,7 @@ function App() {
|
||||||
<Physics
|
<Physics
|
||||||
gravity={[0, -90, 0]}
|
gravity={[0, -90, 0]}
|
||||||
timeStep={'vary'}
|
timeStep={'vary'}
|
||||||
|
// debug
|
||||||
>
|
>
|
||||||
<KeyboardControls map={map}>
|
<KeyboardControls map={map}>
|
||||||
<Experience />
|
<Experience />
|
||||||
|
|
|
@ -1,21 +1,62 @@
|
||||||
import { Environment, OrbitControls, PerspectiveCamera, Lightformer } from '@react-three/drei'
|
import {
|
||||||
import { Ground } from './Ground'
|
Environment,
|
||||||
import { PlayerController } from './PlayerController'
|
OrbitControls,
|
||||||
import { Paris } from './models/tracks/Tour_paris_promenade'
|
PerspectiveCamera,
|
||||||
import { EffectComposer, N8AO, Bloom, TiltShift2, HueSaturation, SMAA, ChromaticAberration, Vignette } from '@react-three/postprocessing'
|
Lightformer,
|
||||||
import { Skid } from './Skid'
|
Bvh,
|
||||||
|
} from "@react-three/drei";
|
||||||
|
import { Ground } from "./Ground";
|
||||||
|
import { PlayerController } from "./PlayerController";
|
||||||
|
import { Paris } from "./models/tracks/Tour_paris_promenade";
|
||||||
|
import {
|
||||||
|
EffectComposer,
|
||||||
|
N8AO,
|
||||||
|
Bloom,
|
||||||
|
TiltShift2,
|
||||||
|
HueSaturation,
|
||||||
|
SMAA,
|
||||||
|
ChromaticAberration,
|
||||||
|
Vignette,
|
||||||
|
} from "@react-three/postprocessing";
|
||||||
|
import { Skid } from "./Skid";
|
||||||
|
import { Banana } from "./models/items/Banana_peel_mario_kart";
|
||||||
|
import { ItemBox } from "./models/misc/Mario_kart_item_box";
|
||||||
|
import { useStore } from "./store";
|
||||||
|
import { Shell } from "./models/items/Mario_shell_red";
|
||||||
|
|
||||||
export const Experience = () => {
|
export const Experience = () => {
|
||||||
|
const onCollide = (event) => {
|
||||||
|
console.log(event);
|
||||||
|
};
|
||||||
|
const { bananas, shells } = useStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PlayerController />
|
<PlayerController />
|
||||||
|
<Paris position={[0, 0, 0]} />
|
||||||
|
<Banana onCollide={onCollide} position={[-10, 1.8, -119]} />
|
||||||
|
{/* <Shell position={[-20, 2, -119]} /> */}
|
||||||
|
<ItemBox position={[-20, 2, -119]} />
|
||||||
{/* <Skid /> */}
|
{/* <Skid /> */}
|
||||||
<Ground position={[0, 0, 0]} />
|
<Ground position={[0, 0, 0]} />
|
||||||
<Environment
|
<Environment resolution={256} preset="lobby" />
|
||||||
resolution={256}
|
|
||||||
preset='lobby'
|
|
||||||
|
|
||||||
/>
|
{bananas.map((banana) => (
|
||||||
|
<Banana
|
||||||
|
key={banana.id}
|
||||||
|
position={banana.position}
|
||||||
|
|
||||||
|
// rotation={banana.rotation}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{shells.map((shell) => (
|
||||||
|
<Shell
|
||||||
|
key={shell.id}
|
||||||
|
position={shell.position}
|
||||||
|
rotation={shell.rotation}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
<directionalLight
|
<directionalLight
|
||||||
position={[10, 50, -30]}
|
position={[10, 50, -30]}
|
||||||
|
@ -25,17 +66,15 @@ export const Experience = () => {
|
||||||
shadow-camera-left={-300}
|
shadow-camera-left={-300}
|
||||||
shadow-camera-right={300}
|
shadow-camera-right={300}
|
||||||
shadow-camera-top={300}
|
shadow-camera-top={300}
|
||||||
shadow-camera-bottom={-3000}
|
shadow-camera-bottom={-300}
|
||||||
castShadow
|
castShadow
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Paris position={[0, 0, 0]} />
|
|
||||||
<EffectComposer
|
<EffectComposer
|
||||||
multisampling={0}
|
multisampling={0}
|
||||||
disableNormalPass
|
disableNormalPass
|
||||||
disableSSAO
|
disableSSAO
|
||||||
disableDepthPass
|
disableDepthPass
|
||||||
|
|
||||||
>
|
>
|
||||||
<SMAA />
|
<SMAA />
|
||||||
<N8AO distanceFalloff={1} aoRadius={1} intensity={3} />
|
<N8AO distanceFalloff={1} aoRadius={1} intensity={3} />
|
||||||
|
@ -46,11 +85,11 @@ export const Experience = () => {
|
||||||
intensity={0.5}
|
intensity={0.5}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TiltShift2/>
|
<TiltShift2 />
|
||||||
<ChromaticAberration offset={[0.0006, 0.0006]} />
|
<ChromaticAberration offset={[0.0006, 0.0006]} />
|
||||||
<HueSaturation saturation={0.1} />
|
<HueSaturation saturation={0.1} />
|
||||||
<Vignette eskil={false} offset={0.1} darkness={0.4} />
|
<Vignette eskil={false} offset={0.1} darkness={0.4} />
|
||||||
</EffectComposer>
|
</EffectComposer>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import { Canvas, useLoader, useFrame, extend } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { shaderMaterial } from "@react-three/drei";
|
||||||
|
|
||||||
|
export const HitParticle = ({ position, shouldLaunch }) => {
|
||||||
|
const texture = useLoader(THREE.TextureLoader, "./particles/star_symbol.png");
|
||||||
|
const pointsRef = useRef();
|
||||||
|
const materialRef = useRef();
|
||||||
|
const [size, setSize] = useState(3);
|
||||||
|
const frames = useRef(50);
|
||||||
|
|
||||||
|
const gravity = -0.03;
|
||||||
|
const velocity = useRef({
|
||||||
|
x: (Math.random() - 0.5) * 33,
|
||||||
|
y: (Math.random() + 0.3) * 4,
|
||||||
|
});
|
||||||
|
const points = React.useMemo(() => {
|
||||||
|
const geom = new THREE.BufferGeometry();
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new THREE.Float32BufferAttribute(position, 3)
|
||||||
|
);
|
||||||
|
return geom;
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (shouldLaunch) {
|
||||||
|
if (pointsRef.current) {
|
||||||
|
// Reset position
|
||||||
|
pointsRef.current.position.set(0, 0, 0);
|
||||||
|
|
||||||
|
// Reset velocity
|
||||||
|
velocity.current = {
|
||||||
|
x: (Math.random() - 0.5) * 33,
|
||||||
|
y: (Math.random() + 0.3) * 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset opacity if needed
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.opacity = 1;
|
||||||
|
materialRef.current.size = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
frames.current = 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [shouldLaunch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.color.multiplyScalar(15);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useFrame((_state, delta) => {
|
||||||
|
if (pointsRef.current) {
|
||||||
|
let position = pointsRef.current.position;
|
||||||
|
|
||||||
|
// Normalized time value for ease-out effect
|
||||||
|
let t = 1 - Math.max(frames.current / 150, 0); // Ensure t is between 0 and 1
|
||||||
|
let easeOutCubic = 1 - Math.pow(1 - t, 3);
|
||||||
|
|
||||||
|
// Apply the velocity to the position
|
||||||
|
position.x += velocity.current.x * delta * easeOutCubic;
|
||||||
|
position.y += velocity.current.y * delta * easeOutCubic;
|
||||||
|
|
||||||
|
// Adjust gravity based on delta
|
||||||
|
velocity.current.y += gravity * delta * 144;
|
||||||
|
|
||||||
|
// Decrease frames
|
||||||
|
frames.current -= 1;
|
||||||
|
|
||||||
|
if (materialRef.current) {
|
||||||
|
// materialRef.current.size = 3 + Math.sin(frames.current * 0.1) * 2;
|
||||||
|
materialRef.current.opacity = Math.abs(Math.sin(frames.current * 0.1));
|
||||||
|
}
|
||||||
|
// Reset the particle position and velocity when it goes too far
|
||||||
|
if (frames.current < 0) {
|
||||||
|
// if(materialRef.current.opacity > 0) {
|
||||||
|
// Math.max(materialRef.current.opacity -= 0.01 * delta * 144, 0);
|
||||||
|
// }
|
||||||
|
if (materialRef.current.size > 0) {
|
||||||
|
Math.max((materialRef.current.size -= 0.1 * delta * 144), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pointsRef.current.position.set(position.x, position.y, position.z);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<points ref={pointsRef} geometry={points}>
|
||||||
|
<pointsMaterial
|
||||||
|
ref={materialRef}
|
||||||
|
size={size}
|
||||||
|
alphaMap={texture}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
color={0xfafad2}
|
||||||
|
opacity={1}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</points>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PointsShaderMaterial = shaderMaterial(
|
||||||
|
{
|
||||||
|
time: 0,
|
||||||
|
tex: undefined,
|
||||||
|
color: new THREE.Color(0xfafad2),
|
||||||
|
},
|
||||||
|
// Vertex Shader
|
||||||
|
`
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
// Fragment Shader
|
||||||
|
`
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
uniform vec3 color;
|
||||||
|
uniform float time; // Using the declared 'time' uniform
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = vUv;
|
||||||
|
vec4 texColor = texture2D(tex, uv);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color, 1.0) * texColor * vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
extend({ PointsShaderMaterial });
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import { Canvas, useLoader, useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import gsap from "gsap";
|
||||||
|
|
||||||
|
|
||||||
|
export const HitParticleTwo = ({ position, shouldLaunch}) => {
|
||||||
|
const texture = useLoader(THREE.TextureLoader, "./particles/star_symbol.png");
|
||||||
|
const pointsRef = useRef();
|
||||||
|
const materialRef = useRef();
|
||||||
|
const [size, setSize] = useState(1);
|
||||||
|
const frames = useRef(100);
|
||||||
|
|
||||||
|
const gravity = -0.03;
|
||||||
|
const velocity = useRef({
|
||||||
|
x: (Math.random() - 0.5) * 16,
|
||||||
|
y: (Math.random() + 0.3) *4,
|
||||||
|
});
|
||||||
|
const points = React.useMemo(() => {
|
||||||
|
const geom = new THREE.BufferGeometry();
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new THREE.Float32BufferAttribute(position, 3)
|
||||||
|
);
|
||||||
|
return geom;
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (shouldLaunch) {
|
||||||
|
if (pointsRef.current) {
|
||||||
|
// Reset position
|
||||||
|
pointsRef.current.position.set(0, 0, 0);
|
||||||
|
|
||||||
|
// Reset velocity
|
||||||
|
velocity.current = {
|
||||||
|
x: (Math.random() - 0.5) * 16,
|
||||||
|
y: (Math.random() + 0.3) * 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset opacity if needed
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.opacity = 1;
|
||||||
|
}
|
||||||
|
frames.current = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [shouldLaunch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.color.multiplyScalar(15);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<points ref={pointsRef} geometry={points}>
|
||||||
|
<pointsMaterial
|
||||||
|
ref={materialRef}
|
||||||
|
size={size}
|
||||||
|
alphaMap={texture}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
color={0xFAFAD2}
|
||||||
|
opacity={1}
|
||||||
|
// toneMapped={false}
|
||||||
|
/>
|
||||||
|
</points>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { HitParticle } from './HitParticle'
|
||||||
|
|
||||||
|
export const HitParticles = ({ position, shouldLaunch }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
<HitParticle position={[0,0,0]} shouldLaunch={shouldLaunch}/>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import * as THREE from "three";
|
||||||
export const PointParticle = ({ position, png, turboColor }) => {
|
export const PointParticle = ({ position, png, turboColor }) => {
|
||||||
const texture = useLoader(THREE.TextureLoader, png);
|
const texture = useLoader(THREE.TextureLoader, png);
|
||||||
const pointsRef = useRef();
|
const pointsRef = useRef();
|
||||||
|
const materialRef = useRef();
|
||||||
const [size, setSize] = useState(0);
|
const [size, setSize] = useState(0);
|
||||||
const [opacity, setOpacity] = useState(1);
|
const [opacity, setOpacity] = useState(1);
|
||||||
|
|
||||||
|
@ -18,7 +19,9 @@ export const PointParticle = ({ position, png, turboColor }) => {
|
||||||
}, [position]);
|
}, [position]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.color.multiplyScalar(10);
|
||||||
|
}
|
||||||
setSize(0);
|
setSize(0);
|
||||||
setOpacity(1);
|
setOpacity(1);
|
||||||
}, [turboColor]);
|
}, [turboColor]);
|
||||||
|
@ -36,6 +39,7 @@ export const PointParticle = ({ position, png, turboColor }) => {
|
||||||
return (
|
return (
|
||||||
<points ref={pointsRef} geometry={points}>
|
<points ref={pointsRef} geometry={points}>
|
||||||
<pointsMaterial
|
<pointsMaterial
|
||||||
|
ref={materialRef}
|
||||||
size={size}
|
size={size}
|
||||||
alphaMap={texture}
|
alphaMap={texture}
|
||||||
transparent={true}
|
transparent={true}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { FlameParticles } from "./Particles/FlameParticles";
|
||||||
import { useStore } from "./store";
|
import { useStore } from "./store";
|
||||||
import { Cylinder } from "@react-three/drei";
|
import { Cylinder } from "@react-three/drei";
|
||||||
import FakeGlowMaterial from "./ShaderMaterials/FakeGlow/FakeGlowMaterial";
|
import FakeGlowMaterial from "./ShaderMaterials/FakeGlow/FakeGlowMaterial";
|
||||||
|
import { HitParticles } from "./Particles/HitParticles";
|
||||||
|
|
||||||
export const PlayerController = () => {
|
export const PlayerController = () => {
|
||||||
const upPressed = useKeyboardControls((state) => state[Controls.up]);
|
const upPressed = useKeyboardControls((state) => state[Controls.up]);
|
||||||
|
@ -30,6 +31,8 @@ export const PlayerController = () => {
|
||||||
const leftPressed = useKeyboardControls((state) => state[Controls.left]);
|
const leftPressed = useKeyboardControls((state) => state[Controls.left]);
|
||||||
const rightPressed = useKeyboardControls((state) => state[Controls.right]);
|
const rightPressed = useKeyboardControls((state) => state[Controls.right]);
|
||||||
const jumpPressed = useKeyboardControls((state) => state[Controls.jump]);
|
const jumpPressed = useKeyboardControls((state) => state[Controls.jump]);
|
||||||
|
const shootPressed = useKeyboardControls((state) => state[Controls.shoot]);
|
||||||
|
|
||||||
const [isOnGround, setIsOnGround] = useState(false);
|
const [isOnGround, setIsOnGround] = useState(false);
|
||||||
const body = useRef();
|
const body = useRef();
|
||||||
const kart = useRef();
|
const kart = useRef();
|
||||||
|
@ -73,6 +76,14 @@ export const PlayerController = () => {
|
||||||
const landingSound = useRef();
|
const landingSound = useRef();
|
||||||
const turboSound = useRef();
|
const turboSound = useRef();
|
||||||
const [scale, setScale] = useState(0);
|
const [scale, setScale] = useState(0);
|
||||||
|
const raycaster = new THREE.Raycaster();
|
||||||
|
const downDirection = new THREE.Vector3(0, -1, 0);
|
||||||
|
const [shouldLaunch, setShouldLaunch] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
const { actions, shouldSlowDown, item, bananas} = useStore();
|
||||||
|
const slowDownDuration = useRef(1500);
|
||||||
|
|
||||||
|
|
||||||
useFrame(({ pointer, clock }, delta) => {
|
useFrame(({ pointer, clock }, delta) => {
|
||||||
const time = clock.getElapsedTime();
|
const time = clock.getElapsedTime();
|
||||||
|
@ -86,7 +97,6 @@ export const PlayerController = () => {
|
||||||
driftBlueSound.current.setVolume(0.5);
|
driftBlueSound.current.setVolume(0.5);
|
||||||
driftOrangeSound.current.setVolume(0.6);
|
driftOrangeSound.current.setVolume(0.6);
|
||||||
driftPurpleSound.current.setVolume(0.7);
|
driftPurpleSound.current.setVolume(0.7);
|
||||||
|
|
||||||
// HANDLING AND STEERING
|
// HANDLING AND STEERING
|
||||||
const kartRotation =
|
const kartRotation =
|
||||||
kart.current.rotation.y - driftDirection.current * driftForce.current;
|
kart.current.rotation.y - driftDirection.current * driftForce.current;
|
||||||
|
@ -121,6 +131,8 @@ export const PlayerController = () => {
|
||||||
targetXPosition = -camMaxOffset * -pointer.x;
|
targetXPosition = -camMaxOffset * -pointer.x;
|
||||||
}
|
}
|
||||||
// ACCELERATING
|
// ACCELERATING
|
||||||
|
const shouldSlow = actions.getShouldSlowDown();
|
||||||
|
|
||||||
|
|
||||||
if (upPressed && currentSpeed < maxSpeed) {
|
if (upPressed && currentSpeed < maxSpeed) {
|
||||||
// Accelerate the kart within the maximum speed limit
|
// Accelerate the kart within the maximum speed limit
|
||||||
|
@ -147,6 +159,22 @@ export const PlayerController = () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
// REVERSING
|
||||||
if (downPressed && currentSpeed < -maxSpeed) {
|
if (downPressed && currentSpeed < -maxSpeed) {
|
||||||
setCurrentSpeed(
|
setCurrentSpeed(
|
||||||
|
@ -359,6 +387,55 @@ export const PlayerController = () => {
|
||||||
|
|
||||||
// SOUND WORK
|
// SOUND WORK
|
||||||
|
|
||||||
|
// MISC
|
||||||
|
|
||||||
|
|
||||||
|
// ITEMS
|
||||||
|
if(shootPressed && item === "banana") {
|
||||||
|
const distanceBehind = 2; // Adjust this value as needed
|
||||||
|
const scaledBackwardDirection = forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
// Get the current position of the kart
|
||||||
|
const kartPosition = new THREE.Vector3(...vec3(body.current.translation()));
|
||||||
|
|
||||||
|
// Calculate the position for the new banana
|
||||||
|
const bananaPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newBanana = {
|
||||||
|
id: Math.random() + "-" + new Date(),
|
||||||
|
position: bananaPosition,
|
||||||
|
player: true,
|
||||||
|
};
|
||||||
|
actions.addBanana(newBanana);
|
||||||
|
actions.useItem();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(shootPressed && item === "shell") {
|
||||||
|
const distanceBehind = -1; // Adjust this value as needed
|
||||||
|
const scaledBackwardDirection = forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
// Get the current position of the kart
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
body.current.translation().x,
|
||||||
|
body.current.translation().y,
|
||||||
|
body.current.translation().z
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate the position for the new banana
|
||||||
|
const shellPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newShell = {
|
||||||
|
id: Math.random() + "-" + new Date(),
|
||||||
|
position: shellPosition,
|
||||||
|
player: true,
|
||||||
|
rotation: kartRotation
|
||||||
|
};
|
||||||
|
actions.addShell(newShell);
|
||||||
|
actions.useItem();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(item) console.log(item)
|
||||||
|
|
||||||
// console.lowg(body.current.translation())
|
// console.lowg(body.current.translation())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -367,20 +444,19 @@ export const PlayerController = () => {
|
||||||
<RigidBody
|
<RigidBody
|
||||||
ref={body}
|
ref={body}
|
||||||
colliders={false}
|
colliders={false}
|
||||||
position={[8, 20, -119]}
|
position={[8, 60, -119]}
|
||||||
centerOfMass={[0, -1, 0]}
|
centerOfMass={[0, -1, 0]}
|
||||||
mass={3}
|
mass={3}
|
||||||
ccd
|
ccd
|
||||||
|
name="player"
|
||||||
>
|
>
|
||||||
<BallCollider
|
<BallCollider
|
||||||
args={[0.5]}
|
args={[0.5]}
|
||||||
mass={3}
|
mass={3}
|
||||||
onCollisionEnter={(event) => {
|
onCollisionEnter={({other}) => {
|
||||||
isOnFloor.current = true;
|
isOnFloor.current = true;
|
||||||
}}
|
}}
|
||||||
// onCollisionExit={(event) => {
|
|
||||||
// isOnFloor.current = false
|
|
||||||
// }}
|
|
||||||
/>
|
/>
|
||||||
</RigidBody>
|
</RigidBody>
|
||||||
|
|
||||||
|
@ -391,12 +467,11 @@ export const PlayerController = () => {
|
||||||
steeringAngleWheels={steeringAngleWheels}
|
steeringAngleWheels={steeringAngleWheels}
|
||||||
isBoosting={isBoosting}
|
isBoosting={isBoosting}
|
||||||
/>
|
/>
|
||||||
{/* <pointLight
|
<pointLight
|
||||||
position={[0.6, 0.05, 0.5]}
|
position={[0.6, 0.05, 0.5]}
|
||||||
intensity={scale}
|
intensity={scale}
|
||||||
color={turboColor}
|
color={turboColor}
|
||||||
distance={1}
|
/>
|
||||||
/> */}
|
|
||||||
|
|
||||||
<mesh position={[0.6, 0.05, 0.5]} scale={scale}>
|
<mesh position={[0.6, 0.05, 0.5]} scale={scale}>
|
||||||
<sphereGeometry args={[0.05, 16, 16]} />
|
<sphereGeometry args={[0.05, 16, 16]} />
|
||||||
|
@ -417,12 +492,11 @@ export const PlayerController = () => {
|
||||||
glowSharpness={1}
|
glowSharpness={1}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
{/* <pointLight
|
<pointLight
|
||||||
position={[-0.6, 0.05, 0.5]}
|
position={[-0.6, 0.05, 0.5]}
|
||||||
intensity={scale}
|
intensity={scale}
|
||||||
color={turboColor}
|
color={turboColor}
|
||||||
distance={1}
|
/>
|
||||||
/> */}
|
|
||||||
<mesh position={[-0.6, 0.05, 0.5]} scale={scale}>
|
<mesh position={[-0.6, 0.05, 0.5]} scale={scale}>
|
||||||
<sphereGeometry args={[0.05, 16, 16]} />
|
<sphereGeometry args={[0.05, 16, 16]} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
|
@ -448,24 +522,25 @@ export const PlayerController = () => {
|
||||||
<DriftParticlesRight turboColor={turboColor} scale={scale} />
|
<DriftParticlesRight turboColor={turboColor} scale={scale} />
|
||||||
<PointParticle
|
<PointParticle
|
||||||
position={[-0.6, 0.05, 0.5]}
|
position={[-0.6, 0.05, 0.5]}
|
||||||
png="./circle.png"
|
png="./particles/circle.png"
|
||||||
turboColor={turboColor}
|
turboColor={turboColor}
|
||||||
/>
|
/>
|
||||||
<PointParticle
|
<PointParticle
|
||||||
position={[0.6, 0.05, 0.5]}
|
position={[0.6, 0.05, 0.5]}
|
||||||
png="./circle.png"
|
png="./particles/circle.png"
|
||||||
turboColor={turboColor}
|
turboColor={turboColor}
|
||||||
/>
|
/>
|
||||||
<PointParticle
|
<PointParticle
|
||||||
position={[-0.6, 0.05, 0.5]}
|
position={[-0.6, 0.05, 0.5]}
|
||||||
png="./star.png"
|
png="./particles/star.png"
|
||||||
turboColor={turboColor}
|
turboColor={turboColor}
|
||||||
/>
|
/>
|
||||||
<PointParticle
|
<PointParticle
|
||||||
position={[0.6, 0.05, 0.5]}
|
position={[0.6, 0.05, 0.5]}
|
||||||
png="./star.png"
|
png="./particles/star.png"
|
||||||
turboColor={turboColor}
|
turboColor={turboColor}
|
||||||
/>
|
/>
|
||||||
|
<HitParticles shouldLaunch={shouldLaunch}/>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
{/* <ContactShadows frames={1} /> */}
|
{/* <ContactShadows frames={1} /> */}
|
||||||
|
|
|
@ -57,7 +57,6 @@ function setItemAt(instances, bodyPosition, bodyRotation, index) {
|
||||||
.add(bodyPosition);
|
.add(bodyPosition);
|
||||||
|
|
||||||
// Apply the offset to position the skid marks behind the body
|
// Apply the offset to position the skid marks behind the body
|
||||||
console.log(bodyPosition);
|
|
||||||
o.position.copy(bodyPosition.x, bodyPosition.y + 2, bodyPosition.z);
|
o.position.copy(bodyPosition.x, bodyPosition.y + 2, bodyPosition.z);
|
||||||
|
|
||||||
o.rotation.set(0, bodyRotation, 0);
|
o.rotation.set(0, bodyRotation, 0);
|
||||||
|
|
|
@ -3,11 +3,13 @@ Auto-generated by: https://github.com/pmndrs/gltfjsx
|
||||||
Command: npx gltfjsx@6.2.16 .\mariokarttest.glb --shadows
|
Command: npx gltfjsx@6.2.16 .\mariokarttest.glb --shadows
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useRef } from 'react'
|
import React, { useEffect, useRef } from 'react'
|
||||||
import { Cylinder, OrbitControls, Sphere, useGLTF } from '@react-three/drei'
|
import { Cylinder, OrbitControls, Sphere, useGLTF } from '@react-three/drei'
|
||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
import FakeGlowMaterial from '../../ShaderMaterials/FakeGlow/FakeGlowMaterial'
|
import FakeGlowMaterial from '../../ShaderMaterials/FakeGlow/FakeGlowMaterial'
|
||||||
import FakeFlame from '../../ShaderMaterials/FakeFlame/FakeFlame'
|
import FakeFlame from '../../ShaderMaterials/FakeFlame/FakeFlame'
|
||||||
|
import { useStore } from '../../store'
|
||||||
|
import gsap from 'gsap'
|
||||||
|
|
||||||
export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props }) {
|
export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props }) {
|
||||||
const { nodes, materials } = useGLTF('./models/characters/mariokarttest.glb')
|
const { nodes, materials } = useGLTF('./models/characters/mariokarttest.glb')
|
||||||
|
@ -17,6 +19,9 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
|
||||||
const rearWheels = useRef()
|
const rearWheels = useRef()
|
||||||
const frontWheels = useRef()
|
const frontWheels = useRef()
|
||||||
const [scale, setScale] = React.useState(1)
|
const [scale, setScale] = React.useState(1)
|
||||||
|
const { actions } = useStore()
|
||||||
|
const [shouldSlow, setShouldSlow] = React.useState(false)
|
||||||
|
const mario = useRef();
|
||||||
// isBoosting = true;
|
// isBoosting = true;
|
||||||
|
|
||||||
useFrame((_,delta) => {
|
useFrame((_,delta) => {
|
||||||
|
@ -30,12 +35,21 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
|
||||||
} else {
|
} else {
|
||||||
setScale(Math.max(scale - 0.03 * 144 * delta, 0))
|
setScale(Math.max(scale - 0.03 * 144 * delta, 0))
|
||||||
}
|
}
|
||||||
|
setShouldSlow(actions.getShouldSlowDown());
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (shouldSlow) {
|
||||||
|
gsap.to(mario.current.rotation, {duration: 1.5, y: Math.PI * 3})
|
||||||
|
mario.current.rotation.set(0, 0, 0);
|
||||||
|
}
|
||||||
|
}, [shouldSlow])
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
{...props}
|
{...props}
|
||||||
rotation={[0, Math.PI, 0]}
|
rotation={[0, Math.PI, 0]}
|
||||||
dispose={null}
|
dispose={null}
|
||||||
|
ref={mario}
|
||||||
>
|
>
|
||||||
<mesh
|
<mesh
|
||||||
castShadow
|
castShadow
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
Auto-generated by: https://github.com/pmndrs/gltfjsx
|
||||||
|
Command: npx gltfjsx@6.2.16 .\banana_peel_mario_kart.glb --transform --shadows
|
||||||
|
Files: .\banana_peel_mario_kart.glb [186.36KB] > C:\Users\mouli\dev\r3f-vite-starter\public\models\items\banana_peel_mario_kart-transformed.glb [20.2KB] (89%)
|
||||||
|
Author: Anthony Yanez (https://sketchfab.com/paulyanez)
|
||||||
|
License: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
||||||
|
Source: https://sketchfab.com/3d-models/banana-peel-mario-kart-c7fd163741614859ba02f302ce0bce32
|
||||||
|
Title: Banana Peel (Mario Kart)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useRef } from 'react'
|
||||||
|
import { useGLTF } from '@react-three/drei'
|
||||||
|
import { BallCollider, RigidBody } from '@react-three/rapier'
|
||||||
|
import { useFrame } from '@react-three/fiber';
|
||||||
|
import { useStore } from '../../store';
|
||||||
|
|
||||||
|
export function Banana({onCollide, ...props}) {
|
||||||
|
const { nodes, materials } = useGLTF('./models/items/banana_peel_mario_kart-transformed.glb');
|
||||||
|
const rigidBody = useRef();
|
||||||
|
const ref = useRef();
|
||||||
|
const [scale, setScale] = React.useState(0.002);
|
||||||
|
|
||||||
|
const {actions} = useStore();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RigidBody
|
||||||
|
ref={rigidBody}
|
||||||
|
type='fixed'
|
||||||
|
position={props.position}
|
||||||
|
sensor
|
||||||
|
onIntersectionEnter={(event) => {
|
||||||
|
|
||||||
|
if(scale === 0.002) {
|
||||||
|
actions.setShouldSlowDown(true);
|
||||||
|
console.log(ref.current, rigidBody.current);
|
||||||
|
ref.current.visible = false;
|
||||||
|
setScale(0);
|
||||||
|
rigidBody.setEnable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
colliders={false}
|
||||||
|
name='banana'
|
||||||
|
>
|
||||||
|
<BallCollider args={[0.5]} />
|
||||||
|
|
||||||
|
</RigidBody>
|
||||||
|
|
||||||
|
<group {...props} ref={ref} scale={scale} dispose={null}>
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes['Banana_Peel_02_-_Default_0'].geometry} material={materials['02_-_Default']} position={[39.973, -25.006, -0.017]} rotation={[-Math.PI / 2, 0, 0]} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes['Banana_Peel_07_-_Default_0'].geometry} material={materials['07_-_Default']} position={[39.973, -25.006, -0.017]} rotation={[-Math.PI / 2, 0, 0]} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes['Banana_Peel_03_-_Default_0'].geometry} material={materials['03_-_Default']} position={[39.973, -25.006, -0.017]} rotation={[-Math.PI / 2, 0, 0]} />
|
||||||
|
</group>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
useGLTF.preload('./models/items/banana_peel_mario_kart-transformed.glb')
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Auto-generated by: https://github.com/pmndrs/gltfjsx
|
||||||
|
Command: npx gltfjsx@6.2.16 .\mario_shell_red.glb
|
||||||
|
Author: Thomas Fugier (https://sketchfab.com/thomas.fugier)
|
||||||
|
License: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
||||||
|
Source: https://sketchfab.com/3d-models/mario-shell-red-76a81ff57398423d80800259c3d48262
|
||||||
|
Title: Mario Shell Red
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useRef } from 'react'
|
||||||
|
import { useGLTF } from '@react-three/drei'
|
||||||
|
import { BallCollider, RigidBody, vec3 } from '@react-three/rapier'
|
||||||
|
import { useFrame } from '@react-three/fiber';
|
||||||
|
|
||||||
|
export function Shell(props) {
|
||||||
|
const { nodes, materials } = useGLTF('./models/items/mario_shell_red.glb')
|
||||||
|
const rigidBody = useRef();
|
||||||
|
const ref = useRef();
|
||||||
|
|
||||||
|
const shell_speed = 100;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const velocity = {
|
||||||
|
x : -Math.sin(props.rotation) * shell_speed,
|
||||||
|
y : 0,
|
||||||
|
z : -Math.cos(props.rotation) * shell_speed,
|
||||||
|
};
|
||||||
|
|
||||||
|
rigidBody.current.setLinvel(velocity, true);
|
||||||
|
},[]);
|
||||||
|
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
const rigidBodyPosition = rigidBody.current.translation();
|
||||||
|
ref.current.position.set(rigidBodyPosition.x, rigidBodyPosition.y, rigidBodyPosition.z);
|
||||||
|
ref.current.rotation.z += 0.2 * delta * 144;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group dispose={null}>
|
||||||
|
|
||||||
|
<RigidBody ref={rigidBody} type="dynamic" position={props.position} name="shell" colliders={false}>
|
||||||
|
<BallCollider args={[0.5]} />
|
||||||
|
|
||||||
|
</RigidBody>
|
||||||
|
<mesh ref={ref} geometry={nodes.defaultMaterial.geometry} material={materials.Shell} rotation={[-Math.PI / 2, 0, 0]} scale={0.6} />
|
||||||
|
</group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
useGLTF.preload('./models/items/mario_shell_red.glb')
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Auto-generated by: https://github.com/pmndrs/gltfjsx
|
||||||
|
Command: npx gltfjsx@6.2.16 .\mario_kart_item_box.glb
|
||||||
|
Author: Bscott5 (https://sketchfab.com/Bscott5)
|
||||||
|
License: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
||||||
|
Source: https://sketchfab.com/3d-models/mario-kart-item-box-8f6a2b6b17b844c5b5dfa38375289975
|
||||||
|
Title: Mario Kart Item Box
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useRef } from "react";
|
||||||
|
import { useGLTF, Float, MeshTransmissionMaterial, RoundedBox } from "@react-three/drei";
|
||||||
|
import { RigidBody } from "@react-three/rapier";
|
||||||
|
import { useStore } from "../../store";
|
||||||
|
|
||||||
|
export function ItemBox(props) {
|
||||||
|
|
||||||
|
const { actions } = useStore();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RigidBody type="fixed" name="itemBox"
|
||||||
|
sensor
|
||||||
|
onIntersectionEnter={() => {
|
||||||
|
console.log("item box hit");
|
||||||
|
actions.setItem();
|
||||||
|
|
||||||
|
}}
|
||||||
|
position={props.position}
|
||||||
|
>
|
||||||
|
<mesh>
|
||||||
|
<boxGeometry args={[1, 1, 1]} />
|
||||||
|
<meshStandardMaterial color="green" />
|
||||||
|
</mesh>
|
||||||
|
</RigidBody>
|
||||||
|
<group {...props} dispose={null}>
|
||||||
|
|
||||||
|
|
||||||
|
<Float
|
||||||
|
speed={2} // Animation speed, defaults to 1
|
||||||
|
rotationIntensity={20} // XYZ rotation intensity, defaults to 1
|
||||||
|
floatIntensity={1} // Up/down float intensity, works like a multiplier with floatingRange,defaults to 1
|
||||||
|
floatingRange={[1, 2]} // Range of y-axis values the object will float within, defaults to [-0.1,0.1]
|
||||||
|
>
|
||||||
|
<mesh>
|
||||||
|
<boxGeometry args={[1, 1, 1]} />
|
||||||
|
<meshBasicMaterial color="green" />
|
||||||
|
</mesh>
|
||||||
|
</Float>
|
||||||
|
</group>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -7,6 +7,10 @@ export const playAudio = (path, callback) => {
|
||||||
}
|
}
|
||||||
audio.play();
|
audio.play();
|
||||||
};
|
};
|
||||||
|
export const items = [
|
||||||
|
"banana",
|
||||||
|
"shell",
|
||||||
|
]
|
||||||
|
|
||||||
export const useStore = create((set, get) => ({
|
export const useStore = create((set, get) => ({
|
||||||
particles1: [],
|
particles1: [],
|
||||||
|
@ -14,6 +18,11 @@ export const useStore = create((set, get) => ({
|
||||||
bodyPosition: [0, 0, 0],
|
bodyPosition: [0, 0, 0],
|
||||||
bodyRotation: [0, 0, 0],
|
bodyRotation: [0, 0, 0],
|
||||||
pastPositions: [],
|
pastPositions: [],
|
||||||
|
shouldSlowdown: false,
|
||||||
|
bananas: [],
|
||||||
|
items: ["banana", "shell"],
|
||||||
|
item: "",
|
||||||
|
shells: [],
|
||||||
addPastPosition: (position) => {
|
addPastPosition: (position) => {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
pastPositions: [position, ...state.pastPositions.slice(0, 499)],
|
pastPositions: [position, ...state.pastPositions.slice(0, 499)],
|
||||||
|
@ -52,5 +61,45 @@ export const useStore = create((set, get) => ({
|
||||||
getBodyRotation: () => {
|
getBodyRotation: () => {
|
||||||
return get().bodyRotation;
|
return get().bodyRotation;
|
||||||
},
|
},
|
||||||
|
setShouldSlowDown: (shouldSlowdown) => {
|
||||||
|
set({ shouldSlowdown });
|
||||||
|
},
|
||||||
|
getShouldSlowDown: () => {
|
||||||
|
return get().shouldSlowdown;
|
||||||
|
},
|
||||||
|
addBanana: (banana) => {
|
||||||
|
set((state) => ({
|
||||||
|
bananas: [...state.bananas, banana],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
removeBanana: (banana) => {
|
||||||
|
set((state) => ({
|
||||||
|
bananas: state.bananas.filter((b) => b.id !== banana.id),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
getBananas: () => {
|
||||||
|
return get().bananas;
|
||||||
|
},
|
||||||
|
setItem:() => {
|
||||||
|
set((state) => ({
|
||||||
|
item: state.items[Math.floor(Math.random() * state.items.length)],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
useItem:() => {
|
||||||
|
set((state) => ({
|
||||||
|
item: "",
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
addShell: (shell) => {
|
||||||
|
set((state) => ({
|
||||||
|
shells: [...state.shells, shell],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
removeShell: (shell) => {
|
||||||
|
set((state) => ({
|
||||||
|
shells: state.shells.filter((s) => s.id !== shell.id),
|
||||||
|
}));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
export const useGamepad = () => {
|
||||||
|
const [gamepadInfo, setGamepadInfo] = useState({ connected: false, buttonA: false, buttonB :false, buttonX: false, buttonY:false, joystick: [0, 0], joystickRight : [0,0], RB: false, LB: false, RT: false, LT: false, start: false, select: false, up: false, down: false, left: false, right: false});
|
||||||
|
|
||||||
|
// Function to update gamepad state
|
||||||
|
const updateGamepadState = () => {
|
||||||
|
const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
|
||||||
|
const gamepad = gamepads[0]; // Assuming the first gamepad
|
||||||
|
|
||||||
|
if (gamepad) {
|
||||||
|
const newGamepadInfo = {
|
||||||
|
connected: true,
|
||||||
|
buttonA: gamepad.buttons[0].pressed,
|
||||||
|
buttonB: gamepad.buttons[1].pressed,
|
||||||
|
buttonX: gamepad.buttons[2].pressed,
|
||||||
|
buttonY: gamepad.buttons[3].pressed,
|
||||||
|
joystickRight: [gamepad.axes[2], gamepad.axes[3]],
|
||||||
|
LT: gamepad.buttons[6].pressed,
|
||||||
|
RT: gamepad.buttons[7].pressed,
|
||||||
|
LB: gamepad.buttons[4].pressed,
|
||||||
|
RB: gamepad.buttons[5].pressed,
|
||||||
|
|
||||||
|
start: gamepad.buttons[9].pressed,
|
||||||
|
select: gamepad.buttons[8].pressed,
|
||||||
|
up: gamepad.buttons[12].pressed,
|
||||||
|
down: gamepad.buttons[13].pressed,
|
||||||
|
left: gamepad.buttons[14].pressed,
|
||||||
|
right: gamepad.buttons[15].pressed,
|
||||||
|
joystick: [gamepad.axes[0], gamepad.axes[1]]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update state only if there's a change
|
||||||
|
if (JSON.stringify(newGamepadInfo) !== JSON.stringify(gamepadInfo)) {
|
||||||
|
setGamepadInfo(newGamepadInfo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (gamepadInfo.connected) {
|
||||||
|
setGamepadInfo({ connected: false, buttonA: false, buttonB :false, buttonX: false, buttonY:false, joystick: [0, 0], joystickRight : [0,0], RB: false, LB: false, RT: false, LT: false, start: false, select: false, up: false, down: false, left: false, right: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const gamepadConnected = () => {
|
||||||
|
console.log('Gamepad connected!');
|
||||||
|
updateGamepadState();
|
||||||
|
};
|
||||||
|
|
||||||
|
const gamepadDisconnected = () => {
|
||||||
|
console.log('Gamepad disconnected!');
|
||||||
|
setGamepadInfo({ connected : false, buttonA: false, buttonB :false, buttonX: false, buttonY:false, joystick: [0, 0], joystickRight : [0,0], RB: false, LB: false, RT: false, LT: false, start: false, select: false, up: false, down: false, left: false, right: false});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('gamepadconnected', gamepadConnected);
|
||||||
|
window.addEventListener('gamepaddisconnected', gamepadDisconnected);
|
||||||
|
|
||||||
|
const interval = setInterval(updateGamepadState, 100);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('gamepadconnected', gamepadConnected);
|
||||||
|
window.removeEventListener('gamepaddisconnected', gamepadDisconnected);
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, [gamepadInfo]);
|
||||||
|
|
||||||
|
return gamepadInfo;
|
||||||
|
};
|