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

Unity中非MonoBehaviour类Inspector序列化值保留问题咨询

解决方案与最佳实践

1. 优先复用Unity序列化的实例(最优方案)

Unity会自动为标记[Serializable]的非MonoBehaviour字段创建实例并填充Inspector编辑的值,不要轻易重新new实例。如果需要传递依赖(比如PlayerController),用初始化方法替代带参构造函数即可:

public class PlayerController : MonoBehaviour
{
    public GroundDetection groundDetection;
   
    void Awake()
    {
        // 给已序列化的实例注入依赖,无需重新创建
        groundDetection.Setup(this);
    }
}

[System.Serializable]
public class GroundDetection
{
    public float RayDistance;
    public LayerMask GroundLayer;
    private PlayerController _owner;

    // 必须显式保留无参构造函数(Unity序列化要求)
    public GroundDetection() {}

    // 用初始化方法传递依赖,替代带参构造函数
    public void Setup(PlayerController owner)
    {
        _owner = owner;
    }

    // 带参构造函数可保留,供代码手动创建实例的场景使用
    public GroundDetection(float rayDistance, LayerMask groundLayer, PlayerController owner)
    {
        RayDistance = rayDistance;
        GroundLayer = groundLayer;
        _owner = owner;
    }
}

2. 通用深拷贝工具(需创建新实例时)

如果必须创建全新实例且保留Inspector配置,用Unity自带的JsonUtility实现通用深拷贝,避免每个类都写拷贝构造函数:

public static class SerializedObjectHelper
{
    // 通用深拷贝方法,支持所有[Serializable]标记的类
    public static T DeepCopy<T>(T original) where T : class, new()
    {
        if (original == null) return null;
        var json = JsonUtility.ToJson(original);
        return JsonUtility.FromJson<T>(json);
    }
}

// 使用示例
public class PlayerController : MonoBehaviour
{
    public GroundDetection groundDetection;
   
    void Start()
    {
        // 创建与Inspector配置一致的新实例
        var newDetection = SerializedObjectHelper.DeepCopy(groundDetection);
        newDetection.Setup(this);
    }
}

3. 用ScriptableObject存储可复用配置

如果GroundDetection的配置需要在多个Player间复用,将其改为ScriptableObject,既保留可视化编辑,又避免实例化时丢失值:

// 可编辑的配置资源
[CreateAssetMenu(fileName = "GroundDetectionConfig", menuName = "Configs/Ground Detection")]
public class GroundDetectionConfig : ScriptableObject
{
    public float RayDistance;
    public LayerMask GroundLayer;
}

// PlayerController中引用配置
public class PlayerController : MonoBehaviour
{
    public GroundDetectionConfig groundConfig;
    private GroundDetection _groundDetection;
   
    void Awake()
    {
        // 用配置实例化逻辑类
        _groundDetection = new GroundDetection(groundConfig.RayDistance, groundConfig.GroundLayer, this);
    }
}

// 纯逻辑类无需标记Serializable
public class GroundDetection
{
    public float RayDistance;
    public LayerMask GroundLayer;
    private PlayerController _owner;

    public GroundDetection(float rayDistance, LayerMask groundLayer, PlayerController owner)
    {
        RayDistance = rayDistance;
        GroundLayer = groundLayer;
        _owner = owner;
    }
}

最佳实践总结

  • 必须保留无参构造函数:如果自定义了带参构造函数,一定要显式添加无参构造函数,否则Unity无法完成序列化。
  • 避免构造函数内的复杂逻辑:构造函数只做简单字段赋值,依赖注入、资源加载等逻辑放到Setup/Initialize方法中执行(序列化时无参构造函数会被调用,此时依赖对象可能未初始化)。
  • 能用原实例就不新建:Unity已经帮你完成了实例化和字段填充,除非业务必须,不要重新new。
  • 复用配置用ScriptableObject:多对象共享的配置优先用ScriptableObject存储,便于统一管理和修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 15:27:49