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

Unity中能否创建含可序列化ScriptableObject数组的ScriptableObject?

方案可行性分析与替代方案

Great question! Let's break this down clearly:

原方案的问题(不完全可行)

Your current setup has a few critical issues that will cause headaches in the Unity Inspector and serialization:

  1. Redundant [Serializable]: Marking a ScriptableObject subclass with [System.Serializable] is unnecessary—ScriptableObject is already handled by Unity's serialization system, so this does nothing here.
  2. Abstract Class Serialization Limitation: Since BehaviourSO is an abstract class, Unity's default serialization won't properly render the TransitionalBehaviour array in the Inspector. You won't be able to select or create concrete subclass instances for the array, which defeats the purpose of having the array in the first place.
  3. Potential Circular References: Having a ScriptableObject reference an array of its own type opens the door to accidental circular references (e.g., Behaviour A references Behaviour B, which references back to A). While Unity can handle this in some cases, it often leads to weird Inspector glitches or load-time issues.

1. Use [SerializeReference] (Unity 2020.1+) – Best Modern Approach

This is the cleanest solution if you're on a recent Unity version. [SerializeReference] enables polymorphic serialization, meaning Unity can properly handle abstract class/interface references in arrays and show them correctly in the Inspector.

Here's the adjusted code:

public abstract class BehaviourSO : ScriptableObject 
{
    public string Name = "New Behaviour";
    [SerializeReference]
    public BehaviourSO[] TransitionalBehaviour = new BehaviourSO[1];
    public abstract BehaviourAI Initialize();
}

Key Notes:

  • Add [System.Serializable] to all concrete subclasses of BehaviourSO (required for SerializeReference to recognize them)
  • In the Inspector, you'll now see a dropdown for each array element where you can select existing BehaviourSO instances or create new ones directly
  • This fully supports polymorphism, so you can mix different subclasses in the same array

2. Object Array with Validation (For Older Unity Versions)

If you're stuck on a pre-2020.1 Unity version, use an Object[] as a backing field, then add type validation to ensure only BehaviourSO instances are allowed.

using System.Collections.Generic;
using UnityEngine;

public abstract class BehaviourSO : ScriptableObject 
{
    public string Name = "New Behaviour";
    [SerializeField, Tooltip("Only add instances of BehaviourSO subclasses!")]
    private Object[] _transitionalBehaviours = new Object[1];

    // Type-safe public accessor
    public BehaviourSO[] TransitionalBehaviour
    {
        get
        {
            var validBehaviours = new List<BehaviourSO>();
            foreach (var obj in _transitionalBehaviours)
            {
                if (obj is BehaviourSO behaviour)
                {
                    validBehaviours.Add(behaviour);
                }
            }
            return validBehaviours.ToArray();
        }
    }

    public abstract BehaviourAI Initialize();

    // Validate in edit mode to catch invalid entries
    private void OnValidate()
    {
        var validObjects = new List<Object>();
        foreach (var obj in _transitionalBehaviours)
        {
            if (obj is BehaviourSO)
            {
                validObjects.Add(obj);
            }
            else if (obj != null)
            {
                Debug.LogWarning($"Object {obj.name} is not a BehaviourSO subclass – removed from array", this);
            }
        }
        _transitionalBehaviours = validObjects.ToArray();
    }
}

Key Notes:

  • The OnValidate method ensures only valid BehaviourSO instances stay in the array, even if someone accidentally adds the wrong object
  • The public TransitionalBehaviour property gives you type-safe access without exposing the raw Object[]

3. Serializable Wrapper Container (Alternative for Older Versions)

If you prefer a more explicit approach without SerializeReference, create a simple serializable container class to wrap each BehaviourSO reference:

using UnityEngine;

[System.Serializable]
public class BehaviourSOContainer
{
    public BehaviourSO Behaviour;
}

public abstract class BehaviourSO : ScriptableObject 
{
    public string Name = "New Behaviour";
    public BehaviourSOContainer[] TransitionalBehaviour = new BehaviourSOContainer[1];
    public abstract BehaviourAI Initialize();
}

Key Notes:

  • Unity will render the array correctly in the Inspector, showing a foldout for each container where you can select a BehaviourSO instance
  • The downside is you'll need to access container.Behaviour to get the actual reference, which adds a tiny bit of boilerplate code

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:20:39