Mirror 中的 ClientRPC 方法调用未正确保留其他客户端的事件引用

ClientRPC method call in Mirror does not properly keep event references for other clients

我正在使用 Unity 的 Mirror 库测试两个客户端之间的聊天系统。在我的 Player 对象上,我附加了一个脚本,它存储了一个应该通过 ClientRPC 调用的事件。

    public delegate void ReceivedChatMessageHandler(string message);
    public event ReceivedChatMessageHandler ChatMessageReceived;

    [Client]
    public void SendChatMessage(string message)
    {
        if (!Keyboard.current.enterKey.wasPressedThisFrame) return;
        if (string.IsNullOrWhiteSpace(message)) return;
        CmdSendMessage(message);
    }

    [Command]
    private void CmdSendMessage(string message)
    {
        RpcHandleMessage(message);
    }

    [ClientRpc]
    public void RpcHandleMessage(string message)
    {
        if (ChatMessageReceived != null)
        {
            ChatMessageReceived($"[Player]: {message}\n");
        }
    }

每个玩家都为他们的本地 UI 将事件处理程序附加到此事件,这应该会在聊天框中显示消息。这工作正常,两个玩家都可以向服务器发送一条消息,它会在他们自己的聊天中弹出(即事件处理程序正确附加到两个客户端)。但是,对于没有发送消息的客户端,ChatMessageReceived returns null,即使它是在本地定义的。

我在网上四处寻找,但找不到任何正确的解释来解释为什么会发生这种情况。我如何正确地将通过 ClientRPC 生成的事件传递给不属于 Player GameObject 本身的其他本地对象?

编辑: 事件侦听器通过附加到玩家游戏对象的脚本附加。在此脚本开始时,即生成玩家游戏对象时,它将找到聊天框游戏对象并将本地玩家的 ChatInterface 脚本(如上所示)传递给另一个脚本。

    public void Start()
    {
        if (!isLocalPlayer) return;

        GameObject.Find("Chat").GetComponent<Chatbox>().PlayerLoaded(this.gameObject.GetComponent<ChatInterface>());
    }

Chatbox 脚本然后将其自己的本地方法附加到事件处理程序。

    public void PlayerLoaded(ChatInterface chat)
    {
        this.chat = chat;
        chat.ChatMessageReceived += Receive;
    }

如前所述,每个客户端都可以很好地接收他们在自己的客户端上发送的事件,但是来自其他客户端的事件会导致整个事件的行为就好像没有定义处理程序一样。

我现在在旧 Unity forum thread 中找到了问题的答案。重要的部分是:

So if Player1 sends a command "CmdMoveForward" the command is executed on the server only on the object that is accociated with Player1. Likewise when the server calls a ClientRPC methhod RpcMovePlayer on the object accociated with Player3, this method is called on all clients on the Player3 object.

由于我正在为本地播放器对象创建事件侦听器,但客户端 RPC 是在与向服务器发送命令的用户对应的播放器对象上执行的,因此事件侦听器未附加到此事件.这也解释了为什么发送 RPC 的客户端没有问题,因为他们的事件侦听器附加到正确的播放器对象。

我现在通过将聊天框脚本转换为单例解决了这个问题,这样它就可以很容易地从任何玩家对象中调用。保留 event-based 架构的另一种选择是将侦听器附加到场景中的每个玩家对象。