Unity Multiplayer Introduction With Photon
The goal of this blog post is to introduce Unity multiplayer to created networked experiences using Photon.
The goal of this blog post is to introduce Unity multiplayer to created networked experiences using Photon.
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!).
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.
We’ll need a couple things before we start coding:
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.
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.
We don’t need to worry about much else in there so let’s continue on to our connection code!
Create a C# script named ‘NetworkConnector.cs’ and input the following:
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
public class NetworkConnector : MonoBehaviourPunCallbacks
{
void Start()
{
PhotonNetwork.ConnectUsingSettings();
}
#region Pun Callbacks
public override void OnConnectedToMaster()
{
Debug.Log("Connected to photon!");
}
public override void OnDisconnected(DisconnectCause cause)
{
Debug.LogWarning($"Failed to connect: {cause}");
}
#endregion
}
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.
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:
public class NetworkConnector : MonoBehaviourPunCallbacks
{
// ...
public override void OnConnectedToMaster()
{
// Try to join a random room
PhotonNetwork.JoinRandomRoom();
}
// ...
public override void OnJoinRandomFailed(short returnCode, string message)
{
// Failed to connect to random, probably because none exist
Debug.Log(message);
// Create a new room
PhotonNetwork.CreateRoom("My First Room");
}
public override void OnJoinedRoom()
{
Debug.Log($"{PhotonNetwork.CurrentRoom.Name} joined!");
}
#endregion
}
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!
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:
After you’ve completed this step, let’s create a new ‘BasicMovement.cs’ script:
using UnityEngine;
using Photon.Pun;
public class BasicMovement : MonoBehaviour
{
public float speed;
Rigidbody rb;
PhotonView photonView;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
photonView = GetComponent<PhotonView>();
}
void FixedUpdate()
{
// Only move the player object if it's the local users player
if (photonView.IsMine)
{
Move();
}
}
void Move()
{
float horizontal = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
float vertical = Input.GetAxis("Vertical") * speed * Time.deltaTime;
Vector3 newVector = new Vector3(horizontal, 0, vertical);
rb.position += newVector;
}
}
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.
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.’
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:
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
public class NetworkConnector : MonoBehaviourPunCallbacks
{
// Add a reference to our player prefab
public GameObject playerPrefab;
// ...
#region Pun Callbacks
// ...
public override void OnJoinedRoom()
{
Debug.Log($"{PhotonNetwork.CurrentRoom.Name} joined!");
// Create our player object when we've joined the room
PhotonNetwork.Instantiate(playerPrefab.name, new Vector3(0, 3.0f, 0), Quaternion.identity);
}
#endregion
}
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.
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:
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!
// NetworkConnector.cs
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
public class NetworkConnector : MonoBehaviourPunCallbacks
{
public GameObject playerPrefab;
void Start()
{
PhotonNetwork.ConnectUsingSettings();
}
#region Pun Callbacks
public override void OnConnectedToMaster()
{
// Try to join a random room
PhotonNetwork.JoinRandomRoom();
}
public override void OnDisconnected(DisconnectCause cause)
{
Debug.LogWarning($"Failed to connect: {cause}");
}
public override void OnJoinRandomFailed(short returnCode, string message)
{
// Failed to connect to random
Debug.Log(message);
// Create room
PhotonNetwork.CreateRoom("My First Room");
}
public override void OnJoinedRoom()
{
Debug.Log($"{PhotonNetwork.CurrentRoom.Name} joined!");
PhotonNetwork.Instantiate(playerPrefab.name, new Vector3(0, 3.0f, 0), Quaternion.identity);
}
#endregion
}
// BasicMovement.cs
using UnityEngine;
using Photon.Pun;
public class BasicMovement : MonoBehaviour
{
public float speed;
Rigidbody rb;
PhotonView photonView;
void Start()
{
rb = GetComponent<Rigidbody>();
photonView = GetComponent<PhotonView>();
}
// Update is called once per frame
void FixedUpdate()
{
if (photonView.IsMine)
{
Move();
}
}
void Move()
{
float horizontal = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
float vertical = Input.GetAxis("Vertical") * speed * Time.deltaTime;
Vector3 newVector = new Vector3(horizontal, 0, vertical);
rb.position += newVector;
}
}
At SLIDEFACTORY, we’re dedicated to turning ideas into impactful realities. With our team’s expertise, we can guide you through every step of the process, ensuring your project exceeds expectations. Reach out to us today and let’s explore how we can bring your vision to life!
Looking for a development partner to help you make something incredible?