Compare commits
62 Commits
gamepadSup
...
main
Author | SHA1 | Date |
---|---|---|
Lunakepio | 8d9623cac8 | |
Michael Dougall | 17543f3b5c | |
Michael Dougall | 1b58a27e9d | |
Lunakepio | b0d0a9126c | |
Lunakepio | c0895c0c84 | |
Spencer Vaughn | 22f2461cc7 | |
Orche | cd1d099392 | |
Alex | ba5be77ae5 | |
Lunakepio | 9f61b3e53c | |
Alex | a96849f10d | |
Alex | c93290cd6d | |
Alex | 7665cd70fa | |
Lunakepio | 8b08815d5c | |
DIDIT PRADITYA | 64f52c4aa9 | |
Lunakepio | 0422ac68c9 | |
Riccardo Venturini | 7275250fe0 | |
Riccardo Venturini | 764a3d2f30 | |
Lunakepio | 4d0200845c | |
Riccardo Venturini | 35085055bc | |
Wai | e466e32483 | |
Wai | f25076b904 | |
Lunakepio | 5ac25a1de8 | |
Justin Dhillon | 5aa2731168 | |
Lunakepio | fa5c8b4f60 | |
Lunakepio | e89f666184 | |
Riccardo Venturini | e4a4d22374 | |
[Naman Kishwan] | a12ccb3452 | |
Lunakepio | ef51e33385 | |
Lunakepio | 2a78567df5 | |
Riccardo Venturini | f19e940c3a | |
CSK | 1a0c1b9b28 | |
CSK | 66e1900296 | |
CSK | c4b9175658 | |
CSK | 9b0f6cbdc6 | |
Lunakepio | d49e47b504 | |
Somunachimso Nzenwa | 628cf726ae | |
Alex | e169e12e55 | |
Alex | b6e723dadc | |
Alex | 06b8beff00 | |
Lunakepio | aab364eb0a | |
Alex | 1504f44959 | |
Alex | 6ed6c0ff27 | |
Alex | 5cc900ab35 | |
Alex | c22c9e9012 | |
Alex | 3ef3ec7d25 | |
Lunakepio | 310e471fab | |
Alex | 140413399f | |
Alex | ef9e58fa21 | |
Alex | 42680aa707 | |
Alex | b825b0fc20 | |
Alex | df76bb5f35 | |
Alex | bfd691e7ab | |
Alex | db52f49e09 | |
Alex | cff143c925 | |
Alex | bf52a80c29 | |
Alex | 8138f4338f | |
Alex | 51c2f3f8f8 | |
Alex | f22f19590d | |
Alex | 256ee77f1f | |
Alex | 77a14c7733 | |
Alex | 11be02d977 | |
Lunakepio | f356585be2 |
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "Mario Kart Dev Container",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/javascript-node",
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"eamodio.gitlens",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"VisualStudioExptTeam.vscodeintellicode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"dsznajder.es7-react-js-snippets",
|
||||||
|
"antfu.vite",
|
||||||
|
"meganrogge.template-string-converter",
|
||||||
|
"ambar.bundle-size",
|
||||||
|
"aaron-bond.better-comments"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwardPorts": [4000],
|
||||||
|
"portsAttributes": {
|
||||||
|
"4000": {
|
||||||
|
"label": "Application",
|
||||||
|
"onAutoForward": "notify"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postCreateCommand": "npm install"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://triplex.dev/config.schema.json",
|
||||||
|
"components": ["../src/components/**/*.(j|t)sx"],
|
||||||
|
"files": ["../src/components/**/*.(j|t)sx"],
|
||||||
|
"provider": "./provider.tsx",
|
||||||
|
"assetsDir": "models"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Physics } from "@react-three/rapier";
|
||||||
|
|
||||||
|
export default function Provider({
|
||||||
|
children,
|
||||||
|
physicsDisabled = true,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
physicsDisabled?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Physics gravity={[0, -90, 0]} timeStep="vary" paused={physicsDisabled}>
|
||||||
|
{children}
|
||||||
|
</Physics>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) [2022] [Ron Waller]
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Mario Kart 3.js - JavaScript/WebGL Mario Kart
|
||||||
|
|
||||||
|
[Link](https://mario-kart-3-js.vercel.app/)
|
||||||
|
|
||||||
|
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/Lunakepio/Mario-Kart-3.js)
|
||||||
|
|
||||||
|
DISCLAIMER : This is not a completed project, I would say around 50% of the work has been done up to now. It takes a lot of time so please enjoy.
|
||||||
|
|
||||||
|
## How to install
|
||||||
|
|
||||||
|
Fork the repository
|
||||||
|
|
||||||
|
Clone the repository or download it
|
||||||
|
|
||||||
|
Open your terminal and inside the project folder, run :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the dev server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to use (Gameplay)
|
||||||
|
|
||||||
|
- Use the <kbd>W</kbd> key to accelerate, the mouse to steer for now (will be updated for mobile/gamepad/keyboard).
|
||||||
|
|
||||||
|
- Steer with the mouse.
|
||||||
|
|
||||||
|
- Press and hold the space bar to initiate a drift. Steer and counter steer to maintain the drift. release it to get a mini - turbo.
|
||||||
|
|
||||||
|
- Press <kbd>E</kbd> key to use the current item.
|
||||||
|
|
||||||
|
- Press <kbd>R</kbd> to reset your position, usable anytime.
|
||||||
|
|
||||||
|
## How to use (Code)
|
||||||
|
|
||||||
|
- Anything needs update.
|
||||||
|
|
||||||
|
- You can also edit the README to add elements to the To-do List.
|
||||||
|
|
||||||
|
- Feel free to bring your ideas to the project even if you can't code them.
|
||||||
|
|
||||||
|
## How to use (Editor)
|
||||||
|
|
||||||
|
- [Download Triplex](https://triplex.dev/download)
|
||||||
|
|
||||||
|
- Open this project in Triplex from the projects root directory
|
||||||
|
|
||||||
|
- Iterate on individual components, set props, have some fun
|
||||||
|
|
||||||
|
- [Learn more about Triplex](https://triplex.dev/docs/get-started/user-interface)
|
||||||
|
|
||||||
|
## TO - DO
|
||||||
|
|
||||||
|
- [ ] Design Landing page
|
||||||
|
|
||||||
|
- [ ] Add items
|
||||||
|
|
||||||
|
- [ ] Add texture to the flame shaders
|
||||||
|
|
||||||
|
- [ ] Add curve/length modifiers to drift particles 3/4
|
||||||
|
|
||||||
|
- [ ] Add Skid marks
|
||||||
|
|
||||||
|
- [x] Add smokes
|
||||||
|
|
||||||
|
- [ ] Add wind screen effect when boosting
|
||||||
|
|
||||||
|
- [ ] Improve sound design quality
|
||||||
|
|
||||||
|
- [ ] Design UI for HUD
|
||||||
|
|
||||||
|
- [ ] Make Time Trial mode
|
||||||
|
|
||||||
|
- [ ] Design tracks and checkpoints
|
||||||
|
|
||||||
|
- [ ] Improve code quality
|
||||||
|
|
||||||
|
- [ ] Add Minimap
|
||||||
|
|
||||||
|
- [ ] Items
|
||||||
|
- [ ] Tennis ball
|
||||||
|
- [ ] Bomb
|
||||||
|
- [ ] Real red shell
|
||||||
|
- [ ] Treats
|
||||||
|
- [ ] ?
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
Auto-generated by: https://github.com/pmndrs/gltfjsx
|
|
||||||
Command: npx gltfjsx@6.2.16 c:\Users\mouli\Downloads\mario_kart.glb -o c:\Users\mouli\Desktop\mario_kart.tsx --types
|
|
||||||
Author: TheShibeLord (https://sketchfab.com/TheShibeLord)
|
|
||||||
License: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
|
||||||
Source: https://sketchfab.com/3d-models/mario-kart-66cc131575344ab69238ec5872f24927
|
|
||||||
Title: Mario Kart
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as THREE from 'three'
|
|
||||||
import React, { useRef } from 'react'
|
|
||||||
import { useGLTF } from '@react-three/drei'
|
|
||||||
import { GLTF } from 'three-stdlib'
|
|
||||||
|
|
||||||
type GLTFResult = GLTF & {
|
|
||||||
nodes: {
|
|
||||||
mt_mario: THREE.Mesh
|
|
||||||
mt_kart_Mario_S: THREE.Mesh
|
|
||||||
mt_Kart_Mario_Tire_S: THREE.Mesh
|
|
||||||
}
|
|
||||||
materials: {
|
|
||||||
mt_mario: THREE.MeshStandardMaterial
|
|
||||||
mt_kart_Mario_S: THREE.MeshStandardMaterial
|
|
||||||
mt_Kart_Mario_Tire_S: THREE.MeshStandardMaterial
|
|
||||||
}
|
|
||||||
animations: GLTFAction[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type ContextType = Record<string, React.ForwardRefExoticComponent<JSX.IntrinsicElements['mesh']>>
|
|
||||||
|
|
||||||
export function Model(props: JSX.IntrinsicElements['group']) {
|
|
||||||
const { nodes, materials } = useGLTF('/mario_kart.glb') as GLTFResult
|
|
||||||
return (
|
|
||||||
<group {...props} dispose={null}>
|
|
||||||
<group rotation={[-Math.PI / 2, 0, 0]}>
|
|
||||||
<group rotation={[Math.PI / 2, 0, 0]}>
|
|
||||||
<mesh geometry={nodes.mt_mario.geometry} material={materials.mt_mario} />
|
|
||||||
<mesh geometry={nodes.mt_kart_Mario_S.geometry} material={materials.mt_kart_Mario_S} />
|
|
||||||
<mesh geometry={nodes.mt_Kart_Mario_Tire_S.geometry} material={materials.mt_Kart_Mario_Tire_S} />
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
useGLTF.preload('/mario_kart.glb')
|
|
|
@ -14,10 +14,13 @@
|
||||||
"@react-three/rapier": "^1.2.1",
|
"@react-three/rapier": "^1.2.1",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.12.5",
|
||||||
"leva": "^0.9.35",
|
"leva": "^0.9.35",
|
||||||
|
"playroomkit": "^0.0.66",
|
||||||
"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",
|
||||||
"zustand": "^4.5.0"
|
"zustand": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1394,9 +1397,9 @@
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/three": {
|
"node_modules/@types/three": {
|
||||||
"version": "0.160.0",
|
"version": "0.161.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.160.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.161.2.tgz",
|
||||||
"integrity": "sha512-jWlbUBovicUKaOYxzgkLlhkiEQJkhCVvg4W2IYD2trqD2om3VK4DGLpHH5zQHNr7RweZK/5re/4IVhbhvxbV9w==",
|
"integrity": "sha512-DazpZ+cIfBzbW/p0zm6G8CS03HBMd748A3R1ZOXHpqaXZLv2I5zNgQUrRG//UfJ6zYFp2cUoCQaOLaz8ubH07w==",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/stats.js": "*",
|
"@types/stats.js": "*",
|
||||||
|
@ -2422,6 +2425,15 @@
|
||||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/playroomkit": {
|
||||||
|
"version": "0.0.66",
|
||||||
|
"resolved": "https://registry.npmjs.org/playroomkit/-/playroomkit-0.0.66.tgz",
|
||||||
|
"integrity": "sha512-lBYSLR/0AebQhWteMgkXiMXyMPxMqGz2uptA3QrBig4NCn5r3ViIXWIFmsdbLSG9spKrFPVTaYRClUq3fSCxDw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17.0.2 <= 18",
|
||||||
|
"react-dom": ">=17.0.2 <= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.33",
|
"version": "8.4.33",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
|
||||||
|
@ -2596,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",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite --host",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
|
@ -15,10 +15,13 @@
|
||||||
"@react-three/rapier": "^1.2.1",
|
"@react-three/rapier": "^1.2.1",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.12.5",
|
||||||
"leva": "^0.9.35",
|
"leva": "^0.9.35",
|
||||||
|
"playroomkit": "^0.0.66",
|
||||||
"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",
|
||||||
"zustand": "^4.5.0"
|
"zustand": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
After Width: | Height: | Size: 932 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 500 KiB |
After Width: | Height: | Size: 408 KiB |
After Width: | Height: | Size: 393 KiB |
After Width: | Height: | Size: 510 KiB |
After Width: | Height: | Size: 450 KiB |
After Width: | Height: | Size: 641 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 68 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 |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 9.9 KiB |
50
src/App.jsx
|
@ -1,8 +1,12 @@
|
||||||
import { Canvas } from '@react-three/fiber'
|
import { Canvas } from '@react-three/fiber'
|
||||||
import { Experience } from './components/Experience'
|
import { Experience } from './components/Experience'
|
||||||
import { Suspense, useMemo } from 'react'
|
import { Suspense, useEffect, useMemo } from 'react'
|
||||||
import { Physics } from '@react-three/rapier'
|
import { Physics } from '@react-three/rapier'
|
||||||
import { KeyboardControls, Loader, OrbitControls, Preload, Stats } from '@react-three/drei'
|
import { Environment, KeyboardControls, Loader, OrbitControls, Preload, Stats } from '@react-three/drei'
|
||||||
|
import { insertCoin, onPlayerJoin } from 'playroomkit'
|
||||||
|
import { useStore } from "./components/store";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { ParisBis } from './components/models/tracks/Paris-bis'
|
||||||
|
|
||||||
export const Controls = {
|
export const Controls = {
|
||||||
up: 'up',
|
up: 'up',
|
||||||
|
@ -12,8 +16,10 @@ export const Controls = {
|
||||||
boost: 'boost',
|
boost: 'boost',
|
||||||
shoot: 'shoot',
|
shoot: 'shoot',
|
||||||
slow: 'slow',
|
slow: 'slow',
|
||||||
reset: 'reset'
|
reset: 'reset',
|
||||||
|
escape: 'escape'
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const map = useMemo(
|
const map = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
@ -24,18 +30,46 @@ function App() {
|
||||||
{ name: Controls.jump, keys: ['Space'] },
|
{ name: Controls.jump, keys: ['Space'] },
|
||||||
{ name: Controls.slow, keys: ['Shift'] },
|
{ name: Controls.slow, keys: ['Shift'] },
|
||||||
{ name: Controls.shoot, keys: ['KeyE', 'Click'] },
|
{ name: Controls.shoot, keys: ['KeyE', 'Click'] },
|
||||||
{ name: Controls.reset, keys: ['KeyR'] }
|
{ name: Controls.reset, keys: ['KeyR'] },
|
||||||
|
{ name: Controls.escape, keys: ['Escape']}
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { actions } = useStore();
|
||||||
|
const start = async () => {
|
||||||
|
await insertCoin({ skipLobby: true});
|
||||||
|
|
||||||
|
onPlayerJoin((state) => {
|
||||||
|
actions.addPlayer(state);
|
||||||
|
|
||||||
|
actions.setId(state.id);
|
||||||
|
|
||||||
|
state.onQuit(() => {
|
||||||
|
actions.removePlayer(state);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
start();
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Loader />
|
||||||
<Canvas
|
<Canvas
|
||||||
shadows
|
// shadows
|
||||||
dpr={1}
|
dpr={1}
|
||||||
gl={{ antialias: false, stencil: false, powerPreference: 'high-performance' }}
|
gl={{ antialias: false, stencil: false, depth:false, powerPreference: 'high-performance' }}
|
||||||
>
|
mode="concurrent"
|
||||||
|
onCreated={({ gl, camera }) => {
|
||||||
|
gl.toneMapping = THREE.AgXToneMapping
|
||||||
|
gl.setClearColor(0x000000, 0)
|
||||||
|
}}>
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
|
<Preload all />
|
||||||
<Physics
|
<Physics
|
||||||
gravity={[0, -90, 0]}
|
gravity={[0, -90, 0]}
|
||||||
timeStep={'vary'}
|
timeStep={'vary'}
|
||||||
|
@ -43,10 +77,10 @@ function App() {
|
||||||
<KeyboardControls map={map}>
|
<KeyboardControls map={map}>
|
||||||
<Experience />
|
<Experience />
|
||||||
</KeyboardControls>
|
</KeyboardControls>
|
||||||
<Stats />
|
|
||||||
</Physics>
|
</Physics>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
128
src/HUD.jsx
|
@ -1,7 +1,11 @@
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
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 { item, gameStarted, actions, controls } = useStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleMouseMove = (e) => {
|
const handleMouseMove = (e) => {
|
||||||
|
@ -23,24 +27,118 @@ export const HUD = () => {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleMove = (e) => {
|
||||||
|
actions.setJoystickX(e.x);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStop = () => {
|
||||||
|
actions.setJoystickX(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
switch (item) {
|
||||||
|
case "banana":
|
||||||
|
setImage("./images/banana.webp");
|
||||||
|
break;
|
||||||
|
case "mushroom":
|
||||||
|
setImage("./images/mushroom.png");
|
||||||
|
break;
|
||||||
|
case "shell":
|
||||||
|
setImage("./images/shell.webp");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setImage("");
|
||||||
|
}
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overlay"> dadasd
|
<div className="overlay">
|
||||||
<div className="logo">
|
{gameStarted && (
|
||||||
<img src="./logo.png" alt="logo" />
|
<>
|
||||||
|
<div className="item">
|
||||||
|
<div className="borderOut">
|
||||||
|
<div className="borderIn">
|
||||||
|
<div className="background">
|
||||||
|
{image && <img src={image} alt="item" width={90} />}
|
||||||
</div>
|
</div>
|
||||||
<div className="wheel">
|
</div>
|
||||||
<img
|
</div>
|
||||||
ref={wheel}
|
</div>
|
||||||
src="./steering_wheel.png"
|
{controls === "touch" && (
|
||||||
alt="steering wheel"
|
<>
|
||||||
className="steering-wheel"
|
<div className="controls joystick">
|
||||||
style={{
|
<Joystick
|
||||||
position: "absolute",
|
size={100}
|
||||||
pointerEvents: "none",
|
sticky={false}
|
||||||
transformOrigin: "center",
|
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);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
actions.setDriftButton(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
actions.setDriftButton(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
drift
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="controls itemButton"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
actions.setItemButton(true);
|
||||||
|
}}
|
||||||
|
onMouseUp={(e) => {
|
||||||
|
actions.setItemButton(false);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
actions.setItemButton(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
actions.setItemButton(false);
|
||||||
|
}}
|
||||||
|
|
||||||
|
>
|
||||||
|
item
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="controls menuButton"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
actions.setMenuButton(true);
|
||||||
|
}}
|
||||||
|
onMouseUp={(e) => {
|
||||||
|
actions.setMenuButton(false);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
actions.setMenuButton(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
actions.setMenuButton(false);
|
||||||
|
}}
|
||||||
|
|
||||||
|
>
|
||||||
|
menu
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import { useStore } from "./components/store";
|
||||||
|
import gsap from "gsap";
|
||||||
|
|
||||||
|
export const Landing = () => {
|
||||||
|
const { gameStarted, actions } = useStore();
|
||||||
|
|
||||||
|
const logo = useRef();
|
||||||
|
const startButton = useRef();
|
||||||
|
const homeRef = useRef();
|
||||||
|
const [setupStatus, setSetupStatus] = useState(0);
|
||||||
|
const [controlStyle, setControlStyle] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const tl = gsap.timeline();
|
||||||
|
|
||||||
|
if (setupStatus === 0) {
|
||||||
|
if (logo.current && startButton.current) {
|
||||||
|
tl.from(logo.current, {
|
||||||
|
scale: 122,
|
||||||
|
opacity: 0,
|
||||||
|
duration: 0,
|
||||||
|
ease: "power4.out",
|
||||||
|
})
|
||||||
|
.to(logo.current, {
|
||||||
|
scale: 1,
|
||||||
|
opacity: 1,
|
||||||
|
duration: 1.5,
|
||||||
|
ease: "power4.out",
|
||||||
|
})
|
||||||
|
.to(startButton.current, {
|
||||||
|
opacity: 1,
|
||||||
|
duration: 3,
|
||||||
|
delay: 1,
|
||||||
|
ease: "power4.out",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeyDown = (event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
setSetupStatus(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.body.addEventListener('keydown', handleKeyDown);
|
||||||
|
return () => {
|
||||||
|
document.body.removeEventListener('keydown', handleKeyDown);
|
||||||
|
};
|
||||||
|
}, [setupStatus]);
|
||||||
|
|
||||||
|
if (gameStarted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{setupStatus === 0 && (
|
||||||
|
<div className="home" ref={homeRef}>
|
||||||
|
<div className="logo">
|
||||||
|
<img ref={logo} src="./logo.png" alt="logo" />
|
||||||
|
</div>
|
||||||
|
<div className="start" ref={startButton}>
|
||||||
|
<button className="start-button"
|
||||||
|
onClick={() => setSetupStatus(1)}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
setSetupStatus(1);
|
||||||
|
}}} autoFocus>
|
||||||
|
PRESS ENTER TO START
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{setupStatus === 1 && (
|
||||||
|
<div className="home">
|
||||||
|
<div className="glassy">
|
||||||
|
<h1>CHOOSE YOUR CONTROL STYLE</h1>
|
||||||
|
|
||||||
|
<div className="articles">
|
||||||
|
<div className={controlStyle === "keyboard" ? "article selected" : "article"} onClick={() =>
|
||||||
|
setControlStyle("keyboard")}>
|
||||||
|
<img src="./images/keyboard.png" alt="keyboard" />
|
||||||
|
<div className="article_label">
|
||||||
|
<p>Keyboard</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={controlStyle === "gamepad" ? "article selected" : "article"} onClick={() =>
|
||||||
|
setControlStyle("gamepad")}>
|
||||||
|
<img src="./images/gamepad.png" alt="gamepad" />
|
||||||
|
<div className="article_label">
|
||||||
|
<p>Gamepad</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={controlStyle === "mouseKeyboard" ? "article selected" : "article"} onClick={() =>
|
||||||
|
setControlStyle("mouseKeyboard")}>
|
||||||
|
<img src="./images/mousekeyboard.png" alt="mouse & keyboard" />
|
||||||
|
<div className="article_label">
|
||||||
|
<p>Mouse & Keyboard</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={controlStyle === "touch" ? "article selected" : "article"} onClick={() =>
|
||||||
|
setControlStyle("touch")}>
|
||||||
|
<img src="./images/mobile.png" alt="mobile" />
|
||||||
|
<div className="article_label">
|
||||||
|
<p>Mobile</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={controlStyle != "" ? "submit" : "submit disabled"}>
|
||||||
|
<button
|
||||||
|
className={controlStyle != "" ? "submit-button" : "submit-button disabled"}
|
||||||
|
onClick={() => {
|
||||||
|
actions.setControls(controlStyle);
|
||||||
|
actions.setGameStarted(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
CONFIRM
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { Euler, Object3D, Vector3, Matrix4, DoubleSide, Quaternion, TextureLoader, BackSide } from 'three'
|
||||||
|
import { useRef, useLayoutEffect } from 'react'
|
||||||
|
import { useFrame, useLoader } from '@react-three/fiber'
|
||||||
|
import { vec3 } from '@react-three/rapier'
|
||||||
|
|
||||||
|
|
||||||
|
import { useStore } from './store'
|
||||||
|
|
||||||
|
|
||||||
|
const e = new Euler()
|
||||||
|
const m = new Matrix4()
|
||||||
|
const o = new Object3D()
|
||||||
|
const v = new Vector3()
|
||||||
|
const q = new Quaternion()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function Dust({ count = 500, opacity = 0.1, size = 0.6 }) {
|
||||||
|
const smoke01 = useLoader(TextureLoader, './particles/smokes/smoke_01.png');
|
||||||
|
const smoke02 = useLoader(TextureLoader, './particles/smokes/smoke_02.png');
|
||||||
|
const smoke03 = useLoader(TextureLoader, './particles/smokes/smoke_03.png');
|
||||||
|
const smoke04 = useLoader(TextureLoader, './particles/smokes/smoke_04.png');
|
||||||
|
const smoke05 = useLoader(TextureLoader, './particles/smokes/smoke_05.png');
|
||||||
|
const smoke06 = useLoader(TextureLoader, './particles/smokes/smoke_06.png');
|
||||||
|
const smoke07 = useLoader(TextureLoader, './particles/smokes/smoke_07.png');
|
||||||
|
const smoke08 = useLoader(TextureLoader, './particles/smokes/smoke_08.png');
|
||||||
|
|
||||||
|
const texture = [smoke01, smoke02, smoke03, smoke04, smoke05, smoke06, smoke07, smoke08]
|
||||||
|
const ref = useRef(null);
|
||||||
|
const { leftWheel, rightWheel } = useStore();
|
||||||
|
let index = 0
|
||||||
|
let time = 0
|
||||||
|
let i = 0
|
||||||
|
useFrame((state,delta ) => {
|
||||||
|
if(!leftWheel && !rightWheel) return;
|
||||||
|
const rotation = leftWheel.kartRotation;
|
||||||
|
if (state.clock.getElapsedTime() - time > 0.02 && leftWheel && rightWheel && ref.current && leftWheel.isSpinning) {
|
||||||
|
time = state.clock.getElapsedTime()
|
||||||
|
setItemAt(ref.current, rotation, leftWheel, index++);
|
||||||
|
setItemAt(ref.current, rotation, rightWheel, index++);
|
||||||
|
|
||||||
|
if (index === count) index = 0
|
||||||
|
} else {
|
||||||
|
// Shrink old one
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
const direction = new Vector3(Math.sin(time * 6 + i * 10) , 2, 0);
|
||||||
|
ref.current.getMatrixAt(i, m)
|
||||||
|
m.decompose(o.position, q, v)
|
||||||
|
o.scale.setScalar(Math.max(0, v.x - 0.005))
|
||||||
|
o.position.addScaledVector(direction, 0.01)
|
||||||
|
o.updateMatrix()
|
||||||
|
ref.current.setMatrixAt(i, o.matrix)
|
||||||
|
ref.current.instanceMatrix.needsUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if(ref.current){
|
||||||
|
ref.current.geometry.rotateY(-Math.PI / 2)
|
||||||
|
return () => {
|
||||||
|
ref.current.geometry.rotateY(Math.PI / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<instancedMesh frustumCulled={false} ref={ref} args={[undefined, undefined, count]}>
|
||||||
|
<planeGeometry args={[size, size]} />
|
||||||
|
<meshBasicMaterial color={0xfcebc5} transparent map={smoke01} opacity={1} depthWrite={false} side={DoubleSide} />
|
||||||
|
</instancedMesh>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setItemAt(instances, rotation, body, index) {
|
||||||
|
const randomOffset = (Math.random() - 0.5) * 0.3 ;
|
||||||
|
const pos = body.getWorldPosition(v);
|
||||||
|
o.rotation.set(0, rotation + Math.PI / 2, 0);
|
||||||
|
pos.x += randomOffset
|
||||||
|
// pos.y += randomOffset
|
||||||
|
pos.z += randomOffset
|
||||||
|
o.position.copy(pos);
|
||||||
|
o.scale.setScalar(1)
|
||||||
|
o.updateMatrix()
|
||||||
|
instances.setMatrixAt(index, o.matrix)
|
||||||
|
instances.instanceMatrix.needsUpdate = true
|
||||||
|
}
|
|
@ -1,21 +1,182 @@
|
||||||
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 { PlayerControllerGamepad } from "./PlayerControllerGamepad";
|
||||||
|
import { PlayerControllerKeyboard } from "./PlayerControllerKeyboard";
|
||||||
|
import { PlayerControllerTouch } from "./PlayerControllerTouch";
|
||||||
|
import { Paris } from "./models/tracks/Tour_paris_promenade";
|
||||||
|
import {
|
||||||
|
EffectComposer,
|
||||||
|
N8AO,
|
||||||
|
Bloom,
|
||||||
|
TiltShift2,
|
||||||
|
HueSaturation,
|
||||||
|
SMAA,
|
||||||
|
ChromaticAberration,
|
||||||
|
Vignette,
|
||||||
|
LUT,
|
||||||
|
} from "@react-three/postprocessing";
|
||||||
|
import { Banana } from "./models/items/Banana_peel_mario_kart";
|
||||||
|
import { ItemBox } from "./models/misc/Gift";
|
||||||
|
import { useStore } from "./store";
|
||||||
|
import { Shell } from "./models/items/Mario_shell_red";
|
||||||
|
import { Coin } from "./models/misc/Super_mario_bros_coin";
|
||||||
|
import {
|
||||||
|
RPC,
|
||||||
|
getState,
|
||||||
|
insertCoin,
|
||||||
|
isHost,
|
||||||
|
myPlayer,
|
||||||
|
onPlayerJoin,
|
||||||
|
useMultiplayerState,
|
||||||
|
} from "playroomkit";
|
||||||
|
import { PlayerDummies } from "./PlayerDummies";
|
||||||
|
import { useEffect, useState, useRef } from "react";
|
||||||
|
import { useFrame, useLoader } from "@react-three/fiber";
|
||||||
|
import { LUTPass, LUTCubeLoader } from "three-stdlib";
|
||||||
|
import { useCurvedPathPoints } from "./useCurvedPath";
|
||||||
|
import { ParisBis } from "./models/tracks/Paris-bis";
|
||||||
|
import { Skid } from "./Skid";
|
||||||
|
import { Dust } from "./Dust";
|
||||||
|
|
||||||
export const Experience = () => {
|
export const Experience = () => {
|
||||||
|
const onCollide = (event) => {};
|
||||||
|
const { gameStarted, bananas, shells, players, id, actions, controls } =
|
||||||
|
useStore();
|
||||||
|
const [networkBananas, setNetworkBananas] = useMultiplayerState(
|
||||||
|
"bananas",
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { points, loading, error } = useCurvedPathPoints("./CurvedPath.json");
|
||||||
|
|
||||||
|
const [networkShells, setNetworkShells] = useMultiplayerState("shells", []);
|
||||||
|
const [pointest, setPointest] = useState([]);
|
||||||
|
const [currentPoint, setCurrentPoint] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
if (points) {
|
||||||
|
//This is adjusted to Paris scale
|
||||||
|
const scaledPoints = points.map((point) => ({
|
||||||
|
x: point.x * 50,
|
||||||
|
y: point.y * 50,
|
||||||
|
z: point.z * 50,
|
||||||
|
}));
|
||||||
|
setPointest(scaledPoints.reverse());
|
||||||
|
}
|
||||||
|
}, [points]);
|
||||||
|
|
||||||
|
const testing = getState("bananas");
|
||||||
|
const cam = useRef();
|
||||||
|
const lookAtTarget = useRef();
|
||||||
|
// useEffect(() => {
|
||||||
|
// setNetworkBananas(bananas);
|
||||||
|
// }, [bananas]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// setNetworkShells(shells);
|
||||||
|
// }, [shells]);
|
||||||
|
const speedFactor = 5;
|
||||||
|
const { texture } = useLoader(LUTCubeLoader, "./cubicle-99.CUBE");
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
if (!gameStarted) {
|
||||||
|
const camera = cam.current;
|
||||||
|
|
||||||
|
if (currentPoint < pointest.length - 1) {
|
||||||
|
camera.position.lerp(pointest[currentPoint], delta * speedFactor);
|
||||||
|
lookAtTarget.current.position.lerp(
|
||||||
|
pointest[currentPoint + 1],
|
||||||
|
delta * speedFactor
|
||||||
|
);
|
||||||
|
camera.lookAt(lookAtTarget.current.position);
|
||||||
|
|
||||||
|
if (camera.position.distanceTo(pointest[currentPoint]) < 5) {
|
||||||
|
setCurrentPoint(currentPoint + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setCurrentPoint(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PlayerController />
|
{gameStarted &&
|
||||||
{/* <Skid /> */}
|
players.map((player) => {
|
||||||
<Ground position={[0, 0, 0]} />
|
const ControllerComponent =
|
||||||
<Environment
|
controls === "keyboard"
|
||||||
resolution={256}
|
? PlayerControllerKeyboard
|
||||||
preset='lobby'
|
: controls === "gamepad"
|
||||||
|
? PlayerControllerGamepad
|
||||||
|
: controls === "touch"
|
||||||
|
? PlayerControllerTouch
|
||||||
|
: PlayerController;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ControllerComponent
|
||||||
|
key={player.id}
|
||||||
|
player={player}
|
||||||
|
userPlayer={player.id === myPlayer()?.id}
|
||||||
|
setNetworkBananas={setNetworkBananas}
|
||||||
|
setNetworkShells={setNetworkShells}
|
||||||
|
networkBananas={networkBananas}
|
||||||
|
networkShells={networkShells}
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{gameStarted &&
|
||||||
|
players.map((player) => (
|
||||||
|
<PlayerDummies
|
||||||
|
key={player.id}
|
||||||
|
player={player}
|
||||||
|
userPlayer={player.id === myPlayer()?.id}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{!gameStarted && (
|
||||||
|
<>
|
||||||
|
<mesh ref={lookAtTarget}></mesh>
|
||||||
|
<PerspectiveCamera
|
||||||
|
ref={cam}
|
||||||
|
makeDefault
|
||||||
|
position={[0, 2, 0]}
|
||||||
|
far={5000}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{/* <Paris position={[0, 0, 0]} /> */}
|
||||||
|
|
||||||
|
<ParisBis position={[0, 0, 0]} />
|
||||||
|
<ItemBox position={[-20, 2.5, -119]} />
|
||||||
|
<Coin position={[-30, 2, -119]} />
|
||||||
|
<Skid />
|
||||||
|
<Dust />
|
||||||
|
|
||||||
|
<Ground position={[0, 0, 0]} />
|
||||||
|
<Environment resolution={256} preset="lobby" />
|
||||||
|
{networkBananas.map((banana) => (
|
||||||
|
<Banana
|
||||||
|
key={banana.id}
|
||||||
|
position={banana.position}
|
||||||
|
setNetworkBananas={setNetworkBananas}
|
||||||
|
networkBananas={networkBananas}
|
||||||
|
id={banana.id}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{networkShells.map((shell) => (
|
||||||
|
<Shell
|
||||||
|
key={shell.id}
|
||||||
|
id={shell.id}
|
||||||
|
position={shell.position}
|
||||||
|
rotation={shell.rotation}
|
||||||
|
setNetworkShells={setNetworkShells}
|
||||||
|
networkShells={networkShells}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
<directionalLight
|
<directionalLight
|
||||||
position={[10, 50, -30]}
|
position={[10, 50, -30]}
|
||||||
|
@ -25,32 +186,29 @@ 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} /> */}
|
||||||
<Bloom
|
<Bloom
|
||||||
luminanceThreshold={0}
|
luminanceThreshold={0}
|
||||||
mipmapBlur
|
mipmapBlur
|
||||||
luminanceSmoothing={0.01}
|
luminanceSmoothing={0.01}
|
||||||
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.05} />
|
||||||
<Vignette eskil={false} offset={0.1} darkness={0.4} />
|
{/* <Vignette eskil={false} offset={0.1} darkness={0.4} /> */}
|
||||||
</EffectComposer>
|
</EffectComposer>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { Particles2 } from "./Particles2";
|
|
||||||
import { Particles4 } from "./Particles4";
|
|
||||||
|
|
||||||
export const DriftParticlesRight = ({turboColor,scale, ...props}) => {
|
|
||||||
|
|
||||||
// if(scale < 0.8) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (
|
|
||||||
<group {...props}>
|
|
||||||
<Particles2 turboColor={turboColor} scale={scale} />
|
|
||||||
|
|
||||||
</group>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
import { useRef, useMemo } from "react";
|
|
||||||
import { useFrame } from "@react-three/fiber";
|
|
||||||
import * as THREE from 'three';
|
|
||||||
|
|
||||||
export const Particles1 = ({ turboColor, scale, numParticles = 50, ...props }) => {
|
|
||||||
const instancedMeshRef = useRef();
|
|
||||||
const particlesData = useMemo(() => {
|
|
||||||
return new Array(numParticles).fill().map(() => ({
|
|
||||||
position: new THREE.Vector3(-0.6, 0.05, 0.5),
|
|
||||||
velocity: new THREE.Vector3(-Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.02),
|
|
||||||
gravity: -0.003
|
|
||||||
}));
|
|
||||||
}, [numParticles]);
|
|
||||||
|
|
||||||
useFrame((state, delta) => {
|
|
||||||
if (!instancedMeshRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manage visibility directly in the animation loop
|
|
||||||
instancedMeshRef.current.visible = scale >= 0.8;
|
|
||||||
|
|
||||||
if (scale < 0.8) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const deltaScaled = delta * 144; // Scale for 144 FPS
|
|
||||||
particlesData.forEach((particle, i) => {
|
|
||||||
particle.velocity.y += particle.gravity * deltaScaled;
|
|
||||||
|
|
||||||
particle.position.x += particle.velocity.x * deltaScaled;
|
|
||||||
particle.position.y += particle.velocity.y * deltaScaled;
|
|
||||||
particle.position.z += particle.velocity.z * deltaScaled;
|
|
||||||
|
|
||||||
if (particle.position.y < 0.05) {
|
|
||||||
particle.position.set(-0.6, 0.05, 0.5);
|
|
||||||
particle.velocity.set(-Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.02);
|
|
||||||
}
|
|
||||||
const matrix = new THREE.Matrix4();
|
|
||||||
matrix.setPosition(particle.position);
|
|
||||||
instancedMeshRef.current.setMatrixAt(i, matrix);
|
|
||||||
});
|
|
||||||
|
|
||||||
instancedMeshRef.current.instanceMatrix.needsUpdate = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<instancedMesh ref={instancedMeshRef} args={[null, null, numParticles]} visible={scale >= 0.8}>
|
|
||||||
<sphereGeometry args={[0.01, 16, 16]} />
|
|
||||||
<meshStandardMaterial
|
|
||||||
emissive={turboColor}
|
|
||||||
toneMapped={false}
|
|
||||||
emissiveIntensity={5}
|
|
||||||
/>
|
|
||||||
</instancedMesh>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,57 +0,0 @@
|
||||||
import { useRef, useMemo } from "react";
|
|
||||||
import { useFrame } from "@react-three/fiber";
|
|
||||||
import * as THREE from 'three';
|
|
||||||
|
|
||||||
export const Particles2 = ({ turboColor, scale, numParticles = 50, ...props }) => {
|
|
||||||
const instancedMeshRef = useRef();
|
|
||||||
const particlesData = useMemo(() => {
|
|
||||||
return new Array(numParticles).fill().map(() => ({
|
|
||||||
position: new THREE.Vector3(0.6, 0.05, 0.5),
|
|
||||||
velocity: new THREE.Vector3(Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.02),
|
|
||||||
gravity: -0.003
|
|
||||||
}));
|
|
||||||
}, [numParticles]);
|
|
||||||
|
|
||||||
useFrame((state, delta) => {
|
|
||||||
if (!instancedMeshRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manage visibility directly in the animation loop
|
|
||||||
instancedMeshRef.current.visible = scale >= 0.8;
|
|
||||||
|
|
||||||
if (scale < 0.8) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const deltaScaled = delta * 144; // Scale for 144 FPS
|
|
||||||
particlesData.forEach((particle, i) => {
|
|
||||||
particle.velocity.y += particle.gravity * deltaScaled;
|
|
||||||
|
|
||||||
particle.position.x += particle.velocity.x * deltaScaled;
|
|
||||||
particle.position.y += particle.velocity.y * deltaScaled;
|
|
||||||
particle.position.z += particle.velocity.z * deltaScaled;
|
|
||||||
|
|
||||||
if (particle.position.y < 0.05) {
|
|
||||||
particle.position.set(0.6, 0.05, 0.5);
|
|
||||||
particle.velocity.set(Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.02);
|
|
||||||
}
|
|
||||||
const matrix = new THREE.Matrix4();
|
|
||||||
matrix.setPosition(particle.position);
|
|
||||||
instancedMeshRef.current.setMatrixAt(i, matrix);
|
|
||||||
});
|
|
||||||
|
|
||||||
instancedMeshRef.current.instanceMatrix.needsUpdate = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<instancedMesh ref={instancedMeshRef} args={[null, null, numParticles]} visible={scale >= 0.8}>
|
|
||||||
<sphereGeometry args={[0.01, 16, 16]} />
|
|
||||||
<meshStandardMaterial
|
|
||||||
emissive={turboColor}
|
|
||||||
toneMapped={false}
|
|
||||||
emissiveIntensity={5}
|
|
||||||
/>
|
|
||||||
</instancedMesh>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import React, { useEffect,useRef, useState } from "react";
|
||||||
|
import { useLoader, useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
|
export const CircleCoinParticle = ({ position, coins }) => {
|
||||||
|
const texture = useLoader(THREE.TextureLoader, "./particles/circle_coin.png");
|
||||||
|
const pointsRef = useRef();
|
||||||
|
const materialRef = useRef();
|
||||||
|
const [size, setSize] = useState(1);
|
||||||
|
const [opacity, setOpacity] = useState(1);
|
||||||
|
|
||||||
|
const points = React.useMemo(() => {
|
||||||
|
const geom = new THREE.BufferGeometry();
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new THREE.Float32BufferAttribute(position, 3)
|
||||||
|
);
|
||||||
|
return geom;
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.color.multiplyScalar(4);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSize(0);
|
||||||
|
setOpacity(1);
|
||||||
|
}, [coins]);
|
||||||
|
|
||||||
|
useFrame((_, delta) => {
|
||||||
|
if (size < 5) {
|
||||||
|
setSize((size) => Math.min(size + 0.2 * delta * 144, 5));
|
||||||
|
} else if (opacity > 0) {
|
||||||
|
setOpacity((opacity) => Math.max(opacity - 0.1 * delta * 144, 0));
|
||||||
|
setSize((size) => Math.max(size - 0.1 * delta * 144, 0));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<points ref={pointsRef} geometry={points}>
|
||||||
|
<pointsMaterial
|
||||||
|
ref={materialRef}
|
||||||
|
size={size}
|
||||||
|
alphaMap={texture}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
opacity={opacity}
|
||||||
|
toneMapped={false}
|
||||||
|
color={0xbf8717}
|
||||||
|
/>
|
||||||
|
</points>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { CircleCoinParticle } from "./CircleCoinParticle"
|
||||||
|
import { StarCoinParticle } from "./StarCoinParticle"
|
||||||
|
|
||||||
|
export const CoinParticles = ({ coins }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CircleCoinParticle position={[0,0.8, 0.2]} coins={coins}/>
|
||||||
|
<StarCoinParticle position={[0,0.8, 0.2]} coins={coins} timeModifier={50}/>
|
||||||
|
<StarCoinParticle position={[0,0.8, 0.2]} coins={coins} timeModifier={60}/>
|
||||||
|
<StarCoinParticle position={[0,0.8, 0.2]} coins={coins} timeModifier={40}/>
|
||||||
|
<StarCoinParticle position={[0,0.8, 0.2]} coins={coins} timeModifier={90}/>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import { useLoader, useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
|
export const StarCoinParticle = ({ position, coins, timeModifier }) => {
|
||||||
|
const texture = useLoader(THREE.TextureLoader, "./particles/star_coin.png");
|
||||||
|
const pointsRef = useRef();
|
||||||
|
const materialRef = useRef();
|
||||||
|
const sizeRef = useRef(1);
|
||||||
|
const opacityRef = useRef(1);
|
||||||
|
const originalYpos = useRef(0);
|
||||||
|
|
||||||
|
const points = React.useMemo(() => {
|
||||||
|
const geom = new THREE.BufferGeometry();
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new THREE.Float32BufferAttribute(position, 3)
|
||||||
|
);
|
||||||
|
return geom;
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.color.multiplyScalar(6);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sizeRef.current = 0;
|
||||||
|
opacityRef.current = 1;
|
||||||
|
pointsRef.current.position.x = Math.random() * 1 - 0.5;
|
||||||
|
pointsRef.current.position.y = Math.random() * 0.5 - 0.25;
|
||||||
|
originalYpos.current = pointsRef.current.position.y;
|
||||||
|
}, [coins]);
|
||||||
|
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
const time = state.clock.getElapsedTime();
|
||||||
|
pointsRef.current.position.y += 0.008 * delta * 144;
|
||||||
|
if (sizeRef.current < 1) {
|
||||||
|
sizeRef.current = Math.min(sizeRef.current + 0.01 * delta * 144, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointsRef.current.position.y > originalYpos.current + 0.01) {
|
||||||
|
opacityRef.current = Math.max(opacityRef.current - 0.01 * delta * 144, 0);
|
||||||
|
} else {
|
||||||
|
opacityRef.current = Math.abs(Math.sin(time * timeModifier * 1500));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update material properties directly
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.size = sizeRef.current;
|
||||||
|
materialRef.current.opacity = opacityRef.current;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<points ref={pointsRef} geometry={points}>
|
||||||
|
<pointsMaterial
|
||||||
|
ref={materialRef}
|
||||||
|
alphaMap={texture}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
toneMapped={false}
|
||||||
|
color={0xbf8717}
|
||||||
|
/>
|
||||||
|
</points>
|
||||||
|
);
|
||||||
|
};
|
|
@ -10,15 +10,23 @@ export const DriftParticlesLeft = ({turboColor,scale, ...props}) => {
|
||||||
return (
|
return (
|
||||||
<group {...props}>
|
<group {...props}>
|
||||||
<Particles1 turboColor={turboColor} scale={scale} />
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
{/* <Particles1 turboColor={turboColor} scale={scale} />
|
|
||||||
<Particles1 turboColor={turboColor} scale={scale} />
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
<Particles1 turboColor={turboColor} scale={scale} />
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
<Particles1 turboColor={turboColor} scale={scale} />
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
<Particles1 turboColor={turboColor} scale={scale} />
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
<Particles1 turboColor={turboColor} scale={scale} />
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
<Particles1 turboColor={turboColor} scale={scale} />
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
<Particles1 turboColor={turboColor} scale={scale} /> */}
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles1 turboColor={turboColor} scale={scale} />
|
||||||
{/* <Particles3 turboColor={turboColor} scale={scale} /> */}
|
{/* <Particles3 turboColor={turboColor} scale={scale} /> */}
|
||||||
|
|
||||||
</group>
|
</group>
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Particles2 } from "./Particles2";
|
||||||
|
import { Particles4 } from "./Particles4";
|
||||||
|
|
||||||
|
export const DriftParticlesRight = ({turboColor,scale, ...props}) => {
|
||||||
|
|
||||||
|
// if(scale < 0.8) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group {...props}>
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
<Particles2 turboColor={turboColor} scale={scale} />
|
||||||
|
</group>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { useRef } from "react";
|
||||||
|
import { useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
|
export const Particles1 = ({ turboColor, scale, ...props }) => {
|
||||||
|
const ref = useRef();
|
||||||
|
const velocity = useRef({
|
||||||
|
x: -Math.random() * 0.05,
|
||||||
|
y: Math.random() * 0.05,
|
||||||
|
z: Math.random() * 0.02,
|
||||||
|
});
|
||||||
|
const gravity = -0.003;
|
||||||
|
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
let position = ref.current.position;
|
||||||
|
let velocityVector = new THREE.Vector3(velocity.current.x, velocity.current.y, velocity.current.z);
|
||||||
|
|
||||||
|
// Adjust gravity and velocity based on delta
|
||||||
|
velocity.current.y += gravity * delta * 144; // Multiply by 144 to scale for 144 FPS
|
||||||
|
|
||||||
|
// Scale velocity changes by delta
|
||||||
|
position.x += velocity.current.x * delta * 144;
|
||||||
|
position.y += velocity.current.y * delta * 144;
|
||||||
|
position.z += velocity.current.z * delta * 144;
|
||||||
|
|
||||||
|
if (!velocityVector.equals(new THREE.Vector3(0, 0, 0))) {
|
||||||
|
const alignmentQuaternion = new THREE.Quaternion();
|
||||||
|
alignmentQuaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), velocityVector.normalize());
|
||||||
|
ref.current.quaternion.slerp(alignmentQuaternion, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.y < 0.05) {
|
||||||
|
position.set(-0.6, 0.05, 0.5);
|
||||||
|
velocity.current = {
|
||||||
|
x: -Math.random() * 0.05,
|
||||||
|
y: Math.random() * 0.05,
|
||||||
|
z: Math.random() * 0.02,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.current.position.set(position.x, position.y, position.z);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh ref={ref} position={[-0.6, 0.05, 0.5]} scale={[scale, scale * 5, scale]}>
|
||||||
|
<sphereGeometry args={[0.01, 16, 16]} />
|
||||||
|
<meshStandardMaterial
|
||||||
|
emissive={turboColor}
|
||||||
|
toneMapped={false}
|
||||||
|
emissiveIntensity={5}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { useRef } from "react";
|
||||||
|
import { useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
|
export const Particles2 = ({ turboColor, scale, ...props }) => {
|
||||||
|
const ref = useRef();
|
||||||
|
const velocity = useRef({
|
||||||
|
x: Math.random() * 0.05,
|
||||||
|
y: Math.random() * 0.05,
|
||||||
|
z: Math.random() * 0.02,
|
||||||
|
});
|
||||||
|
const gravity = -0.003;
|
||||||
|
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
let position = ref.current.position;
|
||||||
|
let velocityVector = new THREE.Vector3(velocity.current.x, velocity.current.y, velocity.current.z);
|
||||||
|
|
||||||
|
|
||||||
|
velocity.current.y += gravity * delta * 144;
|
||||||
|
|
||||||
|
position.x += velocity.current.x * delta * 144;
|
||||||
|
position.y += velocity.current.y * delta * 144;
|
||||||
|
position.z += velocity.current.z * delta * 144;
|
||||||
|
|
||||||
|
if (!velocityVector.equals(new THREE.Vector3(0, 0, 0))) {
|
||||||
|
const alignmentQuaternion = new THREE.Quaternion();
|
||||||
|
alignmentQuaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), velocityVector.normalize());
|
||||||
|
ref.current.quaternion.slerp(alignmentQuaternion, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.y < 0.05) {
|
||||||
|
position.set(0.6, 0.05, 0.5);
|
||||||
|
velocity.current = {
|
||||||
|
x: Math.random() * 0.05,
|
||||||
|
y: Math.random() * 0.05,
|
||||||
|
z: Math.random() * 0.02,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.current.position.set(position.x, position.y, position.z);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh ref={ref} position={[0.6, 0.05, 0.5]} scale={[scale, scale * 5, scale]}>
|
||||||
|
<sphereGeometry args={[0.01, 16, 16]} />
|
||||||
|
<meshStandardMaterial
|
||||||
|
emissive={turboColor}
|
||||||
|
toneMapped={false}
|
||||||
|
emissiveIntensity={5}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
};
|
|
@ -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}
|
|
@ -58,7 +58,7 @@ export const FlameParticle = ({ position, png, isBoosting, delay = 0 }) => {
|
||||||
alphaMap={texture}
|
alphaMap={texture}
|
||||||
transparent={true}
|
transparent={true}
|
||||||
depthWrite={false}
|
depthWrite={false}
|
||||||
color={0x000000}
|
color={0xff9900}
|
||||||
opacity={1}
|
opacity={1}
|
||||||
toneMapped={false}
|
toneMapped={false}
|
||||||
/>
|
/>
|
|
@ -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}/>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import React, { useEffect,useRef, useState } from "react";
|
||||||
|
import { useLoader, useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
|
export const CircleItemParticle = ({ position, item, color }) => {
|
||||||
|
const texture = useLoader(THREE.TextureLoader, "./particles/circle_coin.png");
|
||||||
|
const pointsRef = useRef();
|
||||||
|
const materialRef = useRef();
|
||||||
|
const [size, setSize] = useState(1);
|
||||||
|
const [opacity, setOpacity] = useState(1);
|
||||||
|
|
||||||
|
const [currentColor, setCurrentColor] = useState(color);
|
||||||
|
|
||||||
|
const points = React.useMemo(() => {
|
||||||
|
const geom = new THREE.BufferGeometry();
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new THREE.Float32BufferAttribute(position, 3)
|
||||||
|
);
|
||||||
|
return geom;
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.color.multiplyScalar(4);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(item){
|
||||||
|
setSize(0);
|
||||||
|
setOpacity(1);
|
||||||
|
}
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
|
useFrame((_, delta) => {
|
||||||
|
if (size < 5) {
|
||||||
|
setSize((size) => Math.min(size + 0.2 * delta * 144, 5));
|
||||||
|
} else if (opacity > 0) {
|
||||||
|
setOpacity((opacity) => Math.max(opacity - 0.1 * delta * 144, 0));
|
||||||
|
setSize((size) => Math.max(size - 0.1 * delta * 144, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<points ref={pointsRef} geometry={points}>
|
||||||
|
<pointsMaterial
|
||||||
|
ref={materialRef}
|
||||||
|
size={size}
|
||||||
|
alphaMap={texture}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
opacity={opacity}
|
||||||
|
toneMapped={false}
|
||||||
|
color={currentColor}
|
||||||
|
/>
|
||||||
|
</points>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { CircleItemParticle } from "./CircleItemParticle"
|
||||||
|
import { StarItemParticle } from "./StarItemParticle"
|
||||||
|
import { SmallCircleParticle } from "./SmallCircleParticle"
|
||||||
|
|
||||||
|
export const ItemParticles = ({item}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CircleItemParticle position={[0,0.8, 0.2]} item={item} color={0x75ff9a}/>
|
||||||
|
<StarItemParticle position={[0,0.8, 0.2]} item={item} timeModifier={50} color={0x75ff9a}/>
|
||||||
|
<StarItemParticle position={[0,0.8, 0.2]} item={item} timeModifier={60} color={0xe872fc}/>
|
||||||
|
<StarItemParticle position={[0,0.8, 0.2]} item={item} timeModifier={40} color={0x72e5fc}/>
|
||||||
|
<StarItemParticle position={[0,0.8, 0.2]} item={item} timeModifier={90} color={0xf0db7f}/>
|
||||||
|
<SmallCircleParticle position={[0,0.8, 0.2]} item={item} timeModifier={50} color={0x75ff9a}/>
|
||||||
|
<SmallCircleParticle position={[0,0.8, 0.2]} item={item} timeModifier={60} color={0xe872fc}/>
|
||||||
|
<SmallCircleParticle position={[0,0.8, 0.2]} item={item} timeModifier={40} color={0x72e5fc}/>
|
||||||
|
<SmallCircleParticle position={[0,0.8, 0.2]} item={item} timeModifier={90} color={0xf0db7f}/>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import { useLoader, useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
|
export const SmallCircleParticle = ({ position, item, timeModifier, color }) => {
|
||||||
|
const texture = useLoader(THREE.TextureLoader, "./particles/circle_01.png");
|
||||||
|
const pointsRef = useRef();
|
||||||
|
const materialRef = useRef();
|
||||||
|
const sizeRef = useRef(1);
|
||||||
|
const opacityRef = useRef(1);
|
||||||
|
const originalYpos = useRef(0);
|
||||||
|
|
||||||
|
const points = React.useMemo(() => {
|
||||||
|
const geom = new THREE.BufferGeometry();
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new THREE.Float32BufferAttribute(position, 3)
|
||||||
|
);
|
||||||
|
return geom;
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.color.multiplyScalar(6);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(item){
|
||||||
|
sizeRef.current = 0;
|
||||||
|
opacityRef.current = 1;
|
||||||
|
pointsRef.current.position.x = Math.random() * 1 - 0.5;
|
||||||
|
pointsRef.current.position.y = Math.random() * 0.5 - 0.25;
|
||||||
|
originalYpos.current = pointsRef.current.position.y;
|
||||||
|
}
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
const time = state.clock.getElapsedTime();
|
||||||
|
pointsRef.current.position.y += 0.008 * delta * 144;
|
||||||
|
if (sizeRef.current < 1) {
|
||||||
|
sizeRef.current = Math.min(sizeRef.current + 0.01 * delta * 144, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointsRef.current.position.y > originalYpos.current + 0.01) {
|
||||||
|
opacityRef.current = Math.max(opacityRef.current - 0.01 * delta * 144, 0);
|
||||||
|
} else {
|
||||||
|
opacityRef.current = Math.abs(Math.sin(time * timeModifier * 1500));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update material properties directly
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.size = sizeRef.current;
|
||||||
|
materialRef.current.opacity = opacityRef.current;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<points ref={pointsRef} geometry={points}>
|
||||||
|
<pointsMaterial
|
||||||
|
ref={materialRef}
|
||||||
|
alphaMap={texture}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
toneMapped={false}
|
||||||
|
color={color}
|
||||||
|
/>
|
||||||
|
</points>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,70 @@
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import { useLoader, useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
|
export const StarItemParticle = ({ position, item, timeModifier, color }) => {
|
||||||
|
const texture = useLoader(THREE.TextureLoader, "./particles/star_coin.png");
|
||||||
|
const pointsRef = useRef();
|
||||||
|
const materialRef = useRef();
|
||||||
|
const sizeRef = useRef(1);
|
||||||
|
const opacityRef = useRef(1);
|
||||||
|
const originalYpos = useRef(0);
|
||||||
|
|
||||||
|
const points = React.useMemo(() => {
|
||||||
|
const geom = new THREE.BufferGeometry();
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new THREE.Float32BufferAttribute(position, 3)
|
||||||
|
);
|
||||||
|
return geom;
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.color.multiplyScalar(6);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(item){
|
||||||
|
sizeRef.current = 0;
|
||||||
|
opacityRef.current = 1;
|
||||||
|
pointsRef.current.position.x = Math.random() * 1 - 0.5;
|
||||||
|
pointsRef.current.position.y = Math.random() * 0.5 - 0.25;
|
||||||
|
originalYpos.current = pointsRef.current.position.y;
|
||||||
|
}
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
const time = state.clock.getElapsedTime();
|
||||||
|
pointsRef.current.position.y += 0.008 * delta * 144;
|
||||||
|
if (sizeRef.current < 1) {
|
||||||
|
sizeRef.current = Math.min(sizeRef.current + 0.01 * delta * 144, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointsRef.current.position.y > originalYpos.current + 0.01) {
|
||||||
|
opacityRef.current = Math.max(opacityRef.current - 0.01 * delta * 144, 0);
|
||||||
|
} else {
|
||||||
|
opacityRef.current = Math.abs(Math.sin(time * timeModifier * 1500));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update material properties directly
|
||||||
|
if (materialRef.current) {
|
||||||
|
materialRef.current.size = sizeRef.current;
|
||||||
|
materialRef.current.opacity = opacityRef.current;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<points ref={pointsRef} geometry={points}>
|
||||||
|
<pointsMaterial
|
||||||
|
ref={materialRef}
|
||||||
|
alphaMap={texture}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
toneMapped={false}
|
||||||
|
color={color}
|
||||||
|
/>
|
||||||
|
</points>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,82 @@
|
||||||
|
import React, { useRef, useMemo } from "react";
|
||||||
|
import { useLoader, useFrame } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
|
export const SmokeParticle = ({ position, png, leftDrift, rightDrift, delay = 0 }) => {
|
||||||
|
const texture = useLoader(THREE.TextureLoader, png);
|
||||||
|
const pointsRef = useRef();
|
||||||
|
const initialized = useRef(false);
|
||||||
|
|
||||||
|
// Initialize after delay
|
||||||
|
useMemo(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
initialized.current = true;
|
||||||
|
}, delay);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [delay]);
|
||||||
|
|
||||||
|
const points = useMemo(() => {
|
||||||
|
const geom = new THREE.BufferGeometry();
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new THREE.Float32BufferAttribute(position, 3)
|
||||||
|
);
|
||||||
|
return geom;
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
useFrame(({clock}, delta) => {
|
||||||
|
if (!initialized.current) return;
|
||||||
|
|
||||||
|
const pointsCurrent = pointsRef.current;
|
||||||
|
|
||||||
|
if(leftDrift || rightDrift){
|
||||||
|
pointsCurrent.position.z += 0.1 * delta * 144;
|
||||||
|
|
||||||
|
//Set inclination
|
||||||
|
if(leftDrift){
|
||||||
|
pointsCurrent.position.x -= 0.09 * delta * 144;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rightDrift){
|
||||||
|
pointsCurrent.position.x += 0.09 * delta * 144;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pointsCurrent.position.x < -1.8 || pointsCurrent.position.x > 1.8) {
|
||||||
|
pointsCurrent.position.z = 0;
|
||||||
|
pointsCurrent.position.x = 0;
|
||||||
|
pointsCurrent.material.opacity = 1.5;
|
||||||
|
pointsCurrent.material.size = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pointsCurrent.material.opacity > 0) {
|
||||||
|
pointsCurrent.material.opacity -= 0.01 * delta * 144;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pointsCurrent.material.size > 0) {
|
||||||
|
//Shrinking effect
|
||||||
|
pointsCurrent.material.size -= 0.1* delta * 144;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pointsCurrent.position.z = 0;
|
||||||
|
pointsCurrent.position.x = 0;
|
||||||
|
pointsCurrent.material.opacity = 0;
|
||||||
|
pointsCurrent.material.size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<points ref={pointsRef} geometry={points}>
|
||||||
|
<pointsMaterial
|
||||||
|
size={1}
|
||||||
|
alphaMap={texture}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
color={0xBFBFBF}
|
||||||
|
opacity={1}
|
||||||
|
toneMapped={false}
|
||||||
|
/>
|
||||||
|
</points>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { SmokeParticle } from "./SmokeParticle";
|
||||||
|
|
||||||
|
export const SmokeParticles = ({ driftRight, driftLeft }) => {
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group>
|
||||||
|
<SmokeParticle
|
||||||
|
position={[-0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/fire_02.png"
|
||||||
|
leftDrift={driftLeft}
|
||||||
|
rightDrift={driftRight}
|
||||||
|
delay={200}
|
||||||
|
/>
|
||||||
|
<SmokeParticle
|
||||||
|
position={[0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/fire_01.png"
|
||||||
|
leftDrift={driftLeft}
|
||||||
|
rightDrift={driftRight}
|
||||||
|
delay={200}
|
||||||
|
/>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
};
|
|
@ -3,32 +3,45 @@ import { BallCollider, RigidBody, useRapier, vec3 } from "@react-three/rapier";
|
||||||
import {
|
import {
|
||||||
useKeyboardControls,
|
useKeyboardControls,
|
||||||
PerspectiveCamera,
|
PerspectiveCamera,
|
||||||
ContactShadows,
|
|
||||||
Sphere,
|
|
||||||
OrbitControls,
|
|
||||||
Trail,
|
|
||||||
PositionalAudio,
|
PositionalAudio,
|
||||||
} from "@react-three/drei";
|
} from "@react-three/drei";
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree, extend } from "@react-three/fiber";
|
||||||
import { useRef, useState, useEffect, useCallback } from "react";
|
import { useRef, useState, useEffect, useCallback } from "react";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
|
|
||||||
import { Mario } from "./models/characters/Mario_kart";
|
import { Mario } from "./models/characters/Mario_kart";
|
||||||
import { DriftParticlesLeft } from "./Particles/DriftParticlesLeft";
|
import { DriftParticlesLeft } from "./Particles/drifts/DriftParticlesLeft";
|
||||||
import { DriftParticlesRight } from "./Particles/DriftParticlesRight";
|
import { DriftParticlesRight } from "./Particles/drifts/DriftParticlesRight";
|
||||||
|
|
||||||
import { PointParticle } from "./Particles/PointParticle";
|
import { PointParticle } from "./Particles/drifts/PointParticle";
|
||||||
|
|
||||||
import { FlameParticles } from "./Particles/FlameParticles";
|
import { SmokeParticles } from "./Particles/smoke/SmokeParticles";
|
||||||
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 { HitParticles } from "./Particles/hits/HitParticles";
|
||||||
|
import { CoinParticles } from "./Particles/coins/CoinParticles";
|
||||||
|
import { ItemParticles } from "./Particles/items/ItemParticles";
|
||||||
|
import { geometry } from "maath";
|
||||||
|
extend(geometry);
|
||||||
|
|
||||||
export const PlayerController = () => {
|
export const PlayerController = ({
|
||||||
|
player,
|
||||||
|
userPlayer,
|
||||||
|
setNetworkBananas,
|
||||||
|
setNetworkShells,
|
||||||
|
networkBananas,
|
||||||
|
networkShells,
|
||||||
|
}) => {
|
||||||
const upPressed = useKeyboardControls((state) => state[Controls.up]);
|
const upPressed = useKeyboardControls((state) => state[Controls.up]);
|
||||||
const downPressed = useKeyboardControls((state) => state[Controls.down]);
|
const downPressed = useKeyboardControls((state) => state[Controls.down]);
|
||||||
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 resetPressed = useKeyboardControls((state) => state[Controls.reset]);
|
||||||
|
const escPressed = useKeyboardControls((state) => state[Controls.escape]);
|
||||||
|
|
||||||
const [isOnGround, setIsOnGround] = useState(false);
|
const [isOnGround, setIsOnGround] = useState(false);
|
||||||
const body = useRef();
|
const body = useRef();
|
||||||
const kart = useRef();
|
const kart = useRef();
|
||||||
|
@ -63,14 +76,46 @@ export const PlayerController = () => {
|
||||||
let targetZPosition = 8;
|
let targetZPosition = 8;
|
||||||
const [steeringAngleWheels, setSteeringAngleWheels] = useState(0);
|
const [steeringAngleWheels, setSteeringAngleWheels] = useState(0);
|
||||||
const engineSound = useRef();
|
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 [scale, setScale] = useState(0);
|
||||||
const { actions, addPastPosition } = useStore();
|
const raycaster = new THREE.Raycaster();
|
||||||
|
const downDirection = new THREE.Vector3(0, -1, 0);
|
||||||
|
const [shouldLaunch, setShouldLaunch] = useState(false);
|
||||||
|
const effectiveBoost = useRef(0);
|
||||||
|
const text = useRef();
|
||||||
|
|
||||||
|
const { actions, shouldSlowDown, item, bananas, coins, id, controls } =
|
||||||
|
useStore();
|
||||||
|
const slowDownDuration = useRef(1500);
|
||||||
|
|
||||||
|
const rightWheel = useRef();
|
||||||
|
const leftWheel = useRef();
|
||||||
|
useEffect(() => {
|
||||||
|
if (leftWheel.current && rightWheel.current && body.current) {
|
||||||
|
actions.setLeftWheel(leftWheel.current);
|
||||||
|
actions.setRightWheel(rightWheel.current);
|
||||||
|
}
|
||||||
|
}, [body.current]);
|
||||||
useFrame(({ pointer, clock }, delta) => {
|
useFrame(({ pointer, clock }, delta) => {
|
||||||
|
if (player.id !== id) return;
|
||||||
const time = clock.getElapsedTime();
|
const time = clock.getElapsedTime();
|
||||||
if (!body.current && !mario.current) return;
|
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
|
// HANDLING AND STEERING
|
||||||
const kartRotation =
|
const kartRotation =
|
||||||
kart.current.rotation.y - driftDirection.current * driftForce.current;
|
kart.current.rotation.y - driftDirection.current * driftForce.current;
|
||||||
|
@ -79,15 +124,25 @@ export const PlayerController = () => {
|
||||||
0,
|
0,
|
||||||
-Math.cos(kartRotation)
|
-Math.cos(kartRotation)
|
||||||
);
|
);
|
||||||
actions.setBodyPosition(body.current.translation());
|
|
||||||
actions.setBodyRotation(kart.current.rotation);
|
|
||||||
|
|
||||||
|
if (escPressed) {
|
||||||
|
actions.setGameStarted(false);
|
||||||
|
}
|
||||||
|
if(kartRotation){
|
||||||
|
leftWheel.current.kartRotation = kartRotation;
|
||||||
|
}
|
||||||
if (leftPressed && currentSpeed > 0) {
|
if (leftPressed && currentSpeed > 0) {
|
||||||
steeringAngle = currentSteeringSpeed;
|
steeringAngle = currentSteeringSpeed;
|
||||||
targetXPosition = -camMaxOffset;
|
targetXPosition = -camMaxOffset;
|
||||||
} else if (rightPressed && currentSpeed > 0) {
|
} else if (rightPressed && currentSpeed > 0) {
|
||||||
steeringAngle = -currentSteeringSpeed;
|
steeringAngle = -currentSteeringSpeed;
|
||||||
targetXPosition = camMaxOffset;
|
targetXPosition = camMaxOffset;
|
||||||
|
} else if (rightPressed && currentSpeed < 0) {
|
||||||
|
steeringAngle = currentSteeringSpeed;
|
||||||
|
targetXPosition = -camMaxOffset;
|
||||||
|
} else if (leftPressed && currentSpeed < 0) {
|
||||||
|
steeringAngle = -currentSteeringSpeed;
|
||||||
|
targetXPosition = camMaxOffset;
|
||||||
} else {
|
} else {
|
||||||
steeringAngle = 0;
|
steeringAngle = 0;
|
||||||
targetXPosition = 0;
|
targetXPosition = 0;
|
||||||
|
@ -100,13 +155,14 @@ export const PlayerController = () => {
|
||||||
steeringAngle = currentSteeringSpeed * -pointer.x;
|
steeringAngle = currentSteeringSpeed * -pointer.x;
|
||||||
targetXPosition = -camMaxOffset * -pointer.x;
|
targetXPosition = -camMaxOffset * -pointer.x;
|
||||||
} else if (driftLeft.current && !driftRight.current) {
|
} else if (driftLeft.current && !driftRight.current) {
|
||||||
steeringAngle = currentSteeringSpeed * -(pointer.x - 0.5);
|
steeringAngle = currentSteeringSpeed * -(pointer.x - 1);
|
||||||
targetXPosition = -camMaxOffset * -pointer.x;
|
targetXPosition = -camMaxOffset * -pointer.x;
|
||||||
} else if (driftRight.current && !driftLeft.current) {
|
} else if (driftRight.current && !driftLeft.current) {
|
||||||
steeringAngle = currentSteeringSpeed * -(pointer.x + 0.5);
|
steeringAngle = currentSteeringSpeed * -(pointer.x + 1);
|
||||||
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
|
||||||
|
@ -116,7 +172,7 @@ export const PlayerController = () => {
|
||||||
} else if (
|
} else if (
|
||||||
upPressed &&
|
upPressed &&
|
||||||
currentSpeed > maxSpeed &&
|
currentSpeed > maxSpeed &&
|
||||||
boostDuration.current > 0
|
effectiveBoost.current > 0
|
||||||
) {
|
) {
|
||||||
setCurrentSpeed(
|
setCurrentSpeed(
|
||||||
Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)
|
Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)
|
||||||
|
@ -133,14 +189,42 @@ export const PlayerController = () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldSlow) {
|
||||||
|
rightWheel.current.isSpinning = true;
|
||||||
|
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - decceleration * 2 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
setCurrentSteeringSpeed(0);
|
||||||
|
slowDownDuration.current -= 1500 * delta;
|
||||||
|
setShouldLaunch(true);
|
||||||
|
if (slowDownDuration.current <= 1) {
|
||||||
|
rightWheel.current.isSpinning = false;
|
||||||
|
actions.setShouldSlowDown(false);
|
||||||
|
slowDownDuration.current = 1500;
|
||||||
|
setShouldLaunch(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// REVERSING
|
// REVERSING
|
||||||
if (downPressed && currentSpeed < -maxSpeed) {
|
if (downPressed) {
|
||||||
|
if (currentSteeringSpeed < MaxSteeringSpeed) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.min(
|
||||||
|
currentSteeringSpeed + 0.0001 * delta * 144,
|
||||||
|
MaxSteeringSpeed
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downPressed && currentSpeed <= 0) {
|
||||||
setCurrentSpeed(
|
setCurrentSpeed(
|
||||||
Math.max(currentSpeed - acceleration * delta * 144, -maxSpeed)
|
Math.max(currentSpeed - acceleration * delta * 144, -maxSpeed)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// DECELERATING
|
// DECELERATING
|
||||||
else if (!upPressed && !downPressed) {
|
else if (!upPressed) {
|
||||||
if (currentSteeringSpeed > 0) {
|
if (currentSteeringSpeed > 0) {
|
||||||
setCurrentSteeringSpeed(
|
setCurrentSteeringSpeed(
|
||||||
Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)
|
Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)
|
||||||
|
@ -173,13 +257,23 @@ export const PlayerController = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// JUMPING
|
// JUMPING
|
||||||
if (jumpPressed && isOnFloor.current && !jumpIsHeld.current) {
|
if (jumpPressed && isOnGround && !jumpIsHeld.current) {
|
||||||
jumpForce.current += 10;
|
jumpForce.current += 10;
|
||||||
isOnFloor.current = false;
|
isOnFloor.current = false;
|
||||||
jumpIsHeld.current = true;
|
jumpIsHeld.current = true;
|
||||||
|
jumpSound.current.play();
|
||||||
|
setIsOnGround(false);
|
||||||
|
|
||||||
|
if (jumpSound.current.isPlaying) {
|
||||||
|
jumpSound.current.stop();
|
||||||
|
jumpSound.current.play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isOnFloor.current && jumpForce.current > 0) {
|
if (isOnFloor.current && jumpForce.current > 0) {
|
||||||
|
landingSound.current.play();
|
||||||
|
}
|
||||||
|
if (!isOnGround && jumpForce.current > 0) {
|
||||||
jumpForce.current -= 1 * delta * 144;
|
jumpForce.current -= 1 * delta * 144;
|
||||||
}
|
}
|
||||||
if (!jumpPressed) {
|
if (!jumpPressed) {
|
||||||
|
@ -193,6 +287,7 @@ export const PlayerController = () => {
|
||||||
if (
|
if (
|
||||||
jumpIsHeld.current &&
|
jumpIsHeld.current &&
|
||||||
currentSteeringSpeed > 0 &&
|
currentSteeringSpeed > 0 &&
|
||||||
|
upPressed &&
|
||||||
pointer.x < -0.1 &&
|
pointer.x < -0.1 &&
|
||||||
!driftRight.current
|
!driftRight.current
|
||||||
) {
|
) {
|
||||||
|
@ -201,6 +296,7 @@ export const PlayerController = () => {
|
||||||
if (
|
if (
|
||||||
jumpIsHeld.current &&
|
jumpIsHeld.current &&
|
||||||
currentSteeringSpeed > 0 &&
|
currentSteeringSpeed > 0 &&
|
||||||
|
upPressed &&
|
||||||
pointer.x > 0.1 &&
|
pointer.x > 0.1 &&
|
||||||
!driftLeft.current
|
!driftLeft.current
|
||||||
) {
|
) {
|
||||||
|
@ -215,6 +311,10 @@ export const PlayerController = () => {
|
||||||
);
|
);
|
||||||
setTurboColor(0xffffff);
|
setTurboColor(0xffffff);
|
||||||
accumulatedDriftPower.current = 0;
|
accumulatedDriftPower.current = 0;
|
||||||
|
driftSound.current.stop();
|
||||||
|
driftTwoSound.current.stop();
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driftLeft.current) {
|
if (driftLeft.current) {
|
||||||
|
@ -222,20 +322,28 @@ export const PlayerController = () => {
|
||||||
driftForce.current = 0.4;
|
driftForce.current = 0.4;
|
||||||
mario.current.rotation.y = THREE.MathUtils.lerp(
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
mario.current.rotation.y,
|
mario.current.rotation.y,
|
||||||
steeringAngle * 50 + 0.5,
|
steeringAngle * 25 + 0.4,
|
||||||
0.05 * delta * 144
|
0.05 * delta * 144
|
||||||
);
|
);
|
||||||
accumulatedDriftPower.current += 0.1 * (steeringAngle + 1) * delta * 144;
|
if (isOnFloor.current) {
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
accumulatedDriftPower.current +=
|
||||||
|
0.1 * (steeringAngle + 1) * delta * 144;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (driftRight.current) {
|
if (driftRight.current) {
|
||||||
driftDirection.current = -1;
|
driftDirection.current = -1;
|
||||||
driftForce.current = 0.4;
|
driftForce.current = 0.4;
|
||||||
mario.current.rotation.y = THREE.MathUtils.lerp(
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
mario.current.rotation.y,
|
mario.current.rotation.y,
|
||||||
-(-steeringAngle * 50 + 0.5),
|
-(-steeringAngle * 25 + 0.4),
|
||||||
0.05 * delta * 144
|
0.05 * delta * 144
|
||||||
);
|
);
|
||||||
|
if(isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144;
|
accumulatedDriftPower.current += 0.1 * (-steeringAngle + 1) * delta * 144;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!driftLeft.current && !driftRight.current) {
|
if (!driftLeft.current && !driftRight.current) {
|
||||||
mario.current.rotation.y = THREE.MathUtils.lerp(
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
@ -244,46 +352,68 @@ export const PlayerController = () => {
|
||||||
0.05 * delta * 144
|
0.05 * delta * 144
|
||||||
);
|
);
|
||||||
setScale(0);
|
setScale(0);
|
||||||
|
if((pointer.x > 0.8 || pointer.x < -0.8) && currentSpeed > 20 && isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
} else {
|
||||||
|
leftWheel.current.isSpinning = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (accumulatedDriftPower.current > blueTurboThreshold) {
|
if (accumulatedDriftPower.current > blueTurboThreshold) {
|
||||||
setTurboColor(0x00ffff);
|
setTurboColor(0x00ffff);
|
||||||
boostDuration.current = 50;
|
boostDuration.current = 50;
|
||||||
|
driftBlueSound.current.play();
|
||||||
}
|
}
|
||||||
if (accumulatedDriftPower.current > orangeTurboThreshold) {
|
if (accumulatedDriftPower.current > orangeTurboThreshold) {
|
||||||
setTurboColor(0xffcf00);
|
setTurboColor(0xffcf00);
|
||||||
boostDuration.current = 100;
|
boostDuration.current = 100;
|
||||||
|
driftBlueSound.current.stop();
|
||||||
|
driftOrangeSound.current.play();
|
||||||
}
|
}
|
||||||
if (accumulatedDriftPower.current > purpleTurboThreshold) {
|
if (accumulatedDriftPower.current > purpleTurboThreshold) {
|
||||||
setTurboColor(0xff00ff);
|
setTurboColor(0xff00ff);
|
||||||
boostDuration.current = 250;
|
boostDuration.current = 250;
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driftLeft.current || driftRight.current) {
|
if (driftLeft.current || driftRight.current) {
|
||||||
const oscillation = Math.sin(time * 1000) * 0.1;
|
const oscillation = Math.sin(time * 1000) * 0.1;
|
||||||
const vibration = oscillation + 0.9;
|
const vibration = oscillation + 0.9;
|
||||||
|
|
||||||
if (turboColor === 0xffffff) {
|
if (turboColor === 0xffffff) {
|
||||||
setScale(vibration * 0.8);
|
setScale(vibration * 0.8);
|
||||||
} else {
|
} else {
|
||||||
setScale(vibration);
|
setScale(vibration);
|
||||||
}
|
}
|
||||||
|
if (isOnFloor.current && !driftSound.current.isPlaying) {
|
||||||
|
driftSound.current.play();
|
||||||
|
driftTwoSound.current.play();
|
||||||
|
landingSound.current.play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// RELEASING DRIFT
|
// RELEASING DRIFT
|
||||||
|
|
||||||
if (boostDuration.current > 1 && !jumpIsHeld.current) {
|
if (boostDuration.current > 1 && !jumpIsHeld.current) {
|
||||||
setIsBoosting(true);
|
setIsBoosting(true);
|
||||||
} else if (boostDuration.current <= 1) {
|
effectiveBoost.current = boostDuration.current;
|
||||||
|
boostDuration.current = 0;
|
||||||
|
} else if (effectiveBoost.current <= 1) {
|
||||||
targetZPosition = 8;
|
targetZPosition = 8;
|
||||||
setIsBoosting(false);
|
setIsBoosting(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBoosting && boostDuration.current > 1) {
|
if (isBoosting && effectiveBoost.current > 1) {
|
||||||
setCurrentSpeed(boostSpeed);
|
setCurrentSpeed(boostSpeed);
|
||||||
boostDuration.current -= 1 * delta * 144;
|
effectiveBoost.current -= 1 * delta * 144;
|
||||||
targetZPosition = 10;
|
targetZPosition = 10;
|
||||||
} else if (boostDuration.current <= 1) {
|
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);
|
setIsBoosting(false);
|
||||||
targetZPosition = 8;
|
targetZPosition = 8;
|
||||||
|
turboSound.current.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAMERA WORK
|
// CAMERA WORK
|
||||||
|
@ -316,28 +446,104 @@ export const PlayerController = () => {
|
||||||
|
|
||||||
// SOUND WORK
|
// SOUND WORK
|
||||||
|
|
||||||
// console.lowg(body.current.translation())
|
// MISC
|
||||||
|
|
||||||
|
if (resetPressed) {
|
||||||
|
body.current.setTranslation({ x: 8, y: 2, z: -119 });
|
||||||
|
body.current.setLinvel({ x: 0, y: 0, z: 0 });
|
||||||
|
body.current.setAngvel({ x: 0, y: 0, z: 0 });
|
||||||
|
setCurrentSpeed(0);
|
||||||
|
setCurrentSteeringSpeed(0);
|
||||||
|
setIsBoosting(false);
|
||||||
|
effectiveBoost.current = 0;
|
||||||
|
setIsOnGround(false);
|
||||||
|
jumpForce.current = 0;
|
||||||
|
driftDirection.current = 0;
|
||||||
|
kart.current.rotation.y = Math.PI / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITEMS
|
||||||
|
|
||||||
|
if (shootPressed && item === "banana") {
|
||||||
|
const distanceBehind = 2;
|
||||||
|
const scaledBackwardDirection =
|
||||||
|
forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
...vec3(body.current.translation())
|
||||||
|
);
|
||||||
|
|
||||||
|
const bananaPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newBanana = {
|
||||||
|
id: Math.random() + "-" + +new Date(),
|
||||||
|
position: bananaPosition,
|
||||||
|
player: true,
|
||||||
|
};
|
||||||
|
setNetworkBananas([...networkBananas, newBanana]);
|
||||||
|
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shootPressed && item === "shell") {
|
||||||
|
const distanceBehind = -2;
|
||||||
|
const scaledBackwardDirection =
|
||||||
|
forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
body.current.translation().x,
|
||||||
|
body.current.translation().y,
|
||||||
|
body.current.translation().z
|
||||||
|
);
|
||||||
|
|
||||||
|
const shellPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newShell = {
|
||||||
|
id: Math.random() + "-" + +new Date(),
|
||||||
|
position: shellPosition,
|
||||||
|
player: true,
|
||||||
|
rotation: kartRotation,
|
||||||
|
};
|
||||||
|
setNetworkShells([...networkShells, newShell]);
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shootPressed && item === "mushroom") {
|
||||||
|
setIsBoosting(true);
|
||||||
|
effectiveBoost.current = 300;
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
player.setState("position", body.current.translation());
|
||||||
|
player.setState("rotation", kartRotation + mario.current.rotation.y);
|
||||||
|
player.setState("isBoosting", isBoosting);
|
||||||
|
player.setState("shouldLaunch", shouldLaunch);
|
||||||
|
player.setState("turboColor", turboColor);
|
||||||
|
player.setState("scale", scale);
|
||||||
|
player.setState("bananas", bananas);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return player.id === id ? (
|
||||||
<group>
|
<group>
|
||||||
<RigidBody
|
<RigidBody
|
||||||
ref={body}
|
ref={body}
|
||||||
colliders={false}
|
colliders={false}
|
||||||
position={[8, 20, -96]}
|
position={[8, 60, -119]}
|
||||||
centerOfMass={[0, -1, 0]}
|
centerOfMass={[0, -1, 0]}
|
||||||
mass={3}
|
mass={3}
|
||||||
ccd
|
ccd
|
||||||
|
name="player"
|
||||||
|
type={player.id === id ? "dynamic" : "kinematic"}
|
||||||
>
|
>
|
||||||
<BallCollider
|
<BallCollider
|
||||||
args={[0.5]}
|
args={[0.5]}
|
||||||
mass={3}
|
mass={3}
|
||||||
onCollisionEnter={(event) => {
|
onCollisionEnter={({ other }) => {
|
||||||
isOnFloor.current = true;
|
isOnFloor.current = true;
|
||||||
|
setIsOnGround(true);
|
||||||
|
}}
|
||||||
|
onCollisionExit={({ other }) => {
|
||||||
|
isOnFloor.current = false;
|
||||||
|
setIsOnGround(false);
|
||||||
}}
|
}}
|
||||||
// onCollisionExit={(event) => {
|
|
||||||
// isOnFloor.current = false
|
|
||||||
// }}
|
|
||||||
/>
|
/>
|
||||||
</RigidBody>
|
</RigidBody>
|
||||||
|
|
||||||
|
@ -347,14 +553,10 @@ export const PlayerController = () => {
|
||||||
currentSpeed={currentSpeed}
|
currentSpeed={currentSpeed}
|
||||||
steeringAngleWheels={steeringAngleWheels}
|
steeringAngleWheels={steeringAngleWheels}
|
||||||
isBoosting={isBoosting}
|
isBoosting={isBoosting}
|
||||||
|
shouldLaunch={shouldLaunch}
|
||||||
/>
|
/>
|
||||||
{/* <pointLight
|
<CoinParticles coins={coins} />
|
||||||
position={[0.6, 0.05, 0.5]}
|
<ItemParticles item={item} />
|
||||||
intensity={scale}
|
|
||||||
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
|
||||||
|
@ -365,12 +567,15 @@ export const PlayerController = () => {
|
||||||
opacity={0.4}
|
opacity={0.4}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
{/* <pointLight
|
<mesh position={[0.6, 0.05, 0.5]} scale={scale * 10}>
|
||||||
position={[-0.6, 0.05, 0.5]}
|
<sphereGeometry args={[0.05, 16, 16]} />
|
||||||
intensity={scale}
|
<FakeGlowMaterial
|
||||||
color={turboColor}
|
falloff={3}
|
||||||
distance={1}
|
glowInternalRadius={1}
|
||||||
/> */}
|
glowColor={turboColor}
|
||||||
|
glowSharpness={1}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
<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
|
||||||
|
@ -381,38 +586,45 @@ export const PlayerController = () => {
|
||||||
opacity={0.4}
|
opacity={0.4}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
|
<mesh position={[-0.46, 0.05, 0.3]} ref={leftWheel}></mesh>
|
||||||
{/* <Cylinder
|
<mesh position={[0.46, 0.05, 0.3]} ref={rightWheel}></mesh>
|
||||||
args={[0.1, 0, 1, 128, 64, true]}
|
<mesh position={[-0.6, 0.05, 0.5]} scale={scale * 10}>
|
||||||
position={[-0.6, 0.05, 0.5]}
|
<sphereGeometry args={[0.05, 16, 16]} />
|
||||||
rotation={[Math.PI / 3, 0 , 0]}
|
<FakeGlowMaterial
|
||||||
>
|
falloff={3}
|
||||||
<meshStandardMaterial side={THREE.DoubleSide} />
|
glowInternalRadius={1}
|
||||||
</Cylinder> */}
|
glowColor={turboColor}
|
||||||
{/* <Flame/> */}
|
glowSharpness={1}
|
||||||
<FlameParticles isBoosting={isBoosting} />
|
/>
|
||||||
|
</mesh>
|
||||||
|
{/* <FlameParticles isBoosting={isBoosting} /> */}
|
||||||
<DriftParticlesLeft turboColor={turboColor} scale={scale} />
|
<DriftParticlesLeft turboColor={turboColor} scale={scale} />
|
||||||
<DriftParticlesRight turboColor={turboColor} scale={scale} />
|
<DriftParticlesRight turboColor={turboColor} scale={scale} />
|
||||||
|
<SmokeParticles
|
||||||
|
driftRight={driftRight.current}
|
||||||
|
driftLeft={driftLeft.current}
|
||||||
|
/>
|
||||||
<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} /> */}
|
||||||
|
@ -421,9 +633,65 @@ export const PlayerController = () => {
|
||||||
position={[0, 2, 8]}
|
position={[0, 2, 8]}
|
||||||
fov={50}
|
fov={50}
|
||||||
ref={cam}
|
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}
|
||||||
/>
|
/>
|
||||||
{/* <PositionalAudio ref={engineSound} url="./sounds/engine.wav" autoplay loop distance={10}/> */}
|
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
);
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,670 @@
|
||||||
|
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 { useStore } from "./store";
|
||||||
|
import { Cylinder } from "@react-three/drei";
|
||||||
|
import FakeGlowMaterial from "./ShaderMaterials/FakeGlow/FakeGlowMaterial";
|
||||||
|
import { HitParticles } from "./Particles/hits/HitParticles";
|
||||||
|
import { CoinParticles } from "./Particles/coins/CoinParticles";
|
||||||
|
import { ItemParticles } from "./Particles/items/ItemParticles";
|
||||||
|
import { geometry } from "maath";
|
||||||
|
import { useGamepad } from "./useGamepad";
|
||||||
|
extend(geometry);
|
||||||
|
|
||||||
|
export const PlayerControllerGamepad = ({
|
||||||
|
player,
|
||||||
|
userPlayer,
|
||||||
|
setNetworkBananas,
|
||||||
|
setNetworkShells,
|
||||||
|
networkBananas,
|
||||||
|
networkShells,
|
||||||
|
}) => {
|
||||||
|
const [isOnGround, setIsOnGround] = useState(false);
|
||||||
|
const body = useRef();
|
||||||
|
const kart = useRef();
|
||||||
|
const cam = useRef();
|
||||||
|
const initialSpeed = 0;
|
||||||
|
const maxSpeed = 30;
|
||||||
|
const boostSpeed = 50;
|
||||||
|
const acceleration = 0.1;
|
||||||
|
const decceleration = 0.2;
|
||||||
|
const damping = -0.1;
|
||||||
|
const MaxSteeringSpeed = 0.01;
|
||||||
|
const [currentSteeringSpeed, setCurrentSteeringSpeed] = useState(0);
|
||||||
|
const [currentSpeed, setCurrentSpeed] = useState(initialSpeed);
|
||||||
|
const camMaxOffset = 1;
|
||||||
|
let steeringAngle = 0;
|
||||||
|
const isOnFloor = useRef(false);
|
||||||
|
const jumpForce = useRef(0);
|
||||||
|
const jumpIsHeld = useRef(false);
|
||||||
|
const driftDirection = useRef(0);
|
||||||
|
const driftLeft = useRef(false);
|
||||||
|
const driftRight = useRef(false);
|
||||||
|
const driftForce = useRef(0);
|
||||||
|
const mario = useRef();
|
||||||
|
const accumulatedDriftPower = useRef(0);
|
||||||
|
const blueTurboThreshold = 10;
|
||||||
|
const orangeTurboThreshold = 30;
|
||||||
|
const purpleTurboThreshold = 60;
|
||||||
|
const [turboColor, setTurboColor] = useState(0xffffff);
|
||||||
|
const boostDuration = useRef(0);
|
||||||
|
const [isBoosting, setIsBoosting] = useState(false);
|
||||||
|
let targetXPosition = 0;
|
||||||
|
let targetZPosition = 8;
|
||||||
|
const [steeringAngleWheels, setSteeringAngleWheels] = useState(0);
|
||||||
|
const engineSound = useRef();
|
||||||
|
const driftSound = useRef();
|
||||||
|
const driftTwoSound = useRef();
|
||||||
|
const driftOrangeSound = useRef();
|
||||||
|
const driftPurpleSound = useRef();
|
||||||
|
const driftBlueSound = useRef();
|
||||||
|
const jumpSound = useRef();
|
||||||
|
const landingSound = useRef();
|
||||||
|
const turboSound = useRef();
|
||||||
|
const [scale, setScale] = useState(0);
|
||||||
|
const raycaster = new THREE.Raycaster();
|
||||||
|
const downDirection = new THREE.Vector3(0, -1, 0);
|
||||||
|
const [shouldLaunch, setShouldLaunch] = useState(false);
|
||||||
|
const effectiveBoost = useRef(0);
|
||||||
|
const text = useRef();
|
||||||
|
|
||||||
|
const { actions, shouldSlowDown, item, bananas, coins, id, controls } =
|
||||||
|
useStore();
|
||||||
|
const slowDownDuration = useRef(1500);
|
||||||
|
const { buttonA, buttonB, RB, LB, joystick, select, start } = useGamepad();
|
||||||
|
|
||||||
|
const skidRotation = useRef(0);
|
||||||
|
|
||||||
|
const rightWheel = useRef();
|
||||||
|
const leftWheel = useRef();
|
||||||
|
const isDrifting = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (leftWheel.current && rightWheel.current && body.current) {
|
||||||
|
actions.setLeftWheel(leftWheel.current);
|
||||||
|
actions.setRightWheel(rightWheel.current);
|
||||||
|
}
|
||||||
|
}, [body.current]);
|
||||||
|
|
||||||
|
useFrame(({ pointer, clock }, delta) => {
|
||||||
|
if (player.id !== id) return;
|
||||||
|
const time = clock.getElapsedTime();
|
||||||
|
if (!body.current && !mario.current) return;
|
||||||
|
isDrifting.current = driftLeft.current || driftRight.current;
|
||||||
|
engineSound.current.setVolume(currentSpeed / 300 + 0.2);
|
||||||
|
engineSound.current.setPlaybackRate(currentSpeed / 10 + 0.1);
|
||||||
|
jumpSound.current.setPlaybackRate(1.5);
|
||||||
|
jumpSound.current.setVolume(0.5);
|
||||||
|
driftSound.current.setVolume(0.2);
|
||||||
|
|
||||||
|
driftBlueSound.current.setVolume(0.5);
|
||||||
|
driftOrangeSound.current.setVolume(0.6);
|
||||||
|
driftPurpleSound.current.setVolume(0.7);
|
||||||
|
// HANDLING AND STEERING
|
||||||
|
const kartRotation =
|
||||||
|
kart.current.rotation.y - driftDirection.current * driftForce.current;
|
||||||
|
const forwardDirection = new THREE.Vector3(
|
||||||
|
-Math.sin(kartRotation),
|
||||||
|
0,
|
||||||
|
-Math.cos(kartRotation)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (start) {
|
||||||
|
actions.setGameStarted(false);
|
||||||
|
}
|
||||||
|
leftWheel.current.kartRotation = kartRotation ;
|
||||||
|
|
||||||
|
if (!driftLeft.current && !driftRight.current) {
|
||||||
|
steeringAngle = currentSteeringSpeed * -joystick[0];
|
||||||
|
targetXPosition = -camMaxOffset * -joystick[0];
|
||||||
|
} else if (driftLeft.current && !driftRight.current) {
|
||||||
|
steeringAngle = currentSteeringSpeed * -(joystick[0] - 1);
|
||||||
|
targetXPosition = -camMaxOffset * -joystick[0];
|
||||||
|
} else if (driftRight.current && !driftLeft.current) {
|
||||||
|
steeringAngle = currentSteeringSpeed * -(joystick[0] + 1);
|
||||||
|
targetXPosition = -camMaxOffset * -joystick[0];
|
||||||
|
}
|
||||||
|
// ACCELERATING
|
||||||
|
const shouldSlow = actions.getShouldSlowDown();
|
||||||
|
|
||||||
|
if (buttonA && currentSpeed < maxSpeed) {
|
||||||
|
// Accelerate the kart within the maximum speed limit
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.min(currentSpeed + acceleration * delta * 144, maxSpeed)
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
buttonA &&
|
||||||
|
currentSpeed > maxSpeed &&
|
||||||
|
effectiveBoost.current > 0
|
||||||
|
) {
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonA) {
|
||||||
|
if (currentSteeringSpeed < MaxSteeringSpeed) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.min(
|
||||||
|
currentSteeringSpeed + 0.0001 * delta * 144,
|
||||||
|
MaxSteeringSpeed
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldSlow) {
|
||||||
|
rightWheel.current.isSpinning = true;
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - decceleration * 2 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
setCurrentSteeringSpeed(0);
|
||||||
|
slowDownDuration.current -= 1500 * delta;
|
||||||
|
setShouldLaunch(true);
|
||||||
|
if (slowDownDuration.current <= 1) {
|
||||||
|
rightWheel.current.isSpinning = false;
|
||||||
|
actions.setShouldSlowDown(false);
|
||||||
|
slowDownDuration.current = 1500;
|
||||||
|
setShouldLaunch(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVERSING
|
||||||
|
if (buttonB) {
|
||||||
|
if (currentSteeringSpeed < MaxSteeringSpeed) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.min(
|
||||||
|
currentSteeringSpeed + 0.0001 * delta * 144,
|
||||||
|
MaxSteeringSpeed
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonB && currentSpeed <= 0) {
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - acceleration * delta * 144, -maxSpeed)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// DECELERATING
|
||||||
|
else if (!buttonA) {
|
||||||
|
if (currentSteeringSpeed > 0) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
} else if (currentSteeringSpeed < 0) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.min(currentSteeringSpeed + 0.00005 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setCurrentSpeed(Math.max(currentSpeed - decceleration * delta * 144, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the kart's rotation based on the steering angle
|
||||||
|
kart.current.rotation.y += steeringAngle * delta * 144;
|
||||||
|
|
||||||
|
// Apply damping to simulate slowdown when no keys are pressed
|
||||||
|
body.current.applyImpulse(
|
||||||
|
{
|
||||||
|
x: -body.current.linvel().x * (1 - damping) * delta * 144,
|
||||||
|
y: 0,
|
||||||
|
z: -body.current.linvel().z * (1 - damping) * delta * 144,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const bodyPosition = body.current.translation();
|
||||||
|
kart.current.position.set(
|
||||||
|
bodyPosition.x,
|
||||||
|
bodyPosition.y - 0.5,
|
||||||
|
bodyPosition.z
|
||||||
|
);
|
||||||
|
|
||||||
|
// JUMPING
|
||||||
|
if (RB && isOnGround && !jumpIsHeld.current) {
|
||||||
|
jumpForce.current += 10;
|
||||||
|
isOnFloor.current = false;
|
||||||
|
jumpIsHeld.current = true;
|
||||||
|
jumpSound.current.play();
|
||||||
|
setIsOnGround(false);
|
||||||
|
|
||||||
|
if (jumpSound.current.isPlaying) {
|
||||||
|
jumpSound.current.stop();
|
||||||
|
jumpSound.current.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOnFloor.current && jumpForce.current > 0) {
|
||||||
|
landingSound.current.play();
|
||||||
|
}
|
||||||
|
if (!isOnGround && jumpForce.current > 0) {
|
||||||
|
jumpForce.current -= 1 * delta * 144;
|
||||||
|
}
|
||||||
|
if (!RB) {
|
||||||
|
jumpIsHeld.current = false;
|
||||||
|
driftDirection.current = 0;
|
||||||
|
driftForce.current = 0;
|
||||||
|
driftLeft.current = false;
|
||||||
|
driftRight.current = false;
|
||||||
|
}
|
||||||
|
// DRIFTING
|
||||||
|
if (
|
||||||
|
jumpIsHeld.current &&
|
||||||
|
currentSteeringSpeed > 0 &&
|
||||||
|
joystick[0] < -0.1 &&
|
||||||
|
!driftRight.current
|
||||||
|
) {
|
||||||
|
driftLeft.current = true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
jumpIsHeld.current &&
|
||||||
|
currentSteeringSpeed > 0 &&
|
||||||
|
joystick[0] > 0.1 &&
|
||||||
|
!driftLeft.current
|
||||||
|
) {
|
||||||
|
driftRight.current = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jumpIsHeld.current && !driftLeft.current && !driftRight.current) {
|
||||||
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
mario.current.rotation.y,
|
||||||
|
0,
|
||||||
|
0.0001 * delta * 144
|
||||||
|
);
|
||||||
|
setTurboColor(0xffffff);
|
||||||
|
accumulatedDriftPower.current = 0;
|
||||||
|
driftSound.current.stop();
|
||||||
|
driftTwoSound.current.stop();
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driftLeft.current) {
|
||||||
|
driftDirection.current = 1;
|
||||||
|
driftForce.current = 0.4;
|
||||||
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
mario.current.rotation.y,
|
||||||
|
steeringAngle * 25 + 0.4,
|
||||||
|
0.05 * delta * 144
|
||||||
|
);
|
||||||
|
if(isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
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
|
||||||
|
);
|
||||||
|
if(isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
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((joystick[0] > 0.8 || joystick[0] < -0.8) && currentSpeed > 20 && isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
} else {
|
||||||
|
leftWheel.current.isSpinning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > blueTurboThreshold) {
|
||||||
|
setTurboColor(0x00ffff);
|
||||||
|
boostDuration.current = 50;
|
||||||
|
driftBlueSound.current.play();
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > orangeTurboThreshold) {
|
||||||
|
setTurboColor(0xffcf00);
|
||||||
|
boostDuration.current = 100;
|
||||||
|
driftBlueSound.current.stop();
|
||||||
|
driftOrangeSound.current.play();
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > purpleTurboThreshold) {
|
||||||
|
setTurboColor(0xff00ff);
|
||||||
|
boostDuration.current = 250;
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driftLeft.current || driftRight.current) {
|
||||||
|
const oscillation = Math.sin(time * 1000) * 0.1;
|
||||||
|
const vibration = oscillation + 0.9;
|
||||||
|
if (turboColor === 0xffffff) {
|
||||||
|
setScale(vibration * 0.8);
|
||||||
|
} else {
|
||||||
|
setScale(vibration);
|
||||||
|
}
|
||||||
|
if (isOnFloor.current && !driftSound.current.isPlaying) {
|
||||||
|
driftSound.current.play();
|
||||||
|
driftTwoSound.current.play();
|
||||||
|
landingSound.current.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// RELEASING DRIFT
|
||||||
|
|
||||||
|
if (boostDuration.current > 1 && !jumpIsHeld.current) {
|
||||||
|
setIsBoosting(true);
|
||||||
|
effectiveBoost.current = boostDuration.current;
|
||||||
|
boostDuration.current = 0;
|
||||||
|
} else if (effectiveBoost.current <= 1) {
|
||||||
|
targetZPosition = 8;
|
||||||
|
setIsBoosting(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBoosting && effectiveBoost.current > 1) {
|
||||||
|
setCurrentSpeed(boostSpeed);
|
||||||
|
effectiveBoost.current -= 1 * delta * 144;
|
||||||
|
targetZPosition = 10;
|
||||||
|
if (!turboSound.current.isPlaying) turboSound.current.play();
|
||||||
|
driftTwoSound.current.play();
|
||||||
|
driftBlueSound.current.stop();
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.stop();
|
||||||
|
} else if (effectiveBoost.current <= 1) {
|
||||||
|
setIsBoosting(false);
|
||||||
|
targetZPosition = 8;
|
||||||
|
turboSound.current.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAMERA WORK
|
||||||
|
|
||||||
|
cam.current.updateMatrixWorld();
|
||||||
|
|
||||||
|
cam.current.position.x = THREE.MathUtils.lerp(
|
||||||
|
cam.current.position.x,
|
||||||
|
targetXPosition,
|
||||||
|
0.01 * delta * 144
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
cam.current.position.z = THREE.MathUtils.lerp(
|
||||||
|
cam.current.position.z,
|
||||||
|
targetZPosition,
|
||||||
|
0.01 * delta * 144
|
||||||
|
);
|
||||||
|
|
||||||
|
body.current.applyImpulse(
|
||||||
|
{
|
||||||
|
x: forwardDirection.x * currentSpeed * delta * 144,
|
||||||
|
y: 0 + jumpForce.current * delta * 144,
|
||||||
|
z: forwardDirection.z * currentSpeed * delta * 144,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the kart's rotation based on the steering angle
|
||||||
|
setSteeringAngleWheels(steeringAngle * 25);
|
||||||
|
|
||||||
|
// SOUND WORK
|
||||||
|
|
||||||
|
// MISC
|
||||||
|
|
||||||
|
if (select) {
|
||||||
|
body.current.setTranslation({ x: 8, y: 2, z: -119 });
|
||||||
|
body.current.setLinvel({ x: 0, y: 0, z: 0 });
|
||||||
|
body.current.setAngvel({ x: 0, y: 0, z: 0 });
|
||||||
|
setCurrentSpeed(0);
|
||||||
|
setCurrentSteeringSpeed(0);
|
||||||
|
setIsBoosting(false);
|
||||||
|
effectiveBoost.current = 0;
|
||||||
|
setIsOnGround(false);
|
||||||
|
jumpForce.current = 0;
|
||||||
|
driftDirection.current = 0;
|
||||||
|
kart.current.rotation.y = Math.PI / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITEMS
|
||||||
|
|
||||||
|
if (LB && item === "banana") {
|
||||||
|
const distanceBehind = 2;
|
||||||
|
const scaledBackwardDirection =
|
||||||
|
forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
...vec3(body.current.translation())
|
||||||
|
);
|
||||||
|
|
||||||
|
const bananaPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newBanana = {
|
||||||
|
id: Math.random() + "-" + +new Date(),
|
||||||
|
position: bananaPosition,
|
||||||
|
player: true,
|
||||||
|
};
|
||||||
|
setNetworkBananas([...networkBananas, newBanana]);
|
||||||
|
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LB && item === "shell") {
|
||||||
|
const distanceBehind = -2;
|
||||||
|
const scaledBackwardDirection =
|
||||||
|
forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
body.current.translation().x,
|
||||||
|
body.current.translation().y,
|
||||||
|
body.current.translation().z
|
||||||
|
);
|
||||||
|
|
||||||
|
const shellPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newShell = {
|
||||||
|
id: Math.random() + "-" + +new Date(),
|
||||||
|
position: shellPosition,
|
||||||
|
player: true,
|
||||||
|
rotation: kartRotation,
|
||||||
|
};
|
||||||
|
setNetworkShells([...networkShells, newShell]);
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LB && item === "mushroom") {
|
||||||
|
setIsBoosting(true);
|
||||||
|
effectiveBoost.current = 300;
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
player.setState("position", body.current.translation());
|
||||||
|
player.setState("rotation", kartRotation + mario.current.rotation.y);
|
||||||
|
player.setState("isBoosting", isBoosting);
|
||||||
|
player.setState("shouldLaunch", shouldLaunch);
|
||||||
|
player.setState("turboColor", turboColor);
|
||||||
|
player.setState("scale", scale);
|
||||||
|
player.setState("bananas", bananas);
|
||||||
|
|
||||||
|
skidRotation.current = kartRotation + mario.current.rotation.y;
|
||||||
|
});
|
||||||
|
|
||||||
|
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} ref={rightWheel}>
|
||||||
|
<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.46, 0.05, 0.3]} ref={leftWheel}></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.46, 0.05, 0.3]} ref={rightWheel}></mesh>
|
||||||
|
{/* <FlameParticles isBoosting={isBoosting} /> */}
|
||||||
|
<DriftParticlesLeft turboColor={turboColor} scale={scale} />
|
||||||
|
<DriftParticlesRight turboColor={turboColor} scale={scale} />
|
||||||
|
<PointParticle
|
||||||
|
position={[-0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/circle.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/circle.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[-0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/star.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/star.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<HitParticles shouldLaunch={shouldLaunch} />
|
||||||
|
</group>
|
||||||
|
|
||||||
|
{/* <ContactShadows frames={1} /> */}
|
||||||
|
<PerspectiveCamera
|
||||||
|
makeDefault
|
||||||
|
position={[0, 2, 8]}
|
||||||
|
fov={50}
|
||||||
|
ref={cam}
|
||||||
|
far={5000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={engineSound}
|
||||||
|
url="./sounds/engine.wav"
|
||||||
|
autoplay
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftSound}
|
||||||
|
url="./sounds/drifting.mp3"
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftTwoSound}
|
||||||
|
url="./sounds/driftingTwo.mp3"
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftOrangeSound}
|
||||||
|
url="./sounds/driftOrange.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftBlueSound}
|
||||||
|
url="./sounds/driftBlue.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftPurpleSound}
|
||||||
|
url="./sounds/driftPurple.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={jumpSound}
|
||||||
|
url="./sounds/jump.mp3"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={landingSound}
|
||||||
|
url="./sounds/landing.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={turboSound}
|
||||||
|
url="./sounds/turbo.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
) : null;
|
||||||
|
};
|
|
@ -0,0 +1,686 @@
|
||||||
|
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 { SmokeParticles } from "./Particles/smoke/SmokeParticles";
|
||||||
|
import { FlameParticle } from "./Particles/flames/FlameParticle";
|
||||||
|
import { useStore } from "./store";
|
||||||
|
import { Cylinder } from "@react-three/drei";
|
||||||
|
import FakeGlowMaterial from "./ShaderMaterials/FakeGlow/FakeGlowMaterial";
|
||||||
|
import { HitParticles } from "./Particles/hits/HitParticles";
|
||||||
|
import { CoinParticles } from "./Particles/coins/CoinParticles";
|
||||||
|
import { ItemParticles } from "./Particles/items/ItemParticles";
|
||||||
|
import { geometry } from "maath";
|
||||||
|
extend(geometry);
|
||||||
|
|
||||||
|
export const PlayerControllerKeyboard = ({
|
||||||
|
player,
|
||||||
|
userPlayer,
|
||||||
|
setNetworkBananas,
|
||||||
|
setNetworkShells,
|
||||||
|
networkBananas,
|
||||||
|
networkShells,
|
||||||
|
}) => {
|
||||||
|
const upPressed = useKeyboardControls((state) => state[Controls.up]);
|
||||||
|
const downPressed = useKeyboardControls((state) => state[Controls.down]);
|
||||||
|
const leftPressed = useKeyboardControls((state) => state[Controls.left]);
|
||||||
|
const rightPressed = useKeyboardControls((state) => state[Controls.right]);
|
||||||
|
const jumpPressed = useKeyboardControls((state) => state[Controls.jump]);
|
||||||
|
const shootPressed = useKeyboardControls((state) => state[Controls.shoot]);
|
||||||
|
const resetPressed = useKeyboardControls((state) => state[Controls.reset]);
|
||||||
|
const escPressed = useKeyboardControls((state) => state[Controls.escape]);
|
||||||
|
|
||||||
|
const [isOnGround, setIsOnGround] = useState(false);
|
||||||
|
const body = useRef();
|
||||||
|
const kart = useRef();
|
||||||
|
const cam = useRef();
|
||||||
|
const initialSpeed = 0;
|
||||||
|
const maxSpeed = 30;
|
||||||
|
const boostSpeed = 50;
|
||||||
|
const acceleration = 0.1;
|
||||||
|
const decceleration = 0.2;
|
||||||
|
const damping = -0.1;
|
||||||
|
const MaxSteeringSpeed = 0.01;
|
||||||
|
const [currentSteeringSpeed, setCurrentSteeringSpeed] = useState(0);
|
||||||
|
const [currentSpeed, setCurrentSpeed] = useState(initialSpeed);
|
||||||
|
const camMaxOffset = 1;
|
||||||
|
let steeringAngle = 0;
|
||||||
|
const isOnFloor = useRef(false);
|
||||||
|
const jumpForce = useRef(0);
|
||||||
|
const jumpIsHeld = useRef(false);
|
||||||
|
const driftDirection = useRef(0);
|
||||||
|
const driftLeft = useRef(false);
|
||||||
|
const driftRight = useRef(false);
|
||||||
|
const driftForce = useRef(0);
|
||||||
|
const mario = useRef();
|
||||||
|
const accumulatedDriftPower = useRef(0);
|
||||||
|
const blueTurboThreshold = 10;
|
||||||
|
const orangeTurboThreshold = 30;
|
||||||
|
const purpleTurboThreshold = 60;
|
||||||
|
const [turboColor, setTurboColor] = useState(0xffffff);
|
||||||
|
const boostDuration = useRef(0);
|
||||||
|
const [isBoosting, setIsBoosting] = useState(false);
|
||||||
|
let targetXPosition = 0;
|
||||||
|
let targetZPosition = 8;
|
||||||
|
const [steeringAngleWheels, setSteeringAngleWheels] = useState(0);
|
||||||
|
const engineSound = useRef();
|
||||||
|
const driftSound = useRef();
|
||||||
|
const driftTwoSound = useRef();
|
||||||
|
const driftOrangeSound = useRef();
|
||||||
|
const driftPurpleSound = useRef();
|
||||||
|
const driftBlueSound = useRef();
|
||||||
|
const jumpSound = useRef();
|
||||||
|
const landingSound = useRef();
|
||||||
|
const turboSound = useRef();
|
||||||
|
const [scale, setScale] = useState(0);
|
||||||
|
const raycaster = new THREE.Raycaster();
|
||||||
|
const downDirection = new THREE.Vector3(0, -1, 0);
|
||||||
|
const [shouldLaunch, setShouldLaunch] = useState(false);
|
||||||
|
const effectiveBoost = useRef(0);
|
||||||
|
const text = useRef();
|
||||||
|
|
||||||
|
const { actions, shouldSlowDown, item, bananas, coins, id, controls } =
|
||||||
|
useStore();
|
||||||
|
const slowDownDuration = useRef(1500);
|
||||||
|
|
||||||
|
const rightWheel = useRef();
|
||||||
|
const leftWheel = useRef();
|
||||||
|
const isDrifting = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (leftWheel.current && rightWheel.current && body.current) {
|
||||||
|
actions.setLeftWheel(leftWheel.current);
|
||||||
|
actions.setRightWheel(rightWheel.current);
|
||||||
|
}
|
||||||
|
}, [body.current]);
|
||||||
|
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
leftWheel.current.kartRotation = kartRotation;
|
||||||
|
if (escPressed) {
|
||||||
|
actions.setGameStarted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftPressed && currentSpeed > 0) {
|
||||||
|
steeringAngle = currentSteeringSpeed;
|
||||||
|
targetXPosition = -camMaxOffset;
|
||||||
|
} else if (rightPressed && currentSpeed > 0) {
|
||||||
|
steeringAngle = -currentSteeringSpeed;
|
||||||
|
targetXPosition = camMaxOffset;
|
||||||
|
} else if (rightPressed && currentSpeed < 0) {
|
||||||
|
steeringAngle = currentSteeringSpeed;
|
||||||
|
targetXPosition = -camMaxOffset;
|
||||||
|
} else if (leftPressed && currentSpeed < 0) {
|
||||||
|
steeringAngle = -currentSteeringSpeed;
|
||||||
|
targetXPosition = camMaxOffset;
|
||||||
|
} else {
|
||||||
|
steeringAngle = 0;
|
||||||
|
targetXPosition = 0;
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACCELERATING
|
||||||
|
const shouldSlow = actions.getShouldSlowDown();
|
||||||
|
|
||||||
|
if (upPressed && currentSpeed < maxSpeed) {
|
||||||
|
// Accelerate the kart within the maximum speed limit
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.min(currentSpeed + acceleration * delta * 144, maxSpeed)
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
upPressed &&
|
||||||
|
currentSpeed > maxSpeed &&
|
||||||
|
effectiveBoost.current > 0
|
||||||
|
) {
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - decceleration * delta * 144, maxSpeed)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upPressed) {
|
||||||
|
if (currentSteeringSpeed < MaxSteeringSpeed) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.min(
|
||||||
|
currentSteeringSpeed + 0.0001 * delta * 144,
|
||||||
|
MaxSteeringSpeed
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldSlow) {
|
||||||
|
rightWheel.current.isSpinning = true;
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - decceleration * 2 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
setCurrentSteeringSpeed(0);
|
||||||
|
slowDownDuration.current -= 1500 * delta;
|
||||||
|
setShouldLaunch(true);
|
||||||
|
if (slowDownDuration.current <= 1) {
|
||||||
|
rightWheel.current.isSpinning = false;
|
||||||
|
actions.setShouldSlowDown(false);
|
||||||
|
slowDownDuration.current = 1500;
|
||||||
|
setShouldLaunch(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVERSING
|
||||||
|
if (downPressed) {
|
||||||
|
if (currentSteeringSpeed < MaxSteeringSpeed) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.min(
|
||||||
|
currentSteeringSpeed + 0.0001 * delta * 144,
|
||||||
|
MaxSteeringSpeed
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downPressed && currentSpeed <= 0) {
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - acceleration * delta * 144, -maxSpeed)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// DECELERATING
|
||||||
|
else if (!upPressed) {
|
||||||
|
if (currentSteeringSpeed > 0) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
} else if (currentSteeringSpeed < 0) {
|
||||||
|
setCurrentSteeringSpeed(
|
||||||
|
Math.min(currentSteeringSpeed + 0.00005 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setCurrentSpeed(Math.max(currentSpeed - decceleration * delta * 144, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the kart's rotation based on the steering angle
|
||||||
|
kart.current.rotation.y += steeringAngle * delta * 144;
|
||||||
|
|
||||||
|
// Apply damping to simulate slowdown when no keys are pressed
|
||||||
|
body.current.applyImpulse(
|
||||||
|
{
|
||||||
|
x: -body.current.linvel().x * (1 - damping) * delta * 144,
|
||||||
|
y: 0,
|
||||||
|
z: -body.current.linvel().z * (1 - damping) * delta * 144,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const bodyPosition = body.current.translation();
|
||||||
|
kart.current.position.set(
|
||||||
|
bodyPosition.x,
|
||||||
|
bodyPosition.y - 0.5,
|
||||||
|
bodyPosition.z
|
||||||
|
);
|
||||||
|
|
||||||
|
// JUMPING
|
||||||
|
if (jumpPressed && isOnGround && !jumpIsHeld.current) {
|
||||||
|
jumpForce.current += 10;
|
||||||
|
isOnFloor.current = false;
|
||||||
|
jumpIsHeld.current = true;
|
||||||
|
jumpSound.current.play();
|
||||||
|
setIsOnGround(false);
|
||||||
|
|
||||||
|
if (jumpSound.current.isPlaying) {
|
||||||
|
jumpSound.current.stop();
|
||||||
|
jumpSound.current.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOnFloor.current && jumpForce.current > 0) {
|
||||||
|
landingSound.current.play();
|
||||||
|
}
|
||||||
|
if (!isOnGround && jumpForce.current > 0) {
|
||||||
|
jumpForce.current -= 1 * delta * 144;
|
||||||
|
}
|
||||||
|
if (!jumpPressed) {
|
||||||
|
jumpIsHeld.current = false;
|
||||||
|
driftDirection.current = 0;
|
||||||
|
driftForce.current = 0;
|
||||||
|
driftLeft.current = false;
|
||||||
|
driftRight.current = false;
|
||||||
|
}
|
||||||
|
// DRIFTING
|
||||||
|
if (
|
||||||
|
jumpIsHeld.current &&
|
||||||
|
currentSteeringSpeed > 0 &&
|
||||||
|
upPressed &&
|
||||||
|
leftPressed &&
|
||||||
|
!driftRight.current
|
||||||
|
) {
|
||||||
|
driftLeft.current = true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
jumpIsHeld.current &&
|
||||||
|
currentSteeringSpeed > 0 &&
|
||||||
|
upPressed &&
|
||||||
|
rightPressed > 0.1 &&
|
||||||
|
!driftLeft.current
|
||||||
|
) {
|
||||||
|
driftRight.current = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jumpIsHeld.current && !driftLeft.current && !driftRight.current) {
|
||||||
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
mario.current.rotation.y,
|
||||||
|
0,
|
||||||
|
0.0001 * delta * 144
|
||||||
|
);
|
||||||
|
setTurboColor(0xffffff);
|
||||||
|
accumulatedDriftPower.current = 0;
|
||||||
|
driftSound.current.stop();
|
||||||
|
driftTwoSound.current.stop();
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driftLeft.current) {
|
||||||
|
driftDirection.current = 1;
|
||||||
|
driftForce.current = 0.4;
|
||||||
|
mario.current.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
mario.current.rotation.y,
|
||||||
|
steeringAngle * 25 + 0.4,
|
||||||
|
0.05 * delta * 144
|
||||||
|
);
|
||||||
|
if (isOnFloor.current) {
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
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
|
||||||
|
);
|
||||||
|
if (isOnFloor.current) {
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
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((leftPressed || rightPressed) && currentSpeed > 20 && isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
} else {
|
||||||
|
leftWheel.current.isSpinning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > blueTurboThreshold) {
|
||||||
|
setTurboColor(0x00ffff);
|
||||||
|
boostDuration.current = 50;
|
||||||
|
driftBlueSound.current.play();
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > orangeTurboThreshold) {
|
||||||
|
setTurboColor(0xffcf00);
|
||||||
|
boostDuration.current = 100;
|
||||||
|
driftBlueSound.current.stop();
|
||||||
|
driftOrangeSound.current.play();
|
||||||
|
}
|
||||||
|
if (accumulatedDriftPower.current > purpleTurboThreshold) {
|
||||||
|
setTurboColor(0xff00ff);
|
||||||
|
boostDuration.current = 250;
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driftLeft.current || driftRight.current) {
|
||||||
|
const oscillation = Math.sin(time * 1000) * 0.1;
|
||||||
|
const vibration = oscillation + 0.9;
|
||||||
|
if (turboColor === 0xffffff) {
|
||||||
|
setScale(vibration * 0.8);
|
||||||
|
} else {
|
||||||
|
setScale(vibration);
|
||||||
|
}
|
||||||
|
if (isOnFloor.current && !driftSound.current.isPlaying) {
|
||||||
|
driftSound.current.play();
|
||||||
|
driftTwoSound.current.play();
|
||||||
|
landingSound.current.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// RELEASING DRIFT
|
||||||
|
|
||||||
|
if (boostDuration.current > 1 && !jumpIsHeld.current) {
|
||||||
|
setIsBoosting(true);
|
||||||
|
effectiveBoost.current = boostDuration.current;
|
||||||
|
boostDuration.current = 0;
|
||||||
|
} else if (effectiveBoost.current <= 1) {
|
||||||
|
targetZPosition = 8;
|
||||||
|
setIsBoosting(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBoosting && effectiveBoost.current > 1) {
|
||||||
|
setCurrentSpeed(boostSpeed);
|
||||||
|
effectiveBoost.current -= 1 * delta * 144;
|
||||||
|
targetZPosition = 10;
|
||||||
|
if (!turboSound.current.isPlaying) turboSound.current.play();
|
||||||
|
driftTwoSound.current.play();
|
||||||
|
driftBlueSound.current.stop();
|
||||||
|
driftOrangeSound.current.stop();
|
||||||
|
driftPurpleSound.current.stop();
|
||||||
|
} else if (effectiveBoost.current <= 1) {
|
||||||
|
setIsBoosting(false);
|
||||||
|
targetZPosition = 8;
|
||||||
|
turboSound.current.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAMERA WORK
|
||||||
|
|
||||||
|
cam.current.updateMatrixWorld();
|
||||||
|
|
||||||
|
cam.current.position.x = THREE.MathUtils.lerp(
|
||||||
|
cam.current.position.x,
|
||||||
|
targetXPosition,
|
||||||
|
0.01 * delta * 144
|
||||||
|
);
|
||||||
|
|
||||||
|
cam.current.position.z = THREE.MathUtils.lerp(
|
||||||
|
cam.current.position.z,
|
||||||
|
targetZPosition,
|
||||||
|
0.01 * delta * 144
|
||||||
|
);
|
||||||
|
|
||||||
|
body.current.applyImpulse(
|
||||||
|
{
|
||||||
|
x: forwardDirection.x * currentSpeed * delta * 144,
|
||||||
|
y: 0 + jumpForce.current * delta * 144,
|
||||||
|
z: forwardDirection.z * currentSpeed * delta * 144,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the kart's rotation based on the steering angle
|
||||||
|
setSteeringAngleWheels(steeringAngle * 25);
|
||||||
|
|
||||||
|
// SOUND WORK
|
||||||
|
|
||||||
|
// MISC
|
||||||
|
|
||||||
|
if (resetPressed) {
|
||||||
|
body.current.setTranslation({ x: 8, y: 2, z: -119 });
|
||||||
|
body.current.setLinvel({ x: 0, y: 0, z: 0 });
|
||||||
|
body.current.setAngvel({ x: 0, y: 0, z: 0 });
|
||||||
|
setCurrentSpeed(0);
|
||||||
|
setCurrentSteeringSpeed(0);
|
||||||
|
setIsBoosting(false);
|
||||||
|
effectiveBoost.current = 0;
|
||||||
|
setIsOnGround(false);
|
||||||
|
jumpForce.current = 0;
|
||||||
|
driftDirection.current = 0;
|
||||||
|
kart.current.rotation.y = Math.PI / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITEMS
|
||||||
|
|
||||||
|
if (shootPressed && item === "banana") {
|
||||||
|
const distanceBehind = 2;
|
||||||
|
const scaledBackwardDirection =
|
||||||
|
forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
...vec3(body.current.translation())
|
||||||
|
);
|
||||||
|
|
||||||
|
const bananaPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newBanana = {
|
||||||
|
id: Math.random() + "-" + +new Date(),
|
||||||
|
position: bananaPosition,
|
||||||
|
player: true,
|
||||||
|
};
|
||||||
|
setNetworkBananas([...networkBananas, newBanana]);
|
||||||
|
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shootPressed && item === "shell") {
|
||||||
|
const distanceBehind = -2;
|
||||||
|
const scaledBackwardDirection =
|
||||||
|
forwardDirection.multiplyScalar(distanceBehind);
|
||||||
|
|
||||||
|
const kartPosition = new THREE.Vector3(
|
||||||
|
body.current.translation().x,
|
||||||
|
body.current.translation().y,
|
||||||
|
body.current.translation().z
|
||||||
|
);
|
||||||
|
|
||||||
|
const shellPosition = kartPosition.sub(scaledBackwardDirection);
|
||||||
|
const newShell = {
|
||||||
|
id: Math.random() + "-" + +new Date(),
|
||||||
|
position: shellPosition,
|
||||||
|
player: true,
|
||||||
|
rotation: kartRotation,
|
||||||
|
};
|
||||||
|
setNetworkShells([...networkShells, newShell]);
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shootPressed && item === "mushroom") {
|
||||||
|
setIsBoosting(true);
|
||||||
|
effectiveBoost.current = 300;
|
||||||
|
actions.useItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
player.setState("position", body.current.translation());
|
||||||
|
player.setState("rotation", kartRotation + mario.current.rotation.y);
|
||||||
|
player.setState("isBoosting", isBoosting);
|
||||||
|
player.setState("shouldLaunch", shouldLaunch);
|
||||||
|
player.setState("turboColor", turboColor);
|
||||||
|
player.setState("scale", scale);
|
||||||
|
player.setState("bananas", bananas);
|
||||||
|
});
|
||||||
|
|
||||||
|
return player.id === id ? (
|
||||||
|
<group>
|
||||||
|
<RigidBody
|
||||||
|
ref={body}
|
||||||
|
colliders={false}
|
||||||
|
position={[8, 60, -119]}
|
||||||
|
centerOfMass={[0, -1, 0]}
|
||||||
|
mass={3}
|
||||||
|
ccd
|
||||||
|
name="player"
|
||||||
|
type={player.id === id ? "dynamic" : "kinematic"}
|
||||||
|
>
|
||||||
|
<BallCollider
|
||||||
|
args={[0.5]}
|
||||||
|
mass={3}
|
||||||
|
onCollisionEnter={({ other }) => {
|
||||||
|
isOnFloor.current = true;
|
||||||
|
setIsOnGround(true);
|
||||||
|
}}
|
||||||
|
onCollisionExit={({ other }) => {
|
||||||
|
isOnFloor.current = false;
|
||||||
|
setIsOnGround(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</RigidBody>
|
||||||
|
|
||||||
|
<group ref={kart} rotation={[0, Math.PI / 2, 0]}>
|
||||||
|
<group ref={mario}>
|
||||||
|
<Mario
|
||||||
|
currentSpeed={currentSpeed}
|
||||||
|
steeringAngleWheels={steeringAngleWheels}
|
||||||
|
isBoosting={isBoosting}
|
||||||
|
shouldLaunch={shouldLaunch}
|
||||||
|
/>
|
||||||
|
<CoinParticles coins={coins} />
|
||||||
|
<ItemParticles item={item} />
|
||||||
|
<mesh position={[0.6, 0.05, 0.5]} scale={scale}>
|
||||||
|
<sphereGeometry args={[0.05, 16, 16]} />
|
||||||
|
<meshStandardMaterial
|
||||||
|
emissive={turboColor}
|
||||||
|
toneMapped={false}
|
||||||
|
emissiveIntensity={100}
|
||||||
|
transparent
|
||||||
|
opacity={0.4}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
<mesh position={[-0.46, 0.05, 0.3]} ref={leftWheel}></mesh>
|
||||||
|
<mesh position={[0.46, 0.05, 0.3]} ref={rightWheel}></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} />
|
||||||
|
<SmokeParticles
|
||||||
|
driftRight={driftRight.current}
|
||||||
|
driftLeft={driftLeft.current}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[-0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/circle.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/circle.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[-0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/star.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/star.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<HitParticles shouldLaunch={shouldLaunch} />
|
||||||
|
</group>
|
||||||
|
|
||||||
|
{/* <ContactShadows frames={1} /> */}
|
||||||
|
<PerspectiveCamera
|
||||||
|
makeDefault
|
||||||
|
position={[0, 2, 8]}
|
||||||
|
fov={50}
|
||||||
|
ref={cam}
|
||||||
|
far={5000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={engineSound}
|
||||||
|
url="./sounds/engine.wav"
|
||||||
|
autoplay
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftSound}
|
||||||
|
url="./sounds/drifting.mp3"
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftTwoSound}
|
||||||
|
url="./sounds/driftingTwo.mp3"
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftOrangeSound}
|
||||||
|
url="./sounds/driftOrange.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftBlueSound}
|
||||||
|
url="./sounds/driftBlue.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftPurpleSound}
|
||||||
|
url="./sounds/driftPurple.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={jumpSound}
|
||||||
|
url="./sounds/jump.mp3"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={landingSound}
|
||||||
|
url="./sounds/landing.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={turboSound}
|
||||||
|
url="./sounds/turbo.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
) : null;
|
||||||
|
};
|
|
@ -0,0 +1,619 @@
|
||||||
|
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 { SmokeParticles } from "./Particles/smoke/SmokeParticles";
|
||||||
|
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, menuButton } = useStore();
|
||||||
|
const slowDownDuration = useRef(1500);
|
||||||
|
const rightWheel = useRef();
|
||||||
|
const leftWheel = useRef();
|
||||||
|
const isDrifting = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (leftWheel.current && rightWheel.current && body.current) {
|
||||||
|
actions.setLeftWheel(leftWheel.current);
|
||||||
|
actions.setRightWheel(rightWheel.current);
|
||||||
|
}
|
||||||
|
}, [body.current]);
|
||||||
|
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
leftWheel.current.kartRotation = kartRotation;
|
||||||
|
if (menuButton) {
|
||||||
|
actions.setGameStarted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
rightWheel.current.isSpinning = true;
|
||||||
|
|
||||||
|
setCurrentSpeed(
|
||||||
|
Math.max(currentSpeed - decceleration * 2 * delta * 144, 0)
|
||||||
|
);
|
||||||
|
setCurrentSteeringSpeed(0);
|
||||||
|
slowDownDuration.current -= 1500 * delta;
|
||||||
|
setShouldLaunch(true);
|
||||||
|
if (slowDownDuration.current <= 1) {
|
||||||
|
rightWheel.current.isSpinning = false;
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
if(isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
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
|
||||||
|
);
|
||||||
|
if(isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
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((joystickX > 0.8 || joystickX < -0.8) && currentSpeed > 20 && isOnFloor.current){
|
||||||
|
leftWheel.current.isSpinning = true;
|
||||||
|
} else {
|
||||||
|
leftWheel.current.isSpinning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.46, 0.05, 0.3]} ref={leftWheel}></mesh>
|
||||||
|
<mesh position={[0.46, 0.05, 0.3]} ref={rightWheel}></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} />
|
||||||
|
<SmokeParticles driftRight={driftRight.current} driftLeft={driftLeft.current} />
|
||||||
|
<PointParticle
|
||||||
|
position={[-0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/circle.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/circle.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[-0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/star.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<PointParticle
|
||||||
|
position={[0.6, 0.05, 0.5]}
|
||||||
|
png="./particles/star.png"
|
||||||
|
turboColor={turboColor}
|
||||||
|
/>
|
||||||
|
<HitParticles shouldLaunch={shouldLaunch} />
|
||||||
|
</group>
|
||||||
|
|
||||||
|
{/* <ContactShadows frames={1} /> */}
|
||||||
|
<PerspectiveCamera
|
||||||
|
makeDefault
|
||||||
|
position={[0, 2, 8]}
|
||||||
|
fov={50}
|
||||||
|
ref={cam}
|
||||||
|
far={5000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={engineSound}
|
||||||
|
url="./sounds/engine.wav"
|
||||||
|
autoplay
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftSound}
|
||||||
|
url="./sounds/drifting.mp3"
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftTwoSound}
|
||||||
|
url="./sounds/driftingTwo.mp3"
|
||||||
|
loop
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftOrangeSound}
|
||||||
|
url="./sounds/driftOrange.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftBlueSound}
|
||||||
|
url="./sounds/driftBlue.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PositionalAudio
|
||||||
|
ref={driftPurpleSound}
|
||||||
|
url="./sounds/driftPurple.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={jumpSound}
|
||||||
|
url="./sounds/jump.mp3"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={landingSound}
|
||||||
|
url="./sounds/landing.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
<PositionalAudio
|
||||||
|
ref={turboSound}
|
||||||
|
url="./sounds/turbo.wav"
|
||||||
|
loop={false}
|
||||||
|
distance={1000}
|
||||||
|
/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
) : null;
|
||||||
|
};
|
|
@ -0,0 +1,271 @@
|
||||||
|
import { Controls } from "../App";
|
||||||
|
import { BallCollider, RigidBody, useRapier, vec3 } from "@react-three/rapier";
|
||||||
|
import {
|
||||||
|
useKeyboardControls,
|
||||||
|
PerspectiveCamera,
|
||||||
|
ContactShadows,
|
||||||
|
Sphere,
|
||||||
|
OrbitControls,
|
||||||
|
Trail,
|
||||||
|
PositionalAudio,
|
||||||
|
Text,
|
||||||
|
Billboard,
|
||||||
|
} from "@react-three/drei";
|
||||||
|
import { useFrame, useThree } 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 { isHost } from "playroomkit";
|
||||||
|
import { Banana } from "./models/items/Banana_peel_mario_kart";
|
||||||
|
|
||||||
|
export const PlayerDummies = ( { player, userPlayer }) => {
|
||||||
|
const upPressed = useKeyboardControls((state) => state[Controls.up]);
|
||||||
|
const downPressed = useKeyboardControls((state) => state[Controls.down]);
|
||||||
|
const leftPressed = useKeyboardControls((state) => state[Controls.left]);
|
||||||
|
const rightPressed = useKeyboardControls((state) => state[Controls.right]);
|
||||||
|
const jumpPressed = useKeyboardControls((state) => state[Controls.jump]);
|
||||||
|
const shootPressed = useKeyboardControls((state) => state[Controls.shoot]);
|
||||||
|
const resetPressed = useKeyboardControls((state) => state[Controls.reset]);
|
||||||
|
|
||||||
|
const [isOnGround, setIsOnGround] = useState(false);
|
||||||
|
const body = useRef();
|
||||||
|
const kart = useRef();
|
||||||
|
const cam = useRef();
|
||||||
|
const text = 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 [networkBananas, setNetworkBananas] = useState([]);
|
||||||
|
const [networkShells, setNetworkShells] = useState([]);
|
||||||
|
|
||||||
|
|
||||||
|
const { actions, shouldSlowDown, item, coins, id} = useStore();
|
||||||
|
const slowDownDuration = useRef(1500);
|
||||||
|
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
const bodyPosition = player.getState("position");
|
||||||
|
const bodyRotation = player.getState("rotation");
|
||||||
|
setIsBoosting(player.getState("isBoosting"));
|
||||||
|
setShouldLaunch(player.getState("shouldLaunch"));
|
||||||
|
setTurboColor(player.getState("turboColor"));
|
||||||
|
setScale(player.getState("scale"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(bodyPosition && bodyRotation && kart.current && mario.current){
|
||||||
|
kart.current.position.set(bodyPosition.x, bodyPosition.y -.5, bodyPosition.z);
|
||||||
|
kart.current.rotation.set(0, bodyRotation, 0);
|
||||||
|
body.current.setTranslation([bodyPosition.x, bodyPosition.y, bodyPosition.z]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return player.id != id? (
|
||||||
|
<>
|
||||||
|
<group>
|
||||||
|
<RigidBody
|
||||||
|
type="kinematic"
|
||||||
|
ref={body}
|
||||||
|
position={[0, 0, 0]}
|
||||||
|
rotation={[0, 0, 0]}
|
||||||
|
colliders={false}
|
||||||
|
name="player"
|
||||||
|
>
|
||||||
|
<BallCollider args={[0.5]} />
|
||||||
|
</RigidBody>
|
||||||
|
|
||||||
|
<group ref={kart} rotation={[0, Math.PI / 2, 0]}>
|
||||||
|
<group ref={mario}>
|
||||||
|
<Billboard>
|
||||||
|
<Text font={"./fonts/HK.ttf"} ref={text} fontSize={0.4} outlineWidth={0.03} position={[0, 2, 0]}>{player.state.profile.name}</Text>
|
||||||
|
</Billboard>
|
||||||
|
<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} /> */}
|
||||||
|
<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;
|
||||||
|
};
|
|
@ -39,11 +39,11 @@ export default function FakeFlame({ falloff = 3, glowInternalRadius = 1.0, glowC
|
||||||
float glitchStrength = sin(glitchTime) + sin(glitchTime * .05) + sin(glitchTime * .36);
|
float glitchStrength = sin(glitchTime) + sin(glitchTime * .05) + sin(glitchTime * .36);
|
||||||
glitchStrength /= 2.0;
|
glitchStrength /= 2.0;
|
||||||
glitchStrength = smoothstep(0.2, 0.8, glitchStrength);
|
glitchStrength = smoothstep(0.2, 0.8, glitchStrength);
|
||||||
glitchStrength *= 0.05;
|
glitchStrength *= 0.;
|
||||||
modelPosition.x += (random2D(modelNormal.xx + time) - 0.5) * glitchStrength;
|
modelPosition.x += (random2D(modelNormal.xx + time) - 0.5) * glitchStrength;
|
||||||
modelPosition.x += (random2D(modelNormal.xx - time) - 0.2) * glitchStrength;
|
modelPosition.x += (random2D(modelNormal.xx - time) - 0.2) * glitchStrength;
|
||||||
modelPosition.y += sin(smoothstep(0.3, vUv.y - 2.2, position.y) * 2.) * sin(time * 48.);
|
modelPosition.y += sin(smoothstep(0.4, vUv.y - 2.5, position.y) * 2.) * sin(time * 48.);
|
||||||
modelPosition.z += sin(smoothstep(0., vUv.x - 0.8, position.z) * 2.) * sin(time * 24.) * .6;
|
modelPosition.z += sin(smoothstep(0., vUv.x - 1.8, position.z) * 2.) * sin(time * 24.);
|
||||||
|
|
||||||
gl_Position = projectionMatrix * viewMatrix * modelPosition;
|
gl_Position = projectionMatrix * viewMatrix * modelPosition;
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ export default function FakeFlame({ falloff = 3, glowInternalRadius = 1.0, glowC
|
||||||
q.x *= 2.;
|
q.x *= 2.;
|
||||||
q.y *= 2.;
|
q.y *= 2.;
|
||||||
float strength = floor(q.x+1.5);
|
float strength = floor(q.x+1.5);
|
||||||
float T3 = max(2.,2.25*strength)*time * 3.;
|
float T3 = max(2.,2.25*strength)*time * 4.;
|
||||||
q.x = mod(q.x,1.)-0.5;
|
q.x = mod(q.x,1.)-0.5;
|
||||||
q.y -= 0.05;
|
q.y -= 0.05;
|
||||||
float n = fbm(strength*q + vec2(0,T3));
|
float n = fbm(strength*q + vec2(0,T3));
|
||||||
|
|
|
@ -1,68 +1,56 @@
|
||||||
import { Euler, Object3D, BackSide, Vector3 } from "three";
|
import { Euler, Object3D, Vector3, Matrix4, DoubleSide } from 'three'
|
||||||
import { useRef, useLayoutEffect } from "react";
|
import { useRef, useLayoutEffect } from 'react'
|
||||||
import { useFrame } from "@react-three/fiber";
|
import { useFrame } from '@react-three/fiber'
|
||||||
import { useStore } from "./store";
|
import { vec3 } from '@react-three/rapier'
|
||||||
|
|
||||||
const o = new Object3D();
|
|
||||||
|
|
||||||
export function Skid({ count = 500, opacity = 1, size = 0.4 }) {
|
import { useStore } from './store'
|
||||||
|
|
||||||
|
|
||||||
|
const e = new Euler()
|
||||||
|
const m = new Matrix4()
|
||||||
|
const o = new Object3D()
|
||||||
|
const v = new Vector3()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function Skid({ count = 50000, opacity = 0.5, size = 0.3 }) {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const [bodyPosition, bodyRotation] = useStore((state) => [
|
const { leftWheel, rightWheel } = useStore();
|
||||||
state.bodyPosition,
|
let index = 0
|
||||||
state.bodyRotation,
|
|
||||||
]);
|
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
// console.log(bodyPosition, bodyRotation);
|
if(!leftWheel && !rightWheel) return;
|
||||||
if (ref.current && bodyPosition && bodyRotation !== undefined) {
|
const rotation = leftWheel.kartRotation;
|
||||||
setItemAt(ref.current, bodyPosition, bodyRotation, index++);
|
if (leftWheel && rightWheel && ref.current && (leftWheel.isSpinning || rightWheel.isSpinning)) {
|
||||||
if (index === count) index = 0;
|
setItemAt(ref.current, rotation, leftWheel, index++);
|
||||||
|
setItemAt(ref.current, rotation, rightWheel, index++);
|
||||||
|
|
||||||
|
if (index === count) index = 0
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if(ref.current){
|
if(ref.current){
|
||||||
ref.current.geometry.rotateX(-Math.PI / 2);
|
ref.current.geometry.rotateX(-Math.PI / 2)
|
||||||
return () => {
|
return () => {
|
||||||
ref.current.geometry.rotateX(Math.PI / 2);
|
ref.current.geometry.rotateX(Math.PI / 2)
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<instancedMesh ref={ref} args={[undefined, undefined, count]}>
|
<instancedMesh frustumCulled={false} ref={ref} args={[undefined, undefined, count]}>
|
||||||
<planeGeometry args={[size, size * 2]} />
|
<planeGeometry args={[size, size]} />
|
||||||
<meshBasicMaterial
|
<meshBasicMaterial color="black" side={DoubleSide} transparent opacity={opacity} />
|
||||||
color={0x000000}
|
|
||||||
transparent
|
|
||||||
opacity={opacity}
|
|
||||||
depthWrite={false}
|
|
||||||
side={BackSide}
|
|
||||||
/>
|
|
||||||
</instancedMesh>
|
</instancedMesh>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setItemAt(instances, bodyPosition, bodyRotation, index) {
|
function setItemAt(instances, rotation, body, index) {
|
||||||
// Calculate the backward offset
|
o.position.copy(body.getWorldPosition(v));
|
||||||
const backwardOffset = 0.5; // Adjust this value as needed
|
o.rotation.set(0, rotation, 0);
|
||||||
const forwardDirection = new Vector3(
|
o.scale.setScalar(1)
|
||||||
-Math.sin(bodyRotation),
|
o.updateMatrix()
|
||||||
0,
|
instances.setMatrixAt(index, o.matrix)
|
||||||
-Math.cos(bodyRotation)
|
instances.instanceMatrix.needsUpdate = true
|
||||||
);
|
|
||||||
const backwardPosition = forwardDirection
|
|
||||||
.multiplyScalar(-backwardOffset)
|
|
||||||
.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);
|
|
||||||
o.scale.setScalar(1);
|
|
||||||
o.updateMatrix();
|
|
||||||
instances.setMatrixAt(index, o.matrix);
|
|
||||||
instances.instanceMatrix.needsUpdate = true;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,15 @@ 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, shouldLaunch, ...props }) {
|
||||||
const { nodes, materials } = useGLTF('./models/characters/mariokarttest.glb')
|
const { nodes, materials } = useGLTF('./models/characters/mariokarttest.glb')
|
||||||
|
|
||||||
const frontLeftWheel = useRef()
|
const frontLeftWheel = useRef()
|
||||||
|
@ -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 (shouldLaunch) {
|
||||||
|
gsap.to(mario.current.rotation, {duration: 1.5, y: Math.PI * 3})
|
||||||
|
mario.current.rotation.set(0, 0, 0);
|
||||||
|
}
|
||||||
|
}, [shouldLaunch])
|
||||||
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
|
||||||
|
@ -86,7 +100,7 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
|
||||||
|
|
||||||
<Cylinder
|
<Cylinder
|
||||||
args={[0.1, 0, 1, 128, 64, true]}
|
args={[0.1, 0, 1, 128, 64, true]}
|
||||||
position={[0.3, 0.6, -1.2]}
|
position={[0.3, 0.65, -1.35]}
|
||||||
rotation={[Math.PI / 1.5, 0, 0]}
|
rotation={[Math.PI / 1.5, 0, 0]}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
|
|
||||||
|
@ -96,7 +110,7 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
|
||||||
|
|
||||||
<Cylinder
|
<Cylinder
|
||||||
args={[0.1, 0, 1, 128, 64, true]}
|
args={[0.1, 0, 1, 128, 64, true]}
|
||||||
position={[-0.3, 0.6, -1.2]}
|
position={[-0.3, 0.65, -1.35]}
|
||||||
rotation={[Math.PI / 1.5, 0, 0]}
|
rotation={[Math.PI / 1.5, 0, 0]}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
>
|
>
|
||||||
|
@ -104,17 +118,17 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
|
||||||
</Cylinder>
|
</Cylinder>
|
||||||
<Cylinder
|
<Cylinder
|
||||||
args={[0.09, 0, 1, 128, 64, true]}
|
args={[0.09, 0, 1, 128, 64, true]}
|
||||||
position={[0.18, 0.7, -0.8]}
|
position={[0.18, 0.75, -1.1]}
|
||||||
rotation={[Math.PI / 1.5, 0, 0]}
|
rotation={[Math.PI / 1.5, 0, 0]}
|
||||||
scale={scale}
|
scale={scale * 0.8}
|
||||||
>
|
>
|
||||||
<FakeFlame isBoosting={isBoosting} />
|
<FakeFlame isBoosting={isBoosting} />
|
||||||
</Cylinder>
|
</Cylinder>
|
||||||
<Cylinder
|
<Cylinder
|
||||||
args={[0.09, 0, 1, 128, 64, true]}
|
args={[0.09, 0, 1, 128, 64, true]}
|
||||||
position={[-0.18, 0.7, -0.8]}
|
position={[-0.18, 0.75, -1.1]}
|
||||||
rotation={[Math.PI / 1.5, 0, 0]}
|
rotation={[Math.PI / 1.5, 0, 0]}
|
||||||
scale={scale}
|
scale={scale * 0.8}
|
||||||
>
|
>
|
||||||
<FakeFlame isBoosting={isBoosting}/>
|
<FakeFlame isBoosting={isBoosting}/>
|
||||||
</Cylinder>
|
</Cylinder>
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
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/Yanez-Designs)
|
||||||
|
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, { useEffect, 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, id, position, setNetworkBananas, networkBananas}) {
|
||||||
|
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={[position.x, position.y, position.z]}
|
||||||
|
sensor
|
||||||
|
onIntersectionEnter={({other}) => {
|
||||||
|
if(other.rigidBodyObject.name === "player"){
|
||||||
|
actions.setShouldSlowDown(true);
|
||||||
|
setNetworkBananas(networkBananas.filter((banana) => banana.id !== id));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
colliders={false}
|
||||||
|
name='banana'
|
||||||
|
>
|
||||||
|
<BallCollider args={[0.5]} />
|
||||||
|
|
||||||
|
</RigidBody>
|
||||||
|
|
||||||
|
<group position={[position.x, position.y, position.z]} 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,76 @@
|
||||||
|
/*
|
||||||
|
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";
|
||||||
|
import { useStore } from "../../store";
|
||||||
|
|
||||||
|
export function Shell({ id, position, rotation, setNetworkShells, networkShells, ...props}) {
|
||||||
|
const { nodes, materials } = useGLTF("./models/items/mario_shell_red.glb");
|
||||||
|
const rigidBody = useRef();
|
||||||
|
const ref = useRef();
|
||||||
|
|
||||||
|
const shell_speed = 100;
|
||||||
|
const {actions} = useStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const velocity = {
|
||||||
|
x: -Math.sin(rotation) * shell_speed,
|
||||||
|
y: 0,
|
||||||
|
z: -Math.cos(rotation) * shell_speed,
|
||||||
|
};
|
||||||
|
|
||||||
|
rigidBody.current.setLinvel(velocity, true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
if(rigidBody.current && ref.current){
|
||||||
|
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={[position.x, position.y, position.z]}
|
||||||
|
name="shell"
|
||||||
|
colliders={false}
|
||||||
|
onCollisionEnter={(other) => {
|
||||||
|
if (other.rigidBodyObject.name === "player") {
|
||||||
|
actions.setShouldSlowDown(true);
|
||||||
|
setNetworkShells(
|
||||||
|
networkShells.filter((shell) => shell.id !== id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<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,70 @@
|
||||||
|
/*
|
||||||
|
Auto-generated by: https://github.com/pmndrs/gltfjsx
|
||||||
|
Command: npx gltfjsx@6.2.16 .\gift.glb --shadows --transform
|
||||||
|
Files: .\gift.glb [694.14KB] > C:\Users\mouli\dev\r3f-vite-starter\public\models\misc\gift-transformed.glb [86.34KB] (88%)
|
||||||
|
Author: gorzi (https://sketchfab.com/gorzi90)
|
||||||
|
License: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
||||||
|
Source: https://sketchfab.com/3d-models/gift-f3d8abcd3b9442f39a9a2017d59b56a1
|
||||||
|
Title: Gift
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useRef } from 'react'
|
||||||
|
import { useGLTF, Float } from '@react-three/drei'
|
||||||
|
import { CuboidCollider, RigidBody } from "@react-three/rapier";
|
||||||
|
import { useStore } from "../../store";
|
||||||
|
import { useFrame } from '@react-three/fiber';
|
||||||
|
|
||||||
|
export function ItemBox(props) {
|
||||||
|
const { nodes, materials } = useGLTF('./models/misc/gift-transformed.glb');
|
||||||
|
const { actions } = useStore();
|
||||||
|
const ref = useRef();
|
||||||
|
const [scale, setScale] = React.useState(0.6);
|
||||||
|
const frames = useRef(0);
|
||||||
|
const body = useRef();
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
const time = state.clock.getElapsedTime();
|
||||||
|
ref.current.position.y = Math.sin(time) * 0.1 + 2.5;
|
||||||
|
ref.current.rotation.x = Math.sin(time) * 0.1;
|
||||||
|
ref.current.rotation.y += delta;
|
||||||
|
ref.current.rotation.z = Math.sin(time) * 0.5;
|
||||||
|
if(scale < 0.6 && frames.current > 0){
|
||||||
|
frames.current -= 1 * delta * 144;
|
||||||
|
|
||||||
|
}
|
||||||
|
if(frames.current <= 0){
|
||||||
|
setScale(Math.min(scale + 0.5 * delta, 0.6));
|
||||||
|
if(body.current){
|
||||||
|
body.current.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RigidBody type="fixed" name="itemBox"
|
||||||
|
sensor
|
||||||
|
ref={body}
|
||||||
|
onIntersectionEnter={({other}) => {
|
||||||
|
if(other.rigidBodyObject.name === "player"){
|
||||||
|
|
||||||
|
actions.setItem();
|
||||||
|
setScale(0);
|
||||||
|
frames.current = 400;
|
||||||
|
body.current.setEnabled(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
position={props.position}
|
||||||
|
colliders={false}
|
||||||
|
>
|
||||||
|
<CuboidCollider args={[1.5, 1.5, 1.5]} />
|
||||||
|
</RigidBody>
|
||||||
|
<group ref={ref} position={props.position} scale={scale} dispose={null}>
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.Cube000.geometry} material={materials.Material} position={[0.077, 0.5, -0.019]} rotation={[-Math.PI / 2, 0, 0]} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.Cube000_1.geometry} material={materials['Material.001']} position={[0.077, 0.5, -0.019]} rotation={[-Math.PI / 2, 0, 0]} />
|
||||||
|
</group>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
useGLTF.preload('./models/misc/gift-transformed.glb')
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
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={() => {
|
||||||
|
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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
Auto-generated by: https://github.com/pmndrs/gltfjsx
|
||||||
|
Command: npx gltfjsx@6.2.16 .\super_mario_bros_coin.glb --shadows --transform
|
||||||
|
Files: .\super_mario_bros_coin.glb [48.84KB] > C:\Users\mouli\dev\r3f-vite-starter\public\models\misc\super_mario_bros_coin-transformed.glb [6.12KB] (87%)
|
||||||
|
Author: BranHelsing (https://sketchfab.com/BranHelsing)
|
||||||
|
License: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
||||||
|
Source: https://sketchfab.com/3d-models/super-mario-bros-coin-aa97e093847a439f9feb064134813806
|
||||||
|
Title: Super Mario Bros Coin
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import { useGLTF, useAnimations } from "@react-three/drei";
|
||||||
|
import { useFrame } from "@react-three/fiber";
|
||||||
|
import { RigidBody } from "@react-three/rapier";
|
||||||
|
import { useStore } from "../../store";
|
||||||
|
|
||||||
|
export function Coin(props) {
|
||||||
|
const group = useRef();
|
||||||
|
const { nodes, materials } = useGLTF(
|
||||||
|
"./models/misc/super_mario_bros_coin-transformed.glb"
|
||||||
|
);
|
||||||
|
const { actions } = useStore();
|
||||||
|
const [scale, setScale] = React.useState(0.424);
|
||||||
|
const frames = useRef(0);
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
|
||||||
|
group.current.rotation.y += 4 * delta;
|
||||||
|
if(scale < 0.424 && frames.current > 0){
|
||||||
|
frames.current -= 1 * delta * 144;
|
||||||
|
|
||||||
|
}
|
||||||
|
if(frames.current <= 0){
|
||||||
|
setScale(Math.min(scale + 0.5 * delta, 0.424));
|
||||||
|
if(body.current){
|
||||||
|
body.current.setEnable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const body = useRef();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RigidBody
|
||||||
|
type="fixed"
|
||||||
|
name="coin"
|
||||||
|
sensor
|
||||||
|
onIntersectionEnter={({ manifold, target, other}) => {
|
||||||
|
if(other.rigidBodyObject.name === "player"){
|
||||||
|
actions.addCoins();
|
||||||
|
setScale(0);
|
||||||
|
frames.current = 600;
|
||||||
|
body.current.setEnable(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
position={props.position}
|
||||||
|
>
|
||||||
|
<mesh
|
||||||
|
ref={group}
|
||||||
|
castShadow
|
||||||
|
receiveShadow
|
||||||
|
geometry={nodes.Coin_CoinBlinn_0.geometry}
|
||||||
|
material={materials.CoinBlinn}
|
||||||
|
scale={scale}
|
||||||
|
/>
|
||||||
|
</RigidBody>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
useGLTF.preload("./models/misc/super_mario_bros_coin-transformed.glb");
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
Auto-generated by: https://github.com/pmndrs/gltfjsx
|
||||||
|
Command: npx gltfjsx@6.2.16 .\paris-bis.glb --shadows
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useRef } from 'react'
|
||||||
|
import { useGLTF } from '@react-three/drei'
|
||||||
|
import { RigidBody } from '@react-three/rapier'
|
||||||
|
|
||||||
|
export function ParisBis(props) {
|
||||||
|
const { nodes, materials } = useGLTF('./models/tracks/paris-bis.glb')
|
||||||
|
materials.M_Cmn_ShadowCollision.opacity = 0
|
||||||
|
materials.M_Cmn_ShadowCollision.transparent = true
|
||||||
|
return (
|
||||||
|
<group {...props} scale={50} position={[0,-3.6,0]} dispose={null}>
|
||||||
|
<group scale={0.01}>
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Audience_NoMove_01_M_Cmn_AudienceModel_0.geometry} material={materials.M_Cmn_AudienceModel} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_Building_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_Building_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_Obj_01_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.M_GeneralMansion_Roof_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.M_GeneralMansion_Roof_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_MansionObj_01_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_MansionObj_01_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_MansionObj_01_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_MansionObj_02_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_MansionObj_02_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_MansionObj_02_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_001_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_001_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_002_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_002_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_003_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_003_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_003_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_004_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_004_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_005_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_005_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_005_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_006_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_006_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_007_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_007_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_008_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_008_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_009_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_009_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_010_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_010_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_UniqueBuilding_Parts_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_UniqueBuilding_Parts_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.Rainbow_model_M_Mobp1_Rainbow_0.geometry} material={materials.M_Mobp1_Rainbow} position={[-16, 0.14, 0]} rotation={[0, -1.571, 0]} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_BlindBuilding_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_BlindBuilding_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_effel_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_Ground_01_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_Ground_01_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_Ground_01_M_Mobp1_Water_0.geometry} material={materials.M_Mobp1_Water} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.F_Ground_01_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.Gmob_Paris_VR_M_Mobp1_VR_0.geometry} material={materials.M_Mobp1_VR} position={[0, -451.06, 0]} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.M_Building_001_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.M_Building_001_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.M_Building_001_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_001_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_001_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_001_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_002_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_002_M_Mobp1_gaisenmon_tex_0.geometry} material={materials.M_Mobp1_gaisenmon_tex} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_002_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_002_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_002_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_003_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_003_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_003_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_004_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_004_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Building_004_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_effel_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_effel_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_000_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_000_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_000_M_Mobp1_Transparent_0.geometry} material={materials.M_Mobp1_Transparent} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Obj_000_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_UniqueBuilding_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_UniqueBuilding_M_Mobp1_gaisenmon_tex_0.geometry} material={materials.M_Mobp1_gaisenmon_tex} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_UniqueBuilding_M_Mobp1_Textures01_0.geometry} material={materials.M_Mobp1_Textures01} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_UniqueBuilding_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.BlindTree_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.Low_Obj_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.Low_Obj_M_Mobp1_kanban_0.geometry} material={materials.M_Mobp1_kanban} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Road_Ground_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Road_Ground_M_Mobp1_Road_A_0.geometry} material={materials.M_Mobp1_Road_A} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Road_Ground_M_Mobp1_Road_B_0.geometry} material={materials.M_Mobp1_Road_B} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Road_Ground_M_Mobp1_RoadColor_0.geometry} material={materials.M_Mobp1_RoadColor} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Road_Ground_M_Mobp1_Water_0.geometry} material={materials.M_Mobp1_Water} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Road_Ground_M_Mobp1_Window_0.geometry} material={materials.M_Mobp1_Window} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Trocadero_M_Cmn_MainColor_Detail_0.geometry} material={materials.M_Cmn_MainColor_Detail} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Trocadero_M_Mobp1_RoadColor_0.geometry} material={materials.M_Mobp1_RoadColor} />
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.N_Trocadero_Water_M_Mobp1_Water_0.geometry} material={materials.M_Mobp1_Water} />
|
||||||
|
<RigidBody type="fixed" colliders="trimesh" name="terrain">
|
||||||
|
<mesh castShadow receiveShadow geometry={nodes.ShadowCollision_M_Cmn_ShadowCollision_0.geometry} material={materials.M_Cmn_ShadowCollision} position={[0, 0.244, 0]} />
|
||||||
|
</RigidBody>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
useGLTF.preload('./models/tracks/paris-bis.glb')
|