This is a sample multiplayer project with support for PUN (Photon Unity Networking) v1 and v2.
This is a crash course/refresher, not a tutorial.
- You already know how to program games using Unity 3D;
- You need a quick walkthrough to understand Photon networking.
- You'd like to migrate from PUN1 to PUN2 (check the migration notes and this diff?)
PUN provides peer-to-peer multiplayer as a service. This means that there is no 'master server'; therefore instead of a unique source of truth for what is happening in the game, every client manages networked game objects (NGOs).
Each NGO synchronizes state (position, rotation, animation) and will message session-wise clones via RPC (remote procedure call)
PUN provides its own 'Instantiate' and 'Destroy' methods thereby a client creates/destroys NGOs, such as an avatar representing the player, or a projectile.
Install either PUN2 or PUN classic. At the time of writing PUN2 (rel. August 2018) looks ready for production use.
Create/login here; make a new app in the dashboard:
- Type: Photon Realtime
- Name: same as your unity project (keep it neat).
- URL: don't care
Copy your app id from the app's 'manage' section to your project's
PhotonServerSettings
. Can't see PhotonServerSettings
? Reload your project
and it should regenerate automatically.
TIP: add PhotonServerSettings
to .gitignore
: your app id is a secret which
shouldn't be shared.
This utility class exposes the flow of connecting to the Photon server and creating/joining a room; added to any project it will create or join an existing room. No UI makes it perfect for iterating your multiplayer prototype.
- Create an
Avatar
game object; for this the sample uses a simple Blender model. - Add
PhotonView
to the avatar; this enables message passing and state synchronization. - Add
PhotonTransformView
orPhotonTransformViewClassic
; these are handy components which do the work of syncing position, rotation and scale. Do check the boxes to sync position and rotation. - Make
Avatar
into a prefab and place it in aResources
folder; then deactivateAvatar
in the scene (you could remove it but keeping clone in the scene helps modify and inspect the prefab).
To instantiate the player avatar across the network, after joining a room, call:
PhotonNetwork.Instantiate(avatar, Vector3.zero, Quaternion.identity, 0);
Note: the Vector
and Quaternion
are used to init the position and
rotation of the avatar.
You can adapt single player controllers for multiplayer but since all scripts
are running on all clients, how do we know to control 'our' avatar vs everybody
else's? PhotonView
provides the IsMine
property, often demonstrated like
this:
if(photonView.isMine){
// ... check input and move avatar
}
This pattern has disadvantages:
- Needs to be repeated in many places, which makes it error prone.
- Conflates multiplayer logic with control logic.
Instead, you might leverage a utility component like Disown
to
remove the controller (and other functionality specific to the local avatar)
from un-owned NGOs.
In this sample Shooter implements firing at other avatars via
ray-cast. On success, instead of calling Hit()
on the
ShootingTarget, the function is messaged as an RPC:
that.view.RPC("Hit", RpcTarget.All, that.view.Owner.ActorNumber)
For this to work the target function must be tagged [PunRPC]
;
[PunRPC] public void Hit(int id)
What's the difference between a procedure call (the usual thing) and a remote procedure call?
- The procedure call (
that.GetComponent<ShootingTarget>().Hit()
) would only get invoked locally. - The RPC gets called on every clone of the receiving NGO.
Since NGOs are owned, only the 'original clone' can destroy its avatar. In
this case I didn't use Disown
. If we removed ShootingTarget
from other
clones, the RPC couldn't be handled, which would cause a PUN error; we'd
have to elaborate on the original pattern to make this work.
This walkthrough is not exhaustive. If you enjoyed it, follow Eelstork on Unity Connect.
Finally, official P.U.N resources: