PUN2 实例化对象

(大致翻译,原文见Photon Documentation

 

Unity 使用 Instantiate 和 Destory 管理游戏对象的寿命,PUN 2 使用对象池来创建对象。必须为每个网络对象添加 PhotonView 组件(和 ViewID)标识符。

 

本文介绍如何创建、同步和使用网络对象。

 

PhotonNetwork.Instantiate

 

创建网络对象需要使用 PhotonNetwork.Instantiate 而不是 Object.Instantiate ,房间中的任一客户端都可以调用该函数创建对象,新创建的对象控制权默认属于创建者。

PhotonNetwork.Instantiate(string prefabName, Vector3 position, Quaternion rotation, byte group = 0, object[] data = null)

 

参数:预制体名称,新建对象的位置,姿态,分组,其他数据。稍后加入房间的玩家,会在初始时刻在设定的位置实例化该对象,即使对象已经被移动。

 

预制体必须有 PhotonView 组件。一个网络对象可以有多个 PhotonView 组件,建议一个。

 

默认情况下,PUN 使用 DefaultPool 实例化,即从 Resources 文件夹加载预制体并在稍后销毁。一个更复杂的接口 IPunPrefabPool 可以实现在销毁对象时将其返回到对象池并在实例化时复用。在这种情况下,在 Instantiate 时并没有真的创建对象,意味着 Start() 并没有被 Unity 调用。因此,网络对象上挂载的对象必须实现 OnEnable()OnDisable()

 

为了在游戏对象被实例化时配置该对象,可以在脚本中实现 IPUNInstantiateMagicCallback 接口。PUN 会检查接口是否在某个组件中实现并在该实例开始使用时调用 OnPhotonInstantiate(PhotonMessageInfo info) 。info 包含实例化该对象的对象和时间信息。

 

## PhotonNetwork.Instantiate 在服务器端为稍后加入房间的玩家缓存一个事件。

 

网络对象的寿命

 

网络对象默认和房间中使用 PhotonNetwork.Instantiate 创建该对象的对象有相同的寿命,切换房间将被销毁。

 

当一个客户端离开房间时,其余玩家将会销毁该玩家创建的对象,将 RoomOptions.CleanuoCacheOnLeave 设置为 False 则不会。

 

房主(MasterClient) 可以使用 PhotonNetwork.InstantiateSceneObject() 创建和房间有相同寿命的对象。该对象将和房间关联而不是房主,默认受房主控制,但可以使用 photonView.TransferOwnership() 交换控制权。

 

Networked Scene Objects

 

将 PhotonViews 放置在场景中的对象上是完美的。默认情况下,它们将由主客户端控制,并且“中性”对象在发送与房间相关的RPC时非常有用。 
 
重要提示:当您在房间中加载具有网络对象的场景时,某些 PhotonView 值尚无用。例如:当您不在房间时,您无法在 Awake()中检查 isMine!

 

切换场景 SWitching Scenes

 

加载新的场景时,Unity 将会销毁当前 hierarchy 中的所以对象,包括网络对象。

 

为了避免这个问题,将 PhotonNetwork.AutomaticallyAsyncScene 设置为 True ,并使用 PhotonNetwork.LoadLevel() 切换场景。

 

Using the PrefabPool

 

默认情况下,PUN 使用简单的 DefaultPool 实例化和销毁对象。使用 Resource 文件夹加载预制体,且不会汇集(pool)销毁的对象。如果此操作对游戏性能产生负面影响,那么就该自定义的 PrefabPool 了。

 

自定义 Pool 类必须实现 IpunPrefabPool 接口,仅包含两个方法:

 

 GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation) 获取预制体的实例,必须返回一个包含 PhotonView 组件的有效的、被禁用的 GameObject 。

 

 Destroy(GameObject gameObject) 用于销毁(或者说返回)预制体的实例。 游戏对象已被禁用且 pool 会被重置,缓存用于之后的实例化。

 

Note:自定义实现 IPunPrefabPool 后,PhotonNetwork.Instantiate 可能不能再创建 GameObject ,Start()  未被调用。相应地使用 OnEnable 和 OnDisable 并禁用可能仍然运行的任何物理或其他组件!

 

手动实例化 Manual Instantiation

 

若不想使用 PUN 内建实例方法和 pools,可以使用 RPCs 或 RaiseEvent 重新实现该行为,以下举例。

 

需要告知远程客户端实例化哪个对象(预制体名称)和如何辨别它(ViewID)。

 

PhotonView.ViewID 是将网络消息传递到正确的游戏对象/脚本的关键。如果手动实例化,则必须使用  PhotonNetwork.AllocateViewID()  分配并发送新的 ViewID 。房间里的每个人都必须在新对象上设置相同的 ID。

 

注意,需要为手动实例化缓存事件,以便稍后连接的客户端也能够接收生成指令。

 

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);
    }
    else
    {
        Debug.LogError("Failed to allocate a ViewId.");

        Destroy(player);
    }
}

 

必须首先在本地实例化玩家预制体,因为需要引用对象的 PhotonView 组件。成功为 PhotonView 分配ID后,收集所有要发送给其他客户端的数据并将它们存储在一个数组对象中。本例中我们发送实例化对象的位置和姿态,最重要的是发送分配的 ViewID。随后创建 RaiseEventOptions 和 SendOptions。使用 RaiseEventOptions 确保将此事件添加到房间缓存中,并仅发送给其他客户端,因为我们已经在本地实例化了我们的对象。使用刚刚定义的SendOptions,可以发送此事件。最后,使用 PhotonNetwork.RaiseEvent(...)将我们的自定义事件发送到服务器。在这种情况下,我们使用 CustomManualInstantiationEventCode,它只是表示此特定事件的字节值。如果为 PhotonView 分配ID失败,我们会记录一条错误消息并销毁先前实例化的对象。

 
使用 PhotonNetwork.RaiseEvent 时,必须使用 OnEvent 回调处理程序,不要忘记正确注册该回调。要了解其工作原理,请查看 RPC 和 RaiseEvent 文档页面。本例中,OnEvent 处理程序如下:

 

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];
    }
}

 

首先检查收到的事件是否时我们自定义的手动实例化事件。如果是这样,我们使用我们收到的位置和姿态信息来实例化玩家预制件。然后我们获取对象的 PhotonView 组件并分配我们收到的 ViewID。 
 
若想使用 asset bundles 加载网络对象,只需添加 asset bundles 的加载代码,并将示例中的PlayerPrefab 替换为 asset bundles 中的预制件。

posted @ 2019-04-30 20:41  ~k~  阅读(1496)  评论(0)    收藏  举报