Making a Multiplayer FPS in Unity (E07. Damage) - uNet Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
thanks for tuning in at brakus hello I want to welcome to video number seven on making a multiplayer FPS in unity in today's video we're going to take a look at damaging our player so we'll be introducing a health variable and a cool system for kind of keeping track of players in our game we'll have this dictionary that associates a player ID with an actual player component but before we get started I quickly want to apologize for not making a video last week but I got sick but I'm fairly healthy now so that's good and also next week you might not see a video either because I'm attending Dreamhack in Sweden if you don't know it it's this huge land event it's really awesome and I'll probably tweet a bit about it while I'm down there so stay tuned for that and also as always if you have any questions go to from top brackets comm so you can basically go ahead and skipped right here use the annotation on the screen through the actual video but I quickly want to answer some questions that you've got that you guys have left in the comments about preventing cheaters in the game so in the on the last video you guys wrote that it would be a good idea to kind of shift the raycasting the shooting mechanic from the client side to the server because clients will they can't be trusted and you're actually very right about that but and I did write to you that I wanted to just change it in this video however I thought about it some more and a lot of problems kind of arise because we can't just change the raycast over and that would be a fairly easy process and we would have to do some layer management and and that but we could do that but there would still be a lot of issues because the some important variables like our damage variable and the future we're going to have a rate of fire and stuff like the range and and all that would still be very exposed to cheaters so it wouldn't really prevent anything and we would have to move all of that logic over to the server and things start getting really comprehensive and difficult so and kind of without the scope of this tutorial so what I suggest we do instead is we continue making the game with a minimal amount of security and what I would really recommend you do is if you find your game to be fun and you want to publish it and expect to get some users and maybe some that want to cheat well then I suggest you check out a find some kind of acid that will prevent cheating and there are a bunch of solutions for this out there I mean these guys have put their lives work into preventing cheaters and the one that I would recommend is what is called entity NC cheat toolkit and it's on the acid storm it costs $30 but it's very easy to implement so it's not something that you need to buy right now but it's something that you can think of buying once you feel that your game is ready to be launched and this the most important functionality I believe in here is the ability to kind of protect your types protect your variables so that it's not easily to insert a new value into them so you can protect the damage variable so the players can do insane amounts of damage and they also have stuff preventing a wall hack and speed hack and stuff like that so this is a really good tool kit it's very easy to use and they suggest you check that out if you worry about cheaters or another solution it's completely up to you so that's just my reasoning behind this and now I just think we should move on to today's video so we're here in unity and the first thing that I want to do here is just take a look at the player and if we just double click on the player shoot script and open it up in visual studio we can kind of see where we left off in the last video here so move this over here and scroll down and we can see that we have this command which is of course cost called on the server that takes in an ID and then says that that ID has been shot well now we need kind of way for us to easily find the player with this ID and then call a method on that player and we could create a player script or health script that will subtract a certain amount of health and I think a good way to do this would be to introduce a kind of central game manager component that has a dictionary of players in it so what we're going to do is we are going to rename this from ID to our player ID and do the same down here and we can just leave the debug log statement here and this will only be called on the server and what we can then do is we can go ahead and create a new component so let's go on to create and then c-sharp script and let's name these this the game a manager and while we're in here let's also create another c-sharp script and let's name this the arm oops the player so this is our central player component you can also call the player manager if you want and let's just go ahead and open up the game manager here and the game manager is of course going to be of type on network behavior but actually as I'm thinking about it I think we can go ahead and keep this as a mono behavior for now and then we'll add on to it later if if needed so basically I want to create a static dictionary in here so we are going to be using system collections dot generic so that's something you want to type up here and then we are going to create a public actually I'll make the list itself private and then we'll access it through public methods so a private static dictionary and it's going to take in as the key a type string that's going to be ID and it's going to give us the value which is going to be a type player remember the component that we just created well that's a component we want all of our players to have and therefore well it makes sense to use that component that not a game object or anything else because this then that way we restrict ourselves to only having players in our dictionary or not other game objects and then we call this our players and we set it equal to a new dictionary of type string and player and then open closed parenthesis and a semicolon so that's kind of the syntax for creating a dictionary here I believe it's the first time we do this on this series so then we need some methods that will actually interact with this dictionary and the first one is going to be a public void and it's simply going to register a player and of course we want some arguments here and the first one is going to be a string and we are going to call this the net ID we are simply going to be taking in the network ID from the player's network identity component and then we're going to be kind of transforming that into a player ID so we can give it any kind of format that we want and then we are going to also be taking in a player of course so a player underscore player there we go and we could just say that well the player ID and nettie ID should be the same if you don't know when a player joins he gets assigned a number that's his net ID and by default that's just going to be one two three so the first player is 1 the second player is 2 and you get the idea but instead of kind of just renaming all of our players to 1 2 & 3 we should maybe call them player 1 player 2 or player ID 1 or whatever format you want to kind of do for this so I want to just prefix this net ID with another string and instead of hard-coding it down here we could just say that the string player ID should be equal to player + net ID well then I just want to kind of create a variable up here that's easy to spot and change and we're simply going to call this we're going to make it a private constant string because it's not going to be changed and with constants the naming convention is all capital letters so we're going to call this the player prefix our ID prefix like this I will simply do player like that and then we can simply take this down here instead so we can change it up here and it's easily visible ah cool so we get the player ID here and now we can simply go in and add this to the dictionary so we can say players dot add and you can see it takes in first a key and then a value and the play is going to the key is going to be player ID and the value is going to be player there we go and I also quickly want to rename on the player transform to the player ID so that we can quickly see what players are what in the hierarchy and in order to do this we're simply going to say player dot transform dot name equals player ID there we go and I believe that's all we need to do inside of this method so now in the player setup you can see that we have this start method that disabled the components enables the camera or disables the scene camera and then it registers the player locally and all it does is it says sets are transformed our name equal to player plus the net ID so that's kind of the same that we've done here but now we can remove it here and know that the game manager takes care of this for us so I'm actually going to move this completely out of the start method and then instead to create a another method and new method we're going to say public override board and it's going to be on start client and OnStar client is a method already in the network behavior class and it's basically called every time a client is set up locally so what this will allow us to do you can see it calls its own base here is we can simply add the register player onto this so we can now say game manager dot register oops did we not do this correctly game manager this of course needs to be static - game manager dot register player there we go and then we give it first off the net ID and then the player itself so we need to get some components here the first one is going to be the net ID and that's going to be equal to get component network identity dot net ID and the second one is going to be the player itself so that's going to be on the score player equals get component player there we go and we can simply put these in here so we give it the net ID and the player reference oops cold and we know that the of course we need to convert this to a string so we call the to string method we know that the network ID is always going to sit on this because it's soft type it derived from network behavior and therefore it requires a net ID but we want to make sure that it always has the player component and therefore we should require it up here so we'll call require component type of and then player there we go so now we'll always get a a valid component here and not a null type so this will just work and we don't need to do any error checking now down here in the on disable method well we want to of course also deregister players once they are killed so on the under the game-manager we can now say dot unregister and we'll create this in a second unregistered player and we'll give it the ID of the player with this which is equal to transform dot name we could also pass over the UM the player component and search for that but we'll just give it the ID it's the ID because that would be easiest so of course registering and D registering players is something that we will do once a player joins and disconnects and this will do that every time a player joins and then when he is destroyed and that's not the same as him disconnecting right now it is because we don't have any way to destroy our player other than if we disconnect but at some point we're going to have to make a destroy a distinction there but for now we'll write it in like this and we'll handle things as they come so now we can make a public static void called unregistered player actually this should probably do register unread dear you English stalkers are going to probably correct me on that but I'm going to write unregister and face the facts it doesn't matter okay so we'll take in a net ID I don't know a player ID of course and we'll basically say that players don't remove and then give it the key which is player ID and it will be removed from the dictionary and it's it's that easy so when it comes to dictionaries that can be kind of hard to work with in unity because unity doesn't have a way to actually visualize these in the inspector so you have to kind of create your own UI if you want to do that and we could go down here and create a on GUI method that lists all of the registered players but I don't really think it's something that we should be spending time on right now I'm thin looking about it okay you know what let's do this so we'll create a void on GUI and in here we're simply going to be using a GUI layout so we'll say GUI layout dot begin area and we're going to give it a new rectangle this is by no means something that you need to do this is just something I'm doing to visualize what is going on and you can just copy off me here I'm not going to be explaining to more much so we are creating an area for GUI to reside in and that's going to be at 200 200 and with the width of 300 200 and a height of 500 and of course when we begin something we have to end it so in area and GUI layout dot pick in vertical UI layout dot nth vertical and so now we will list things like whoops like this and then we can loop through our dictionary here so for each on what are we going to do player in and we'll just call this player in players is this going to work I don't remember how the proper way to loop through a dictionary is give me just a moment and I'll check what we are going to do here so of course I was being a bit stupid here well we want to loop through the key so we are going to create a string here for each player ID in players dot keys that's going to be the array stowing all of our keys but then we want to create a GUI layout label and this is going to store our ID first so we're going to say player ID Plus and then we'll just create some space here and then the players and then we're going to input the key in order to get the value so we're going to say player ID here and then dot transform dot name so we'll give it the name there and that should be fine I believe cool so we'll just create a bunch of labels here and lay them out I think that should work and we'll test this in a second but also I want to create one last method which is kind of just a utility method for getting a certain player with an ID so we'll create a public static void get player and it's going to take in a string player ID and of course its return type is going to be player and not board and it's simply going to return players and then input the player ID and we'll get the value there we go and that's basically all for our game managers now under our player setup we can see that we are setting up the player we're registering him and we are D registering him down there and then in a player shoot script we can use to get player but we're going to do that in a second for now let's just check that these players are attribute appropriately add it to a list so you can see here that something is going wrong and what is going wrong here so reference not set to instance of an object well first off we need to make sure that our player here actually has the player script attached and he might not because well remember we added the require component after actually having added the scripts and therefore that sometimes one work so yeah we'll simply drag that in so all we need to do is find our player lock that on there take our player script here and added on to their and now that that is added we also need to create actually right now everything is static with this game manager so we don't need to add it but I don't think everything is going to be static so therefore I'll create an empty game manager component up here and we don't need that locked anymore and we'll reset that and add the game manager answer there but now it's just going to sit there and do nothing because we don't instance this in any way but at some point we probably will so now let's clear this out and hopefully we will get no errors land host and you can see here that it says player 1 that's the ID and its associated here they player 1 up here and I know it looks stupid that it just sets player 1 and player 1 but it means that it's actually working and of course we had to add this here because we've done the gooey stuff so it's a great thing that we remember to do that so now if we just go ahead and build this it's not going to be kind of especially impressive in any way but we are going to see the fact that we have this central manager component that keeps track of our players so let's hit play there go under your host here select client over here and you can see that we have the two players in here in the dictionary that's just a way for us to visualize what is going on cool so that was all we needed to do and now we can actually go ahead and use this so I'll just go into the game manager here and comment out this on GUI method we might be needing it at some point but not for now so under our players shoot script you can see that we are currently just writing out what player has been shot well we can use this information much much for to do much more so we are going to call it game manager don't get player and this will of course return a player object and we can input the player ID and therefore down here let's create a player object let's just call this player and set it equal to that now we can call methods on that player component so let's go ahead and make a method for this we can say player Don to take damage and we can give it the damage amount but in order to give it the and this is a method we're going to be creating in a second in order to give it the damage amount we of course needs need to pass this on so down here when we call the command we give it the ID which is the colliders name and we also wanted to want to give it the player or the weapon damage variable and I've changed my mind I don't want this to be a float I want it to be an integer so up here we're just going to say int damage and we're going to pass it on down here because we want to pass it into the take damage method and now we need to go under the player weapon script and change this from a float to an integer and just remove the F here there we go so now all damage is calculated with integers and so will our health now we can go and you are into our player script and actually configure that so let's open that up and in here this is of course going to be of type network behavior behavior and we need to be using unity engine dot networking as always and we're going to need to create a couple of variables here so the first one is a public actually I don't want it to be public I just wanted to show up in the inspector so we're going to make it a serialize field and we'll mark it as private and that's going to be an integer storing our max health and let's just default that to 800 then we are going to have another variable and this one is definitely going to be private and this is going to be our current health and we don't want to set that equal to anything yet however our current health needs to be synced across all the different clients so it would be really stupid if we damage the player and the only one who knew we damaged the player was the server the clients were never notified of this and if we just go in and change this by using this command and calling a take damage function on the player well then it's only going to be changed on this server so in order to sync this information about the value being changed out to the all of the different clients so they like so that they can update their GUI or kill off the player if you died and all of that well then we need to create a sync variable and all we have to do in order to mark this as a sync variable is simply use an attribute so we just type in sync var and now every time the value changes it will push be pushed out to all of the clients it's that easy and this is an amazing feature in unit so that's all we needed to do there and then we can maybe stay in the start function here or we could do this in a wake even we can simply call some kind of method that will set our current health equal to max health and all of the other stuff that we might want to reset in the future so let's create a set defaults method and it's not going to take in any arguments at all here and we're just going to create that down here and we're going to make it public because I imagine that we're going to be calling this this from outside the script and it's going to be called set defaults and it's simply going to say that current health equals max health and it's that easy cool and next up a public void take damage that takes in a amount and simply says well we want to subtract our current health with the amount and again we don't need to do anything here it's going to be synced out because we mark this as a sync variable and then we can just a debug ID lock and this is only be going to be done on the server of course so you're going to send out a debug deadlock statement saying that transform dot name plus hat now has plus current health plus health there we go so we can see the health updating in the inspector and of course this is again only called on the server so only the host will be able to see this in their console log but that's ok for us so now we actually have this structure in place where our player checks all of the different players check locally if they hit something if they do they take they send our over information or an ID of what we've hit and some damage this is called on the server so the server now takes the information about this ID and finds the player component by using a utility method on our game manager that tracks all of our different players it then it takes our damage variable and puts it into a take damage method that we call on that player we jump over here the player locally on the server applies this damage to the current health variable and unity recognizes that this variable has changed and then swings it over out to all of the clients so that's kind of the route that that's kind of how the networking is it's working in this case and I hope like you can follow along here if not don't worry you'll get a hang of this in no time by just playing around with it and if this is working well I hope it is let's test it out so let's build this actually I just build it locally here so we can see if there's any errors they aren't so let's build this to a standalone and see if it's actually working for us we'll also build it here I'm going to select host here and client here we'll move up here and shoot the guy you can see here that it says player now a player has been shot player one now has ninety health remember we start at 100 and we subtract ten and we can actually view this if we select our player one and you can see that this is indeed the first player to join this is the host we can then actually inspect the private variable here and this is a fairly handy feature if we go up here in the inspector we can change to debug mode and a bunch of information is going to be appearing but this will allow us to view private variable so you can see down here now that current health is indeed 90 on player two it's still 100 because we haven't hit that player yet let's try and actually do that so let's try and shoot that player so I'm going to shoot him here click you can see player 2 has been shot player 2 now has 90 health I'm going to do this a few more times bring him down to 60 and you can see here that current health on player 2 is indeed 60 and this is going to work regardless of who is hosting so you can see here that we got a timeout error that's because the host disconnected that we can simply close the developer console let's now host on this system let's host on and join us a client on this system you can see we don't get any errors because we are properly registering and D registering players into the dictionary well you can see that I can shoot this player two times and I can shoot this player three times nothing is appearing in the console because this is the one this client is or on this game instance is the host and therefore all console logs are being sent over here on the server but we can see that it's still updated so down here this guy now has 70 health and this guy has 80 so that was basically it for damaging a player's taking damage over on the network kind of keeping track of players in a intuitive way using IDs and yeah I don't have any more to show you today I hope you were able to follow along and maybe I'll see you at dreamhack until then enjoy
Info
Channel: Brackeys
Views: 119,531
Rating: undefined out of 5
Keywords: unity, unity3d, tutorial, material, materials, function, and, settings, how, to, howto, learn, course, series, tips, tricks, tutorials, workflow, technology, game, development, develop, games, programming, coding, basic, basics, C#, damage, id, player, dictionary, generic, generics, collections, setup, syncvar, sync, variable, Multiplayer Video Game (Website Category), Unity (Software), cheat, cheating, anti-cheat, toolkit, protect, security
Id: jVy-ecjCP_A
Channel Id: undefined
Length: 31min 29sec (1889 seconds)
Published: Sun Nov 22 2015
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.