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,核心原因有两个:
- 事件触发时机不匹配:新项目若使用URP/HDRP管线,
RenderPipelineManager.endFrameRendering可能在TakeScreenshot设置相机目标纹理前就触发,或者事件每帧执行,第二次执行时纹理已被释放。 - 缺少必要的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
相关产品推荐
相关产品推荐

