Photonnetwork



Overview

  1. Photon Networking Tutorial
  2. Photon For Unity

The goal of this blog post is to introduce Unity multiplayer to created networked experiences using Photon. To begin, we will download and set up the packages we will need. Then, we will set up some basic scripts to get a feel for how we can use Photon’s Unity integration (PUN). Finally, we will build our simple game and test it locally (or with friends!).

Why Photon for Unity Multiplayer?

App ID may be hard coded, but, in this example,. We are accessing it using convenient static field on PhotonNetwork class. We pass in AuthenticateWithPhoton as a callback to be our next step, if. we have acquired token successfully./ private void RequestPhotonToken(LoginResult obj) LogMessage('PlayFab authenticated. PhotonNetwork.CreateRoom(roomName, true, true,maxPlayer); // then create a photon room visible, and open with the maxplayers provide by user.

Photon is a company that created a Unity asset (PUN) that allows us to quickly create multiplayer games or apps. Photon Cloud also provides servers that we can connect to for testing our apps. We can use Photon on our own servers, but we will save time setting up by using Photon’s Free plan. This gives us 20 concurrent users (CCU) for free. All we need to do is set up an account.

Before we start

We’ll need a couple things before we start coding:

Networking
  • Unity (This tutorial was created with 2019.3)
  • PUN 2 – FREE asset from the Unity Asset Store
  • A Photon free account with a registered AppId (we’ll set this up now)

Free Photon Cloud Account setup

In order to get a Photon App Id, we’ll need a photon account. Go to the photon registration page, or search for Photon Unity and find your way to the registration form. Once you’ve confirmed your account and logged in, you’ll be taken to a dashboard with a Create A New App button. Click the button and on the next page, choose ‘Photon PUN’ as the Photon Type, and name your app something like ‘PhotonTutorial.’ Click Create and you should be taken back to your applications list. You should see something like this:

Click on the App ID section like highlighted in yellow in the image above and copy the full string. We’ll use it later in Unity.

Creating our Unity Multiplayer Project

Now that we have our Photon App ID, we can create a new Unity project and import the PUN 2 – FREE asset. Open up the asset store in Unity and search for ‘PUN’ and the package should pop up. Once you’ve downloaded and imported the project is should ask for your app id. Enter it and you should be good to go. If you accidentally close this window, you can always find your PhotonServerSettings.asset by going to ‘Window -> Photon Unity Networking -> Highlight Server Settings.’

In fact, we’re going to go there now and take a look. If you open the server settings, you’ll notice a couple of options.

  • AppId – Required for Photon Cloud. This is how Photon connects our users to the same server. If you didn’t paste in your AppId earlier, do it now.
  • AppChatId – Optional. If we supported chat we could register a new app for Photon Chat
  • AppVersion – This is important. You should set it to 1 until you need to make breaking changes to a live game. Users on different app versions won’t connect to each other’s rooms.
  • Use Name Servers – Required for Photon Cloud. We’d uncheck this if we were hosting our own multiplayer servers.
  • PUN Logging – Optional. You can set this to Full if you’d like to see everything PUN is logging. Might help solve bugs.

We don’t need to worry about much else in there so let’s continue on to our connection code!

Connecting to Photon Servers

Create a C# script named ‘NetworkConnector.cs’ and input the following:

The script is currently pretty simple. When it starts it will try to connect to Photon using the settings we’ve configured in our Server Settings file above. Below that, you’ll notice the Pun Callbacks region. You may also notice that our class extends MonoBehaviorPunCallbacks. The two sections are related. The MonoBehaviorPunCallbacks class holds all of the methods we can override to react to events in Photon. For example, here we’d like to log a message when we’ve connected to the Photon Master server. Otherwise, we’ll log a warning on disconnect (if we have no internet, for example).

Go back into Unity and create an Empty GameObject. Name it “NetworkConnector” then add our NetworkConnector.cs script onto it. Now, run the scene with or without internet and you should see the appropriate message. If you see a different message, or no message, make sure you entered your AppId or turn your Photon Server Settings PUN Logging option to Full for possible reasons.

Photon Unity Multiplayer Rooms

The code we’ve set up here only connects to the master Photon server. We still need to handle actually adding our users into a room so that they can play together. In Part II, we’ll set up a lobby to search for and create games, but for now we’ll join a random room. Let’s update the script:

Now, when you run your game, you should see a message saying ‘No match found’ (meaning there was no random room to join) followed by ‘My First Room joined!’ This means that we’re actually in a game room that can share data with other players. So, let’s make something to share!

Creating a Player Prefab with Networking

When creating multiple games, we need to think about networked objects a little differently. We can’t use Unity’s Instantiate method anymore because this will create the player object only for the local player, instead of for all players in the room. Photon solves this by letting us use its PhotonNetwork.Instantiate method. But this only creates the object in the room for all players to see. If we want to update its values (say position) and share those to other users, we need to add a PhotonView component onto our object.

Let’s create a simple capsule player with some basic movement to illustrate this. Go back to Unity and create our Player Prefab:

  • Click GameObject -> 3D Objects -> Capsule to create and name it ‘Player’.
  • Reset its position to 0, 0, 0.
  • Add a Rigibody component, under Constraints -> Freeze Rotation, check X, Y, and Z.
  • Add a PhotonView component. In the Observed Components section, drag the Player GameObject we just created. This should create a Photon Transform View which will automatically sync our object’s transform to other players in the room.
  • Uncheck ‘Rotation’ under the Photon Transform View

After you’ve completed this step, let’s create a new ‘BasicMovement.cs’ script:

The script should look pretty straight forward, based on the WASD or keyboard arrow keys we’ll move our player forward, back, left, and right. The exception is the photonView section. We’ve already added the ‘PhotonView’ component to our player object. Here we get a reference to it and use it to make sure we only move the player object that belongs to the local user (photonView.IsMine). Each player will need to see the other player’s object, so we’ll create a player object for each player when they join the room. However, we don’t want other users controlling our local character.

Saving the Prefab

Finally, we need to add the ‘BasicMovement.cs’ script to our player object and set the speed to 10. Then create a ‘Resources’ folder and drag the player object into it to create a prefab. This is a very important step. Because of the way Photon searches for GameObjects to instantiate, they must be inside of a ‘Resources’ folder. The folder could be nested within a ‘Prefab’ folder, but it must be named ‘Resources.’

Setting up the room for Unity Multiplayer

Let’s add a blank cube, reset it’s position to 0, 0, 0 and then make it’s scale 100, 1, 100. This will be our ground. Now, adjust the Main Camera GameObject’s position to 0, 2, -10. We’ll create our player prefabs when the user joins the room, so delete any you still have in the scene. Feel free to first test that the movement script is working.

Let’s go back into our NetworkConnector.cs script and add the follow to create our player objects when a new player joins the room:

Once this is complete, drag your player prefab that you added to a Resources folder into the NetworkConnector component and test! You should see your connection messages logged in the console and afterwards your player prefab created in the room. Using WASD or the arrow keys you should be able to move your player character.

Building the Game to Test Unity Multiplayer

You can test the multiplayer functionality by building your game locally to your computer and running it twice, once inside the Unity Editor and the other from your standalone build. To do this:

  • Go to File -> Build Settings
  • Click Add Open Scenes
  • Select the Platform PC, Mac & Linux Standalone. Choose your target platform (the Operating System you are running Unity on)
  • Check Development Build
  • Click Build And Run. We usually save our builds into a Build folder next to Assets in our unity project, but up to you!
  • Once the game loads and you see your player, tab back to the Unity Editor and enter Play Mode. You should see two players that each update on the other’s screen in real time!

Conclusion

We’ve seen how we can quickly add multiplayer functionality and test using Photon while using very little code. We’ve set up our own Photon PUN App, connected it to Unity, created a basic network connector to handle room joining, and created a basic player prefab. In Part II, we’ll look at building out a lobby for our users so that they can choose which rooms they’d like to join. See you then!

Scripts

Hello again and welcome to part 6 of my PUN networking tutorial. In this part, we will add score and health display, name labels and destructible tanks.

I’ve made a number of changes to the scripts from the previous part, so before you continue you should download the complete project files for this part which can be found here.

You can download the project .exe here.


Part 6a – Nickname and name label

It would be nice if we could enter a nickname that will be shown above the tank so that we can identify our intended target, so I’ve added a text input field to the start menu where you can type your chosen nickname and a public static property in the MainMenu script which affords us easy access to the name entered.

In the Awake() function, as a convenience, we prefill the nickname input field with the previously saved nickname if we’ve logged in before (the nickname is saved to playerprefs in the JoinGame() function of the GameManager).

To enable other players to see your nickname it will need to be synchronised with all players over the network. Handily PUN comes with a built-in way to do this in the form of The PhotonNetwork.playerName property. We just need to pass the value from the NickName input field to this before we join a room and this will set the NickName property of your PhotonPlayer once created to the same value for retrieval during the game when required. This is automatically synchronized between all clients.

A convenient place to set the PhotonNetwork.playerName property is in the JoinGame() function of the GameManager script as so:-

We want to display the name of other players in the game above their tanks, so to do this I’ve added a canvas and UIText object to the player prefab to act as a name label.
A handy time to set the name label text would be in OnPhotonInstantiate, but as the name label is a child of the player prefab it won’t receive that callback. To solve this, I use BroadcastMessage in the OnPhotonInstatiate method of the Player script to call a method on all ll scripts on the player, including children. In this case, I named the method OnInstantiate. This can be quite a handy way to initialise child GameObjects as soon as the player object is created. BroadcastMessage should be used with a certain amount of care, as it isn’t a particularly fast function, and should be avoided in things like update loops. As it’s only being called once here when the player is first created it won’t affect performance.

I also took the opportunity to rename the Player GameObject to reflect the chosen nickname, as this makes it easier identifying specific clients player objects in the hierarchy.

Photon Networking Tutorial

So the Player script OnPhotonInstantiate method now looks like this:-

I also added a simple script to the name label GameObject:-

As you can see, this script implements a single method, OnInstantiate, wherein it checks to see if it is running on the local player (PhotonView.isMine) and if it is, it disables the name label GameObject because we don’t want to display the name above our own tank (we will display our own nickname at the top of the screen, as described a bit later on), otherwise, it sets the name label text to the PhotonNetwork.playerName value.

This method is invoked by the Player script when the player object is instantiated as described above.

I’ve also added a script to the name label that keeps it orientated towards the screen regardless of the rotation of the tank:-

That’s all that’s required to display the player’s nickname above other player’s tanks.

Photon For Unity

To display the nickname for the localplayer I have created a gameUI game object as a child of the HUD gameobject, which also displays the health and remaining hit points. This is the script that is attached to the gameUI gameobject:-

the gameUI script uses the SceneLoaded event to enable or disable itself depending on whether we are in a room or not. It also uses the OnJoinedRoom callback to set the NickName text to the value of the PhotonNetwork.playerName, which as described above is the nick name chosen on the main menu.

It has a couple of other public static methods which handle setting the HP text and score text.

Part 6b – Handling player damage
In the previous part of the tutorial, we didn’t synchronize the player health value over the network, as we only showed its value to the local player. However, I want to show a health bar above all players tanks and for this to be possible the player’s health value needs to be synchronised on all clients.

We’ll achieve this by making the HP value a PhotonPlayer.CustomProperty. Custom Properties are a key-value set (Hashtable) which is available to all players in a room. They can relate to the room or individual players and are useful when only the current value of something is of interest. (More info regarding CustomProperties is available from the Photon Unity Networking class reference here.)

Custom properties values are automatically updated on all clients when their value changes and an event is also raised; You can listen for this event and use it to update visual elements to reflect the new value; in this case, we’ll update the health bar on all network instances of the player’s tank and the HUD display for the local player.

To facilitate this, we’ll change the PlayerHealth.hitPoints field into a property and implement the Getter and Setter to manipulate the HP Custom Property as so:-

The getter of the property checks to see if the key exists in the hashtable of custom properties associated with the client, and if it does it returns the associated value. If it doesn’t exist, that means this property value hasn’t been set yet, so it just returns the default value.
The setter of the property updates the key/pair value in the hashtable. For the local player, this happens instantly, for other clients the new value will be sent via the Photon server so there will be a small delay.

As I mentioned earlier whenever a custom property value changes it raises an event which we can handle by overriding the OnPhotonPlayerPropertiesChanged() method which in this case we do like so:-

The OnPhotonPlayerPropertiesChanged() method is passed an array of data which contains the PhotonPlayer of the client that updated its property (in element 0 of the array) and a hash table of the updated properties and their values (in element 1 of the array), so we can use this information to take the appropriate action.

The first thing we do is retrieve the PhotonPlayer.id of the client that updated its property and compare that with the id of the PhotonPlayer of the photonView that owns this script, and only continue if they match, this is to make sure we only continue on scripts that belong to the player that has had an updated property. Then we check that this is a message relating to a change in the hitPoints by checking if the hash table contains the key “HP”. Assuming the two previous checks are true we call a method (DisplayHealth) to handle the display of the updated hitPoints, which is implemented as follows:-

The first thing displayHealth does is update the size of the health bar above the tank by calling the healthBar.SetHealthBarValue() method ; this will happen on all clients.
Secondly we want to display the HP figure in the HUD for the localplayer only, so we wrap the call to GameUI.SetHealth() in a photonView.isMine check.

I’ve also updated the playerHealth script to check if our hitPoints have reduced to zero, so we can take the appropriate action, which in this case is to blow the tank up and add a point to the attacking player’s score. To do this we send an RPC to the missile owner that will add 1 point to their score and we start a coroutine that will handle the visual display of the tank exploding and respawning.

This is the updated PlayerHealth.DoDamage method :-

and this is the tank explosion coroutine :-

The explosion coroutine invokes the following RPC on all clients, which means that everyone sees the tank explode. It also temporarily disables movement, shooting and collisions…

it then waits 7 seconds and invokes the RPC_Respawn method, passing the respawn position and rotation as arguments. The position vector is converted to an array of 2 Shorts to reduce bandwidth.

The respawn method re-enables movement and shooting for the local player, and then for all clients it re-enables collisions and the tank model, and places it in the respawn position and orientation. It also resets the hitPoints to maximum.

Dashboard

The last line is a bit of a workaround to handle the fact that positioning of remote client tanks doesn’t happen instantly, so to avoid the chance of seeing them reappear in the previous position for a few frames before being moved to the new spawnpoint we wait for a short while before reenabling the tank model for remote clients.

Part 6c – Displaying the score
The final thing we need to do is make a small change to the PlayerScore script to make it display the score on the HUD of the local player which we do by calling the GameUI.SetScore() method as so:-

That’s everything for this part. As always comments and suggestions are very welcome, or if any parts require further clarification post a comment.

In the next part we’ll be adding ammo pickups, hope to see you then.