Godot Multiplayer Tutorial - Server Side (NPC) Enemies | Godot Dedicated Server #15

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this goldup multiplayer tutorial i'll teach you how you can move your npc enemies from the client to the server so that they're all shared among your peers and you can go hunt and kill some enemies together with your party let's get started to communicate all that information we will be generating and tracking of all the enemies on the map we're gonna be making use of the world state now this gives me a good opportunity to point out one line of code that i forgot in the last tutorial i know shame on me in the last tutorial on clock synchronization we had the render time still is operating system time because i forgot to mention to you that you should change that into the now updated game server client clock by doing so we're making sure we're using that clock synchronization in our interpolation and extrapolation functions if you follow the tutorial series so far you've probably noticed and i mentioned it before that when one player attacks an enemy that is not registered for any other players so if you need three shots to kill an enemy and you fire two at it with one player then another player would still have to fire three at this entity also if that entity dies it only dies for the player that actually killed it and not for all the other players in the game that's what we're going to be addressing in this tutorial so i'm right here on the game server and on the game server i've created this new note map map is going to keep track of a variety of elements for us including the enemies that are on our map now if you have different maps or you have chunks then you might want to create a singleton for every single map or chunk or you might want a different kind of architecture i'll leave that up to you in this tutorial our map is super short so only going to be needing one node we have a couple of variables on the top to start it off with first we have the enemy id counter which is basically an integer that's going to add plus one every time that a new spawn is coming and that number is going to be where that integer is going to be our enemy id so we can keep track of it the enemy maximum is the maximum amount of enemies that are allowed to spawn on this map the enemy types is the type of enemies that are allowed to spawn on this map currently i only got one entry i did implement some rng function here but that's always going to draw the same whereby brown but i could add um several other types to this list and if i do that i would also have random enemy spawning all i would have to do on the client is to make sure that i add the right sprite sheet to that kinematic bodies sprite so that you actually have the right rendering of that specific type of enemy we're not going to get into that now because that's not what this tutorial is about then we have the enemy spawn point which are three vector twos with possible locations where an enemy could spawn then we have the open locations or basically the unoccupied locations these are index references to this array so that means that currently as the server loads all locations are open because of course we didn't spawn any enemy yet but as enemies are going to spawn we're going to take these numbers these index references to disarray out of here and put them into the occupied locations dictionary with the enemy id so we can keep track of which enemy was spawned where so that when we kill an enemy we can bring that spawn location back into the open locations so we can redraw from those locations in our random spawn function then lastly we have the enemy list which is basically keeping track of all the enemies that are spawned and their stats then to get it started unready so when the service starts we create a new timer with a wait time of three seconds it also starts and every time it times out we're gonna spawn an enemy of course if certain conditions are met lastly we add that timer to the tree on that timeout spawn enemy we first gonna check if the enemy lists that's that dictionary if that size of that dictionary is equal to or bigger than the enemy maximum which would then of course mean that we don't want any enemies to spawn so we pass otherwise we do need to spawn some enemies and we first randomize the seed to ensure we get some random numbers we're first going to randomize the type which of course in this case wouldn't work because we only got one type but in case you want to add some more enemies here you go and we're going to determine the random number generator location index for all of these three variables the enemy spawn points open location and occupied locations first what we do once we have that rng location index is we determine the location where this enemy is going to spawn so we're going to take the enemy spawn points that's all those vector 2's then we're going to take the open locations and the random number generator then once we have determined that location we can move that location into the occupied locations so we're going to say occupied locations now we're going to use the enemy id number because we need to keep track of which enemy is in which location so once an enemy dies we can give that specific location back to the open locations and we're going to basically use the same reference as we use right there to determine the location put that into the occupied locations now of course that also means that we have to remove that location from the open locations because we don't want to redraw that number again once the next enemy needs to start spawning it okay so now we pretty much have all the information we need now we can add a new entry to the enemy list we're going to use an enemy id counter to give that enemy a specific id once we have done this we add one to the enemy id counter so that number is never used again then the enemy type is going to be the type we determined on there the enemy location is going to be this location the enemy health i've set to 500 both the health and the maximum health we need both numbers in cases enemy ever needs to heal up and also we need both numbers to calculate the percentage of hp so we can properly display it in an hp bar of course you can put a database behind this and extract that health data out of a database to make sure that with the right type you match the right amount of hp or possible leave it up to you and currently we don't even have anything according to attack or defense or anything like that because our wearers cannot fight back not part of this tutorial this is all about just giving you one example how you can do this then we have the enemy state which is going to be idle and we have a timeout now that timeout is important because that's basically what helps us respawn enemies once they have died we do that pretty much in the next couple of lines right here because once it has spawned an enemy in we're going to use that same timer that same three seconds to also check for every enemy that has already been spawned if that enemy state is dead now how we kill them we'll get into later in this tutorial that's this function right here npc hit but let's do one thing at the same time if that enemy is dead and also the timeout is zero then we erase that enemy from the enemy list by erasing it from the enemy list then this if function then of course allows for new enemies to start spawning in because the enemy list size is then reduced by one and then obviously we're gonna spawn some things in if however that timeout which we set at the start at what is it one is not zero then we're gonna deduct one from that timeout so basically right now if i kill an enemy it's gonna take six seconds the first three seconds to get that initial one to zero and another three seconds to then erase it from the enemy list before the next enemy spawns in six seconds is really short but of course do that for demonstration purposes to you so all of this data now we have basically the enemy list right here that is where all this data is collected and which is always going to be let's say the truth on the server that all the peers need to be aware of so to do that on our state processing uh script where we generate the world state every time that the world state is being sent to all the pairs all the players we're going to add a new key to this world state dictionary that is going to be enemies and for that we're going to get that enemy list out of that map node now with that we're sending off all this information to all the peers all the players in the network and we can start writing some code on the client side to make sure that this data is extracted and that actual enemies are being spawned and then we can start working on killing them on the client side now i'm on the map script the script that we used before to process our world state into interpolation and extrapolation so we can see other players move around we're going to be expanding this code to include the enemy data that we've just communicated in our world state from the server so within our physics process function where we are doing the interpolation extrapolation the first thing we have to do is we have to exclude that key that new key in the world state dictionary enemies that is not misinterpreted as another player so we have to include this extra exception i've put it here in extra lines this is new in this tutorial so i can remove these lines now for you so once we've got that include exclusion we make sure that the player interpolation stays working and of course we have to do exactly the same on the extrapolation part so also here we have the if string player enemies then continue so now that we got that as you can see here we're iterating over every player in that world state as we coded in previous tutorial and now new in this tutorial we are also going to be iterating over every enemy in world state buffer enemies now it might be a good idea i haven't changed that myself yet because i didn't want to confuse you but it might be a good idea to also include all the players into a separate sub dictionary within the world state just like we now have all the enemies in the sub dictionary that might make that you can you can delete some of these exclusions as probably would otherwise have to add on top of these exclusions more and more with resource notes and all kinds of other stuff so might be a good idea to optimize the code there a little bit i'll leave it up to you it should be pretty easy so for all the enemies that's where we are that's what's important right now first of all we're gonna simply say that if not world state buffer enemies has enemy one then we continue that's pretty much exactly the same line of code we have right here for the players that's basically saying like okay if you found this enemy in this world state but that enemy is not present in the previous world state that i received like 50 milliseconds ago then don't do anything with it because we don't have two locations to interplay between then once that is not the case we're going to check if that enemy already exists within the note 3 and if that's the case we're going to determine the new in position by lerping that location of the previous world state and the future world state with the interpolation factor exactly the same like on the player of course this is not really going to do anything for us because right now our enemies are always standing still but as soon as you give the movement this code is going to work for you it's going to interpolate all those enemies and they'll all be moving around on the map super smooth so another line of code that we got right here is health and that is something which is new so on health right here we are going to be communicating that to the note um constantly on every world frame so in many cases that health might be exactly the same as the previous world frame but as soon as it changes we can start updating the hp bar i'll show you that in a moment but that's an extra line of code we don't have on the player that of course would be something if you're using this code to create a pvp experience you would add this health on top of the code right here for the players to make sure that the health bars of other players are updated if you want hell bars to show for other players of course now with all of that said if this is not the case that that enemy already exists within the note 3 then we're going to be spawning that enemy in and we're gonna add the enemy so that would be the actual index number of the enemy the enemy id i should say and the world state buffer two enemies enemy we're gonna push that to this function so that has all the information it needs to properly display this enemy on the map this spawn new enemy function is on the top right here so it's going to receive that enemy id and that enemy dictionary we create a new enemy which is going to be an instance of enemy spawn enemy spawn is just like a player spawn is a pre-loaded scene of this case this ware bear you might want to put a sort of general enemy in there that you can add things to based on the type that it is i'll leave it up to you i just need as well there for now as it's just a tutorial to demonstrate to you the multiplayer framework and not specifically how to you know in the most elaborate way spawn uh enemies in your multiplayer games so once we got that instance we're going to set its position by the enemy dictionary enemy location its maximum and current hp by their respective values the type the state and we are going to rename that node to that enemy id so that whenever we kill or attack or do something we can use the note name to communicate the right enemy id back to the server so that the server knows which enemy was attacked or it needs to do other things then we're going to get the node y sort enemies and we're gonna add that child so that is going to be when you look at the map um we of course have the y sword we have already the other players that we use and just like other plays we also have enemies that enemies used to have one or two air bears i believe one wherever i already deleted the weber out of here that used to be on the player side um that rarely used to be there you know if you want to be 100 sure or higher for now you can see that wherever it used to be here in the corner it's gone it's not there anymore so now we are basically spawning in new um enemies new wear bears all we got to make sure is that whenever we hit a wear there with a nice spear it needs to start dying so let's have a look at the wear bear let's see on the new code there is quite a bit of code also for the hp bars that we that we added and then i think we are ready for test and you can uh code all of this in your own game right so that wherever let me look up i got them right here so first of all i've added an hp bar a rather big hp bar i might say to wherever so we can keep track of what's happening with these webbers and you can see on two different clients that the hp is actually updating so for health bars like you can you can pause the code if you want to on the hp barcode i'm not going to go over it because i've got one specific tutorial all on hp bars as part of the combat series like an 11 part series on all kinds of combat mechanics from aoe spells to nova spells and all kinds of other stuff so i would suggest you watch that tutorial i'll link it up on the top and then you can get all in this hp bar stuff um this hp bar is made exactly the same way including the tween and everything as that tutorial so that should set you set you up properly now let's have a look at the code of the weber we used to have previously whenever the player hits the web air we had on hit we communicated the damage and we were basically going to be reducing the damage from the current hp and we played on death whenever that were ever had zero hp left now of course this function created that the weber would only die locally for that one player and not for the other players because of course this current hp is a variable on this local client and we need to deduct the hp on the server that the server can communicate the new hp back to all the players so all the players see this update so we are going to be retiring this little piece of code and we're going to be substituting it for a game server interface calls that's going to be a call to the server interface the singleton we're going to call mpc hit we are going to get the integer of the name of this node remember that's going to be the enemy id as we set the enemy id as the node name and then of course we're going to be communicating the damage now there's a big if here this code is exploit sensitive because all the physics are still being simulated on the client it also means the coalition detection is done by the client and you could write an aimbot for this game also the damage value is being communicated by the client that damage value could be intercepted increased by an absurd number and suddenly a player is walking around with god mode so this is going to be an intermediate stage of where we're going to go to in this tutorial series i can't smack everything into one tutorial because it will become one and a half hour long so this is just going to be intermediate and on the next couple of tutorials we'll also move the physics simulation from the client to the server and also make sure that the client is not in between anymore when it comes to the damage value so for now we'll just leave it at this we'll continue with the code and we'll add on top of this in the next two or three tutorials right so now let's follow this mpc hit function to the server and back so we can see how the server processes the health and then we'll have a look at these last four functions and how they interact with the web air to make sure that we render the right data at the right time for every single player whether they're already in the game or just logging in so the npc hit is going to go to the game server interface so the game server interface has that function npc hit calls the server send npc hit with the enemy id and damage switching to the game server on the game server interface we have that remote function sent npc hit we get the node map and we run that mpc hit function that you have seen earlier underneath the spawn enemy function so opened up this function let's have a look at it we first filter if this enemy does not already has its hp reduced to zero or less that looks like a little bit of a weird line of code at the start of this function but it's crucial let me explain by first explaining the rest and then it will start making sense first thing we do is we subtract the damage we've done to this enemy from the enemy health and we saved that as the new value for enemy health then we check if that enemy health has reached zero or less in which case we are oh i got two prints here don't need those in which case we set the enemy state to death by communicating that to all the ps we can play the right animation then under the open locations we are going to be appending these spots that this enemy was occupying so of course as his enemy is that we now want new spawns to be able to spawn in that location then finally we don't want that location to be present in both the open locations and the occupied locations at the same time of course so we're going to erase the enemy id from the occupied locations thereby we're totally freeing up that spot so why are we filtering for this you might ask well the server knows that this enemy is dead before the clients know it the moment that this enemy dies on the server and we set this enemy state to death only once the client has received this dead state we can turn off the coalition shape of the enemy on the client side and until we do that client can still hit the the enemy for the client side that hp bar is not to zero yet on the enemy and the spears and the ak-47 bullets and the lightsabers and the swords are still hitting it that means that on all those hits the clients are still going to be sending in those hit this npc hit functions that means that once we have erased this enemy id from the dictionary and we get an other hit in there because the clients are buffered 100 milliseconds in the past and they don't know that that that enemy is dead we are going to get new npc hits and if we wouldn't filter for the fact that enemy health is already being reduced to zero or less then we would try to re-erase that enemy id from the occupied locations but that occupied locations doesn't have that id anymore so you get an error that the key was not found in the dictionary so because of that reason because of that buffering of the world state on the client side and because the server knows that enemy is dead a little bit earlier than the clients know we need to filter for that enemy health the moment that this function starts that we don't throw any weird errors okay so now we got updated health on all these enemies so what now well on the server we actually don't have to do anything because this enemy health is being updated in the enemy list that enemy list being the main dictionary that holds all the data and in state processing on the next state within 15 milliseconds we're going to be sending that enemy list from the map node in the world state to all the peers so we're sending that new health everywhere so all we got to do is make sure that the client is processing all this data so going back to the client and let's first start by going back to the map where we were doing that interpolation of our enemies i've already explained to you that we have an extra function call here for health and every time this world state comes in we're going to be pushing that health to that enemy so if soon as this health is being updated by the server the next time the client receives a packet which is within 50 milliseconds we're going to be updating or at least we have to be updating that health in the function on the weber so let's move to the web air and let's let's start when we actually spawn this wherever in so we have to you know go back a little bit because well it makes sense to explain to you now why things are going to be happening the way they are happening here so the moment that a werewear spawns in the player has received a world state with a new weber a new enemy in there and spawns it into the map at that point the spawn in function already sets our maximum hp current hp the state and the type under the ready function we are now going to set a couple of things we are going to set the hp bar value to the percentage of hp which is the current hp divided by the maximum hp multiplied by 100. if the state is idle we're going to play the idle southwest animation and if the state is dead then we're going to stop the animation player we set the sprite frame to 219 which in my case is a dead wear bear sprite we're gonna set the third disable the coalition polygon 2d so we cannot bump into the enemy anymore we're going to do the same with the hitbox so we cannot hit it anymore and then we're going to hide the health bar and now you might be wondering and that's why i've explained explaining this to you now why in name are we doing another ready function things other than let's set the health bar to 100 and let's stay to idle and play animation play southwest well here's the catch this is a multiplayer game but it could be that when a player is logging in that at that point another player is already attacking an enemy making that enemies hp bar not being 100 and could also be that another player has just killed the werebear within the six seconds before that player logged in in that case we've got a dead wear bearer on the map and we need to display that weber as a dead wear bear because you know will be cool we can actually see those so that's why even though this happens under the ready function when a weber is just spawned in we're still allowing that wherever to be dead or to be partially dead as that can be the case as by the actions of another player just before you logged in so now that we have got that the move function is pretty easy that's the same move function as we have on other players and that's pretty much what's part of the standard interpolation function here move enemy where we also had here move player so that's nothing special we also have health now that health is an extra function that we had right here so that's where we actually have to change health we got that function health right here that checks if the health that we received out of the world state is different than the currently known hp of this enemy if that's the case then we set the current hp as the health that we received we run the function health bar update and if that current hp is equal or lower than zero we run the on that function the on the function does nothing really special you already seen most of this it turns off the correlation shapes so that we can bump into the enemy or still hit it with our lightsabers and ak-47s and ice spears and fireballs we run the animation player play death southwest that is different than when we set sprite to set frame 219 because as a player logs in we want that enemies to just be a body on the ground and not play their death animation the moment you log in that would be a little bit weird but of course if a player is killing an enemy or if the player is in a vicinity of another player killing an enemy then of course we want to play the proper animations for that we hide the health bar so don't don't clutter the the screen and the z index minus one is very specific to my game and my tile map and this little bit of a demo you probably don't need that so probably ignore that then the last thing we have is a health bar update this is all part of the um tutorial i mentioned about health bars so i'm not going to go into that pause right now if you need it all right with that we have pretty much uh an entire function so i guess it's time for a test and demonstrate to you how this works so i got two clients here i'm first going to log in with one client and once we've got that client logged in we're gonna kill a weber with this guy and then i'm quickly gonna log in with this client here and see if it all updates properly so let's kill this one we quickly log in with this one you see a dead weber appear and i want a live one of course the code in the meantime running and soon we'll have a new wherever spawning there we have a new wear there and if i hit this one you can see both clients are updating their health bars of that particular enemy if i were to switch this one and kill this one or hit it and kill it it dies on both of the clients now as you probably noticed you can't see the ice spears of one of the other players because the ice spears are still being generated and rendered and they live on the client side so that's up in the next tutorials is moving that ice spirit to the client or sorry to the server and then also doing the physics coalition simulation on the server so everything is done by the server and the players cannot exploit in any way anymore with that set let's uh kill some webers and that's it for today that was it for today guys hope you like it if you did smash that like button hit subscribe don't forget that little bell icon to make sure that you don't miss out on the next tutorial in this multiplayer series now it's the 28th of december today so i'm going to wish you all a very happy and especially healthy 2021 2020 was a weird year for all the obvious reasons uh weird both because there's a positives and negatives on the positive side the community of the game development center has grown exponentially and for that i'm super thankful to uh to all of you let's try and see if we can make 2021 an even better year with even more growth that we have even more people in our community that can learn together and learn from each other and share experiences so if you want to contribute to that growth and if you want to give me a little bit of a a late year holiday present pick your favorite tutorial series whether it's the combat series the movement series the animation series or this multiplayer series and post it on your favorite forum you know let other people know that this channel exists that in your opinion hopefully it's making good content and that you can learn something here and if all of us you know make a shout out to this channel i'm sure we can grow the channel even so much faster than we already do and that's gonna benefit everybody so if you're with me on that i thank you in advance and i hope to see you in a new year and until then keep on gaming keep on coding see you later guys
Info
Channel: Game Development Center
Views: 3,799
Rating: undefined out of 5
Keywords: Godot Multiplayer, Godot Multiplayer Tutorial, Godot Multiplayer Enemies, Godot Sync Enemies, Godot Synching, Godot Synchronization, Godot Multiplayer Syncing, Godot Network, Godot Networking, Godot Dedicated Server, Godot Multiplayer Server, How to make a multiplayer game, Godot MMO, Godot Beginner Tutorial, Godot 2d Tutorial, Godot Tutorial, Godot
Id: tj24IrCy1hs
Channel Id: undefined
Length: 27min 47sec (1667 seconds)
Published: Tue Dec 29 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.