【问题标题】:PUN2: Specify an instanced player gameObject through RPC callPUN2:通过 RPC 调用指定一个实例化的玩家游戏对象
【发布时间】:2021-10-26 15:13:23
【问题描述】:

我知道您不能通过 RPC 调用传递 gameObject 引用,因为每个客户端对每个实例都使用它自己的唯一引用。有没有办法通过 RPC 调用引用所有客户端上的特定游戏对象?

这是我想要完成的一个示例

    if(Input.GetKeyDown("r")){
PhotonView.RPC("ChangeReady", RPCTarget.AllBuffered, gameObject)
}
 [PunRPC]
    void ChangeReady(GameObject PLAYER) {
        PlayerScript PSCRIPT = PLAYER.GetComponent<PlayerScript>();
        if (PSCRIPT.READY)
        {
            PSCRIPT.GetComponent<PlayerScript>().READY = false;
        }
        else {
            PSCRIPT.READY = true;
        }
        
    }

我正在尝试为玩家创建一个就绪检查系统,以便他们可以打开和关闭就绪状态,并将信息传输给其他客户端。不确定如何引用特定的游戏对象,因为如前所述,这在 RPC 中是不可能的。

我该怎么做呢?

【问题讨论】:

    标签: c# unity3d photon


    【解决方案1】:

    有几种方法可以做到这一点,如果你只想设置一个就绪状态:

    a) 使用CustomProperties,它允许您将特定于玩家的数据存储在键值对中。然后,您可以在本地检查玩家游戏对象上的这些值,并使用您的 RPC 更改它们的行为。这是一个例子:

    bool ready = (bool)PhotonNetwork.player.customProperties["Ready"];
    Hashtable hash = new Hashtable();
    hash.Add("Ready", true);
    PhotonNetwork.player.SetCustomProperties(hash);
    

    Follow this link for info on CustomProperties

    b) 通过在你的 Monobehaviour 上实现 IPunMagicInstantiateCallback 来使用 OnPhotonInstantiate

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

    TagObject 可帮助您将 GameObject 与特定玩家相关联。因此,一旦它们被实例化,就会自动调用,并且您的 GameObject 可以绑定到玩家并使用 Player.TagObject 检索并执行您的就绪状态逻辑(通过 RPC 或任何其他方式)

    Follow this link for info about OnPhotonInstantiate

    【讨论】:

      【解决方案2】:

      Photon RPC and RaiseEvent => "你可以添加多个参数只要PUN可以序列化它们" -> 见Photon - Supported Types for Serialization

      => 不,这是不可能的。


      解决方案

      您总是需要一种方法来告诉其他设备您指的是哪个对象,因为它们与您的引用不同。

      如果您指的是您的光子播放器的对象,但您可以改为发送

      PhotonNetwork.LocalPlayer.ActorNumber
      

      另一方面,通过ActorNumber 获取GameObject

      有多种方法可以做到这一点。

      我曾经通过像这样的中心类来做到这一点

      public class PhotonUserManager : IInRoomCallbacks, IMatchmakingCallbacks
      {
          private static PhotonUserManager _instance;
      
          private readonly Dictionary<int, GameObject> _playerObjects = new Dictionary<int, GameObject>();
      
          public static bool TryGetPlayerObject(int actorNumber, out GameObject gameObject)
          {
              return _instance._playerObjects.TryGetValue(actorNumber, out gameObject);
          }
      
          [RuntimeInitializeOnLoadMethod]
          private static void Init()
          {
              _instance = new PhotonUserManager();
              PhotonNetwork.AddCallbackTarget(_instance);
          }
      
          public void OnCreateRoomFailed(short returnCode, string message){ }
      
          public void OnJoinRoomFailed(short returnCode, string message){ }
      
          public void OnCreatedRoom() { }
      
          public void OnJoinedRoom()
          {
              Refresh();
          }
      
          public void OnLeftRoom()
          {
              _playerObjects.Clear();
          }
      
          public void OnJoinRandomFailed(short returnCode, string message){ }
      
          public void OnFriendListUpdate(List<FriendInfo> friendList) { }
      
          public void OnPlayerEnteredRoom(Player newPlayer) 
          { 
              Refresh();
          }
      
          public void OnPlayerLeftRoom(Player otherPlayer)
          {
              if(_playerObjects.ContainsKey(otherPlayer.ActorNumber))
              {
                  _playerObjects.Remove(otherPlayer.ActorNumber);
              }
          }
      
          public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged){ }
      
          public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps){ }
      
          public void OnMasterClientSwitched(Player newMasterClient){ }
      
          private void Refresh()
          {
              var photonViews = UnityEngine.Object.FindObjectsOfType<PhotonView>();
              foreach (var view in photonViews)
              {
                  var player = view.owner;
                  //Objects in the scene don't have an owner, its means view.owner will be null => skip
                  if(player == null) continue;
      
                  // we already know this player's object -> nothing to do
                  if(_playerObjects.ContainsKey(player.ActorNumber)) continue;
      
                  var playerObject = view.gameObject;
                  _playerObjects[player.ActorNumber] = playerObject;
              }
          }
      }
      

      这会自动收集加入玩家拥有的GameObject。当然有一个限制:如果玩家自己生成任何东西并拥有它......它可能会失败,因为那时每个玩家有多个PhotonViews => 在这种情况下使用具有最低 NetworkIdendity 的那个。在 Photon 中,viewID 由第一个数字 = ActorNumber + 3 个数字 id 组成;)

      以后做

      if(!PhotonUserManager.TryGetPlayerObject(out var obj))
      {
          Debug.LogError($"Couldn't get object for actor number {receivedActorNumber}");
      }
      

      作为另一种选择,您可以使用OnPhotonInstantiate

      public class SomeComponentOnYourPlayerPrefab : PunBehaviour
      {
          public override void OnPhotonInstantiate(PhotonMessageInfo info)
          {
              // only set if not already set in order to not
              // overwrite the tag for a player that spawns something later
              if(info.sender.TagObject == null)
              {
                  info.sender.TagObject = gameObject;
              }
          }
      }
      

      然后再做

      var obj = PhotonNetwork.LocalPlayer.Get(receivedActorNumber).TagObject;
      

      【讨论】:

        猜你喜欢
        • 2022-01-14
        • 2018-10-28
        • 2016-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多