added item system
|
@ -18,6 +18,7 @@
|
|||
"react-dom": "^18.2.0",
|
||||
"react-gamepad": "^1.0.3",
|
||||
"three": "^0.160.1",
|
||||
"three-mesh-bvh": "^0.7.0",
|
||||
"zustand": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"react-dom": "^18.2.0",
|
||||
"react-gamepad": "^1.0.3",
|
||||
"three": "^0.160.1",
|
||||
"three-mesh-bvh": "^0.7.0",
|
||||
"zustand": "^4.5.0"
|
||||
},
|
||||
"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
|
||||
gravity={[0, -90, 0]}
|
||||
timeStep={'vary'}
|
||||
// debug
|
||||
>
|
||||
<KeyboardControls map={map}>
|
||||
<Experience />
|
||||
|
|
|
@ -1,21 +1,62 @@
|
|||
import { Environment, OrbitControls, PerspectiveCamera, Lightformer } 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 {
|
||||
Environment,
|
||||
OrbitControls,
|
||||
PerspectiveCamera,
|
||||
Lightformer,
|
||||
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 = () => {
|
||||
const onCollide = (event) => {
|
||||
console.log(event);
|
||||
};
|
||||
const { bananas, shells } = useStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
<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 /> */}
|
||||
<Ground position={[0, 0, 0]} />
|
||||
<Environment
|
||||
resolution={256}
|
||||
preset='lobby'
|
||||
<Environment 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
|
||||
position={[10, 50, -30]}
|
||||
|
@ -25,17 +66,15 @@ export const Experience = () => {
|
|||
shadow-camera-left={-300}
|
||||
shadow-camera-right={300}
|
||||
shadow-camera-top={300}
|
||||
shadow-camera-bottom={-3000}
|
||||
shadow-camera-bottom={-300}
|
||||
castShadow
|
||||
/>
|
||||
|
||||
<Paris position={[0, 0, 0]} />
|
||||
<EffectComposer
|
||||
multisampling={0}
|
||||
disableNormalPass
|
||||
disableSSAO
|
||||
disableDepthPass
|
||||
|
||||
>
|
||||
<SMAA />
|
||||
<N8AO distanceFalloff={1} aoRadius={1} intensity={3} />
|
||||
|
@ -46,11 +85,11 @@ export const Experience = () => {
|
|||
intensity={0.5}
|
||||
/>
|
||||
|
||||
<TiltShift2/>
|
||||
<TiltShift2 />
|
||||
<ChromaticAberration offset={[0.0006, 0.0006]} />
|
||||
<HueSaturation saturation={0.1} />
|
||||
<Vignette eskil={false} offset={0.1} darkness={0.4} />
|
||||
</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 }) => {
|
||||
const texture = useLoader(THREE.TextureLoader, png);
|
||||
const pointsRef = useRef();
|
||||
const materialRef = useRef();
|
||||
const [size, setSize] = useState(0);
|
||||
const [opacity, setOpacity] = useState(1);
|
||||
|
||||
|
@ -18,7 +19,9 @@ export const PointParticle = ({ position, png, turboColor }) => {
|
|||
}, [position]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (materialRef.current) {
|
||||
materialRef.current.color.multiplyScalar(10);
|
||||
}
|
||||
setSize(0);
|
||||
setOpacity(1);
|
||||
}, [turboColor]);
|
||||
|
@ -36,6 +39,7 @@ export const PointParticle = ({ position, png, turboColor }) => {
|
|||
return (
|
||||
<points ref={pointsRef} geometry={points}>
|
||||
<pointsMaterial
|
||||
ref={materialRef}
|
||||
size={size}
|
||||
alphaMap={texture}
|
||||
transparent={true}
|
||||
|
|
|
@ -23,6 +23,7 @@ import { FlameParticles } from "./Particles/FlameParticles";
|
|||
import { useStore } from "./store";
|
||||
import { Cylinder } from "@react-three/drei";
|
||||
import FakeGlowMaterial from "./ShaderMaterials/FakeGlow/FakeGlowMaterial";
|
||||
import { HitParticles } from "./Particles/HitParticles";
|
||||
|
||||
export const PlayerController = () => {
|
||||
const upPressed = useKeyboardControls((state) => state[Controls.up]);
|
||||
|
@ -30,6 +31,8 @@ export const PlayerController = () => {
|
|||
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 [isOnGround, setIsOnGround] = useState(false);
|
||||
const body = useRef();
|
||||
const kart = useRef();
|
||||
|
@ -73,6 +76,14 @@ export const PlayerController = () => {
|
|||
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 { actions, shouldSlowDown, item, bananas} = useStore();
|
||||
const slowDownDuration = useRef(1500);
|
||||
|
||||
|
||||
useFrame(({ pointer, clock }, delta) => {
|
||||
const time = clock.getElapsedTime();
|
||||
|
@ -86,7 +97,6 @@ export const PlayerController = () => {
|
|||
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;
|
||||
|
@ -121,6 +131,8 @@ export const PlayerController = () => {
|
|||
targetXPosition = -camMaxOffset * -pointer.x;
|
||||
}
|
||||
// ACCELERATING
|
||||
const shouldSlow = actions.getShouldSlowDown();
|
||||
|
||||
|
||||
if (upPressed && currentSpeed < maxSpeed) {
|
||||
// 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
|
||||
if (downPressed && currentSpeed < -maxSpeed) {
|
||||
setCurrentSpeed(
|
||||
|
@ -359,6 +387,55 @@ export const PlayerController = () => {
|
|||
|
||||
// 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())
|
||||
});
|
||||
|
||||
|
@ -367,20 +444,19 @@ export const PlayerController = () => {
|
|||
<RigidBody
|
||||
ref={body}
|
||||
colliders={false}
|
||||
position={[8, 20, -119]}
|
||||
position={[8, 60, -119]}
|
||||
centerOfMass={[0, -1, 0]}
|
||||
mass={3}
|
||||
ccd
|
||||
name="player"
|
||||
>
|
||||
<BallCollider
|
||||
args={[0.5]}
|
||||
mass={3}
|
||||
onCollisionEnter={(event) => {
|
||||
onCollisionEnter={({other}) => {
|
||||
isOnFloor.current = true;
|
||||
}}
|
||||
// onCollisionExit={(event) => {
|
||||
// isOnFloor.current = false
|
||||
// }}
|
||||
|
||||
/>
|
||||
</RigidBody>
|
||||
|
||||
|
@ -391,12 +467,11 @@ export const PlayerController = () => {
|
|||
steeringAngleWheels={steeringAngleWheels}
|
||||
isBoosting={isBoosting}
|
||||
/>
|
||||
{/* <pointLight
|
||||
<pointLight
|
||||
position={[0.6, 0.05, 0.5]}
|
||||
intensity={scale}
|
||||
color={turboColor}
|
||||
distance={1}
|
||||
/> */}
|
||||
/>
|
||||
|
||||
<mesh position={[0.6, 0.05, 0.5]} scale={scale}>
|
||||
<sphereGeometry args={[0.05, 16, 16]} />
|
||||
|
@ -417,12 +492,11 @@ export const PlayerController = () => {
|
|||
glowSharpness={1}
|
||||
/>
|
||||
</mesh>
|
||||
{/* <pointLight
|
||||
<pointLight
|
||||
position={[-0.6, 0.05, 0.5]}
|
||||
intensity={scale}
|
||||
color={turboColor}
|
||||
distance={1}
|
||||
/> */}
|
||||
/>
|
||||
<mesh position={[-0.6, 0.05, 0.5]} scale={scale}>
|
||||
<sphereGeometry args={[0.05, 16, 16]} />
|
||||
<meshStandardMaterial
|
||||
|
@ -448,24 +522,25 @@ export const PlayerController = () => {
|
|||
<DriftParticlesRight turboColor={turboColor} scale={scale} />
|
||||
<PointParticle
|
||||
position={[-0.6, 0.05, 0.5]}
|
||||
png="./circle.png"
|
||||
png="./particles/circle.png"
|
||||
turboColor={turboColor}
|
||||
/>
|
||||
<PointParticle
|
||||
position={[0.6, 0.05, 0.5]}
|
||||
png="./circle.png"
|
||||
png="./particles/circle.png"
|
||||
turboColor={turboColor}
|
||||
/>
|
||||
<PointParticle
|
||||
position={[-0.6, 0.05, 0.5]}
|
||||
png="./star.png"
|
||||
png="./particles/star.png"
|
||||
turboColor={turboColor}
|
||||
/>
|
||||
<PointParticle
|
||||
position={[0.6, 0.05, 0.5]}
|
||||
png="./star.png"
|
||||
png="./particles/star.png"
|
||||
turboColor={turboColor}
|
||||
/>
|
||||
<HitParticles shouldLaunch={shouldLaunch}/>
|
||||
</group>
|
||||
|
||||
{/* <ContactShadows frames={1} /> */}
|
||||
|
|
|
@ -57,7 +57,6 @@ function setItemAt(instances, bodyPosition, bodyRotation, index) {
|
|||
.add(bodyPosition);
|
||||
|
||||
// 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.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
|
||||
*/
|
||||
|
||||
import React, { useRef } from 'react'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { Cylinder, OrbitControls, Sphere, useGLTF } from '@react-three/drei'
|
||||
import { useFrame } from '@react-three/fiber'
|
||||
import FakeGlowMaterial from '../../ShaderMaterials/FakeGlow/FakeGlowMaterial'
|
||||
import FakeFlame from '../../ShaderMaterials/FakeFlame/FakeFlame'
|
||||
import { useStore } from '../../store'
|
||||
import gsap from 'gsap'
|
||||
|
||||
export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props }) {
|
||||
const { nodes, materials } = useGLTF('./models/characters/mariokarttest.glb')
|
||||
|
@ -17,6 +19,9 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
|
|||
const rearWheels = useRef()
|
||||
const frontWheels = useRef()
|
||||
const [scale, setScale] = React.useState(1)
|
||||
const { actions } = useStore()
|
||||
const [shouldSlow, setShouldSlow] = React.useState(false)
|
||||
const mario = useRef();
|
||||
// isBoosting = true;
|
||||
|
||||
useFrame((_,delta) => {
|
||||
|
@ -30,12 +35,21 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
|
|||
} else {
|
||||
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 (
|
||||
<group
|
||||
{...props}
|
||||
rotation={[0, Math.PI, 0]}
|
||||
dispose={null}
|
||||
ref={mario}
|
||||
>
|
||||
<mesh
|
||||
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();
|
||||
};
|
||||
export const items = [
|
||||
"banana",
|
||||
"shell",
|
||||
]
|
||||
|
||||
export const useStore = create((set, get) => ({
|
||||
particles1: [],
|
||||
|
@ -14,6 +18,11 @@ export const useStore = create((set, get) => ({
|
|||
bodyPosition: [0, 0, 0],
|
||||
bodyRotation: [0, 0, 0],
|
||||
pastPositions: [],
|
||||
shouldSlowdown: false,
|
||||
bananas: [],
|
||||
items: ["banana", "shell"],
|
||||
item: "",
|
||||
shells: [],
|
||||
addPastPosition: (position) => {
|
||||
set((state) => ({
|
||||
pastPositions: [position, ...state.pastPositions.slice(0, 499)],
|
||||
|
@ -52,5 +61,45 @@ export const useStore = create((set, get) => ({
|
|||
getBodyRotation: () => {
|
||||
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;
|
||||
};
|