gghlc 2025-11-30 19:20 采纳率: 0%
浏览 6

unity的聊天基于mirror


using Mirror;
using UnityEngine;
using TMPro;
using UnityEngine.UI;

public class Sent_Messages : NetworkBehaviour
{
    [Header("简单聊天设置")]
    private TMP_InputField chatInput;
    private Text chatDisplay;
    public int maxMessages = 10;
    /*
     
     OnStartServer() - 只在服务器执行

    OnStartClient() - 在每个客户端执行

    OnStartLocalPlayer() - 只在本地玩家执行

    OnStartAuthority() - 当获得权限时执行
     
     */
    public override void OnStartLocalPlayer()
    {
      chatInput = GameObject.FindGameObjectWithTag("ChatInput").GetComponent<TMP_InputField>();
chatDisplay = GameObject.FindGameObjectWithTag("ChatDisplay").GetComponent<Text>();
if (chatInput != null && chatDisplay != null)
{
    Debug.Log("正常,不为空");
}
else if (chatInput != null)
{
    Debug.Log("不正常,chatInput = null");
}
else if (chatDisplay != null) 
{
    Debug.Log("不正常,chatDisplay = null");
}
    }
    // 存储聊天记录
    private string chatHistory = "";

    void Update()
    {
        
    }

    public void SendMessage()
    {
        if (chatInput == null)
        {
            Debug.Log("chatInput为null");
        }
        if (!gameObject.activeInHierarchy)
        {
            Debug.LogError($"玩家对象 {gameObject.name} 被禁用,无法发送消息");

        }
        // 检查 NetworkIdentity 是否存在
        // 第52行之前添加安全检查
        if (netIdentity == null)
        {
            Debug.LogError("NetworkIdentity 为 null,对象可能未正确初始化为网络对象");
            Debug.LogError($"游戏对象: {gameObject.name}, 活跃状态: {gameObject.activeInHierarchy}");
            return;
        }


        if (!isLocalPlayer)
        {
            Debug.Log("我不是本地玩家,不能发送消息");
            return;
        }
        string message = chatInput.text.Trim();
        if (!string.IsNullOrEmpty(message))
        {
            // 发送到服务器
            CmdSendMessage(message);
            Debug.Log("信息发送到服务器了");
        }

        chatInput.text = "";
    }

    [Command]
    void CmdSendMessage(string message)
    {
        // 获取玩家名字
        string playerName = $"玩家{connectionToClient.connectionId}";

        // 广播给所有客户端
        RpcReceiveMessage(playerName, message);
        Debug.Log(" 广播给所有客户端");
    }

    [ClientRpc]
    void RpcReceiveMessage(string playerName, string message)
    {
        // 添加新消息
        string newMessage = $"\n{playerName}: {message}";
        chatHistory += newMessage;

        // 限制消息数量
        string[] lines = chatHistory.Split('\n');
        if (lines.Length > maxMessages)
        {
            chatHistory = string.Join("\n", lines, lines.Length - maxMessages, maxMessages);
        }

        // 更新显示
        chatDisplay.text = chatHistory;
    }
}

脚本挂载在本地Player预制体上,有网络身份组件和其他必要组件,SendMessage()的触发为button点击,canvas上有网络身份组件。
问题1:我在OnStartLocalPlayer()中用if语句检测chatInput 和chatDisplay 都不为空,但是在SendMessage()中检测chatInput 却为空。
问题2:在点击button后,SendMessage()下我用来测试的if语句都报错(即gameObject.activeInHierarchy为flase)。应该是因为我的是预制体资源
问题3:netIdentity == null;但是我明明加了网络身份组件。

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2025-11-30 19:22
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    你提出了三个问题,我们逐一分析并给出解决方案。以下是详细说明和修复方法:


    问题1:在 OnStartLocalPlayer() 中检测 chatInputchatDisplay 都不为空,但在 SendMessage() 中却为 null

    原因分析:

    • OnStartLocalPlayer() 是在本地玩家初始化时调用的,但如果你的脚本挂载在 预制体(Prefab) 上,并且该预制体被实例化后作为本地玩家使用,那么可能在某些情况下,chatInputchatDisplay 的引用没有正确绑定。
    • 另一个可能性是:GameObject.FindGameObjectWithTag("ChatInput")GameObject.FindGameObjectWithTag("ChatDisplay") 在场景中可能有多个对象具有相同的标签,导致获取到的是错误的对象。

    解决方案:

    ✅ 1. 确保 chatInputchatDisplay 正确赋值

    OnStartLocalPlayer() 中确保它们被正确初始化,并且在 SendMessage() 中也检查是否为 null。

    public override void OnStartLocalPlayer()
    {
        chatInput = GameObject.FindGameObjectWithTag("ChatInput").GetComponent<TMP_InputField>();
        chatDisplay = GameObject.FindGameObjectWithTag("ChatDisplay").GetComponent<Text>();
    
        if (chatInput != null && chatDisplay != null)
        {
            Debug.Log("正常,不为空");
        }
        else
        {
            Debug.LogError("无法找到 ChatInput 或 ChatDisplay!");
        }
    }
    

    ✅ 2. 在 SendMessage() 中再次检查 chatInput

    即使你在 OnStartLocalPlayer() 中赋值了,仍然需要在 SendMessage() 中再次检查是否为 null,防止运行时异常。

    public void SendMessage()
    {
        if (chatInput == null || chatDisplay == null)
        {
            Debug.LogError("chatInput 或 chatDisplay 未正确初始化!");
            return;
        }
    
        if (!gameObject.activeInHierarchy)
        {
            Debug.LogError($"玩家对象 {gameObject.name} 被禁用,无法发送消息");
            return;
        }
    
        if (netIdentity == null)
        {
            Debug.LogError("NetworkIdentity 为 null,对象可能未正确初始化为网络对象");
            return;
        }
    
        if (!isLocalPlayer)
        {
            Debug.Log("我不是本地玩家,不能发送消息");
            return;
        }
    
        string message = chatInput.text.Trim();
        if (!string.IsNullOrEmpty(message))
        {
            CmdSendMessage(message);
            Debug.Log("信息发送到服务器了");
        }
    
        chatInput.text = "";
    }
    

    问题2:点击 Button 后,SendMessage() 中的 if (!gameObject.activeInHierarchy) 报错

    原因分析:

    • 你的脚本挂载在 预制体(Prefab) 上,而预制体本身可能不是激活状态(即 activeInHierarchy 为 false),但它被实例化后成为本地玩家时,应该处于激活状态。
    • 如果你在 OnStartLocalPlayer() 中没有正确设置 chatInputchatDisplay,可能导致后续逻辑出错。

    解决方案:

    ✅ 1. 检查预制体是否正确实例化

    确保你通过 Instantiate 创建本地玩家对象,并将其添加到场景中,否则它不会被激活。

    // 示例:在 NetworkManager 中创建本地玩家
    void Start()
    {
        NetworkManager.singleton.StartClient();
    }
    

    ✅ 2. 使用 IsLocalPlayer 代替 activeInHierarchy

    不要依赖 activeInHierarchy 来判断是否是本地玩家,而是直接使用 isLocalPlayer 属性。

    if (!isLocalPlayer)
    {
        Debug.Log("我不是本地玩家,不能发送消息");
        return;
    }
    

    问题3:netIdentity == null,但你已经添加了网络身份组件

    原因分析:

    • 你可能在 预制体上 添加了 NetworkIdentity,但 没有在实例化时正确设置
    • 或者你没有将预制体设置为“网络对象”,导致 NetworkIdentity 未被正确初始化。

    解决方案:

    ✅ 1. 确保预制体上有 NetworkIdentity 组件

    • 打开 Unity 编辑器,选择你的本地玩家预制体。
    • 确保 NetworkIdentity 组件存在,并且 Assign ID 设置为 Auto 或手动分配。
    • 确保 RPCsCommands 的权限设置正确。

    ✅ 2. 在 OnStartLocalPlayer() 中验证 netIdentity

    public override void OnStartLocalPlayer()
    {
        if (netIdentity == null)
        {
            Debug.LogError("NetworkIdentity 为 null,对象可能未正确初始化为网络对象");
            return;
        }
    
        // 其他代码...
    }
    

    ✅ 3. 确保预制体被正确实例化为网络对象

    • 如果你是通过 NetworkManager 创建本地玩家,请确保调用了正确的 API:
    NetworkManager.singleton.OnClientConnect(); // 或其他相关方法
    

    ✅ 总结:关键修复点

    1. 确保 chatInputchatDisplay 正确赋值

    • OnStartLocalPlayer() 中赋值,并在 SendMessage() 中再次检查。

    2. 不要依赖 activeInHierarchy 判断本地玩家

    • 改用 isLocalPlayer 属性。

    3. 确保预制体有 NetworkIdentity 并被正确实例化

    • 检查预制体配置和实例化逻辑。

    ✅ 修改后的完整代码(部分优化)

    using Mirror;
    using UnityEngine;
    using TMPro;
    using UnityEngine.UI;
    
    public class Sent_Messages : NetworkBehaviour
    {
        [Header("简单聊天设置")]
        private TMP_InputField chatInput;
        private Text chatDisplay;
        public int maxMessages = 10;
    
        public override void OnStartLocalPlayer()
        {
            chatInput = GameObject.FindGameObjectWithTag("ChatInput").GetComponent<TMP_InputField>();
            chatDisplay = GameObject.FindGameObjectWithTag("ChatDisplay").GetComponent<Text>();
    
            if (chatInput != null && chatDisplay != null)
            {
                Debug.Log("正常,不为空");
            }
            else
            {
                Debug.LogError("无法找到 ChatInput 或 ChatDisplay!");
            }
    
            if (netIdentity == null)
            {
                Debug.LogError("NetworkIdentity 为 null,对象可能未正确初始化为网络对象");
                return;
            }
        }
    
        public void SendMessage()
        {
            if (chatInput == null || chatDisplay == null)
            {
                Debug.LogError("chatInput 或 chatDisplay 未正确初始化!");
                return;
            }
    
            if (!isLocalPlayer)
            {
                Debug.Log("我不是本地玩家,不能发送消息");
                return;
            }
    
            string message = chatInput.text.Trim();
            if (!string.IsNullOrEmpty(message))
            {
                CmdSendMessage(message);
                Debug.Log("信息发送到服务器了");
            }
    
            chatInput.text = "";
        }
    
        [Command]
        void CmdSendMessage(string message)
        {
            string playerName = $"玩家{connectionToClient.connectionId}";
            RpcReceiveMessage(playerName, message);
        }
    
        [ClientRpc]
        void RpcReceiveMessage(string playerName, string message)
        {
            string newMessage = $"\n{playerName}: {message}";
            chatHistory += newMessage;
    
            string[] lines = chatHistory.Split('\n');
            if (lines.Length > maxMessages)
            {
                chatHistory = string.Join("\n", lines, lines.Length - maxMessages, maxMessages);
            }
    
            chatDisplay.text = chatHistory;
        }
    }
    

    如需进一步调试,可以使用以下命令查看网络对象状态:

    Debug.Log($"NetIdentity: {netIdentity}, IsLocalPlayer: {isLocalPlayer}, ActiveInHierarchy: {gameObject.activeInHierarchy}");
    

    希望这些修改能解决你的问题!如果还有其他疑问,欢迎继续提问。

    评论

报告相同问题?

问题事件

  • 创建了问题 11月30日