PUN2:通过 RPC 调用指定一个实例化的玩家游戏对象

Posted

技术标签:

【中文标题】PUN2:通过 RPC 调用指定一个实例化的玩家游戏对象【英文标题】:PUN2: Specify an instanced player gameObject through RPC call 【发布时间】: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 中是不可能的。

我该怎么做呢?

【问题讨论】:

【参考方案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;

【讨论】:

以上是关于PUN2:通过 RPC 调用指定一个实例化的玩家游戏对象的主要内容,如果未能解决你的问题,请参考以下文章

获取其他玩家实例化的游戏对象 Unity Photon

markdown Hadoop原生的基于Writable Sequence列化的RPC框架应用实例

PUN 2.0 相机为每个玩家渲染

网络编程 -- RPC实现原理 -- RPC -- 迭代版本V3 -- 远程方法调用 整合 Spring

由于某种原因,客户端未调用服务器 RPC

RPC远程调用概念 &amp;&amp; demo实例