From b825b0fc202003058fc0be0a9a36dc59080817c1 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 7 Feb 2024 09:28:13 +0100 Subject: [PATCH] feat:(game) adding multiplayer --- package-lock.json | 10 ++ package.json | 1 + src/App.jsx | 24 ++- src/HUD.jsx | 1 - src/components/Experience.jsx | 24 ++- src/components/PlayerController.jsx | 25 ++- src/components/PlayerDummies.jsx | 252 ++++++++++++++++++++++++++++ src/components/store.jsx | 20 ++- 8 files changed, 345 insertions(+), 12 deletions(-) create mode 100644 src/components/PlayerDummies.jsx diff --git a/package-lock.json b/package-lock.json index 0a91948..1d49092 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@react-three/rapier": "^1.2.1", "gsap": "^3.12.5", "leva": "^0.9.35", + "playroomkit": "^0.0.61", "react": "^18.2.0", "react-dom": "^18.2.0", "react-gamepad": "^1.0.3", @@ -2423,6 +2424,15 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "node_modules/playroomkit": { + "version": "0.0.61", + "resolved": "https://registry.npmjs.org/playroomkit/-/playroomkit-0.0.61.tgz", + "integrity": "sha512-BUjGkZZcV5OQ29sTmjjI3Ad0SZLRvuBYWAdu4MqhnCjiaHTDJp4KvacVx6rJEJe9jdamtCimkZ/5XXuGm9h2AQ==", + "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", diff --git a/package.json b/package.json index 0aae4ba..cca01c6 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@react-three/rapier": "^1.2.1", "gsap": "^3.12.5", "leva": "^0.9.35", + "playroomkit": "^0.0.61", "react": "^18.2.0", "react-dom": "^18.2.0", "react-gamepad": "^1.0.3", diff --git a/src/App.jsx b/src/App.jsx index 5906eb4..c4aeb19 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,10 @@ 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 { insertCoin, onPlayerJoin } from 'playroomkit' +import { useStore } from "./components/store"; export const Controls = { up: 'up', @@ -29,6 +31,26 @@ function App() { [] ) + const { actions } = useStore(); + const start = async () => { + await insertCoin(); + + onPlayerJoin((state) => { + actions.addPlayer(state); + console.log('player joined', state); + actions.setId(state.id); + + state.onQuit(() => { + actions.removePlayer(state); + console.log('player quit', state); + }); + }); + } + + useEffect(() => { + start(); + }, []) + return ( <> diff --git a/src/HUD.jsx b/src/HUD.jsx index ef2885d..38d9752 100644 --- a/src/HUD.jsx +++ b/src/HUD.jsx @@ -40,7 +40,6 @@ export const HUD = () => { default: setImage(""); } - console.log(item); }, [item]); return ( diff --git a/src/components/Experience.jsx b/src/components/Experience.jsx index bd50059..2b16f7f 100644 --- a/src/components/Experience.jsx +++ b/src/components/Experience.jsx @@ -23,16 +23,32 @@ 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, insertCoin, myPlayer, onPlayerJoin, useMultiplayerState } from "playroomkit"; +import { PlayerDummies } from "./PlayerDummies"; +import { useEffect } from "react"; +import { useFrame } from "@react-three/fiber"; export const Experience = () => { const onCollide = (event) => { console.log(event); }; - const { bananas, shells} = useStore(); + const { bananas, shells, players, id, actions} = useStore(); + const [networkBananas, setNetworkBananas] = useMultiplayerState("bananas", []); + + const [networkShells, setNetworkShells] = useMultiplayerState("shells", []); + return ( <> - + {players.map((player) => ( + + ))} + + {players.map((player) => ( + + ))} + + {/* */} @@ -44,7 +60,7 @@ export const Experience = () => { - {bananas.map((banana) => ( + {/* {bananas.map((banana) => ( { id={banana.id} // rotation={banana.rotation} /> - ))} + ))} */} {shells.map((shell) => ( { -export const PlayerController = () => { const upPressed = useKeyboardControls((state) => state[Controls.up]); const downPressed = useKeyboardControls((state) => state[Controls.down]); const leftPressed = useKeyboardControls((state) => state[Controls.left]); @@ -85,11 +87,11 @@ export const PlayerController = () => { const effectiveBoost = useRef(0); - const { actions, shouldSlowDown, item, bananas, coins} = useStore(); + const { actions, shouldSlowDown, item, bananas, coins, id} = useStore(); const slowDownDuration = useRef(1500); - useFrame(({ pointer, clock }, delta) => { + if(player.id !== id) return; const time = clock.getElapsedTime(); if (!body.current && !mario.current) return; engineSound.current.setVolume(currentSpeed / 300 + 0.2); @@ -121,6 +123,8 @@ export const PlayerController = () => { targetXPosition = 0; 1; } + + // mouse steering @@ -464,9 +468,19 @@ export const PlayerController = () => { } + + 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 ?( { mass={3} ccd name="player" + type={player.id === id ? "dynamic" : "kinematic"} > { /> - ); + ) : null; }; diff --git a/src/components/PlayerDummies.jsx b/src/components/PlayerDummies.jsx new file mode 100644 index 0000000..de572de --- /dev/null +++ b/src/components/PlayerDummies.jsx @@ -0,0 +1,252 @@ +import { Controls } from "../App"; +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 { 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 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); + } + + }); + + return player.id != id? ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + {/* */} + + + + + + + + + + {/* */} + + + + + + + + + + + + + + ) : null; +}; diff --git a/src/components/store.jsx b/src/components/store.jsx index a1e2bf1..187286c 100644 --- a/src/components/store.jsx +++ b/src/components/store.jsx @@ -20,11 +20,13 @@ export const useStore = create((set, get) => ({ pastPositions: [], shouldSlowdown: false, bananas: [], - items: ["banana", "shell", "mushroom"], + items: ["banana"], item: "", shells: [], skids: [], coins : 0, + players : [], + id : "", addPastPosition: (position) => { set((state) => ({ pastPositions: [position, ...state.pastPositions.slice(0, 499)], @@ -87,6 +89,9 @@ export const useStore = create((set, get) => ({ bananas: state.bananas.filter((b) => b.id !== id), })); }, + setBananas: (bananas) => { + set({ bananas }); + }, setItem:() => { set((state) => ({ item: state.items[Math.floor(Math.random() * state.items.length)], @@ -122,6 +127,19 @@ export const useStore = create((set, get) => ({ coins: state.coins - 1, })); }, + addPlayer : (player) => { + set((state) => ({ + players: [...state.players, player], + })); + }, + removePlayer : (player) => { + set((state) => ({ + players: state.players.filter((p) => p.id !== player.id), + })); + }, + setId : (id) => { + set({id}); + } }, }));