代码改变世界

Photon自定义加载Resource之外的资源

2019-01-16 13:39  独孤残云  阅读(696)  评论(0编辑  收藏  举报

PhotonNetwork.cs 结尾添加如下代码:

    #region >>> Photon自定义异步加载GameObject

    public delegate void CustomLoader(string prefabName, Action<UnityEngine.Object> ac);
    public static CustomLoader _loader;
    public static void RegisterCustomLoaderToPhoton(CustomLoader loader)
    {
        _loader = loader;
    }

    public static bool InstantiateCustom(string prefabName, Vector3 position, Quaternion rotation, byte group, object[] data, Action<GameObject> callback)
    {
        if (!connected || (InstantiateInRoomOnly && !inRoom))
        {
            Debug.LogError("Failed to Instantiate prefab: " + prefabName + ". Client should be in a room. Current connectionStateDetailed: " + PhotonNetwork.connectionStateDetailed);
            return false;
        }

        GameObject prefabGo;
        if (!UsePrefabCache || !PrefabCache.TryGetValue(prefabName, out prefabGo))
        {
            _loader(prefabName, (ab) =>
            {
                prefabGo = (GameObject)(ab);

                if (UsePrefabCache)
                {
                    PrefabCache.Add(prefabName, prefabGo);
                }

                if (prefabGo == null)
                {
                    Debug.LogError("Failed to Instantiate prefab: " + prefabName + ". Verify the Prefab is in a Resources folder (and not in a subfolder)");
                    callback.Invoke(null);
                }

                // a scene object instantiated with network visibility has to contain a PhotonView
                if (prefabGo.GetComponent<PhotonView>() == null)
                {
                    Debug.LogError("Failed to Instantiate prefab:" + prefabName + ". Prefab must have a PhotonView component.");
                    callback.Invoke(null);
                }

                Component[] views = (Component[])prefabGo.GetPhotonViewsInChildren();
                int[] viewIDs = new int[views.Length];
                for (int i = 0; i < viewIDs.Length; i++)
                {
                    //Debug.Log("Instantiate prefabName: " + prefabName + " player.ID: " + player.ID);
                    viewIDs[i] = AllocateViewID(player.ID);
                }

                // Send to others, create info
                Hashtable instantiateEvent = networkingPeer.SendInstantiate(prefabName, position, rotation, group, viewIDs, data, false);

                // Instantiate the GO locally (but the same way as if it was done via event). This will also cache the instantiationId
                callback.Invoke(networkingPeer.DoInstantiate(instantiateEvent, networkingPeer.LocalPlayer, prefabGo));
            });
            return true;
        }
        if (prefabGo != null)
        {
            // a scene object instantiated with network visibility has to contain a PhotonView
            if (prefabGo.GetComponent<PhotonView>() == null)
            {
                Debug.LogError("Failed to Instantiate prefab:" + prefabName + ". Prefab must have a PhotonView component.");
                callback.Invoke(null);
            }

            Component[] views = (Component[])prefabGo.GetPhotonViewsInChildren();
            int[] viewIDs = new int[views.Length];
            for (int i = 0; i < viewIDs.Length; i++)
            {
                //Debug.Log("Instantiate prefabName: " + prefabName + " player.ID: " + player.ID);
                viewIDs[i] = AllocateViewID(player.ID);
            }

            // Send to others, create info
            Hashtable instantiateEvent = networkingPeer.SendInstantiate(prefabName, position, rotation, group, viewIDs, data, false);
            callback.Invoke(networkingPeer.DoInstantiate(instantiateEvent, networkingPeer.LocalPlayer, prefabGo));
            return true;
        }
        return false;
    }

    #endregion

  

NetworkingPeer.cs 结尾添加如下代码:

    #region >>> Photon自定义异步加载GameObject

    public delegate void CustomInstantiatedHandler(PhotonPlayer photonPlayer, GameObject go);
    public static event CustomInstantiatedHandler OnCustomInstantiated;

    internal void DoInstantiateCustom(Hashtable evData, PhotonPlayer photonPlayer, GameObject resourceGameObject, Action<GameObject> callback)
    {
        // some values always present:
        string prefabName = (string)evData[(byte)0];
        int serverTime = (int)evData[(byte)6];
        int instantiationId = (int)evData[(byte)7];

        Vector3 position;
        if (evData.ContainsKey((byte)1))
        {
            position = (Vector3)evData[(byte)1];
        }
        else
        {
            position = Vector3.zero;
        }

        Quaternion rotation = Quaternion.identity;
        if (evData.ContainsKey((byte)2))
        {
            rotation = (Quaternion)evData[(byte)2];
        }

        byte group = 0;
        if (evData.ContainsKey((byte)3))
        {
            group = (byte)evData[(byte)3];
        }

        short objLevelPrefix = 0;
        if (evData.ContainsKey((byte)8))
        {
            objLevelPrefix = (short)evData[(byte)8];
        }

        int[] viewsIDs;
        if (evData.ContainsKey((byte)4))
        {
            viewsIDs = (int[])evData[(byte)4];
        }
        else
        {
            viewsIDs = new int[1] { instantiationId };
        }

        object[] incomingInstantiationData;
        if (evData.ContainsKey((byte)5))
        {
            incomingInstantiationData = (object[])evData[(byte)5];
        }
        else
        {
            incomingInstantiationData = null;
        }

        // SetReceiving filtering
        if (group != 0 && !this.allowedReceivingGroups.Contains(group))
        {
            if (callback != null)
            {
                callback.Invoke(null);
            }
            return; // Ignore group
        }

        if (ObjectPool != null) // 使用对象池的情况
        {
            GameObject go = ObjectPool.Instantiate(prefabName, position, rotation);

            PhotonView[] photonViews = go.GetPhotonViewsInChildren();
            if (photonViews.Length != viewsIDs.Length)
            {
                throw new Exception("Error in Instantiation! The resource's PhotonView count is not the same as in incoming data.");
            }
            for (int i = 0; i < photonViews.Length; i++)
            {
                photonViews[i].didAwake = false;
                photonViews[i].viewID = 0;

                photonViews[i].prefix = objLevelPrefix;
                photonViews[i].instantiationId = instantiationId;
                photonViews[i].isRuntimeInstantiated = true;
                photonViews[i].instantiationDataField = incomingInstantiationData;

                photonViews[i].didAwake = true;
                photonViews[i].viewID = viewsIDs[i];    // with didAwake true and viewID == 0, this will also register the view
            }

            // Send OnPhotonInstantiate callback to newly created GO.
            // GO will be enabled when instantiated from Prefab and it does not matter if the script is enabled or disabled.
            go.SendMessage(OnPhotonInstantiateString, new PhotonMessageInfo(photonPlayer, serverTime, null), SendMessageOptions.DontRequireReceiver);
            if (callback != null)
            {
                callback.Invoke(go);
                if (OnCustomInstantiated != null)
                {
                    OnCustomInstantiated.Invoke(photonPlayer, go);
                }
            }
            return;
        }
        else
        {
            // load prefab, if it wasn't loaded before (calling methods might do this)
            if (resourceGameObject == null)
            {
                if (!NetworkingPeer.UsePrefabCache || !NetworkingPeer.PrefabCache.TryGetValue(prefabName, out resourceGameObject))
                {
                    //resourceGameObject = (GameObject)Resources.Load(prefabName, typeof(GameObject));
                    PhotonNetwork._loader(prefabName, (ab) =>
                    {
                        resourceGameObject = (GameObject)ab;
                        if (NetworkingPeer.UsePrefabCache)
                        {
                            NetworkingPeer.PrefabCache.Add(prefabName, resourceGameObject);
                        }
                        if (resourceGameObject == null)
                        {
                            Debug.LogError("PhotonNetwork error: Could not find the bundle [" + prefabName + "]. Please verify you have this gameobject in a StreamingAssets/Res folder.");
                            if (callback != null)
                            {
                                callback.Invoke(null);
                            }
                            return;
                        }

                        // now modify the loaded "blueprint" object before it becomes a part of the scene (by instantiating it)
                        PhotonView[] resourcePVs = resourceGameObject.GetPhotonViewsInChildren();
                        if (resourcePVs.Length != viewsIDs.Length)
                        {
                            throw new Exception("Error in Instantiation! The resource's PhotonView count is not the same as in incoming data.");
                        }

                        for (int i = 0; i < viewsIDs.Length; i++)
                        {
                            // NOTE instantiating the loaded resource will keep the viewID but would not copy instantiation data, so it's set below
                            // so we only set the viewID and instantiationId now. the instantiationData can be fetched
                            resourcePVs[i].viewID = viewsIDs[i];
                            resourcePVs[i].prefix = objLevelPrefix;
                            resourcePVs[i].instantiationId = instantiationId;
                            resourcePVs[i].isRuntimeInstantiated = true;
                        }

                        this.StoreInstantiationData(instantiationId, incomingInstantiationData);

                        // load the resource and set it's values before instantiating it:
                        GameObject go = (GameObject)GameObject.Instantiate(resourceGameObject, position, rotation);

                        for (int i = 0; i < viewsIDs.Length; i++)
                        {
                            // NOTE instantiating the loaded resource will keep the viewID but would not copy instantiation data, so it's set below
                            // so we only set the viewID and instantiationId now. the instantiationData can be fetched
                            resourcePVs[i].viewID = 0;
                            resourcePVs[i].prefix = -1;
                            resourcePVs[i].prefixBackup = -1;
                            resourcePVs[i].instantiationId = -1;
                            resourcePVs[i].isRuntimeInstantiated = false;
                        }

                        this.RemoveInstantiationData(instantiationId);

                        // Send OnPhotonInstantiate callback to newly created GO.
                        // GO will be enabled when instantiated from Prefab and it does not matter if the script is enabled or disabled.
                        go.SendMessage(OnPhotonInstantiateString, new PhotonMessageInfo(photonPlayer, serverTime, null), SendMessageOptions.DontRequireReceiver);
                        if (callback != null)
                        {
                            callback.Invoke(go);
                            if (OnCustomInstantiated != null)
                            {
                                OnCustomInstantiated.Invoke(photonPlayer, go);
                            }
                        }
                        return;

                    });
                }
                else
                {
                    // now modify the loaded "blueprint" object before it becomes a part of the scene (by instantiating it)
                    PhotonView[] resourcePVs = resourceGameObject.GetPhotonViewsInChildren();
                    if (resourcePVs.Length != viewsIDs.Length)
                    {
                        throw new Exception("Error in Instantiation! The resource's PhotonView count is not the same as in incoming data.");
                    }

                    for (int i = 0; i < viewsIDs.Length; i++)
                    {
                        // NOTE instantiating the loaded resource will keep the viewID but would not copy instantiation data, so it's set below
                        // so we only set the viewID and instantiationId now. the instantiationData can be fetched
                        resourcePVs[i].viewID = viewsIDs[i];
                        resourcePVs[i].prefix = objLevelPrefix;
                        resourcePVs[i].instantiationId = instantiationId;
                        resourcePVs[i].isRuntimeInstantiated = true;
                    }

                    this.StoreInstantiationData(instantiationId, incomingInstantiationData);

                    // load the resource and set it's values before instantiating it:
                    GameObject go = (GameObject)GameObject.Instantiate(resourceGameObject, position, rotation);

                    for (int i = 0; i < viewsIDs.Length; i++)
                    {
                        // NOTE instantiating the loaded resource will keep the viewID but would not copy instantiation data, so it's set below
                        // so we only set the viewID and instantiationId now. the instantiationData can be fetched
                        resourcePVs[i].viewID = 0;
                        resourcePVs[i].prefix = -1;
                        resourcePVs[i].prefixBackup = -1;
                        resourcePVs[i].instantiationId = -1;
                        resourcePVs[i].isRuntimeInstantiated = false;
                    }

                    this.RemoveInstantiationData(instantiationId);

                    // Send OnPhotonInstantiate callback to newly created GO.
                    // GO will be enabled when instantiated from Prefab and it does not matter if the script is enabled or disabled.
                    go.SendMessage(OnPhotonInstantiateString, new PhotonMessageInfo(photonPlayer, serverTime, null), SendMessageOptions.DontRequireReceiver);
                    if (callback != null)
                    {
                        callback.Invoke(go);
                        if (OnCustomInstantiated != null)
                        {
                            OnCustomInstantiated.Invoke(photonPlayer, go);
                        }
                    }
                    return;
                }
            }
        }
    }

    #endregion

  

NetworkingPeer.cs 第2598行:

屏蔽掉原有的 DoInstantiate 调用,改为 DoInstantiateCustom 调用

                //this.DoInstantiate((Hashtable)photonEvent[ParameterCode.Data], originatingPlayer, null);
                this.DoInstantiateCustom((Hashtable)photonEvent[ParameterCode.Data], originatingPlayer, null, (go) => {
                    SendMonoMessage(PhotonNetworkingMessage.OnPhotonInstantiate, new PhotonMessageInfo(originatingPlayer, 0, go.GetComponent<PhotonView>()));
                });

回调的意义在于你可以在初始Photon.MonoBehaviour中捕获全局的OnPhotonInstantiate网络物体初始回调,同样的你也可以通过OnCustomInstantiated事件来自定义回调事件行为

 

之后记得调用 PhotonNetwork.RegisterCustomLoaderToPhoton 注册一个自定义的默认异步资源加载方法给 Photon 即可