Unity中get_isActiveAndEnabled报错,ROS2订阅数据无法显示求助
解决Unity中ROS2订阅回调操作UI的主线程错误
错误原因
ROS2 for Unity的订阅回调函数运行在子线程中,而Unity的所有UI组件(包括TextMeshPro的TMP_Text)只能在主线程中访问和修改,直接在回调里调用subscrib_text.SetText()就会触发UnityException: get_isActiveAndEnabled can only be called from the main thread错误。
修复方案
用线程安全队列缓存订阅到的消息,再在Unity主线程执行的Update()方法中读取队列并更新UI。修改后的代码如下:
using System; using UnityEngine; using TMPro; using System.Collections.Concurrent; namespace ROS2 { /// <summary> /// ROS2订阅消息并显示到TextMeshPro的示例 /// </summary> public class ROS2ListenerExample : MonoBehaviour { [SerializeField] private TMPro.TMP_Text subscrib_text; private ROS2UnityComponent ros2Unity; private ROS2Node ros2Node; private ISubscription<std_msgs.msg.String> chatter_sub; // 线程安全队列,缓存子线程收到的消息 private ConcurrentQueue<string> receivedMessages = new ConcurrentQueue<string>(); void Start() { ros2Unity = GetComponent<ROS2UnityComponent>(); } void Update() { // 初始化ROS2节点(仅执行一次) if (ros2Node == null && ros2Unity.Ok()) { ros2Node = ros2Unity.CreateNode("ROS2UnityListenerNode"); chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>( "chatter", msg => { // 子线程仅缓存消息,不操作UI receivedMessages.Enqueue(msg.Data); }); } // 主线程读取队列并更新UI if (receivedMessages.TryDequeue(out string latestMsg)) { subscrib_text.SetText($"Unity listener heard: [{latestMsg}]"); } } } } // namespace ROS2
关键修改说明
- 使用
ConcurrentQueue:这是线程安全的队列类型,避免子线程和主线程同时操作队列时出现数据竞争问题。 - 拆分逻辑:订阅回调只负责将消息存入队列,UI更新逻辑移到
Update()中执行(Update()始终在Unity主线程运行)。
额外注意事项
- 确保在Unity编辑器中已将
subscrib_text赋值给场景中的TextMeshPro组件。 - 如果订阅消息频率极高,可给队列添加长度限制(比如超过10条则清空旧消息),避免内存占用过高。
内容的提问来源于stack exchange,提问作者anmitsu
相关产品推荐
相关产品推荐

