{"id":294,"date":"2016-03-04T14:23:32","date_gmt":"2016-03-04T14:23:32","guid":{"rendered":"http:\/\/www.doofah.com\/tutorials\/?p=294"},"modified":"2016-03-11T14:17:48","modified_gmt":"2016-03-11T14:17:48","slug":"unity-5-network-tutorial-part-7-improved-respawning-and-health-pickups","status":"publish","type":"post","link":"https:\/\/www.doofah.com\/tutorials\/networking\/unity-5-network-tutorial-part-7-improved-respawning-and-health-pickups\/","title":{"rendered":"Unity 5 Network Tutorial Part 7 \u2013 Improved respawning and health pickups"},"content":{"rendered":"<p>Welcome to part 7 of my Unity 5 basic network game tutorial, in this, the final part, we will improve the respawn process and implement health pickups.<\/p>\n<p><strong>Part 7a &#8211; Improved respawn<\/strong><\/p>\n<p>In the last part of the tutorial, we implemented respawn behaviour when a player&#8217;s health reaches zero, however as it stands there is no delay between the player dying and being respawned, which dosn&#8217;t feel quite right. Also there is a subtle bug, whereby if the player dies close to his respawn point, you will see him &#8216;lerp&#8217; to the respawn point on remote clients.<br \/>\nSo we&#8217;ll go ahead and fix both of those issues now.<\/p>\n<p>First of all, open the <em>NetworkPlayer<\/em> script in your editor and make the <em>playerCam<\/em> variable public.<\/p>\n<p><strong><span style=\"font-family: Calibri;\"><span style=\"color: #800080;\">public\u00a0<\/span><span style=\"color: #008000;\">Camera\u00a0<\/span>playerCam<\/span> <\/strong><\/p>\n<p>The reason for making the <em>playerCam<\/em> variable public is because we want to temporarily detach the camera from a player object when he dies. This enables us to reposition the player without the camera immediately following him. (This is a bit of a workaround to avoid the lerping issue mentioned earlier.<\/p>\n<p>This is the only change we need to make to the <em>NetworkPlayer<\/em> script, so go ahead and save it, and then open the <em>HealthAndDamage<\/em> script in your editor, as we also need to make some changes there.<\/p>\n<p>We&#8217;ll handle detaching and re-attaching the camera in the <em>HealthAndDamage<\/em> script, so we&#8217;ll store a reference to it in a variable in the <em>HealthAndDamage<\/em> script.<br \/>\nAdd this line below the <em>respawnPos<\/em> variable:-<\/p>\n<p><strong><span style=\"font-family: Calibri;\"><span style=\"color: #008000;\">Camera\u00a0<\/span>playerCam;<\/span> <\/strong><\/p>\n<p>And add this line to the <em>OnStartLocalPlayer<\/em>() function:-<\/p>\n<p><strong> <span style=\"font-family: Calibri;\"> playerCam\u00a0=\u00a0GetComponent&lt;<span style=\"color: #008000;\">NetworkPlayer<\/span>&gt;().playerCam;<\/span> <\/strong><\/p>\n<p>That&#8217;s the reference to the player&#8217;s camera sorted, so next we&#8217;ll add three new functions that will perform the improved respawn.<\/p>\n<p>Add the following code just below the <em>GetRandomSpawnPoint<\/em>() function:-<br \/>\n<script src=\"\/\/pastebin.com\/embed_js\/RvTd0aRA\"><\/script><br \/>\n So looking at these new fuctions, we have <em>setVisibleState(bool state), <\/em>what this does is turn on or off the player object renderer and name label, effectively making it invisible or visible depending on the state argument.<\/p>\n<p>Then we have a coroutine, <em>HandlePlayerDeath<\/em>() which calls <em>setVisibleState<\/em> with a value of false, which makes all instances of the player object invisible.<br \/>\n Next, only if it&#8217;s running on the local player, it detaches the camera, preserving the camera&#8217;s current position, and then it repositions the player in the respawn position. As the camera is now not parented to the player, the game view stays in the position it was when the player died, and then it waits for two seconds. This is good for two reasons, first it gives a nice pause between dying and respawning and secondly it allows the clients to lerp to the new spawn position whilst they are invisible, so you don&#8217;t see it happen.<br \/>\n Finally it calls the <em>Respawn<\/em>() function.<\/p>\n<p>The <em>Respawn<\/em>() function checks to see if it&#8217;s running on the local player, and if it is, it re-parents the camera to the player object and sets it&#8217;s local position to the correct value.<br \/>\n Then it tells the server to reset the player&#8217;s health to 100. Finally it makes the player visible again on all PCs on the network (which is why the last command needs to be outside of the <em>isLocalPlayer<\/em> check).<\/p>\n<p>The final change we need to make is to the <em>RpcHandlePlayerDeath()<\/em> function, to make it call the <em>HandlePlayerDeath()<\/em> coroutine instead of handling the respawning itself. So replace the existing <em>RpcHandlePlayerDeath<\/em>() function with this new one:-<br \/>\n<script src=\"\/\/pastebin.com\/embed_js\/icHpNHk9\"><\/script><br \/>\nSo that&#8217;s the new and improved respawn procedure finished, this is the new <em>HealthAndDamage<\/em> script with all the above changes implemented:-<br \/>\n<script src=\"\/\/pastebin.com\/embed_js\/pXQPSVER\"><\/script><br \/>\n Save this and then build and run the game, you will see that after a player dies he disappears for two seconds before reappearing at one of the spawn points with full health.<\/p>\n<p><strong><br \/>\n Part 7B &#8211; Health Pickup<\/strong><\/p>\n<p>Now let&#8217;s add a health pack that you can walk over to replenish your health. We&#8217;ll implement these as scene objects, so we can just place them in the editor and not have to worry about runtime instantiation and health spawn points etc. etc.<\/p>\n<p>So the first thing we need to do is create a health pack game object. Open the online scene and add a 3d Cube game object (<strong>GameObject-&gt;3D Object-&gt;Cube<\/strong>) and rename it <em>HealthPack<\/em>.<br \/>\n Now make the following changes to the <em>HealthPack<\/em> game\u00a0object:-<\/p>\n<ul>\n<li>Set its transform.position to\u00a0 -8,0.25,-5<\/li>\n<li>Set its scale to 0.5,0.5,0.5.<\/li>\n<li>Add a NetworkIdentity component to the HealthPack (<em>with the HealthPack game object selected use the menu option (<strong>Component-&gt;Network-&gt;NetworkIdentity<\/strong><\/em>)).<\/li>\n<li>Set\u00a0its Box Collider\u00a0<em>Is Trigger<\/em> property to ticked<\/li>\n<\/ul>\n<p>Next we&#8217;ll give it some colour; Open the Assets\/Materials folder, create a new material in there\u00a0and rename it <em>HealthPack<\/em>, then set the albedo Colour to a nice bright green.<br \/>\n Then drag this new material onto the <em>HealthPack<\/em> game object in the hierarchy and you should see the cube take on the colour of your new material.<\/p>\n<p>Your scene setup should look like this after you have done all the above.<\/p>\n<p><a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/03\/Image2.png\" rel=\"attachment wp-att-304\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-306 size-large alignnone\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/03\/Image2-1024x639.png\" alt=\"\" width=\"654\" height=\"408\" srcset=\"https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/03\/Image2-1024x639.png 1024w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/03\/Image2-300x187.png 300w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/03\/Image2-768x479.png 768w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/03\/Image2.png 1440w\" sizes=\"(max-width: 654px) 100vw, 654px\" \/><\/a><\/p>\n<p>Now let&#8217;s add a script to the <em>HealthPack<\/em> game object to implement the functionality. Create a new folder in Assets and rename it <em>HealthPack<\/em>, and in that folder create a new c# script and rename it <em>HealthPack<\/em>. Then open the <em>Healthpack<\/em> script in your editor and replace the default code with the following:-<br \/>\n<script src=\"\/\/pastebin.com\/embed_js\/aTXgyRyT\"><\/script><br \/>\nThis is a fairly straightforward script with only five functions, also notice that it derives from <strong>NetworkBehaviour<\/strong> instead of <strong>MonoBehaviour<\/strong>.\u00a0Now let&#8217;s take a look at each part in turn and I&#8217;ll explain what\u00a0each one\u00a0does.<\/p>\n<p>Firstly we declare a variable, <strong>bool<\/strong> <em>visible<\/em>, and make it a SycVar with a hook function. The hook function <em>OnVisibleChanged<\/em> just activates or deactivates the attached Renderer and Collider based on the <em>newValue<\/em> argument. This has the effect of hiding or showing the health pack in the scene. Anytime the value of visible is changed this hook function will run and update the state of the <em>HealthPack<\/em> in the scene.<\/p>\n<p>Next we have <strong>OnStartServer(),<\/strong>\u00a0this will only ever run on a dedicated server when it starts, or the host when it starts. Therefore we can use this function to set the initial state of the <em>HealthPack<\/em>, and as we want all health packs to start enabled we set visible to true.<\/p>\n<p>Next comes <strong>OnStartClient(), <\/strong>this runs on all clients when they join the game, and at this point all SyncVars are guaranteed to have the correct synchronized value, so we manually call the <em>visible<\/em> hook function, passing the current value of visible, so we can update the state of the health pack in our scene. This ensures that if a health pack is hidden on the server at the time we join the game, our local copy also gets hidden.<\/p>\n<p>Then we have the<strong> HandlePickup()<\/strong> coroutine.<\/p>\n<p>All this does is set visible to false, which makes the hook function run, which hides the health pack.<br \/>\nThen it waits 15 seconds and then sets visible to true, which makes the health pack visible and available for use again.<br \/>\nWe can make direct changes to the visible variable as this script is running on the server.<\/p>\n<p>Lastly comes<strong> OnTriggerEnter(Collider other)<\/strong><\/p>\n<p>By default, this function runs on all clients whenever a trigger collision is detected, however we only want to handle collisions with the <em>HealthPack\u00a0<\/em>on the server. We could ensure this in\u00a0a couple of ways, either by checking if <em>isServer<\/em> is true, or (and this is how we do it) with the use of attribute tags.<br \/>\nThe use of the [<strong>Server<\/strong>] tag, would mean that the function will only run on the server, however as we can&#8217;t control when this function is invoked, we can&#8217;t prevent clients from calling this function, and although the function won&#8217;t run on the client, we would get a console littered with debug warnings. Therefore instead of using the [<strong>Server<\/strong>] attribute, we&#8217;ll use the [<strong>ServerCallback<\/strong>] attribute, which still makes it only run on the server, but also supresses the warning messages if the function is called from the client.<\/p>\n<p>So having ensured this will only run on server, what does it do. Firstly it starts the <em>HandlePickup<\/em> coroutine as described above, to hide the health pack.<br \/>\nThe &#8216;<em>other&#8217;<\/em> argument, will contain a reference\u00a0to the player game object that collided with the <em>HealthPack<\/em>, so we use GetComponent to access its <em>HealthAndDamage<\/em> script and add 10 points to its publicly\u00a0exposed <em>health<\/em> variable.<br \/>\nThere again we can do this directly as this script is running on the server.<br \/>\nThis is where the power of SyncVars shows, because this updated <em>health<\/em> value is now\u00a0automatically sent to all clients and because we had previously set up a hook function for the <em>health<\/em> variable, it in turn updates the health display on the HUD. So just by changing the value of the <em>health<\/em> variable everything automatically synchronizes and updates, very neat!<\/p>\n<p>Save the <em>HealthPack<\/em> script and\u00a0add it to the <em>HealthPack<\/em> game object by dragging it onto the <em>HealthPack<\/em> in the hierarchy window.<\/p>\n<p>Then\u00a0save the\u00a0<em>Online<\/em> scene, and the open the <em>Offline<\/em> scene. You can now build and run the game and test the <em>HealthPack<\/em>.<\/p>\n<p>So now we have a working health pack, which when a player walks over it will add some health and disappear, before respawning again 15 seconds later. You can duplicate the health pack game object\u00a0as many times as you like and position them around your game scene.<\/p>\n<p>There are a couple of obvious\u00a0things about the health pack that could be improved, such as preventing it from increasing health above 100, and also stopping the player from flashing yellow when he picks up a health pack when he is below 100 health. But I&#8217;ll leave those as an exercise for you to figure out.<\/p>\n<p>So that&#8217;s it for this tutorial series, I hope it&#8217;s proved understandable and useful and I&#8217;ll do my best to answer any questions that might arise.<\/p>\n<p>You can download the complete Unity project files here:-<br \/>\n<a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/03\/Unity-Networking-Tutorial.zip\" rel=\"\">Unity Networking Tutorial<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Welcome to part 7 of my Unity 5 basic network game tutorial, in this, the final part, we will improve the respawn process and implement health pickups. Part 7a &#8211; Improved respawn In the last part of the tutorial, we implemented respawn behaviour when a player&#8217;s health reaches zero, however as it stands there is [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[11,12,13,16,9,15,10,14,8],"_links":{"self":[{"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/posts\/294"}],"collection":[{"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/comments?post=294"}],"version-history":[{"count":34,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/posts\/294\/revisions"}],"predecessor-version":[{"id":332,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/posts\/294\/revisions\/332"}],"wp:attachment":[{"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/media?parent=294"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/categories?post=294"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/tags?post=294"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}