unity3d 主机本身调用时,Unity Netcode ServerRpc未执行

1u4esq0p  于 2023-01-31  发布在  其他
关注(0)|答案(1)|浏览(500)

我对Unity Netcode还是个新手,我一直在网上看一些视频并阅读文档。
我正在尝试创建自己的大厅系统,为此,我尝试保留一个NetworkList变量,该变量包含每个玩家的用户名列表,只是为了显示已经加入服务器的玩家列表。
我的问题是,当要向列表添加主机的用户名本身时,我无法调用应该更新列表的ServerRpc方法。
这是脚本的一部分,该脚本应保存NetworkList变量并相应地管理更新UI中的列表。

public class PlayersListManager : NetworkBehaviour
 {
     [SerializeField] RectTransform playersListTransform;
     [SerializeField] GameObject playerNamePrefab;
 
     public NetworkList<FixedString32Bytes> playerUsernamesList;
 
     private void Awake()
     {
         playerUsernamesList = new NetworkList<FixedString32Bytes>();
         playerUsernamesList.OnListChanged += OnPlayersListUpdate;
     }
 
     [ServerRpc(RequireOwnership = false)]
     public void AddNewPlayer_ServerRpc(string newUsername)
     {
         Debug.Log("AddNewPlayer_ServerRpc");
         playerUsernamesList.Add(new FixedString32Bytes(newUsername));
     }

     [...]
 }

这是一个播放器对象,它应该发送ServerRpc,以便使服务器将其用户名添加到NetworkList中。

public class NetworkPlayer : NetworkBehaviour
 {
     [SerializeField] private string _username; //DEBUG ONLY
     public string Username { get; private set; }
 
     private MainMenuInteraction mainMenuInteraction;
     private PlayersListManager playersListManager;
 
     public override void OnNetworkSpawn()
     {
         base.OnNetworkSpawn();
         mainMenuInteraction = FindObjectOfType<MainMenuInteraction>();
         playersListManager = FindObjectOfType<PlayersListManager>();
         if (IsLocalPlayer)
         {
             Username = mainMenuInteraction.GetUsernameText();
             _username = Username;
             playersListManager.AddNewPlayer_ServerRpc(Username);
         }
     }

     [...]
 }

基本上发生的事情是,玩家首先被要求选择它的用户名,然后他可以创建/加入一个房间,一旦他连接上NetworkPlayer将在服务器上产生。一旦NetworkPlayer产生,它从本地InputText获取文本,并更新其用户名(这里我添加了一个private _username字符串,只是为了让它在编辑器上序列化,并检查发生了什么). variale Username被正确地更新为正确的用户名,然后是播放器列表管理器.添加新播放器_服务器Rpc(用户名);应该是从主机的客户端调用,然后在服务器上执行,但是什么都没有发生!我添加了Debug.log(“AddNewPlayer_ServerRpc”)这一行;在ServerRpc方法中检查它是否被执行过,但没有执行。
一个“有趣”的事情是,ServerRpc可以从任何其他客户机(不是主机)正确调用。
阅读了ServerRpc文档后,我明白了这并没有什么错,因为Host同时充当客户端和服务器,所以Host应该能够调用和执行ServerRpc本身。

nhaq1z21

nhaq1z211#

经过一整天尽可能多的测试,我发现了问题并解决了它,所以我在这里写下答案,以便让遇到类似问题的每个人都知道如何解决这个问题。
通过添加一个按钮,该按钮在每次单击时调用ServerRpc(用于递增一个简单的int),我已经设法了解了发生了什么。由于该按钮成功递增了int,我了解到问题可能是OnNetworkSpawn()调用ServerRpc太早了,因为OnNetworkSpawn位于播放器NetworkObject内部,播放器在主机启动时由NetworkManager生成。
为了确保服务器准备好运行任何ServerRpc,我实现了一个协程来检查ServerRpc是否实际运行。
我的网络播放器变成这样:

public override void OnNetworkSpawn()
{
    base.OnNetworkSpawn();
    mainMenuInteraction = FindObjectOfType<MainMenuInteraction>();
    playersListManager = FindObjectOfType<PlayersListManager>();
    if (IsLocalPlayer)
    {
        Username = mainMenuInteraction.GetUsernameText();
        _username = Username;
        StartCoroutine(SendUsernameToServer());
    }
}

private IEnumerator SendUsernameToServer()
{
    if (IsHost)
    {
        do
        {
            mainMenuInteraction.CheckIfServerReady_ServerRpc();
            yield return null;
        } while (!mainMenuInteraction.ServerReady);
    }

    playersListManager.AddNewPlayer_ServerRpc(Username);
    yield return null;
}

其中mainMenuInteraction.CheckIfServerReady_ServerRpc();简单为

[ServerRpc]
public void CheckIfServerReady_ServerRpc()
{
    ServerReady = true;
}

其中ServerReady是初始化为假的简单bool:

public bool ServerReady { get; private set; } = false;

这样,主机的(仅主机的)NetworkPlayer脚本在spawn上将等待此ServerReady设置为true,然后再调用实际的AddNewPlayer_ServerRpc

相关问题