Unity使用委托事件调用方法时无法修改TextMeshPro文本的问题
问题描述
通过委托事件调用方法时,该方法可正常执行,但在被调用方法中尝试修改TextMeshPro文本却无任何效果。在其他方法中或直接调用该方法时均可正常修改文本,仅通过事件调用时失效,且无任何报错,遍历MatchCode的TextMeshPro组件也无结果。
相关代码
NetworkingManager 脚本
using DarkRift.Client; using System.Net; using System; using DarkRift; using Scripts.Models; using UnityEngine; using System.Text.RegularExpressions; namespace Scripts.Networking { public class NetworkingManager { private static NetworkingManager instance; public bool GotMatch = false; private DarkRiftClient client; public delegate void MatchCreatedEvent(int matchId); public static event MatchCreatedEvent OnMatchCreated; public void ChangeText () { int matchID = 12345; OnMatchCreated?.Invoke(matchID); } } }
NameForm 脚本
using Scripts.Networking; using System; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; public class NameForm : MonoBehaviour { [SerializeField] private TMPro.TMP_InputField InputNameField; public TMPro.TMP_InputField InputMatchId; public TMPro.TMP_Text MatchCode; private bool IsSubmiting = false; private bool LoadingScene = false; public delegate void MatchCodeEvent(int matchId); public static event MatchCodeEvent OnMatchCodeReceived; void Start() { NetworkingManager.OnMatchCreated += MatchCodeReceived; if (InputNameField == null) { throw new System.Exception("missing input field"); } if (InputMatchId == null) { throw new System.Exception("missing input field"); } } public void MatchCodeReceived (int MatchId) { //Debug.Log("MatchCodeReceived: " + MatchId); MatchCode.text = MatchId.ToString(); } }
解决方案
核心原因
DarkRift的网络回调事件默认在非主线程触发,而Unity所有UI操作(包括修改TextMeshPro文本)必须在主线程执行。事件触发时MatchCodeReceived在后台线程运行,修改UI的操作被Unity静默忽略,因此看不到效果也不会报错。
修复步骤
在
NameForm中添加主线程任务队列,用于存放需要在主线程执行的UI操作:private Queue<Action> _mainThreadActions = new Queue<Action>(); private readonly object _queueLock = new object();在
Update方法中处理队列任务(Update始终在主线程执行):void Update() { lock (_queueLock) { while (_mainThreadActions.Count > 0) { _mainThreadActions.Dequeue().Invoke(); } } }修改
MatchCodeReceived,将UI操作加入主线程队列:public void MatchCodeReceived(int MatchId) { lock (_queueLock) { _mainThreadActions.Enqueue(() => { MatchCode.text = MatchId.ToString(); }); } }
额外验证
- 可以在
MatchCodeReceived中添加日志,打印当前线程ID,确认是否为非主线程:
主线程ID通常为1,如果打印的ID不是1,就坐实了线程问题。Debug.Log($"当前线程ID: {System.Threading.Thread.CurrentThread.ManagedThreadId}"); - 确认
NameForm对象在事件触发时没有被销毁,保证事件注册的实例仍然存在。
内容的提问来源于stack exchange,提问作者Keith Power
相关产品推荐
相关产品推荐

