Most multiplayer games need to create and synchronize some game objects. Maybe it's a character, some units or monsters that should be present on all clients inside a room. PUN provides a conventient way to do just that.

As usual in Unity, Instantiate and Destroy are used to manage the lifetime of game objects. PUN 2 can use a pool to create (and return) them. Each networked game object must have a PhotonView component (and a ViewID) as identifier via the network.

This page and the following ones explain how to create, sync and use networked game objects.


To create networked game objects, use PhotonNetwork.Instantiate instead of Unity's Object.Instantiate. Any client in a room can call this to create objects which it will control.

PhotonNetwork.Instantiate("MyPrefabName", new Vector3(0, 0, 0), Quaternion.identity, 0);

The first parameter of PhotonNetwork.Instantiate is a string which defines the "prefab" to instantiate. Internally, PUN will fetch the GameObject from the PhotonNetwork.PrefabPool, set it up for the network and enable it. The position and rotation where to create the object must be set. Players who join later, will initially instantiate the object at this place, even if it moved already.

Any prefab must have a PhotonView component. This contains a ViewID (the identifier for network messages), who owns the object, which scripts will write and read network updates (the "observed" list) and how those updates are sent (the "Observe option"). Check the inspector to setup a PhotonView via the Editor. A networked object may contain more than one PhotonView but for performance reasons we suggest to use only one.

By default, PUN instantiate uses the DefaultPool, which loads prefabs from Resources folders and Destroys the GameObject later on. A more sophisticated IPunPrefabPool implementation can return objects to a pool in Destroy and re-use them in Instantiate. In that case, the GameObjects are not truly created in Instantiate, which means that Start() is not being called by Unity in such a case. Due to this, scripts on networked game objects should just implement OnEnable and OnDisable.

To setup GameObjects when they got instantiated, you could also implement IPunInstantiateMagicCallback in a script. PUN will check if the interface is implemented on a component and calls OnPhotonInstantiate(PhotonMessageInfo info) when the instance comes into use. The info contains who instantiated the game object and when.

Note: Looking up IPunInstantiateMagicCallback implementations is a costly action, so PUN caches which prefabs are not making use of the interface and skips this lookup when this prefab is used again.

For example, you could setup the instantiated GameObject as a player's Tag object:

void OnPhotonInstantiate(PhotonMessageInfo info) 
    // e.g. store this gameobject as this player's charater in Player.TagObject
    info.sender.TagObject = this.GameObject;

Behind the scenes, PhotonNetwork.Instantiate stores an event on the server for players who join later on.

Lifetime of Networked Objects

By default, GameObjects created with PhotonNetwork.Instantiate exist as long as the creator is in the room. When you swap rooms, objects don't carry over, just like when you switch a scene in Unity.

When a client leaves a room, the remaining players will destroy the GameObjects created by the leaving player. If this doesn't fit your game logic, you can disable this: Set the RoomOptions.CleanupCacheOnLeave to false, when you create a room.

The Master Client can create GameObjects that have the lifetime of the room by using PhotonNetwork.InstantiateSceneObject(). Note: The object is not associated with the Master Client but the room. By default, the Master Client controls these objects but you can pass on control with photonView.TransferOwnership().

Networked Scene Objects

It is perfectly fine to place PhotonViews on objects in a scene. They will be controlled by the Master Client by default and can be useful to have a "neutral" object to send room-related RPCs.

Important: When you load a scene with networked objects before being in a room, some PhotonView values are not useful yet. For example: You can't check isMine in Awake() when you're not in a room!

Switching Scenes

When you load a scene, Unity usually destroys all GameObjects currently in the hierarchy. This includes networked objects, which can be confusing at times.

Example: In a menu scene, you join a room and load another. You might actually arrive in the room a bit too early and get the initial messages of the room. PUN begins to instantiate networked objects but your logic loads another scene and they are gone.

To avoid issues with loading scenes, you can set PhotonNetwork.AutomaticallySyncScene to true and use PhotonNetwork.LoadLevel() to switch scenes.

Read Timing for RPCs and Loading Levels

Using the PrefabPool

By default, PUN uses a simple DefaultPool to instantiate and destroy game objects. This uses the Resources folder to load prefabs and it will not pool objects that get destroyed (to simplify usage). If either affects your game's performance negatively, it is time to set a custom PrefabPool.

A custom pool class must implement the IPunPrefabPool interface with just two methods:

GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation) gets an instance of a prefab. It must return a valid, disabled GameObject with a PhotonView.

Destroy(GameObject gameObject) gets called to destroy (or just return) the instance of a prefab. The game object is already disabled and the pool may reset and cache it for later use in Instantiate.

Note: When a custom IPunPrefabPool is used, PhotonNetwork.Instantiate is probably not creating the GameObject and (e.g.) Start() is not called. Use OnEnable and OnDisable accordingly and disable any physical or other components that may still run otherwise!

Manual Instantiation

If you don't want to use PUN's built-in instantiation and pools, you can reimplement the behaviour with RPCs or RaiseEvent as shown in the following example.

You need to tell the remote clients which object to instantiate (prefab name) and how to identify it (ViewID).

The PhotonView.ViewID is the key to routing network messages to the correct gameobject/scripts. If you instantiate manually, you have to allocate a new ViewID with PhotonNetwork.AllocateViewID() and send it along. Everyone in the room has to set the same ID on the new object.

Keep in mind that a Manual Instantiation event needs to be buffered: Clients that connect later have to receive the spawn instructions as well.

public void SpawnPlayer()
    GameObject player = Instantiate(PlayerPrefab);
    PhotonView photonView = player.GetComponent<PhotonView>();

    if (PhotonNetwork.AllocateViewID(photonView))
        object[] data = new object[]
            player.transform.position, player.transform.rotation, photonView.ViewID

        RaiseEventOptions raiseEventOptions = new RaiseEventOptions
            Receivers = ReceiverGroup.Others,
            CachingOption = EventCaching.AddToRoomCache

        SendOptions sendOptions = new SendOptions
            Reliability = true

        PhotonNetwork.RaiseEvent(CustomManualInstantiationEventCode, data, raiseEventOptions, sendOptions);
        Debug.LogError("Failed to allocate a ViewId.");


We are instantiating the player prefab locally first. This is necessary because we need the reference to the object's PhotonView component. If we have successfully allocated an ID for the PhotonView, we are collecting all the data we want to send to the other clients and store them in an array of objects. In this example we are sending the position and the rotation of the instantiated object and - most important - the allocated ViewID. Afterwards we are creating the RaiseEventOptions and the SendOptions. With the RaiseEventOptions we make sure, that this event is added to the room's cache and only send to the other clients, because we already have instantiated our object locally. With the SendOptions we just define, that this event is sent reliable. Finally we are using PhotonNetwork.RaiseEvent(...) to send our custom event to the server. In this case we are using CustomManualInstantiationEventCode, which is simply a byte value representing this certain event. If allocating an ID for the PhotonView fails, we log an error message and destroy the previously instantiated object.

Since we are using PhotonNetwork.RaiseEvent, we have to use an OnEvent callback handler. Don't forget, to register it properly. To see how this works, you can take a look at the RPCs and RaiseEvent documentation page. In this example the OnEvent handler looks like this:

public void OnEvent(EventData photonEvent)
    if (photonEvent.Code == CustomManualInstantiationEventCode)
        object[] data = (object[]) photonEvent.CustomData;

        GameObject player = (GameObject) Instantiate(PlayerPrefab, (Vector3) data[0], (Quaternion) data[1]);
        PhotonView photonView = player.GetComponent<PhotonView>();
        photonView.ViewID = (int) data[2];

Here we simply check, if the received event is our custom Manual Instantiation event. If so, we instantiate the player prefab with the position and rotation information we have received. Afterwards we get a reference to the object's PhotonView component and assign the ViewID we have received, too.

If you want to use asset bundles to load your network objects, all you have to do is to add your own asset bundle loading code and replace the PlayerPrefab from the example with the prefab from your asset bundle.

 To Document Top