Unity中PanelRaycaster.Raycast空引用异常问题求助
解决方案:Unity 2020.2.1f1 PanelRaycaster空引用错误修复
问题核心
这是Unity 2020.2.1f1版本UI系统的已知bug:当非激活状态的Graphic组件(如Image)被设置raycastTarget = true后销毁,GraphicRegistry会残留该对象的无效引用,后续PanelRaycaster执行射线检测时触发NullReferenceException。
临时规避方案
1. 正确修改非激活对象的raycastTarget
不要直接在非激活对象上修改raycastTarget,先激活对象完成修改,再恢复非激活状态:
var obj = new GameObject("Image"); // 组件初始化逻辑 obj.AddComponent<RectTransform>(); obj.AddComponent<CanvasRenderer>(); var img = obj.AddComponent<Image>(); img.raycastTarget = false; obj.transform.SetParent(this.transform); // 先激活对象再修改raycastTarget obj.SetActive(true); img.raycastTarget = true; obj.SetActive(false); // 后续销毁逻辑 Destroy(obj);
2. 手动清理GraphicRegistry残留引用
通过反射强制清理GraphicRegistry中已销毁的无效引用,可在场景切换(如返回主菜单再加载游戏场景)时执行:
using System.Reflection; using System.Collections.Generic; using UnityEngine.UI; public static class GraphicRegistryCleanup { public static void ClearInvalidReferences() { var graphicRegistryType = typeof(GraphicRegistry); var raycastGraphicsField = graphicRegistryType.GetField("s_RaycastGraphics", BindingFlags.Static | BindingFlags.NonPublic); if (raycastGraphicsField == null) return; var raycastGraphics = raycastGraphicsField.GetValue(null) as Dictionary<Canvas, HashSet<Graphic>>; if (raycastGraphics == null) return; foreach (var pair in raycastGraphics) { if (pair.Key == null) continue; var invalidGraphics = new List<Graphic>(); foreach (var graphic in pair.Value) { if (graphic == null || graphic.canvasRenderer == null || !graphic.canvasRenderer.gameObject) { invalidGraphics.Add(graphic); } } foreach (var graphic in invalidGraphics) { pair.Value.Remove(graphic); } } } }
使用方式:在场景加载前/后调用GraphicRegistryCleanup.ClearInvalidReferences();
3. 自定义PanelRaycaster过滤无效结果
替换默认的PanelRaycaster,在射线检测后自动过滤无效的RaycastResult:
using UnityEngine.UIElements; using UnityEngine.EventSystems; using System.Collections.Generic; public class FixedPanelRaycaster : PanelRaycaster { public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList) { base.Raycast(eventData, resultAppendList); // 移除无效的射线检测结果 resultAppendList.RemoveAll(r => r.gameObject == null || !r.isValid); } }
将场景中的PanelRaycaster组件替换为这个自定义组件即可。
根源修复思路(需修改Unity UI源码)
若有能力修改Unity UI源码,可按照以下方向修复:
- 修改
Graphic.set_raycastTarget:添加对象激活状态判断,非激活时不执行注册逻辑
public bool raycastTarget { get => m_RaycastTarget; set { if (m_RaycastTarget == value) return; m_RaycastTarget = value; // 仅在对象激活时更新GraphicRegistry if (IsActive()) { if (value) GraphicRegistry.RegisterRaycastGraphicForCanvas(canvas, this); else GraphicRegistry.UnregisterRaycastGraphicForCanvas(canvas, this); } SetMaterialDirty(); } }
- 修改
GraphicRegistry.UnregisterGraphicForCanvas:强制清理RaycastGraphic引用,无需依赖普通Graphic列表的判断
public static void UnregisterGraphicForCanvas(Canvas c, Graphic graphic) { if (c == null) return; HashSet<Graphic> graphics; s_Graphics.TryGetValue(c, out graphics); graphics?.Remove(graphic); // 无论是否在普通Graphic列表中,都强制清理射线检测列表 UnregisterRaycastGraphicForCanvas(c, graphic); }
内容的提问来源于stack exchange,提问作者Artem Teslia
相关产品推荐
相关产品推荐

