feat(game): Added multiplayer mode

pull/3/head
Alex 2024-02-09 14:51:35 +01:00
parent b825b0fc20
commit 42680aa707
7 changed files with 129 additions and 61 deletions

View File

@ -23,9 +23,17 @@ 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 {
RPC,
getState,
insertCoin,
isHost,
myPlayer,
onPlayerJoin,
useMultiplayerState,
} from "playroomkit";
import { PlayerDummies } from "./PlayerDummies";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useFrame } from "@react-three/fiber";
export const Experience = () => {
@ -33,24 +41,47 @@ export const Experience = () => {
console.log(event);
};
const { bananas, shells, players, id, actions } = useStore();
const [networkBananas, setNetworkBananas] = useMultiplayerState("bananas", []);
const [networkBananas, setNetworkBananas] = useMultiplayerState(
"bananas",
[]
);
const [networkShells, setNetworkShells] = useMultiplayerState("shells", []);
const testing = getState("bananas");
// useEffect(() => {
// setNetworkBananas(bananas);
// }, [bananas]);
// useEffect(() => {
// setNetworkShells(shells);
// }, [shells]);
return (
<>
{players.map((player) => (
<PlayerController key={player.id} player={player} userPlayer={player.id === myPlayer()?.id} />
<PlayerController
key={player.id}
player={player}
userPlayer={player.id === myPlayer()?.id}
setNetworkBananas={setNetworkBananas}
setNetworkShells={setNetworkShells}
networkBananas={networkBananas}
networkShells={networkShells}
/>
))}
{players.map((player) => (
<PlayerDummies key={player.id} player={player} userPlayer={player.id === myPlayer()?.id} />
<PlayerDummies
key={player.id}
player={player}
userPlayer={player.id === myPlayer()?.id}
/>
))}
<Paris position={[0, 0, 0]} />
<Banana onCollide={onCollide} position={[-10, 1.8, -119]} />
{/* <Banana onCollide={onCollide} position={[-10, 1.8, -119]} /> */}
{/* <Shell position={[-20, 2, -119]} /> */}
<ItemBox position={[-20, 2.5, -119]} />
<Coin position={[-30, 2, -119]} />
@ -58,23 +89,25 @@ export const Experience = () => {
<Ground position={[0, 0, 0]} />
<Environment resolution={256} preset="lobby" />
{/* {bananas.map((banana) => (
{networkBananas.map((banana) => (
<Banana
key={banana.id}
position={banana.position}
banana={banana}
setNetworkBananas={setNetworkBananas}
networkBananas={networkBananas}
id={banana.id}
// rotation={banana.rotation}
/>
))} */}
{shells.map((shell) => (
))}
{networkShells.map((shell) => (
<Shell
key={shell.id}
id={shell.id}
position={shell.position}
rotation={shell.rotation}
setNetworkShells={setNetworkShells}
networkShells={networkShells}
/>
))}

View File

@ -28,7 +28,7 @@ import { CoinParticles } from "./Particles/coins/CoinParticles";
import { ItemParticles } from "./Particles/items/ItemParticles";
import { isHost } from "playroomkit";
export const PlayerController = ( { player, userPlayer }) => {
export const PlayerController = ( { player, userPlayer, setNetworkBananas, setNetworkShells, networkBananas, networkShells }) => {
const upPressed = useKeyboardControls((state) => state[Controls.up]);
const downPressed = useKeyboardControls((state) => state[Controls.down]);
@ -330,7 +330,6 @@ export const PlayerController = ( { player, userPlayer }) => {
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 {
@ -419,36 +418,34 @@ export const PlayerController = ( { player, userPlayer }) => {
// ITEMS
if(shootPressed && item === "banana") {
const distanceBehind = 2; // Adjust this value as needed
const distanceBehind = 2;
const scaledBackwardDirection = forwardDirection.multiplyScalar(distanceBehind);
// Get the current position of the kart
const kartPosition = new THREE.Vector3(...vec3(body.current.translation()));
// Calculate the position for the new banana
const bananaPosition = kartPosition.sub(scaledBackwardDirection);
const newBanana = {
id: Math.random() + "-" + +new Date(),
position: bananaPosition,
player: true,
};
actions.addBanana(newBanana);
actions.useItem();
setNetworkBananas([...networkBananas, newBanana]);
actions.useItem();
}
if(shootPressed && item === "shell") {
const distanceBehind = -1; // Adjust this value as needed
const distanceBehind = -2;
const scaledBackwardDirection = forwardDirection.multiplyScalar(distanceBehind);
// Get the current position of the kart
const kartPosition = new THREE.Vector3(
body.current.translation().x,
body.current.translation().y,
body.current.translation().z
);
// Calculate the position for the new banana
const shellPosition = kartPosition.sub(scaledBackwardDirection);
const newShell = {
id: Math.random() + "-" + +new Date(),
@ -456,7 +453,7 @@ export const PlayerController = ( { player, userPlayer }) => {
player: true,
rotation: kartRotation
};
actions.addShell(newShell);
setNetworkShells([...networkShells, newShell]);
actions.useItem();
}
@ -513,6 +510,7 @@ export const PlayerController = ( { player, userPlayer }) => {
currentSpeed={currentSpeed}
steeringAngleWheels={steeringAngleWheels}
isBoosting={isBoosting}
shouldLaunch={shouldLaunch}
/>
<CoinParticles coins={coins}/>
<ItemParticles item={item}/>

View File

@ -106,6 +106,7 @@ export const PlayerDummies = ( { player, userPlayer }) => {
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]);
}
});
@ -113,6 +114,16 @@ export const PlayerDummies = ( { player, userPlayer }) => {
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}>
@ -120,6 +131,7 @@ export const PlayerDummies = ( { player, userPlayer }) => {
currentSpeed={currentSpeed}
steeringAngleWheels={steeringAngleWheels}
isBoosting={isBoosting}
shouldLaunch={shouldLaunch}
/>
<CoinParticles coins={coins}/>
<ItemParticles item={item}/>

View File

@ -11,7 +11,7 @@ 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()
@ -39,11 +39,11 @@ export function Mario({ currentSpeed, steeringAngleWheels, isBoosting, ...props
})
useEffect(() => {
if (shouldSlow) {
if (shouldLaunch) {
gsap.to(mario.current.rotation, {duration: 1.5, y: Math.PI * 3})
mario.current.rotation.set(0, 0, 0);
}
}, [shouldSlow])
}, [shouldLaunch])
return (
<group
{...props}

View File

@ -8,13 +8,13 @@ Source: https://sketchfab.com/3d-models/banana-peel-mario-kart-c7fd163741614859b
Title: Banana Peel (Mario Kart)
*/
import React, { useRef } from 'react'
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, ...props}) {
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();
@ -28,13 +28,13 @@ export function Banana({onCollide, id, ...props}) {
<RigidBody
ref={rigidBody}
type='fixed'
position={props.position}
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));
}
actions.removeBananaById(id);
}}
colliders={false}
name='banana'
@ -43,7 +43,7 @@ export function Banana({onCollide, id, ...props}) {
</RigidBody>
<group {...props} ref={ref} scale={scale} dispose={null}>
<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]} />

View File

@ -7,45 +7,70 @@ Source: https://sketchfab.com/3d-models/mario-shell-red-76a81ff57398423d80800259
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 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(props) {
const { nodes, materials } = useGLTF('./models/items/mario_shell_red.glb')
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(props.rotation) * shell_speed,
x: -Math.sin(rotation) * shell_speed,
y: 0,
z : -Math.cos(props.rotation) * shell_speed,
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.position.set(
rigidBodyPosition.x,
rigidBodyPosition.y,
rigidBodyPosition.z
);
ref.current.rotation.z += 0.2 * delta * 144;
}
});
return (
<group dispose={null}>
<RigidBody ref={rigidBody} type="dynamic" position={props.position} name="shell" colliders={false}>
<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} />
<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')
useGLTF.preload("./models/items/mario_shell_red.glb");

View File

@ -20,7 +20,7 @@ export const useStore = create((set, get) => ({
pastPositions: [],
shouldSlowdown: false,
bananas: [],
items: ["banana"],
items: ["mushroom"],
item: "",
shells: [],
skids: [],