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

Unity:如何捕获Awake()抛出的异常并通知调用方?

解决Unity预制体Awake初始化异常无法被调用方捕获的问题

为什么直接try-catch无效?

Unity在执行Instantiate时会立即触发预制体上的Awake方法,但Unity内部会捕获生命周期函数中的异常,将错误信息输出到控制台后继续执行流程,导致外部调用代码的try-catch块无法捕获到这些异常。这是Unity为避免单个对象初始化失败导致整个游戏崩溃的保护机制。

优雅的解决方案

1. 自定义事件回调(推荐)

将初始化结果通过事件通知调用方,同时在预制体内部处理销毁逻辑,调用方只需订阅事件被动接收结果。

预制体脚本示例:

using System;
using UnityEngine;

public class PrefabInitializer : MonoBehaviour
{
    // 定义初始化完成事件:参数为是否初始化成功
    public event Action<bool> OnInitFinished;

    private void Awake()
    {
        bool initSuccess = true;
        try
        {
            // 执行你的初始化逻辑
            if (CheckInitConditions() == false)
            {
                throw new InvalidOperationException("初始化条件不满足");
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"预制体初始化失败: {ex.Message}\n{ex.StackTrace}");
            initSuccess = false;
        }
        finally
        {
            // 通知调用方结果
            OnInitFinished?.Invoke(initSuccess);
            // 初始化失败则销毁自身
            if (!initSuccess)
            {
                Destroy(gameObject);
            }
        }
    }

    // 模拟初始化条件检查
    private bool CheckInitConditions()
    {
        // 这里替换成你的实际检查逻辑
        return false;
    }
}

调用方代码示例:

using UnityEngine;

public class SpawnManager : MonoBehaviour
{
    public GameObject targetPrefab;

    public void SpawnPrefab()
    {
        GameObject instance = Instantiate(targetPrefab);
        var initializer = instance.GetComponent<PrefabInitializer>();
        
        // 订阅初始化完成事件
        initializer.OnInitFinished += (isSuccess) =>
        {
            if (!isSuccess)
            {
                // 这里处理失败逻辑:比如更新UI、记录统计、触发后续流程等
                Debug.Log("调用方接收到初始化失败通知");
            }
            else
            {
                Debug.Log("预制体初始化成功");
            }
        };
    }
}

2. 手动初始化(替代生命周期函数)

将初始化逻辑从Awake/Start中移出,改为手动调用的方法,这样就能在调用方用try-catch捕获异常,同时控制销毁逻辑。

预制体脚本示例:

using UnityEngine;

public class PrefabInitializer : MonoBehaviour
{
    public void Initialize()
    {
        // 执行初始化逻辑,失败直接抛出异常
        if (CheckInitConditions() == false)
        {
            throw new InvalidOperationException("初始化条件不满足");
        }
    }

    private bool CheckInitConditions()
    {
        return false;
    }
}

调用方代码示例:

using UnityEngine;

public class SpawnManager : MonoBehaviour
{
    public GameObject targetPrefab;

    public void SpawnPrefab()
    {
        GameObject instance = Instantiate(targetPrefab);
        var initializer = instance.GetComponent<PrefabInitializer>();
        
        try
        {
            initializer.Initialize();
        }
        catch (Exception ex)
        {
            Debug.LogError($"初始化失败: {ex.Message}");
            Destroy(instance);
            // 处理失败通知逻辑
            Debug.Log("调用方捕获到初始化异常");
        }
    }
}

3. 延迟检查初始化状态(适合简单场景)

如果不想改动太多代码,可以在预制体脚本中添加一个IsInitialized属性,调用方实例化后延迟一帧检查该属性(确保Awake已执行完毕)。

预制体脚本示例:

using UnityEngine;

public class PrefabInitializer : MonoBehaviour
{
    public bool IsInitialized { get; private set; }

    private void Awake()
    {
        try
        {
            if (CheckInitConditions() == false)
            {
                throw new InvalidOperationException("初始化失败");
            }
            IsInitialized = true;
        }
        catch (Exception ex)
        {
            Debug.LogError(ex.Message);
            IsInitialized = false;
            Destroy(gameObject);
        }
    }

    private bool CheckInitConditions()
    {
        return false;
    }
}

调用方代码示例:

using UnityEngine;

public class SpawnManager : MonoBehaviour
{
    public GameObject targetPrefab;
    private PrefabInitializer initializer;

    public void SpawnPrefab()
    {
        GameObject instance = Instantiate(targetPrefab);
        initializer = instance.GetComponent<PrefabInitializer>();
        
        // 延迟一帧检查(确保Awake执行完成)
        Invoke(nameof(CheckInitStatus), 0);
    }

    private void CheckInitStatus()
    {
        // 注意:如果对象已被销毁,initializer会是null
        if (initializer != null && !initializer.IsInitialized)
        {
            Debug.Log("预制体初始化失败");
        }
    }
}

方案对比

  • 自定义事件:最优雅,符合事件驱动设计,调用方无需主动轮询,耦合度低。
  • 手动初始化:最直接,异常捕获逻辑清晰,适合需要精确控制初始化时机的场景。
  • 延迟检查状态:改动最小,但需要处理对象已销毁的情况,适合简单场景快速适配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:44:52