Compare commits

...

62 Commits

Author SHA1 Message Date
Lunakepio 8d9623cac8
Merge pull request #42 from itsdouges/add-triplex-config
Set up Triplex, the visual IDE for React Three Fiber
2024-03-06 09:21:53 +01:00
Michael Dougall 17543f3b5c
chore: update readme 2024-03-05 21:52:13 +11:00
Michael Dougall 1b58a27e9d
chore: add triplex config 2024-03-05 21:46:21 +11:00
Lunakepio b0d0a9126c
Merge pull request #41 from savaughn/update-controller
Update controller inputs with useRef
2024-03-05 10:17:17 +01:00
Lunakepio c0895c0c84
Merge pull request #39 from TheDokT0r/Disable-user-select
Disabled User Select
2024-03-05 10:15:54 +01:00
Spencer Vaughn 22f2461cc7 Update controller inputs with useRef 2024-03-04 18:23:19 -06:00
Orche cd1d099392 Disable user select
Disabled the option to select text/image in the CSS in order to prevent unneeded text selection by the user
2024-03-04 20:28:47 +02:00
Alex ba5be77ae5 fix(game): forgot to add menu 2024-03-02 15:21:27 +01:00
Lunakepio 9f61b3e53c
Merge pull request #36 from Lunakepio/skid
Skid
2024-03-02 15:18:17 +01:00
Alex a96849f10d feat:(VFX) Added skids and smoke particles 2024-03-02 15:17:41 +01:00
Alex c93290cd6d feat(VFX): Added skidmarks 2024-02-27 08:39:49 +01:00
Alex 7665cd70fa to fix(VFX): Skids in game 2024-02-24 14:54:24 +01:00
Lunakepio 8b08815d5c
Merge pull request #29 from di2tpd/patch-1
Update README.md
2024-02-22 08:56:18 +01:00
DIDIT PRADITYA 64f52c4aa9
Update README.md
Add minimap to "TO-DO" list
2024-02-22 12:24:57 +07:00
Lunakepio 0422ac68c9
Merge pull request #27 from ctrlVnt/smoke
Add smoke when drift
2024-02-20 09:31:13 +01:00
Riccardo Venturini 7275250fe0 update readme 2024-02-19 21:23:10 +00:00
Riccardo Venturini 764a3d2f30 code corrections smoke 2024-02-19 21:20:11 +00:00
Lunakepio 4d0200845c
Merge pull request #26 from Waiklam/main
Added return to menu (input select) in game on all inputs (issue #20)
2024-02-19 13:15:24 +01:00
Riccardo Venturini 35085055bc add smoke for drift 2024-02-18 18:52:26 +00:00
Wai e466e32483 Added return to menu functionality to mobile with menu button 2024-02-18 13:20:47 -05:00
Wai f25076b904 Added return to menu functionality for kb/ kbm/ gamepad 2024-02-18 13:07:04 -05:00
Lunakepio 5ac25a1de8
Merge pull request #22 from justindhillon/main
Update Link to Anthony Yanez New Account
2024-02-17 06:48:41 +01:00
Justin Dhillon 5aa2731168 update link to authors anew account 2024-02-16 11:27:16 -08:00
Lunakepio fa5c8b4f60
Merge pull request #17 from Espio347/main
Some UI Fixes and Tweaks
2024-02-16 17:11:59 +01:00
Lunakepio e89f666184
Merge pull request #18 from ctrlVnt/fix-reverse-function
fix reverse on gamepad and mouse controller
2024-02-16 17:09:37 +01:00
Riccardo Venturini e4a4d22374 fix reverse on gamepad and mouse controller 2024-02-16 14:11:21 +00:00
[Naman Kishwan] a12ccb3452 Tweaks to UI 2024-02-16 19:01:08 +05:30
Lunakepio ef51e33385
Merge pull request #15 from cr2007/main
Adds Dev Container configuration for GitHub Codespaces
2024-02-16 14:18:11 +01:00
Lunakepio 2a78567df5
Merge pull request #16 from ctrlVnt/fix-reverse-function
Reverse direction Keyboard controller
2024-02-16 14:10:59 +01:00
Riccardo Venturini f19e940c3a before playercontrollerkeyboard reverse didn't work, now it work 2024-02-16 12:02:08 +00:00
CSK 1a0c1b9b28
Merge branch 'Lunakepio:main' into main 2024-02-16 15:29:46 +04:00
CSK 66e1900296 Adds `<kbd>` tags to the keyboard controls 2024-02-16 10:48:24 +00:00
CSK c4b9175658 Add GitHub Codespaces badge to `README.md` 2024-02-16 10:47:50 +00:00
CSK 9b0f6cbdc6 Adds Dev Container configuration
with VS Code extensions and port forwarding
2024-02-16 10:47:30 +00:00
Lunakepio d49e47b504
Merge pull request #14 from somunaexe/main
Added missing instruction on how to install the repository.
2024-02-16 11:12:01 +01:00
Somunachimso Nzenwa 628cf726ae Added missing instruction on how to install the repository. 2024-02-16 00:44:10 +00:00
Alex e169e12e55 fix(UI): fixed scanline not showing in the item placeholder 2024-02-15 11:45:14 +01:00
Alex b6e723dadc forgot to reset the store before pushing 2024-02-15 10:48:38 +01:00
Alex 06b8beff00 fix(game): Added collisions for the entire maps 2024-02-15 10:31:13 +01:00
Lunakepio aab364eb0a
Merge pull request #8 from Lunakepio/mobileControls
Mobile controls
2024-02-14 10:23:17 +01:00
Alex 1504f44959 feat:(game) added mobile controls 2024-02-14 10:22:43 +01:00
Alex 6ed6c0ff27 feat:(game) added mobile controls 2024-02-13 17:40:00 +01:00
Alex 5cc900ab35 fix:(ui) quick fixed import font 2024-02-13 15:34:56 +01:00
Alex c22c9e9012 feat:(game) added a small landing to allow control choice, switch between M&K, K or Gamepad 2024-02-13 14:58:41 +01:00
Alex 3ef3ec7d25 quick fix 2024-02-12 09:54:33 +01:00
Lunakepio 310e471fab
Merge pull request #3 from Lunakepio/multiplayer
Multiplayer
2024-02-12 09:42:30 +01:00
Alex 140413399f ADDED README AND LICENSE 2024-02-12 09:41:40 +01:00
Alex ef9e58fa21 feat:(game) added names for other players 2024-02-10 00:18:55 +01:00
Alex 42680aa707 feat(game): Added multiplayer mode 2024-02-09 14:51:35 +01:00
Alex b825b0fc20 feat:(game) adding multiplayer 2024-02-07 09:28:13 +01:00
Alex df76bb5f35 fixes 2024-02-05 15:39:57 +01:00
Alex bfd691e7ab feat:(game) added HUD for items 2024-02-05 11:27:32 +01:00
Alex db52f49e09 feat:(game) added interraction and particles for items/coins 2024-02-05 10:50:04 +01:00
Alex cff143c925 fix:(game) fixed bananas not removing themselves after intersection 2024-02-02 16:01:18 +01:00
Alex bf52a80c29 fix:(game) fixed infinite jump glitch 2024-02-02 15:13:41 +01:00
Alex 8138f4338f feat:(game) added a reset button to avoid reload 2024-02-02 11:54:29 +01:00
Alex 51c2f3f8f8 feat:(game) added a reset button to avoid reload 2024-02-02 11:41:44 +01:00
Alex f22f19590d fix(game:) fixed infinite boost glitch 2024-02-02 11:17:28 +01:00
Alex 256ee77f1f fix(game:) fixed infinite boost glitch 2024-02-02 11:14:31 +01:00
Alex 77a14c7733 added item system 2024-01-31 15:29:31 +01:00
Alex 11be02d977 feat(game): added sound Effects 2024-01-28 12:12:02 +01:00
Lunakepio f356585be2
Merge pull request #2 from Lunakepio/gamepadSupport
sanitized the project, deleted unused assets and added a real yet sim…
2024-01-26 05:00:12 +01:00
108 changed files with 47836 additions and 403 deletions

View File

@ -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"
}

7
.triplex/config.json Normal file
View File

@ -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"
}

16
.triplex/provider.tsx Normal file
View File

@ -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>
);
}

21
LICENSE Normal file
View File

@ -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.

94
README.md Normal file
View File

@ -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)

View File

@ -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')

27
package-lock.json generated
View File

@ -14,10 +14,13 @@
"@react-three/rapier": "^1.2.1",
"gsap": "^3.12.5",
"leva": "^0.9.35",
"playroomkit": "^0.0.66",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-gamepad": "^1.0.3",
"react-joystick-component": "^6.2.1",
"three": "^0.160.1",
"three-mesh-bvh": "^0.7.0",
"zustand": "^4.5.0"
},
"devDependencies": {
@ -1394,9 +1397,9 @@
"peer": true
},
"node_modules/@types/three": {
"version": "0.160.0",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.160.0.tgz",
"integrity": "sha512-jWlbUBovicUKaOYxzgkLlhkiEQJkhCVvg4W2IYD2trqD2om3VK4DGLpHH5zQHNr7RweZK/5re/4IVhbhvxbV9w==",
"version": "0.161.2",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.161.2.tgz",
"integrity": "sha512-DazpZ+cIfBzbW/p0zm6G8CS03HBMd748A3R1ZOXHpqaXZLv2I5zNgQUrRG//UfJ6zYFp2cUoCQaOLaz8ubH07w==",
"peer": true,
"dependencies": {
"@types/stats.js": "*",
@ -2422,6 +2425,15 @@
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"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": {
"version": "8.4.33",
"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",
"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": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",

View File

@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite --host",
"build": "vite build",
"preview": "vite preview"
},
@ -15,10 +15,13 @@
"@react-three/rapier": "^1.2.1",
"gsap": "^3.12.5",
"leva": "^0.9.35",
"playroomkit": "^0.0.66",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-gamepad": "^1.0.3",
"react-joystick-component": "^6.2.1",
"three": "^0.160.1",
"three-mesh-bvh": "^0.7.0",
"zustand": "^4.5.0"
},
"devDependencies": {

2885
public/CurvedPath.json Normal file

File diff suppressed because it is too large Load Diff

3605
public/SPLINE.json Normal file

File diff suppressed because it is too large Load Diff

35943
public/cubicle-99.CUBE Normal file

File diff suppressed because it is too large Load Diff

BIN
public/fonts/HK.ttf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

BIN
public/images/banana.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
public/images/gamepad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

BIN
public/images/keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

BIN
public/images/mobile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

BIN
public/images/mushroom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

BIN
public/images/shell.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/models/misc/gift.glb Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
public/particles/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
public/sounds/driftBlue.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/sounds/drifting.mp3 Normal file

Binary file not shown.

BIN
public/sounds/drifting.wav Normal file

Binary file not shown.

Binary file not shown.

BIN
public/sounds/engineTwo.wav Normal file

Binary file not shown.

BIN
public/sounds/jump.mp3 Normal file

Binary file not shown.

BIN
public/sounds/landing.wav Normal file

Binary file not shown.

BIN
public/sounds/turbo.wav Normal file

Binary file not shown.

View File

@ -1,8 +1,12 @@
import { Canvas } from '@react-three/fiber'
import { Experience } from './components/Experience'
import { Suspense, useMemo } from 'react'
import { Suspense, useEffect, useMemo } from 'react'
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 = {
up: 'up',
@ -12,8 +16,10 @@ export const Controls = {
boost: 'boost',
shoot: 'shoot',
slow: 'slow',
reset: 'reset'
reset: 'reset',
escape: 'escape'
}
function App() {
const map = useMemo(
() => [
@ -24,18 +30,46 @@ function App() {
{ name: Controls.jump, keys: ['Space'] },
{ name: Controls.slow, keys: ['Shift'] },
{ 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 (
<>
<Loader />
<Canvas
shadows
// shadows
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}>
<Preload all />
<Physics
gravity={[0, -90, 0]}
timeStep={'vary'}
@ -43,10 +77,10 @@ function App() {
<KeyboardControls map={map}>
<Experience />
</KeyboardControls>
<Stats />
</Physics>
</Suspense>
</Canvas>
</>
)
}

View File

@ -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 = () => {
const wheel = useRef();
const [image, setImage] = useState("");
const { item, gameStarted, actions, controls } = useStore();
useEffect(() => {
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 (
<div className="overlay"> dadasd
<div className="logo">
<img src="./logo.png" alt="logo" />
</div>
<div className="wheel">
<img
ref={wheel}
src="./steering_wheel.png"
alt="steering wheel"
className="steering-wheel"
style={{
position: "absolute",
pointerEvents: "none",
transformOrigin: "center",
}}
/>
</div>
<div className="overlay">
{gameStarted && (
<>
<div className="item">
<div className="borderOut">
<div className="borderIn">
<div className="background">
{image && <img src={image} alt="item" width={90} />}
</div>
</div>
</div>
</div>
{controls === "touch" && (
<>
<div className="controls joystick">
<Joystick
size={100}
sticky={false}
baseColor="rgba(255, 255, 255, 0.5)"
stickColor="rgba(255, 255, 255, 0.5)"
move={handleMove}
stop={handleStop}
></Joystick>
</div>
<div
className="controls drift"
onMouseDown={(e) => {
actions.setDriftButton(true);
}}
onMouseUp={(e) => {
actions.setDriftButton(false);
}}
onTouchStart={(e) => {
e.preventDefault();
actions.setDriftButton(true);
}}
onTouchEnd={(e) => {
e.preventDefault();
actions.setDriftButton(false);
}}
>
drift
</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>
);
};

128
src/Landing.jsx Normal file
View File

@ -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>
)}
</>
);
};

87
src/components/Dust.jsx Normal file
View File

@ -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
}

View File

@ -1,21 +1,182 @@
import { Environment, OrbitControls, PerspectiveCamera, Lightformer } from '@react-three/drei'
import { Ground } from './Ground'
import { PlayerController } from './PlayerController'
import { Paris } from './models/tracks/Tour_paris_promenade'
import { EffectComposer, N8AO, Bloom, TiltShift2, HueSaturation, SMAA, ChromaticAberration, Vignette } from '@react-three/postprocessing'
import { Skid } from './Skid'
import {
Environment,
OrbitControls,
PerspectiveCamera,
Lightformer,
Bvh,
} from "@react-three/drei";
import { Ground } from "./Ground";
import { PlayerController } from "./PlayerController";
import { 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 = () => {
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 (
<>
<PlayerController />
{/* <Skid /> */}
<Ground position={[0, 0, 0]} />
<Environment
resolution={256}
preset='lobby'
{gameStarted &&
players.map((player) => {
const ControllerComponent =
controls === "keyboard"
? PlayerControllerKeyboard
: 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
position={[10, 50, -30]}
@ -25,32 +186,29 @@ export const Experience = () => {
shadow-camera-left={-300}
shadow-camera-right={300}
shadow-camera-top={300}
shadow-camera-bottom={-3000}
shadow-camera-bottom={-300}
castShadow
/>
<Paris position={[0, 0, 0]} />
<EffectComposer
multisampling={0}
disableNormalPass
disableSSAO
disableDepthPass
>
<SMAA />
<N8AO distanceFalloff={1} aoRadius={1} intensity={3} />
{/* <N8AO distanceFalloff={1} aoRadius={1} intensity={3} /> */}
<Bloom
luminanceThreshold={0}
mipmapBlur
luminanceSmoothing={0.01}
intensity={0.5}
/>
<TiltShift2/>
<ChromaticAberration offset={[0.0006, 0.0006]} />
<HueSaturation saturation={0.1} />
<Vignette eskil={false} offset={0.1} darkness={0.4} />
<TiltShift2 />
{/* <ChromaticAberration offset={[0.0006, 0.0006]} /> */}
<HueSaturation saturation={0.05} />
{/* <Vignette eskil={false} offset={0.1} darkness={0.4} /> */}
</EffectComposer>
</>
)
}
);
};

View File

@ -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>
)
}

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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}/>
</>
)
}

View File

@ -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>
);
};

View File

@ -10,15 +10,23 @@ export const DriftParticlesLeft = ({turboColor,scale, ...props}) => {
return (
<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} />
{/* <Particles3 turboColor={turboColor} scale={scale} /> */}
</group>

View File

@ -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>
)
}

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -5,6 +5,7 @@ import * as THREE from "three";
export const PointParticle = ({ position, png, turboColor }) => {
const texture = useLoader(THREE.TextureLoader, png);
const pointsRef = useRef();
const materialRef = useRef();
const [size, setSize] = useState(0);
const [opacity, setOpacity] = useState(1);
@ -18,7 +19,9 @@ export const PointParticle = ({ position, png, turboColor }) => {
}, [position]);
useEffect(() => {
if (materialRef.current) {
materialRef.current.color.multiplyScalar(10);
}
setSize(0);
setOpacity(1);
}, [turboColor]);
@ -36,6 +39,7 @@ export const PointParticle = ({ position, png, turboColor }) => {
return (
<points ref={pointsRef} geometry={points}>
<pointsMaterial
ref={materialRef}
size={size}
alphaMap={texture}
transparent={true}

View File

@ -58,7 +58,7 @@ export const FlameParticle = ({ position, png, isBoosting, delay = 0 }) => {
alphaMap={texture}
transparent={true}
depthWrite={false}
color={0x000000}
color={0xff9900}
opacity={1}
toneMapped={false}
/>

View File

@ -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 });

View File

@ -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>
);
};

View File

@ -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}/>
</>
)
}

View File

@ -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>
);
};

View File

@ -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}/>
</>
)
}

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -3,32 +3,45 @@ import { BallCollider, RigidBody, useRapier, vec3 } from "@react-three/rapier";
import {
useKeyboardControls,
PerspectiveCamera,
ContactShadows,
Sphere,
OrbitControls,
Trail,
PositionalAudio,
} 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 * as THREE from "three";
import { Mario } from "./models/characters/Mario_kart";
import { DriftParticlesLeft } from "./Particles/DriftParticlesLeft";
import { DriftParticlesRight } from "./Particles/DriftParticlesRight";
import { DriftParticlesLeft } from "./Particles/drifts/DriftParticlesLeft";
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 { 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 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();
@ -63,14 +76,46 @@ export const PlayerController = () => {
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 { 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) => {
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;
@ -79,15 +124,25 @@ export const PlayerController = () => {
0,
-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) {
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;
@ -100,13 +155,14 @@ export const PlayerController = () => {
steeringAngle = currentSteeringSpeed * -pointer.x;
targetXPosition = -camMaxOffset * -pointer.x;
} else if (driftLeft.current && !driftRight.current) {
steeringAngle = currentSteeringSpeed * -(pointer.x - 0.5);
steeringAngle = currentSteeringSpeed * -(pointer.x - 1);
targetXPosition = -camMaxOffset * -pointer.x;
} else if (driftRight.current && !driftLeft.current) {
steeringAngle = currentSteeringSpeed * -(pointer.x + 0.5);
steeringAngle = currentSteeringSpeed * -(pointer.x + 1);
targetXPosition = -camMaxOffset * -pointer.x;
}
// ACCELERATING
const shouldSlow = actions.getShouldSlowDown();
if (upPressed && currentSpeed < maxSpeed) {
// Accelerate the kart within the maximum speed limit
@ -116,7 +172,7 @@ export const PlayerController = () => {
} else if (
upPressed &&
currentSpeed > maxSpeed &&
boostDuration.current > 0
effectiveBoost.current > 0
) {
setCurrentSpeed(
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
if (downPressed && currentSpeed < -maxSpeed) {
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 && !downPressed) {
else if (!upPressed) {
if (currentSteeringSpeed > 0) {
setCurrentSteeringSpeed(
Math.max(currentSteeringSpeed - 0.00005 * delta * 144, 0)
@ -173,13 +257,23 @@ export const PlayerController = () => {
);
// JUMPING
if (jumpPressed && isOnFloor.current && !jumpIsHeld.current) {
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) {
if (isOnFloor.current && jumpForce.current > 0) {
landingSound.current.play();
}
if (!isOnGround && jumpForce.current > 0) {
jumpForce.current -= 1 * delta * 144;
}
if (!jumpPressed) {
@ -193,6 +287,7 @@ export const PlayerController = () => {
if (
jumpIsHeld.current &&
currentSteeringSpeed > 0 &&
upPressed &&
pointer.x < -0.1 &&
!driftRight.current
) {
@ -201,6 +296,7 @@ export const PlayerController = () => {
if (
jumpIsHeld.current &&
currentSteeringSpeed > 0 &&
upPressed &&
pointer.x > 0.1 &&
!driftLeft.current
) {
@ -215,6 +311,10 @@ export const PlayerController = () => {
);
setTurboColor(0xffffff);
accumulatedDriftPower.current = 0;
driftSound.current.stop();
driftTwoSound.current.stop();
driftOrangeSound.current.stop();
driftPurpleSound.current.stop();
}
if (driftLeft.current) {
@ -222,20 +322,28 @@ export const PlayerController = () => {
driftForce.current = 0.4;
mario.current.rotation.y = THREE.MathUtils.lerp(
mario.current.rotation.y,
steeringAngle * 50 + 0.5,
steeringAngle * 25 + 0.4,
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) {
driftDirection.current = -1;
driftForce.current = 0.4;
mario.current.rotation.y = THREE.MathUtils.lerp(
mario.current.rotation.y,
-(-steeringAngle * 50 + 0.5),
-(-steeringAngle * 25 + 0.4),
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 (!driftLeft.current && !driftRight.current) {
mario.current.rotation.y = THREE.MathUtils.lerp(
@ -244,46 +352,68 @@ export const PlayerController = () => {
0.05 * delta * 144
);
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) {
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);
} else if (boostDuration.current <= 1) {
effectiveBoost.current = boostDuration.current;
boostDuration.current = 0;
} else if (effectiveBoost.current <= 1) {
targetZPosition = 8;
setIsBoosting(false);
}
if (isBoosting && boostDuration.current > 1) {
if (isBoosting && effectiveBoost.current > 1) {
setCurrentSpeed(boostSpeed);
boostDuration.current -= 1 * delta * 144;
effectiveBoost.current -= 1 * delta * 144;
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);
targetZPosition = 8;
turboSound.current.stop();
}
// CAMERA WORK
@ -316,28 +446,104 @@ export const PlayerController = () => {
// 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>
<RigidBody
ref={body}
colliders={false}
position={[8, 20, -96]}
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={(event) => {
onCollisionEnter={({ other }) => {
isOnFloor.current = true;
setIsOnGround(true);
}}
onCollisionExit={({ other }) => {
isOnFloor.current = false;
setIsOnGround(false);
}}
// onCollisionExit={(event) => {
// isOnFloor.current = false
// }}
/>
</RigidBody>
@ -347,14 +553,10 @@ export const PlayerController = () => {
currentSpeed={currentSpeed}
steeringAngleWheels={steeringAngleWheels}
isBoosting={isBoosting}
shouldLaunch={shouldLaunch}
/>
{/* <pointLight
position={[0.6, 0.05, 0.5]}
intensity={scale}
color={turboColor}
distance={1}
/> */}
<CoinParticles coins={coins} />
<ItemParticles item={item} />
<mesh position={[0.6, 0.05, 0.5]} scale={scale}>
<sphereGeometry args={[0.05, 16, 16]} />
<meshStandardMaterial
@ -365,12 +567,15 @@ export const PlayerController = () => {
opacity={0.4}
/>
</mesh>
{/* <pointLight
position={[-0.6, 0.05, 0.5]}
intensity={scale}
color={turboColor}
distance={1}
/> */}
<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
@ -381,38 +586,45 @@ export const PlayerController = () => {
opacity={0.4}
/>
</mesh>
{/* <Cylinder
args={[0.1, 0, 1, 128, 64, true]}
position={[-0.6, 0.05, 0.5]}
rotation={[Math.PI / 3, 0 , 0]}
>
<meshStandardMaterial side={THREE.DoubleSide} />
</Cylinder> */}
{/* <Flame/> */}
<FlameParticles isBoosting={isBoosting} />
<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>
{/* <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="./circle.png"
png="./particles/circle.png"
turboColor={turboColor}
/>
<PointParticle
position={[0.6, 0.05, 0.5]}
png="./circle.png"
png="./particles/circle.png"
turboColor={turboColor}
/>
<PointParticle
position={[-0.6, 0.05, 0.5]}
png="./star.png"
png="./particles/star.png"
turboColor={turboColor}
/>
<PointParticle
position={[0.6, 0.05, 0.5]}
png="./star.png"
png="./particles/star.png"
turboColor={turboColor}
/>
<HitParticles shouldLaunch={shouldLaunch} />
</group>
{/* <ContactShadows frames={1} /> */}
@ -421,9 +633,65 @@ export const PlayerController = () => {
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}
/>
{/* <PositionalAudio ref={engineSound} url="./sounds/engine.wav" autoplay loop distance={10}/> */}
</group>
</group>
);
) : null;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -39,11 +39,11 @@ export default function FakeFlame({ falloff = 3, glowInternalRadius = 1.0, glowC
float glitchStrength = sin(glitchTime) + sin(glitchTime * .05) + sin(glitchTime * .36);
glitchStrength /= 2.0;
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.2) * glitchStrength;
modelPosition.y += sin(smoothstep(0.3, vUv.y - 2.2, position.y) * 2.) * sin(time * 48.);
modelPosition.z += sin(smoothstep(0., vUv.x - 0.8, position.z) * 2.) * sin(time * 24.) * .6;
modelPosition.y += sin(smoothstep(0.4, vUv.y - 2.5, position.y) * 2.) * sin(time * 48.);
modelPosition.z += sin(smoothstep(0., vUv.x - 1.8, position.z) * 2.) * sin(time * 24.);
gl_Position = projectionMatrix * viewMatrix * modelPosition;
@ -107,7 +107,7 @@ export default function FakeFlame({ falloff = 3, glowInternalRadius = 1.0, glowC
q.x *= 2.;
q.y *= 2.;
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.y -= 0.05;
float n = fbm(strength*q + vec2(0,T3));

View File

@ -1,68 +1,56 @@
import { Euler, Object3D, BackSide, Vector3 } from "three";
import { useRef, useLayoutEffect } from "react";
import { useFrame } from "@react-three/fiber";
import { useStore } from "./store";
import { Euler, Object3D, Vector3, Matrix4, DoubleSide } from 'three'
import { useRef, useLayoutEffect } from 'react'
import { useFrame } from '@react-three/fiber'
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 [bodyPosition, bodyRotation] = useStore((state) => [
state.bodyPosition,
state.bodyRotation,
]);
let index = 0;
const { leftWheel, rightWheel } = useStore();
let index = 0
useFrame(() => {
// console.log(bodyPosition, bodyRotation);
if (ref.current && bodyPosition && bodyRotation !== undefined) {
setItemAt(ref.current, bodyPosition, bodyRotation, index++);
if (index === count) index = 0;
if(!leftWheel && !rightWheel) return;
const rotation = leftWheel.kartRotation;
if (leftWheel && rightWheel && ref.current && (leftWheel.isSpinning || rightWheel.isSpinning)) {
setItemAt(ref.current, rotation, leftWheel, index++);
setItemAt(ref.current, rotation, rightWheel, index++);
if (index === count) index = 0
}
});
})
useLayoutEffect(() => {
if (ref.current) {
ref.current.geometry.rotateX(-Math.PI / 2);
if(ref.current){
ref.current.geometry.rotateX(-Math.PI / 2)
return () => {
ref.current.geometry.rotateX(Math.PI / 2);
};
ref.current.geometry.rotateX(Math.PI / 2)
}
}
});
})
return (
<instancedMesh ref={ref} args={[undefined, undefined, count]}>
<planeGeometry args={[size, size * 2]} />
<meshBasicMaterial
color={0x000000}
transparent
opacity={opacity}
depthWrite={false}
side={BackSide}
/>
<instancedMesh frustumCulled={false} ref={ref} args={[undefined, undefined, count]}>
<planeGeometry args={[size, size]} />
<meshBasicMaterial color="black" side={DoubleSide} transparent opacity={opacity} />
</instancedMesh>
);
)
}
function setItemAt(instances, bodyPosition, bodyRotation, index) {
// Calculate the backward offset
const backwardOffset = 0.5; // Adjust this value as needed
const forwardDirection = new Vector3(
-Math.sin(bodyRotation),
0,
-Math.cos(bodyRotation)
);
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;
function setItemAt(instances, rotation, body, index) {
o.position.copy(body.getWorldPosition(v));
o.rotation.set(0, rotation, 0);
o.scale.setScalar(1)
o.updateMatrix()
instances.setMatrixAt(index, o.matrix)
instances.instanceMatrix.needsUpdate = true
}

View File

@ -3,13 +3,15 @@ Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.2.16 .\mariokarttest.glb --shadows
*/
import React, { useRef } from 'react'
import React, { useEffect, useRef } from 'react'
import { Cylinder, OrbitControls, Sphere, useGLTF } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import FakeGlowMaterial from '../../ShaderMaterials/FakeGlow/FakeGlowMaterial'
import FakeFlame from '../../ShaderMaterials/FakeFlame/FakeFlame'
import { useStore } from '../../store'
import gsap from 'gsap'
export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props }) {
export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, shouldLaunch, ...props }) {
const { nodes, materials } = useGLTF('./models/characters/mariokarttest.glb')
const frontLeftWheel = useRef()
@ -17,6 +19,9 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
const rearWheels = useRef()
const frontWheels = useRef()
const [scale, setScale] = React.useState(1)
const { actions } = useStore()
const [shouldSlow, setShouldSlow] = React.useState(false)
const mario = useRef();
// isBoosting = true;
useFrame((_,delta) => {
@ -30,12 +35,21 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
} else {
setScale(Math.max(scale - 0.03 * 144 * delta, 0))
}
setShouldSlow(actions.getShouldSlowDown());
})
useEffect(() => {
if (shouldLaunch) {
gsap.to(mario.current.rotation, {duration: 1.5, y: Math.PI * 3})
mario.current.rotation.set(0, 0, 0);
}
}, [shouldLaunch])
return (
<group
{...props}
rotation={[0, Math.PI, 0]}
dispose={null}
ref={mario}
>
<mesh
castShadow
@ -86,7 +100,7 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
<Cylinder
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]}
scale={scale}
@ -96,7 +110,7 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
<Cylinder
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]}
scale={scale}
>
@ -104,17 +118,17 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
</Cylinder>
<Cylinder
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]}
scale={scale}
scale={scale * 0.8}
>
<FakeFlame isBoosting={isBoosting} />
</Cylinder>
<Cylinder
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]}
scale={scale}
scale={scale * 0.8}
>
<FakeFlame isBoosting={isBoosting}/>
</Cylinder>

View File

@ -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')

View File

@ -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");

View File

@ -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')

View File

@ -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>
</>
);
}

View File

@ -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");

View File

@ -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')

Some files were not shown because too many files have changed in this diff Show More