The plan for the this small tutorial is to do the following:
- Create a simple scene where the player is initially represented by a tank (made of simple boxes) and is controlled using the arrow keys.
- Add networking capabilities so that this scene can be played by (and shared amongst) several players over the same network.
This will be done step-by-step and all the networking aspects will be covered in details, so that you get a solid grasp of implementing a simple networked game; once you understand how to create such a game, you will be able to transfer these skills to a game of your choice.
To create our networked scene, we will basically start from a very simple single-player scene, and then progressively add networking features. Throughout this chapter we will be using several networked components that will add network capabilities to our scene or game. These include the NetworkManager, the NetworkManagerHUD, or the NetworkTransform compomnents.
Creating a simple scene
To start with our game, we will create a simple environment with a ground symbolized by a scaled box and a tank made of scaled boxes. We will also add movement to the tank.
Please do the following:
- Create a new project in Unity (File| New Project).
- Create and save a new scene, using a name of your choice.
- Create a new box (GameObject | 3D Object | Cube).
- Set its position to (0, -1, 0) and rescale it to (100, 1, 100) so that it represents the ground.
- You can also add a texture or a color of your choice to the ground. You can, for example, use the texture called tile available in the resource pack and modify its tiling properties (e.g., 10×10).
- Rename this box ground.
Once the texture has been applied to the object; the corresponding material is stored with the same name in a folder called Materials. The tiling properties of the textures can then be modified by changing the tiling properties of this material using the Inspector window.
Adding the ground to the scene
A new Material is created after applying the tile texture
Tiling attributes of the new material.
- Add a directional light to the scene (GameObject | Light | Directional Light)
- Set its position to (0, 5, 0) and its rotation to (90, 0, 0) so that it is facing the ground.
- Create another box that will be used to initially represent the player.
- Rename this box player.
- Set its position to (0, 1, 0)
Adding the player
Once this box has been created and renamed as player, we will create a prefab from it. This is this because, in the next sections, this prefab will be used to automatically instantiate a new player object whenever a new client connects to the game server.
So let’s create this prefab:
- Please drag and drop the object called player from the Hierarchy window to the Project window. This will create a prefab called player.
- Once we have created the player prefab, we will modify it slightly so that it looks like a tank.
At this stage you should have a prefab called player in the Project view, as well as a player object in the Hierarchy view; for now, we will modify the player object and then apply the changes to the prefab; so let’s get to it:
- You can start by creating a new blue material to apply to this box (i.e., the player object): from the Project view, select Create | Material, set this material to blue, rename this new material blue, and apply it to the box that represents the player.
Coloring the tank
Once this is done, it is time to add wheels to the tank.
- Please create a new box, rename it wheel, and add it as a child of the player object. Once this is done, apply the blue material to it, change the position of the object called wheel to (0, -0.5, 0) and its scale to (1.5, 0.5, 2).
Adding wheels to the tank
This almost looks like a tank; we just need to add a cannon to it:
- Please create a new cylinder and rename it canon; add it as a child of the player object, apply the blue material to it, and change its position to (0,0,1) and its scale to (0.3, 0.7, 0.3).
Adding the cannon
Eh voila…!
Now that we have created our mini-tank, we can update the corresponding prefab by selecting the player object in the Hierarchy, and, using the Inspector, by pressing the Apply button, so that the corresponding prefab includes these recent changes.
Applying the changes to the tank prefab
Last but not least, we will also modify the position of the main camera so that it can display the tank.
- If your scene does not already include a camera, please create a new camera (GameObject | Camera) and rename it Main Camera.
- Please select the object labelled Main Camera, and modify its position to (0, 50, 0) and its rotation to (90, 0, 0). These changes are to ensure that the camera is high enough to capture most of the scene, and rotated in a way that it is facing down towards the ground. So your game view should now look like the following figure.
The tank viewed from above
Adding movement to the player
So at this stage, we have a very simple environment that consists of a tank that represents the player. As for any tank game, we will need to be able to move the tank around the environment. So what we will do in this section is to add movement to the tank when the player presses the arrow keys on the keyboard.
- Please create a new C# script called MoveTank (From the Project window, select Create | C# Script).
- Add the next code inside the Update function
using UnityEngine; public class MoveTank : MonoBehaviour { void Update(){ var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f; var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f; transform.Rotate(0, x, 0); transform.Translate(0, 0, z); } }
In the previous code:
- We check whether the arrows on the keyboard are pressed.
- We create two variables x and z; x will track the left and right arrows, while z will be used to track the up and down arrows.
- The up and down arrows will be used to move forward and back, while the left and right arrows will be used to rotate the tank around its y-axis.
- We use the built-in function Input.GetAxis (“Horizontal”) and Input.GetAxis (“Vertical”) that will track the left and right arrow keys (i.e., horizontal axis) and the up and down keys (i.e., vertical axis).
- Whenever these keys are pressed, the variables x and z will be updated accordingly and the tank will be either rotated (i.e., based on x) or translated (i.e., based on z).
We can now use the script:
- Please check that the code is error free.
- Drag and drop this script on the player object.
Update the corresponding prefab by clicking the button labeled Apply in the Inspector window for the object player.
Updating the tank prefab
You can now test the movement of the player by doing the following:
- Play the scene.
- Use the arrow keys to rotate and move the tank.
- Check that the tank moves as expected.
Transforming the scene from single to multiplayer-networked mode
So at this stage, we have a very simple movement for our tank with four keys; and what we’d like to do now is to network this scene and to make sure that, for each client connected to the server, we create a corresponding player object (i.e., a tank).
First, we will create a network manager, an object that will manage the networking aspect of the scene.
Please do the following:
- Create an empty object (GameObject | Create Empty), and rename this object networkManager or a name of your choice.
- After selecting this object (i.e., the object networkManager), please add two network components to it: a NetworkManager component (Component | Network | NetworkManager) and a NetworkManagerHUD component (Component | Network | NetworkManagerHUD).
- For the last component (NetworkManagerHUD), please make sure that the option Show RunTime GUI is set to true (i.e. ticked).
Setting-up the Network Manager HUD
The idea behind this networkManager object is to be able to manage the networking side of the scene and to also be able to display a useful interface that will make it possible for the player to select whether the game should be running as a server or as a client.
Amongst other things, the NetworkManager component makes it possible to start and stop the clients and the servers. The NetworkManager HUD component provides a visual representation of the connections to your server as well as buttons that you can use to start or stop a client or a server.
Next, we need to make sure that the player prefab will be accounted for whenever our game is networked, so we will add a Network Identity component to this object.
For now, we will work with the prefab only (after making sure that the corresponding prefab has been updated):
- Please delete the player object from the scene, after having updated the corresponding prefab.
- Select the player prefab in the Project view.
- From the top menu, please select: Component | Network | Network Identity.
- Using the Inspector, set the option Local Player Authority to true for this component.
Setting-up the Network Identity
A Network Identity component, when attached to an object, ensures that the network manager is aware of this object.
Next, to make sure that a new player prefab is spawned every time a new client connects to the server, we will register the player prefab with the networking system as follows:
- Please select the object networkManager.
- Drag and drop the prefab player from the Project view to the section NetworkManager | Spawn Info | Player Prefab.
- Set the option NetworkManager | Spawn Info | Auto Create Player to true (i.e., ticked); this will ensure that the player prefab is spawn automatically when a client connects to the server.
Setting-up the object that should be spawned by the network manager
One of the lasts things we need to change now is the script that controls the movement of the player; as it is, if two players, let’s say player1 and player2 connect to our server, the prefab for player1 will be spawned in the scene opened for player1 but also in the scene opened for player2; so by pressing the arrow keys, each player may be able to control the other tank. So we need to make sure that each player can only modify (move) their corresponding tank and not the other one. So we need to determine if a script attached to a prefab is run from the corresponding player. This is often called the localPlayer, or the player that is linked to the prefab that has been instantiated (or spawned).
The idea of a localPlayer is a concept linked to networked games. If you remember the script that we have created to move the player (MoveTank), you may remember that it extends the class MonoBehaviour. This was perfectly fine for a single-player game; however, we may now need to base our script on a class called NetworkBehaviour instead. Whenever you create a script that is attached to an object that will be networked, it is usually good practice to make sure that it extends this class (i.e., NetworkBehaviour), as it will give you access to useful member variables (which are inherited from this class), including a variable called isLocalPlayer that will help us to determine if a script attached to a prefab is run from the corresponding player.
So let’s make this change:
- Please open the script MoveTank.
- Add the following line of code at the beginning of the script.
using UnityEngine.Networking
- In the previous code, we import namespaces related to the Networking library.
- Please change the following line…
public class MoveTank : MonoBehaviour
..to
public class MoveTank : NetworkBehaviour;
- By adding the previous line, we make sure that our class inherits from the class NetworkingBehaviour, and hence, that it avails of useful member methods and variable, including isLocalPlayer, that we will be using in the next code.
- Please change the code in the Update method as follows (new code in bold).
if (!isLocalPlayer){return;} var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f; var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
- In the previous code, if the script is not run by the localPlayer, then we exit the function and no movement is applied. In other words, we ensure that we are modifying our (local) player and our player only.
- Please save your script and ensure that it is error-free.
The last thing we need to do before we can test our networked scene is to ensure that the position of each player is synchronized across the network; as it is, although a new player prefab will be instantiated every time a new player joins the game, and although each player will be able to move their corresponding character, there is nothing in place yet to ensure that if player2 moves, that the view available from player2 accounts from this movement; in other words, we need to make sure that their respective views, positions, and versions of the game are synchronized. So we just need to add a capability (component) that will make sure that each of the players’ positions are sent to the server and then sent (by the server) as an update to all the other clients.
- Please select the player prefab.
- From the top menu, select: Component | Network | NetworkTransform.
- This will add a Network Transform component to the player prefab.
Adding a Network Transform component
We can now test this scene, and see how the network components that we have added can be used to manage the networked game.
Please do the following:
- Play the scene.
- You should see the Network interface in the top left corner, as described on the next figure.
Selecting how the scene should be played
As you will see, it includes several buttons: LAN Host, LAN Client, and LAN Server Only. As we have seen before, you can, in this case, decide to play the scene as a host (in this case you would run the server and also include a new client), connect a client to an existing server, or add a server without a client (a host without its client).
You will also notice that by default, a local client will be connected to the server localhost; this is because by default the game server, when it is created, will have the default address localhost or its equivalent IP address 127.0.0.1.
- For now, we will just create a new host so that we can see the scene; so please click on LAN Host, this will open the scene.
- As you can see on the previous figure, the NetworkManager HUD component now displays that the host includes a server that listens to the port 7777 by default, as well as a client connected to the server (localhost) through the port 7777. This is because a LAN Host setup includes both a server and a client.
- As the scene is played you should see (e.g., in the Hierarchy) that a clone (i.e., an instance) of the player prefab has been created.
A new clone is created
- You can check, that you can move the player using the arrow keys on your keyboard.
Playing the scene as a LAN Host
- You can now click on the button labeled Stop X in the Game view and also stop the game.
Ensuring that the player object will be networked
So at this stage we can see that our character is spawn as we connect to the server (since the host includes both a server and a client). We could now try to test a connection from a remote client and observe how the two clients (both the local client and the remote client) see each other.
Please do the following:
- Stop the scene.
- Open the Build settings (File | Build Settings).
- Add the current scene to the build by clicking on the button Add Open Scenes.
Adding the current scene to the build settings
- Select the Platform PC, Mac & Linux Standalone (you can also export to WebGL if you wish).
Exporting our build (platform)
If you wish not to have a game displayed in full screen, you can click on the Player Settings button and untick the option “Defaults is Full Screen”
Specifying full-screen features
- Once this is done, please click on the button labeled Build and Run.