Build a 3D Multiplayer Mobile Shooter Game with Playroom and React Three Fiber

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
let's build a multiplayer mobile shooter [Music] game I you home make it on your own you got a m and I am not that Bab [Applause] hello in this video we'll learn how to create easily this multiplayer game from scratch with the help of playroom who is kindly sponsoring this video playroom is an incredible toolkit that brings multiplayer functionality to your projects without having to develop or host a back end it's ideal for indie game developers or small Studios seeking a scalable solution to provide players with a seamless gaming experience together we'll be using react free fiber but it's also compatible with the most popular game engines such as Unity phaser and play canvas best of all playroom is free for Indie Games students and game studios just starting their Journey so let's explore its features while we embark on our game building Adventure let's start by getting the starter pack and cloning the repository code copy here the URL then clone it anywhere run yarn to install the dependencies and yarn Dev to start the development server then you can click on the URL here and you should see this Cube that you can rotate around for this tutorial we'll be using the royalty free pack from quat news don't hesitate to support him on patreon I downloaded the different assets and prepar the map from it I just added a floor a basic floor and some walls to not go beyond that zone the zone is 40x 40 m i just added a lot of items from the pack and I also created Spawn the black box is here this this is nothing visual it's empty boxes you can't visualize them when we will load them into our 3D but based on their position and their name we will be able to spawn our player randomly on the map on the final code I will provide you the blender file and the glb file so feel free to use it and to modify it download the assets go to public folder models and drag and drop them here you should have the map it's the one that we've just seen and character Soldier which is the character from the pack let's start by displaying our map so we go into Source components and we create a new one that we name map. jsx we'll create a component named map we use use gtf to load the model located in models and we also do the same but with preload to be sure it's loaded at the very beginning of our app then what we do is we return A Primitive with the object is equal to map do sin now inside our experience instead of showing that box here we can render our map now let's adjust the global settings go to app.jsx change the position of the camera by default we will put it on the top while we are here we can change the background color of our experience and add some default lighting settings in experience we'll add environment with the preset Sunset if we reload we can see our 3D map is ready and loaded that looks a bit flat so let's add some lighting and shadows to enable Shadows we have to go to our map and in a use effect go through all our object in our scene on each mesh and enable Shadow and receive Shadow then we need to add a light that will cast The Shadow on the object so go to experience and we will add a directional light we Define its position its intensity we say cast Shadow so it will apply it on the mesh that receive shadows and we Define other options to make sure that all our map is impacted by the Shadows I won't cover it but you can look at the 3js documentation about Shadow map camera now if we reload we have those nice Shadows available on our object to make it even nicer we can go into app.jsx and on top add a soft Shadows component what it does is just on the existing Shadows it smooth them to make them less harsh our map is ready let's add playroom SDK to load our players you can find the link to playroom in the description let's follow their instruction to install it into our project in the terminal we can stop the development server run yarn add and playroom kit when it's done we can restart our development server now it's pretty easy what we need to do is to follow the the steps so the first one is insert coin we'll add it into our experience we will create a use effect so when it's loaded we will call a start function so not start transition but start and our start function why do we separate it it's because it need to be async inside we can paste our insert coin now automatically when we reload our page we have a room that is created and ready to host a game we can choose a random Avatar by clicking here we can rename ourself and choose a different color if we look at the URL it contain a room code we can also get it by doing invite and we have a QR to scan to share to our friends or the share link button then if I open a second tab we have automatically all the players in the lobby that join the room let's follow the next step we need to handle when other players are joining to add them into our state and to create our logic across the different players we will need a state to store our players so we use State an array of empty players now we can Define our Logic on player join we will have the state of the player that is joining us it's automatically provided by playroom then we need to create a joystick for each player it is sync through the network so each client will have access to the joystick of all the other players but it will only create a UI for the current player there are two types of joystick one is Dad so you know if it's left right top or bottom but us we will be using angular so it can easily rotate at 360° then we'll add a button fire so we can shoot in the game and we create a new player containing our Network State and our joystick inside our array of players then on the network State we Define the health to 100 by default the number of death the number of kill so Zero by default when you join the game and we add the new player to the existing array of players then we need to handle when a user kit the session we remove it from our array of player in our state now we have our array of player we need to create a component to render the actual character let's create the component inside components character soldier. jsx so what I did is I prep the component based on the gltf file available here as usual I used gltf jsx to generate the component for me but I added some code that I will explain to you first to be able to use that component we need to add another Library which is 3 STD lib let's copy it here yarn add and add that Library yarn Dev so let's have a look at what our code is doing we have the list of weapons 3D model contains then I defined three props that we can use on our Avatar so we have its color it's coming from playroom color that we decided in the beginning the current animation we are playing and the weapon our character is holding we won't change it it will be always AK but you can change it later then it's the usual code to display a 3D model we have skeleton. clone and use graph to be able to reuse the same mesh because we'll have multiple players and we need to clone the mesh to be able to reuse it and not to have them disappear then we load the different animations I Define that when it's the Des animation we are setting it to Loop once so it's not looping through the death animation but once it's dead it stay in the dead position and finally we are just defining the player color material which is based on the color we have in the props from playroom and we apply it to a material that is named character main this is the shirt and the headset okay so this character Soldier component is just to render our character but it's not where we will put the logic of our character for this we will create another component named character controller. jsx let's create the component character controller and this we will code it together we will need the following props so the state the joystick user player it's a bullan to know if the current controller is our client or if it's another player that is remote and also the rest of the props for now we will return a group we might need a reference later so let's create one con uh group is equal to use ref and inside we will have a nested group for our character when name character a second reference and inside that group we will add our character Soldier component we need to define the color so we have it from state do state. profile do color and we need to also Define the animation we are currently playing so we need a state to know what animation we are currently on so animation set animation is equal to use State and by default it will be idle let's render our character inside our experience so now inside we can say if we have players we will map through each of them we will get the state the joystick and the index of the current player we are on and based on that we will render a character controller the key will be date. ID it's a unique ident Iden ifier we get from playroom we get the state and the joystick user player is not the correct code we can check if we are the current user by doing state. ID and to check if it's equal to my player so it's a method from playroom kit do ID so if it's the same it means it's me the current player playing the game Let's also adjust the default position we can say position X is equal index multipli by two it's temporary later we'll do it another way go to character controller the rest of the props we will apply them to the controller now if we load two tabs we have our two characters in Idol stand next to each other now we have our player ready we need to react to joystick events to move the character to be able to move our character to handle collisions and that bullets hit the players we will need a physics engine we will be using react 3 Rapier it's a wrapper around Rapier Library I won't explain it in depth as I already have a video dedicated to this topic you can find the link in the description let's install it into our project we need to stop the development server yarn ad at react 3/ Rapier and once it's done yarn Dev now we need to go into app wrap everything into suspense and our experience we will wrap it into physics by doing it it will create a physics based world but alone it won't do anything it is because to have meshes affected by the physic we need to WRA them into rigid bodies let's start with the map because we want it to handle Collision when we hit the objects or when we hit the wall and also our character not to go through the floor instead of just returning a primitive we will return a rigid body from rapure we can Define its colliders and set it to Tre mesh so it's a complex shape it will be based on our meshes inside if we reload you can see that the map is falling it is because it is affected by gravity so it's always falling to avoid the map to be affected by gravity we can change its type to fixed now our map isn't moving anymore to understand what it is doing to Define it to triesh we can go to up physics and add the bug prop that way you can visualize all the rigid bodies applied so it wrapped all our meshes with some colliders so we will be able to use it to move our character and to not go through all the different items now let's make our character move go into character controller and we don't want to move the mesh we want to move its rigid body so first we need to add one inside here our group we will add a rigid body and wrap all our group that we will f later that's why we have a group I will explain to it later we will Define its colliders to force to not have default one collider and we will need to add our collider manually because for our character the one we want to use is a capsule collider here is what it looks like it's wrapping our character and it's what we will use it's not a complicated one so it won't be too expensive for the calculation but it represent correctly a human to be able to move our character we need a reference on the rigid body we'll name it rigid body we need the reference on it and our logic will put it in a use frame first we need to get the angle that is applied on the joystick so we have the E joystick pressed to know if the user is currently pushing the joystick if we have an angle then we will set our animation to run and we will rotate our group in the direction of the angle so here in this group we will rotate it to follow the joystick angle then we will move the character in the right direction so we will create an Impulse it's a force we will apply to the rigid body we are doing some basic calculation based on the angle and a movement speed and the Delta we will Define them later and we apply that impulse into our rigid body so the movement will be physics based and it will react to collisions and gravity then if it's not the case we are not moving the stick we will just set the animation to idle we are missing some variables here we have the state we can ignore it and we need the Delta time to have a smooth movement based on the frames and we need to Define our constant movement speed so find the one that you prefer but I chose one so movement speed is equal to 200 now let's try to move our character if we move the stick we can see it's going way too fast and it's rotating and falling to add some friction to our rigid body and to not move that fast we need to add linear damping and find the appropriate value I decided to use 12 now let's try it again we can move the camera and move it's better but you can see our character is falling because the capsule reacts to the physics and it fall but we don't want our character to be impacted and to rotate so we need to lock the rotation with the lock rotations prop let's try it again we can move our character it's not falling anymore and if we go into walls because it is physics based movement it is automatically blocked by the other rigid bodies while this work with one player if we move our different players you can see that the positions are not exactly the same so we could have game playay issues you see here I'm blocked here but on that screen the position is not exactly the same so it's going to another position to avoid this we need to put another system in place when we use playroom automatically one of the user will be defined as the host so we'll be using that feature to say only the host will do the physic based calculation and the other clients which are not currently the host they will just display what is happening on the screen automatically if the host disconnect the other client one of the other client will become automatically the host so we can continue to play and have the logic let's implement it inside our rigid body will change the type if we are the host or not so if we are the host we can know it using the is host function then we will use Dynamic this is the default one we were using so far if it's not the case we will use the kinematic position so we need to Define ourself the position of the rigid body it won't be impacted by the physic so now let's go to our use frame Loop if we are the host we will set to our current state the position so the state is the network State we have from playroom we will set our position it will be the position from our current rigid body if we are not the host we will get the position from the state and if we find one we apply it to the rigid body now if we move our character it's perfectly sync between the different clients can see if we move we are at the exact position we can push the other it's accurate and we are sure all our players have the same experience now because it is boring to move the camera manually let's make a camera that follow automatically our character go to experience we will get rid of the orbit controls and instead in character controller in our group we will say are we the user player so only for us and if it's the case we'll be using camera controls component be sure it's the one from dry because it's also in three STD lib we'll need a reference on it we'll name it controls let's define our reference with the other ones so controls is equal to use ref and in the use frame we will make it follow our character first we need to check if we have a reference on controls because if it's the other character controller we won't have one if it's the case we Define some distance on the Y and z-axis I Define some conditions to make it closer to the character if we are on mobile but it's a bit dirty where I did it then we get the position of the rigid body and we are using V 3 it's a method from Rapier to convert it from Rapier physics to 3js physics then on controls we are using the set look at method to Define where our camera is so a bit further away from the character and to look at the character position now we can try to move our character on the left the camera is following but it's independent for each client by the way we can disable the debug so it's faster and it's less ugly let's try it we launch and we have correctly all our characters nice because it's a shooter game we need to handle the fire button to shoot actual bullets let's go back to our character controller go at the end of the use frame function to know if the player is pressing the button we have joystick do is pressed and the name we Define earlier so fire we Define automatically the animation to Idol shoot and only if it is the host we will do the action that are physics based we will need to check the date between the last fire so we can shoot like 100 bullets at the same times then we Define the last Sho date we will need to create a reference for that then we create a new bullet we add an ID to it so we'll be able to map them and to avoid a render issues the position will be equal to our position of our character the angle is the one we are currently at and we also save whose player is firing so we'll be able to count the score later and know who killed who once we have our new bullet we will call a on fire method to add that bullet to the list of the bullets nice so to be able to do it we need to define a fire rate go on top con fire rate I chose 380 but it's up to your creat ity then on the character controller we will need a new event it will be on fire and we also need a reference to know when was the last time we fired so last shoot is equal to user F of zero now let's go to our experience and handle that on fire event we will create a method called on fire inside our experience we will need to have a list of bullets so it's a state with set bullets it will be also an empty array by default and what on fire will do it will take the bullet that we pass it as a parameter we just set bullets to the previous bullets array with the new one so every time the player hit the fire button it will call on fire and it will add a bullet to our bullets array now our bullets array should be filled but we need to render them so like we did for the players we will render all our bullets so we'll create a bullet component but we don't have it yet we will be using the ID we defined as the key so it's bullet. ID and then the rest of the props and finally to know when we will make our bullet disappear we will create a unhit call back so we'll call unhit function but we need to pass some parameters it will be unit and we will call unit of bullet do ID now let's go on top where we have our on fire we will have our on hit with the ID of the ballet so I prefer to name it bullet ID and we will set the ballet to the previous array of bullets filtered with the one that has been hit so it will make it disappear from our array of bullets let's create our bullet component go to components new file bullet. jsx export con bullet is equal to we will return a group we need to Define its position so it's based on position. x positiony and position. z and the rotation y will be the angle from where the player is shooting in our bullet we will need a rigid body and inside we will just render a mesh with a box geometry and a special material for the material we'll be using a mesh basic material with a hot pink color turnone map to force and we multiply this color to make some Bloom effect just after we will need a reference on our rigid body we'll name it rigid body const rigid body is equal to use ref and in the use effect we will apply the movement of our bullet so use effect when we instantiate the bullet automatically it will go in the direction it is supposed to to do this we Define a velocity it's the same calculation we did for the movement based on the angle and the bullet speed and we Define the linear velocity because it's a fixed speed for the bullet on our rigid body now we need to Define our bullet speed const bullet speed is equal to and I chose 20 let's try to see what we have let's go to our experience and import the but because we didn't create before so we coolent let's try to see what we have so far if we fire we can see our bullet is moving in the correct direction but as you can see the position of the gun is slightly off track compared to what our bullet is coming from to avoid it go to character controller on top we create something called weapon offset and we will use it to move the default position of our bullet so let's go back to it and we will wrap our rigid body inside a group that contain the weapon offset so automatically it will never be at the wrong starting position let's try it we can move fire and you can see it's almost going from the perfect position of course you can adjust it but it must be difficult for the player to know where it's aiming because there is no indicator we can create a simple crossair for it let's go to character controller and below it I prepared a very simple Crosshair so it's just meshes one next to the other one to draw a line of very small boxes now inside our character group this is the one that rotate next to the character we will display our Crosshair to make it appear at the right position we need to set the position to the weapon offset and we also need to have it only for us and not to see it for each character so we need to add a condition user player if it's de case we display the Crosshair now we have that crossair visible so we know where we are shooting to but you can see we have an issue when we shoot our bullet go off track this is because currently our bullet is impacted by the fact that this is a rigid body it is impacted by gravity and we don't want it we just want to follow its natural path to avoid it we can Define it to gravity scale and set to zero then to prevent that when we hit another object like a player it impact its movement we need to set it to sensor so when it's a sensor it will just notify when it enters in a collision with something but it won't impact the rigid body that it hits now let's add the logic when it hits something so on intersection enter it's because it's a sensor so this is that call back function if it's the case and we are the host because only the host will handle that logic of the rigid body and the physics world and if the other item isn't a bullet because we don't want to have any relation between bullets hitting other bullets then we will disable the rigid body this is to prevent that it hits a player two times or it hits two player at once once a bullet do something it's disabled then we will call the unhit call back and we will pass at the position of where it was hit we also need to add some user data to our rigid body so on the character controller will be able to call our logic to be hit so we'll say it's a type bullet we pass it the player ID and the damage of that bullet it's hardcoded but if you decide to have multiple weapons you can be based on that now let's try to shoot and the bullet disappear and it follows precisely the trajectory of our Crosshair currently our bullet is white let's add the postprocessing effect to do it we need to add react 3 postprocessing then yarn dev then add our effect composer and our Bloom effect the effect composer come from react 3 postprocessing be careful on that and same for Bloom now you can see that when we shoot there is a nice Bloom effect but we have an issue since we added postprocessing there are some artifacts on our object to prevent it we can change the things on the camera and set the near property to two by doing that we won't have the Precision issue and have the correct rendering I also prepared some audio files you can go to public add audios and drag and drop the file is here we have dead when our character has zero Health Point hurt and rifle in the use effect of our bullet when it is intiated we can play the sound of audio rifle if we try to have multiple characters you can see it's moving correctly each one has its Crosshair and if we fire only one is showing the bullets this is because we have a local state to display it for the other clients which are not the host because if we fire on the other client which is not not the host it will fire but only on the host client which is on the left so we will have a network state where we will be able to display those bullets thanks to playroom it's very easy to do we have our bullets here which are our local state but we will create a network bullets and set network bullets and playroom provide a used multiplayer state which is the first parameter is the name so it's named bullet and the second parameter is the default value so an empty array we will have a use effect when our local bullet change so based on our bullets and what we will do is set network bullets with our array of local bullets then we just need to render the correct array so if we are the host we will render the bullet which is the local state but if we are another client we need to display the network bullets to do this we can go on our map function and here we will check if we are the host then we render the bullets if it's not the case we render the network ones now if we fire on both clients we can see the bullets are moving now let's handle when we shoot another player let's go to our character controller and as we did on the bullet we will add an event to know if we are hit so on intersection enter we can get the other and we will put our logic here so if we we are the host and the other rigid body is a bullet and if we are alive so our health is above zero then we will calculate our new heal based on our current one and the Damage coming from the user data of the bullet if it's below zero or equal to zero it means we are dead so we will increase the counter of death on this player we will set its state to dead and health to zero so it's not below zero and we will disable the rigid body so it won't be impact by the other bullets that are firing toward the dead corpse then with a set timeout of two seconds we will call a method spawn randomly to appear at another place and we will reenable our rigid body to be able to play We also set our health to 100 to restart from a fresh start and we are not dead anymore and we also call on killed to increase the startat of the player that killed that player and if we are still alive we just set our new he this contains something that that we didn't Implement yet so we have unkilled we need to add it to the props of our character controller and we also have a function named spawn randomly so let's create it first we are getting the scene from user three so it's the global scene then we create our spawn randomly method we will get all the spones I choose a dirty way to do it so we loop from zero to 1,000 and based on the naming I chose on blender it works because we only have 10 so we search for an object name spone underscore and the number of the spone so for 0 1 2 3 it works then if we find a spone we push it to our array of spones if it doesn't work we stop to Loop through 1,000 items which means we didn't find another spone then we choose randomly a spone from the array of spones and we set the translation to our rigid body with the spone position now that we have that method that we call when we die we can also call it at the very beginning so use effect when our character appears if we are the host only because it's always the host that is in control of what is really happening in the game and we call spawn randomly and because we do this inside experience we can remove the default position we said earlier let's try it and we appear at a random position on the map to try our system to see the health on our character controller I prepared another component named player info so it's using a billboard and some text and also some plane just to display the health of the user and its name as we did for the Crosshair we can add it on our character here but it doesn't have to be conditional because we want everyone to see the ha and the name of the other players okay let's try to shoot the other player we can see the health is decreasing and when it's die we have an issue because it doesn't get back to zero and we should show an animation that the player is dead the issue we get should be because we don't have the on killed yet for the score but we will do it later for now let's go to our use frame and on top of it if we are dead we just set our animation to death and we stop moving our character or doing anything else now we can go to our experience and Define the unkilled method so unkilled equal to unkilled let's define it here anywhere so unkilled we have the victim and the killer so who has a score increased we'll find the player from our array of players based on the ID and we just increase its kills let's create a simple leaderboard to see the kills and deaths of the different Players let's go to components create a new file leaderboard jsx export con leaderboard board to get the list of the players we just do const players and we use the hook named use players list from playroom kit and we can set it to true to have always the up toate values then I prepared a simple UI to render for each player a box containing the Avatar of the player its name and its number of kills and death I also added a button to toggle full screen so it's easier on mobile to play Let's go to our app and add our leaderboard so it will be out of the canvas which is meant for 3D items we will add our leaderboard and we will also add the loader component from react 3 dry all our resources will be loaded before displaying anything and this looks very bad because we need to add Tailwind let's follow the instructions to add it so copy it yarn add the different libraries then we need to run Tailwind CSS in it paste it it generated a config file we need to Define this so far we can run on dev and close it go to Tailwind config.js change the content it's applied to so it will be applied to index and all our jsx file and inside our CSS file we need to paste it so index. CSS on the top we add this let's see if it works launch and we have it correctly let's try to find our other player and kill it I think it's on the top left yes it's not very practical uh to play the joystick with the mouse but there is an option to play with keyboard and for recording the video I can show you on the phone right now so let's click on fire try to kill it the animation is played correctly you can see that the stats are accurate boom has one kill and cornflakes has one dead very nice name by the way we are almost good we will add a final effect when we hit a wall when our bullet disappear we will add an impact effect I prepared an interesting component named bullet hit. jsx I prepared everything what it does is it generate a number of small boxes with a random Target position and it will act as an explosion and I'm using instance mesh to avoid it to L too much so like we did with the bullet ins inside our experience we'll create a new state so also for the network buets we'll name it hit so hits set hits Network hits and set network hits this is very bonus you don't have to do it if you don't want what we need to do is on hit we also get the position from where the hit was down and we will add a new hit to our array of hits here we add a new new hit with the ID of the ballet so it has a new unique ID and the position to know where we instantiate our effect then we create another function on heat ended just to remove the Heat and exactly like we did with the bullets we add a use effect on the hits to synchronize them through the network so set network hits with the new value of hits so all clients can see the same thing now let's go on the bottom of our experience it's not a lot of code here so it's almost the same logic if we are the host we will show the hits if we are not we'll show the one from the network then for each hit we will render a bullet hit the ID will be hit. ID we give it the rest of the props and it's not on hit but on hit end it and we will call on hit and ended with hit. ID but we changed it unit to take two parameters the ID of the bullet and the position of the hit I don't know if we already have it in the bullet on hit we are already passing the position so what we can do is in on experience get the position here and pass it as the second parameter oh and I made a mistake it's unended and not un hit ended let's try to again we launch it on the host we can try on any client we shoot we can see the explosion effect if we try to find the other character we can aim it fire and we see the correct explosion if you want to try it on mobile you don't have to do just yarn Dev but do yarn Dev D- host what it will do is generate that Network URL that you can access to and use that one and on a device connected on the same network you can either copy and send the URL or click on the invite button scan the CER and play together thank you for watching I hope you enjoyed this video a huge thanks to playroom for sponsoring this video I really enjoyed using their SDK to build this amazing project and I highly recommend you to give it a try Link in the description below to go beyond this tutorial you can for example create other Maps add rules such as duration or set a number of Liv to end and display the winner of the game you can also change weapons and create other types of bullets and impact effects another way to motivate you is to join game Jam such as the react Jam or axi Jam if you enjoyed the video please hit the like button and subscribe to not miss the upcoming videos if you want to continue learning I also created react 3 fiber The Ultimate Guide to 3D web development a Project based course to learn everything you need to know to feel confident to build your own 3D experiences or you can continue watching my free video tutorials available here
Info
Channel: Wawa Sensei
Views: 8,146
Rating: undefined out of 5
Keywords: threejs, three js tutorial, threejs tutorial, react three fiber, react three fiber tutorial, three js react, three js, three.js, react, threejs journey, threejs course, react three fiber ultimate guide, threejs for beginners, playroom, playroomkit, unity photon, unity multiplayer game, how to build a multiplayer game, how to build a game with threejs, how to make a game with threejs, how to make a multiplayer game, web game development, 3D web game development tutorial
Id: nQI8UNe6cfA
Channel Id: undefined
Length: 41min 1sec (2461 seconds)
Published: Fri Oct 20 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.