{"id":116,"date":"2016-02-16T19:45:38","date_gmt":"2016-02-16T19:45:38","guid":{"rendered":"http:\/\/www.doofah.com\/tutorials\/?p=116"},"modified":"2016-02-29T09:53:33","modified_gmt":"2016-02-29T09:53:33","slug":"unity-5-network-tutorial-part-3","status":"publish","type":"post","link":"https:\/\/www.doofah.com\/tutorials\/networking\/unity-5-network-tutorial-part-3\/","title":{"rendered":"Unity 5 Network Tutorial Part 3 &#8211; Camera control and name labels"},"content":{"rendered":"<p>Welcome to part 3 of my Unity 5 networking tutorial.<\/p>\n<p>In this part we will give the players their own name and name tag, and we&#8217;ll make the camera follow the player that you are controlling.<\/p>\n<p><strong>Part 3a &#8211; Synchronizing the PlayerID<\/strong><\/p>\n<p>To keep things simple we are going to automatically generate a unique name for each player that joins the game, rather than let the user specify one. So first of all we need to decide on a way to obtain a unique identifier for each player.<br \/>\nLuckily the Unity networking system provides us with an easy way to do this, by virtue of the NetworkIdentity.netId. Every networked GameObject is allocated a netID number by the networking system, which is used by Unity to keep a track of that object, and as the netId is unique for every gameObject we can make use of that.<\/p>\n<p>So with that in mind create a new script in the Assets\/Player folder and name it NetworkPlayer. Double click the new script to open it in MonoDevelop (or whichever editor you use) and replace the default code with the following code.<br \/>\n<script src=\"\/\/pastebin.com\/embed_js\/qxDeSqzV\"><\/script><br \/>\n The job of this script is to detect when the player joins the game, and then generate and store a unique name for said player.<\/p>\n<p>First of all, note that like the PlayerNetworkMove script we wrote in part 2, this is also a <em><strong>NetworkBehaviour<\/strong><\/em>, rather than a <em><strong>MonoBehaviour<\/strong><\/em>.<\/p>\n<p>Then, next we have<\/p>\n<p><strong> <span style=\"font-family: Calibri;\"> [<span style=\"color: #008000;\">SyncVar<\/span>]\u00a0<span style=\"color: #800080;\">public\u00a0string\u00a0<\/span><em>playerID<\/em>;<\/span> <\/strong><\/p>\n<p>Basically this is a variable like any other, except that it has a [<strong><span style=\"color: #008000;\">SyncVar<\/span><\/strong>] attribute (Synchronized Variable). What this attribute does is make sure that any time the variable&#8217;s value is changed, the new value is automatically sent to all connected players. Furthermore, whenever a new player joins, they receive the current value of that variable for all the other players already in the game.<br \/>\n In this case, we are going to use this variable to store the unique player name we generate later in the script, and as mentioned\u00a0this will be automatically synchronized with all the other players so they will be able to see our name.<\/p>\n<p>Let&#8217;s look at the next bit now, the CmdSetPlayer method.<\/p>\n<p><strong><span style=\"font-family: Calibri;\">[<span style=\"color: #008000;\">Command<\/span>]<br \/>\n <span style=\"color: #800080;\">void\u00a0<\/span>CmdSetPlayerID(<span style=\"color: #800080;\">string\u00a0<\/span>newID)<br \/>\n {<br \/>\n \u00a0\u00a0\u00a0\u00a0 playerID\u00a0=\u00a0newID;<br \/>\n }<\/span> <\/strong><\/p>\n<p>If you remember in Part 2 of the tutorial I mentioned that only the server can make changes to networked objects, well, this is a case in point, we want to be able to change the playerID, so we need a way for the server to change it for us. In order to do this, we need to create a method that updates playerID and\u00a0give it an\u00a0attribute, the [<strong><span style=\"color: #008000;\">Command<\/span><\/strong>] attribute. Essentially what this does is make sure the following function will only be run on the server. We also need to follow a specific naming convention for a server command, specifically it <em>has<\/em> to start with Cmd.<\/p>\n<p>In this example, all the CmdSetPlayer method does is set the playerID value to the value of the newID argument, and\u00a0because of the [<strong><span style=\"color: #008000;\">Command<\/span><\/strong>] attribute this can only run on the server. Once the value of the variable has been changed, the [<span style=\"color: #008000;\"><strong>Syncvar<\/strong><\/span>] attribute of the variable\u00a0means that the new value will\u00a0propagate to all connected clients.<\/p>\n<p>So now we come to the final part of the script.<\/p>\n<p><span style=\"font-family: Calibri;\"><strong><span style=\"color: #800080;\">public\u00a0override\u00a0void\u00a0<\/span>OnStartLocalPlayer\u00a0()<\/strong><br \/>\n <strong>{<br \/>\n <span style=\"font-family: Calibri;\"><span style=\"color: #800080;\">\u00a0\u00a0\u00a0 string\u00a0<\/span>myPlayerID\u00a0=\u00a0<span style=\"color: #800080;\">string<\/span>.Format(&#8220;Player\u00a0{0}&#8221;,\u00a0GetComponent&lt;<span style=\"color: #008000;\">NetworkIdentity<\/span>&gt;().netId.Value)<br \/>\n \u00a0\u00a0\u00a0 <\/span><\/strong><strong>CmdSetPlayerID(myPlayerID);<\/strong><br \/>\n <strong>}<\/strong><\/span><\/p>\n<p>This function is called on network player objects (but only on the client that owns the player), when they join the game. <em>What this means is, that when you join the game,\u00a0the OnStartLocalPlayer function will run on your player game object on\u00a0your PC, but not on\u00a0the game object representing your player on anyone else&#8217;s PC.<\/em><br \/>\n First of all it retrieves the player&#8217;s netId, which is a uInt value, and constructs a string in the format &#8216;Player n&#8217; (where n is the netId value).<br \/>\n Then it calls the CmdSetPlayerID method, passing in the new playerID, which instructs the server to change the value, this new value is then sent to all connected clients (including the one that made the call) and their copy of playerID is updated with the new value.<\/p>\n<p>Add this script to the player prefab in the Assets\/Player folder, by dragging it onto the prefab, or using the Component menu (<strong>Component-&gt;Scripts-&gt;NetworkPlayer<\/strong>) and then save the scene.<\/p>\n<p>If you build and run the game now and run one instance in the Editor and one instance in standalone, windowed mode, you will be able to see this working.<br \/>\n With a host running and a client connected you can check the player(clone) game objects in the inspector, and you will see that the exposed playerID syncvar has a different value for each player.<\/p>\n<p><a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerIDSyncvar.png\" rel=\"attachment wp-att-120\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-120\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerIDSyncvar-1024x641.png\" alt=\"PlayerIDSyncvar\" width=\"800\" height=\"501\" srcset=\"https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerIDSyncvar-1024x641.png 1024w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerIDSyncvar-300x188.png 300w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerIDSyncvar-768x481.png 768w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerIDSyncvar.png 1439w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>So far, so good, but what we actually want is for the playerID to be displayed in the game, so that ourselves and other players can see it. However, before we do that I want to deal with the camera, and make it so that it follows your player as you move, we&#8217;ll come back to the playerID label afterwards.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Part 3b &#8211; Setting up the camera<\/strong><\/p>\n<p>The first step in setting up the camera is to load the <strong>Online scene<\/strong>, <em>this is important, because if you do the following steps with the Offline scene loaded you&#8217;ll end up with no camera on the menu and two cameras in the game scene<\/em>.<\/p>\n<p>So, with the Online scene loaded, drag the <em>player prefab<\/em> from the Assets\/Player folder into the Hierarchy window, to create an instance of it in the scene. Then we need to drag the <em>Main Camera<\/em> game object onto the player game object you just created, so that the camera becomes a child of the player, and make sure its transform settings\u00a0are set to\u00a0<em>Position<\/em> (0,10,0) and <em>Rotation<\/em> (90,0,0) like so:-<\/p>\n<p><a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerCamera.png\" rel=\"attachment wp-att-122\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-122\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerCamera.png\" alt=\"PlayerCamera\" width=\"517\" height=\"691\" srcset=\"https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerCamera.png 517w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerCamera-224x300.png 224w\" sizes=\"(max-width: 517px) 100vw, 517px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Once these changes have been made, we need to click the Apply button on our player game object in the hierarchy, to save the changes to the prefab.<\/p>\n<p><a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PrefabApply.png\" rel=\"attachment wp-att-123\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-123\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PrefabApply.png\" alt=\"PrefabApply\" width=\"263\" height=\"81\" \/><\/a><\/p>\n<p>Once the changes have been applied go ahead and delete the player game object from the hierarchy. You will notice in the Game window there is a message saying that the scene is missing a fullscreen camera, you can safely ignore this for our purposes.<\/p>\n<p>One further change I&#8217;d like to make, is to add a texture to the ground material we made in part 2, as this will make it easier to see that the players are moving, compared to how it would be with\u00a0a featureless brown ground surface. So to do this, firstly create a new folder in Assets and call it Textures. Then download this texture (Click on the picture or the link below it to open it in your browser, then right click and <em>select Save Picture As&#8230;<\/em>)<a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/groundTexture.png\" rel=\"attachment wp-att-126\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-thumbnail wp-image-126\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/groundTexture-150x150.png\" alt=\"groundTexture\" width=\"150\" height=\"150\" srcset=\"https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/groundTexture-150x150.png 150w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/groundTexture-300x300.png 300w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/groundTexture.png 512w\" sizes=\"(max-width: 150px) 100vw, 150px\" \/><\/a><\/p>\n<p><a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/groundTexture.png\">http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/groundTexture.png <\/a><\/p>\n<p>Once you have saved the texture to your PC,\u00a0copy it into the textures folder you just made. You can also, if you wish, use instead\u00a0any suitable texture you may already have.<\/p>\n<p>Next, select the GroundMaterial in the Assets\/Materials folder, and click the small circle just to the left of the word Albedo in the inspector.<br \/>\n This will open the Select Texture dialog&#8230; <a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/MaterialAlbedo.png\" rel=\"attachment wp-att-132\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-132 size-full\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/MaterialAlbedo.png\" alt=\"MaterialAlbedo\" width=\"802\" height=\"444\" srcset=\"https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/MaterialAlbedo.png 802w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/MaterialAlbedo-300x166.png 300w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/MaterialAlbedo-768x425.png 768w\" sizes=\"(max-width: 802px) 100vw, 802px\" \/><\/a><\/p>\n<p>&#8230;within which you should see the <em>groundTexture<\/em> you just downloaded. Double click the <em>groundTexture<\/em> to apply it to the material and dismiss the dialog.<br \/>\n Then set the albedo colour to white and lastly set the\u00a0X and\u00a0Y tiling values for the Main Map\u00a0both to 5, so the texture isn&#8217;t stretched quite so much. Your Ground Material should look like this now&#8230;<\/p>\n<p><a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/GroundMaterialTextureSettings.png\" rel=\"attachment wp-att-130\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-134 size-full\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/GroundMaterialTextureSettings.png\" alt=\"\" width=\"278\" height=\"443\" srcset=\"https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/GroundMaterialTextureSettings.png 278w, https:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/GroundMaterialTextureSettings-188x300.png 188w\" sizes=\"(max-width: 278px) 100vw, 278px\" \/><\/a><\/p>\n<p>&#8230; and your ground plane should have a nice tiled texture on it.<\/p>\n<p>Now, save the scene and re-open the <em>Offline scene. <\/em><\/p>\n<p>If you build and run the game, and start a server and client, you&#8217;ll notice that there is some strange behaviour; Namely on one client the camera doesn&#8217;t move when you move the player, but if you move the other player, the camera moves in both clients. This is because both player objects in the game have their own camera and they are fighting for control. We can however,\u00a0fix this with a small change to the\u00a0NetworkPlayer script.<\/p>\n<p>So to fix the camera problem, open the player script in MonoDevelop and just below the playerID variable\u00a0add a private variable of type <em>Camera\u00a0<\/em>and call it playerCam and below that add\u00a0an Awake function like this :-<\/p>\n<p><strong> <span style=\"font-family: Calibri;\"><span style=\"color: #008000;\"> Camera\u00a0<\/span>playerCam;<\/span> <\/strong><\/p>\n<p><span style=\"font-family: Calibri;\"><strong><span style=\"color: #800080;\">void\u00a0<\/span>Awake()<\/strong><br \/>\n <strong>{<\/strong><br \/>\n <strong>\u00a0\u00a0\u00a0\u00a0playerCam\u00a0=\u00a0GetComponentInChildren&lt;<span style=\"color: #008000;\">Camera<\/span>&gt;();<\/strong><br \/>\n <strong>\u00a0\u00a0\u00a0\u00a0playerCam.gameObject.SetActive(<span style=\"color: #993300;\">false<\/span>);<\/strong><br \/>\n <strong>}<\/strong><\/span><\/p>\n<p>What this does, is as soon as any player object is created (local player or otherwise), it gets a reference to the attached camera and then disables it, which is ideal for player objects that belong to other players, but not so good for our own player, on our own player we want the camera to be enabled. To manage this we can make use of the OnStartLocalPlayer function again by the addition of the following line<\/p>\n<p><strong><span style=\"font-family: Calibri;\">playerCam.gameObject.SetActive(<span style=\"color: #993300;\">true<\/span>);<\/span> <\/strong><\/p>\n<p>which is just the opposite to what we did in the Awake function, and re-enables the camera, but because we are doing it in the OnStartLocalPlayer function, it only happens for the player object we are controlling. So the overall effect is that the only camera that is enabled is the one that is on the player object we are controlling. Also a side effect of disabling the Camera GameObject rather than just the Camera component, is that we don&#8217;t need to specifically disable the associated AudioListener as it is automatically disabled along with the camera.<\/p>\n<p>This is the entire NetworkPlayer script as it should now look.<br \/>\n<script src=\"\/\/pastebin.com\/embed_js\/dUTVV82i\"><\/script><br \/>\nIf you build and run the game now, you will see that the camera now works as it should, i.e. it only follows the local player as it moves. Now we have the camera working how we want, it&#8217;s time to sort out the playerID label as previously promised.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Part 3c &#8211; Add The Player ID label<\/strong><\/p>\n<p>What we&#8217;d like is a label that displays every player&#8217;s unique playerID and one way to achieve this is to use a TextMesh component.<\/p>\n<p>So first off, drag the player prefab into the hierarchy to create an instance of it. Create a new empty game object (<strong>GameObject-&gt;Create Empty<\/strong>), rename it <em>LabelHolder,\u00a0<\/em>drag it onto the player game object to parent it to the player game object and make sure its position is set to 0,0,0 and it&#8217;s rotation is set to 0,0,0.<\/p>\n<p>Then create another empty GameObject and rename it <em>Label,<\/em>\u00a0 then drag this onto the <em>LabelHolder\u00a0 <\/em>gameobject to parent it to the <em>LabelHolder<\/em> . Next ,select the <em>Label\u00a0<\/em>game object and add a TextMesh component\u00a0(<strong>Component-&gt;Mesh-&gt;Text Mesh<\/strong>).<\/p>\n<p>Your player should now look like this :-<\/p>\n<p><a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerHierarchy.png\" rel=\"attachment wp-att-141\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-141 alignnone\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/PlayerHierarchy.png\" alt=\"PlayerHierarchy\" width=\"232\" height=\"74\" \/><\/a><\/p>\n<p>Now we need to change some of the default properties of the TextMesh we just added, so\u00a0with the <em>Label<\/em> game object selected,<br \/>\nSet the Position to <strong><em>0,0,1<\/em><\/strong><br \/>\nSet the Rotation to <strong><em>90,0,0<\/em><\/strong><br \/>\nSet the Text Mesh Text property to <strong><em>&#8220;Player ID&#8221;<\/em><\/strong><br \/>\nSet the Character Size to <strong><em>0.2<\/em><\/strong><br \/>\nSet the Anchor to <strong><em>Middle center<\/em><\/strong><br \/>\nSet the Font Size to <strong><em>24<\/em><\/strong><\/p>\n<p>Then select the player game object and click apply to save the changes to the player prefab.<\/p>\n<p>Once you&#8217;ve done all that your player label should look like this in the inspector. (Note that when the prefab was saved the rotation.x\u00a0was subject to some floating point rounding error, so isn&#8217;t exactly 90 any more).<\/p>\n<p><a href=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/TextMesh.png\" rel=\"attachment wp-att-139\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-139\" src=\"http:\/\/www.doofah.com\/tutorials\/wp-content\/uploads\/2016\/02\/TextMesh.png\" alt=\"TextMesh\" width=\"279\" height=\"612\" \/><\/a><br \/>\nHaving saved the changes to the player prefab you can now go ahead and delete the player object in the hierarchy window.<\/p>\n<p>If you build and run the game, you should see that each player has a name tag above them with the default text &#8220;Player ID&#8221;. So all we need to do now is make it show the correct playerID and also tidy up the way the labels are rotated for other players that you can see.<\/p>\n<p>The easiest way to stop the player labels from rotating as the players move around, is to stop both the camera and the labels from rotating at all, and we can do that in the NetworkPlayer script.<\/p>\n<p>Open up the NetworkPlayer script for editing, and add the following under the playerCam variable, this gives us somewhere to store a reference to the label holder.<\/p>\n<p><strong><span style=\"font-family: Calibri;\"><span style=\"color: #008000;\">\u00a0\u00a0\u00a0 Transform\u00a0<\/span>labelHolder;<\/span> <\/strong><\/p>\n<p>Then add the following line to the <em>Awake<\/em> function, this gets a reference to the label holder that we can use later.<\/p>\n<p><strong><span style=\"font-family: Calibri;\">\u00a0\u00a0\u00a0 labelHolder\u00a0=\u00a0transform.Find(<span style=\"color: #33cccc;\">&#8220;LabelHolder&#8221;<\/span>);<\/span> <\/strong><\/p>\n<p>Finally add an Update function to the script as so<\/p>\n<p><span style=\"font-family: Calibri;\"> \u00a0\u00a0\u00a0<strong><span style=\"color: #800080;\"> void\u00a0<\/span>Update()<\/strong><br \/>\n<strong> \u00a0\u00a0\u00a0\u00a0{<\/strong><br \/>\n<strong> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if(isLocalPlayer)<\/strong><br \/>\n<strong> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{<\/strong><br \/>\n<strong> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0playerCam.transform.rotation\u00a0=\u00a0<span style=\"color: #008000;\">Quaternion<\/span>.Euler(<span style=\"color: #800080;\">new\u00a0<\/span><span style=\"color: #008000;\">Vector3<\/span>(90,0,0));<\/strong><br \/>\n<strong> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/strong><\/span><\/p>\n<p><strong> \u00a0\u00a0\u00a0 <span style=\"font-family: Calibri;\"> \u00a0\u00a0\u00a0 labelHolder.rotation\u00a0=\u00a0<span style=\"color: #008000;\">Quaternion<\/span>.identity;<br \/>\n}<\/span> <\/strong><strong><br \/>\n<\/strong><\/p>\n<p>The update function first does a check to see if it&#8217;s running on the local client, and if it is it resets the rotation of the camera, (we are only interested in our own camera).<\/p>\n<p>Then for <strong><em>all<\/em> <\/strong>player objects in the scene\u00a0it sets the label holder rotation to zero.<\/p>\n<p>The net effect of this is that regardless of how the players move and rotate, all the labels will stay in place above the player, which makes it much easier to read them.<\/p>\n<p>This is the new version of the NetworkPlayer script with all the above changes included.<br \/>\n<script src=\"\/\/pastebin.com\/embed_js\/AbDvz4Fs\"><\/script><br \/>\n <strong>Part 3d &#8211; Setting the name label text<\/strong><\/p>\n<p>Now we come to the final bit of this part of the tutorial, setting the label text to match the playerID.<\/p>\n<p>We already know that the playerID is set when the player joins the game and that the value is synchronized to all players, however we then need a way for each player object to update the state of its components (in this case the <em>TextMesh.text<\/em> property) to reflect the change in the playerID variable. We can achieve this in one of two ways, either by using RPCs (<strong>R<\/strong>emote <strong>P<\/strong>rocedure <strong>C<\/strong>alls) or by using a syncvar hook function, and for this task we&#8217;ll go with the hook function. I&#8217;ll cover RPCs later in the tutorial series.<\/p>\n<p>Basically a syncvar hook function is a function that is automatically invoked on all clients\u00a0whenever the associated variable is changed, and then we can make changes to the clients game object based on the value of the variable, in this case we will set the <em>TextMesh<\/em> to show the new name.<\/p>\n<p>To set up our playerID syncvar to use a hook we first need to create the function that it will call when the variable value changes, like so :-<\/p>\n<p><strong> <span style=\"font-family: Calibri;\"> \u00a0\u00a0\u00a0 <span style=\"color: #800080;\">void\u00a0<\/span>OnPlayerIDChanged(<span style=\"color: #800080;\">string\u00a0<\/span>newValue)<br \/>\n \u00a0\u00a0\u00a0\u00a0{<br \/>\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 playerID = newValue;<br \/>\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #800080;\">var\u00a0<\/span>textMesh\u00a0=\u00a0labelHolder.Find(<span style=\"color: #33cccc;\">&#8220;Label&#8221;<\/span>).GetComponent&lt;<span style=\"color: #008000;\">TextMesh<\/span>&gt;();<br \/>\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0textMesh.text\u00a0=\u00a0newValue;<br \/>\n \u00a0\u00a0\u00a0\u00a0}<\/span> <\/strong><\/p>\n<p>As you can see this is quite a simple function, its task is to locate the <em>TextMesh<\/em> component on the player object and then set its <em>text <\/em>to the new value passed into the function.<br \/>\n <em>Also note that\u00a0 the first statement assigns the new value to the playerID variable. if this step is omitted when using hook functions,\u00a0 then when a player joins a game his copy of remote clients won&#8217;t have their version of that syncvar updated, which could lead to problems later on.<\/em><\/p>\n<p>Now if you build the game and run a host in the editor you will see that your player&#8217;s name label is set to &#8216;Player 1&#8217;, and then if you run another instance of the game and join as a client, in the editor the 2nd player has the name &#8216;Player 2&#8217;. So that&#8217;s all as expected, the SyncVar has sent the new value of the second player&#8217;s playerID to the first player and both player objects have updated their labels.<\/p>\n<p>But wait! If you check the player objects on the client instance, the player you control has the label &#8216;Player 2&#8217;, but the other one still has the default &#8216;Player ID&#8217; text, so the hook function hasn&#8217;t been called for any player objects that were already in the game when you joined.<\/p>\n<p>On the face of it, this looks like a bug, however Unity have confirmed that it is intended behaviour as explained by seanr on the Unity forums, much more concisely than I could<\/p>\n<blockquote><p>\n<em>SyncVar Hooks are for changes in state, not for initial state. There is the OnStartClient callback for handling initial state.<\/em><\/p>\n<p><em> There is no context during a SyncVar hook, so if they were called for initial state, the client would not be able to tell the difference between initialization of a variable and a change in the value of a variable. This causes un-expected results.<\/em><\/p>\n<p><em> For example, for a &#8220;[SyncVar] int health&#8221; with a SyncVar hook that causes a client-side &#8220;blood-spurt&#8221; effect when the object takes damage, the object would play the blood-spurt for any objects with non-maximum health when joining a game in progress. The hook would be called with the new health value, which is different from the default health value, so the object thinks it has taken damage &#8211; so it plays its blood-spurt. But this is not what should happen. Setting the initial health of the object to its current value from the server should NOT play the effect. <\/em><\/p>\n<p><em> This applies to all kinds of state changes, such as animation state, particles, etc. Initialization is different from incremental changes and the client-side code needs to know which is happening.<\/em>\n<\/p><\/blockquote>\n<p>So with that in mind we need to add one more simple\u00a0function to the NetworkPlayer script to override OnStartClient as follows :-<\/p>\n<p><strong> <span style=\"font-family: Calibri;\"> \u00a0\u00a0\u00a0 public\u00a0override\u00a0<span style=\"color: #800080;\">void\u00a0<\/span>OnStartClient\u00a0()<br \/>\n \u00a0\u00a0\u00a0\u00a0{<br \/>\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OnPlayerIDChanged(playerID);<br \/>\n \u00a0\u00a0\u00a0\u00a0}<\/span> <\/strong><\/p>\n<p>And that&#8217;s it, all this does is pass current value of playerID to the OnPlayerIDChanged function, so that it can update the text on the TextMesh. The values of SyncVars on objects are guaranteed to be initialized correctly with the latest state from the server when this function is called on the client.<\/p>\n<p>This is the full version of the NetworkPlayer script as it stands<br \/>\n<script src=\"\/\/pastebin.com\/embed_js\/6wTuneL5\"><\/script><br \/>\nIf you save this and then build and run the game you should see that the labels are displayed correctly for all players on all clients and they stay horizontal above the player, no matter how it moves and rotates.<\/p>\n<p>&nbsp;<\/p>\n<p>That&#8217;s it for part 3 of the tutorial\u00a0 &#8211; in the next part we will cover setting up team colors for the players and shooting.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Welcome to part 3 of my Unity 5 networking tutorial. In this part we will give the players their own name and name tag, and we&#8217;ll make the camera follow the player that you are controlling. Part 3a &#8211; Synchronizing the PlayerID To keep things simple we are going to automatically generate a unique name [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":14,"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\/116"}],"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=116"}],"version-history":[{"count":46,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/posts\/116\/revisions"}],"predecessor-version":[{"id":288,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/posts\/116\/revisions\/288"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/media\/14"}],"wp:attachment":[{"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/media?parent=116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/categories?post=116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doofah.com\/tutorials\/wp-json\/wp\/v2\/tags?post=116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}