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

Unity正常代码突发NullReferenceException异常求助

解决Unity截图代码的NullReferenceException异常

问题描述

原本可正常运行的Unity代码修改后突发异常,错误信息为:NullReferenceException: Object reference not set to an instance of an object,错误触发在代码第23行。

问题代码

using System;
using System.IO;
using UnityEngine;
using UnityEngine.Rendering;

public class SaveImage : MonoBehaviour {

    public static SaveImage Instance { get; private set; }

    [SerializeField] private Camera screenshotCamera;

    private Action<Texture2D> onScreenshotTaken;

    private void Awake() {
        Instance = this;
        RenderPipelineManager.endFrameRendering += RenderPipelineManager_endFrameRendering;
    }

    private void RenderPipelineManager_endFrameRendering(ScriptableRenderContext arg1, Camera[] arg2) {
        if (onScreenshotTaken != null) {
            RenderTexture renderTexture = screenshotCamera.targetTexture;

            Texture2D renderResult = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
            Rect rect = new Rect(0, 0, renderTexture.width, renderTexture.height);
            renderResult.ReadPixels(rect, 0, 0);
            renderResult.Apply();

            RenderTexture.ReleaseTemporary(renderTexture);
            screenshotCamera.targetTexture = null;

            onScreenshotTaken(renderResult);
            onScreenshotTaken = null;

            Destroy(renderResult);
        }
    }

    public void Save() {

       TakeScreenshot(Screen.width, Screen.height, (Texture2D screenshotTexture) => {
            string pathfile = Application.persistentDataPath + "/NGallery/ImageData.png";
            string imagestorage = pathfile;
            byte[] screenshotByteArray = screenshotTexture.EncodeToPNG();
            File.WriteAllBytes(imagestorage, screenshotByteArray);
        });
    }

    private void TakeScreenshot(int width, int height, Action<Texture2D> onScreenshotTaken) {
        screenshotCamera.targetTexture = RenderTexture.GetTemporary(width, height, 16);
        this.onScreenshotTaken = onScreenshotTaken;
    }
}

已尝试操作及结果

  • 加载旧Unity项目:运行正常
  • 重新创建项目:触发错误
  • 复制旧项目:触发错误
  • 新项目中Debug.Log(renderTexture)显示该对象为null
  • 旧项目中Debug.Log(renderTexture)显示该对象不为null且正常运行

解决方案

问题根源

第23行报错是因为renderTexture为null,核心原因有两个:

  1. 事件触发时机不匹配:新项目若使用URP/HDRP管线,RenderPipelineManager.endFrameRendering可能在TakeScreenshot设置相机目标纹理前就触发,或者事件每帧执行,第二次执行时纹理已被释放。
  2. 缺少必要的null校验:代码未判断screenshotCamera或renderTexture是否为null,一旦引用丢失或未初始化就会报错。

修复步骤

1. 增加全链路Null校验

在使用关键对象前添加null判断,避免直接访问空对象属性:

private void RenderPipelineManager_endFrameRendering(ScriptableRenderContext arg1, Camera[] arg2) {
    // 增加screenshotCamera和renderTexture的null校验
    if (onScreenshotTaken != null && screenshotCamera != null && screenshotCamera.targetTexture != null) {
        RenderTexture renderTexture = screenshotCamera.targetTexture;
        // 后续原有代码保持不变
    }
}

2. 避免重复请求截图

防止同一帧内多次调用TakeScreenshot导致的状态混乱:

private void TakeScreenshot(int width, int height, Action<Texture2D> onScreenshotTaken) {
    // 如果已有待处理的截图请求,直接返回
    if (this.onScreenshotTaken != null) return;
    screenshotCamera.targetTexture = RenderTexture.GetTemporary(width, height, 16);
    this.onScreenshotTaken = onScreenshotTaken;
}

3. 适配SRP管线的正确事件(推荐)

放弃全局的endFrameRendering事件,改为监听目标截图相机的渲染完成事件,确保只在相机渲染结束后执行截图逻辑:

private void Awake() {
    Instance = this;
    // 替换原事件注册,改为监听目标相机的PostRender事件
    if (screenshotCamera != null) {
        screenshotCamera.onPostRender += OnScreenshotCameraPostRender;
    }
}

private void OnScreenshotCameraPostRender() {
    if (onScreenshotTaken != null && screenshotCamera.targetTexture != null) {
        RenderTexture renderTexture = screenshotCamera.targetTexture;
        Texture2D renderResult = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
        Rect rect = new Rect(0, 0, renderTexture.width, renderTexture.height);
        renderResult.ReadPixels(rect, 0, 0);
        renderResult.Apply();

        RenderTexture.ReleaseTemporary(renderTexture);
        screenshotCamera.targetTexture = null;

        onScreenshotTaken(renderResult);
        onScreenshotTaken = null;

        Destroy(renderResult);
    }
}

// 记得在销毁时移除事件监听,避免内存泄漏
private void OnDestroy() {
    if (screenshotCamera != null) {
        screenshotCamera.onPostRender -= OnScreenshotCameraPostRender;
    }
}

4. 检查相机引用

确保在Inspector面板中,SaveImage脚本的screenshotCamera字段已正确拖入场景中的相机对象,且该相机的Target Texture初始状态为null。

内容的提问来源于stack exchange,提问作者Zelkey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 19:31:04