You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 18:22:53