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

Unity中如何让MovementPattern子类自动填充对应ScriptableObject子类数据?

太空侵略者项目中移动模式与数据类的优化实现方案

问题背景

我正在开发一款类似太空侵略者的项目,需要给敌人、子弹等对象分配移动模式。已创建MovementPattern基类,以及作为ScriptableObject的MovementPatternData基类,计划用子类存储对应移动模式的参数。

目前实现的SinusMovementPattern方式繁琐,需额外声明子类数据字段,且忽略了基类的MovementPatternData。尝试在字段声明处初始化子类数据对象时会报错,无法在编辑器模式下提前初始化并修改数据。

需求:创建MovementPattern的子类对象时,自动填充对应的MovementPatternData子类对象,替换原有的基类数据类型,需基于自定义编辑器、ScriptableObject和[SerializeReference]给出具体实现指导。


实现方案

一、重构数据类与基类

首先修正MovementPatternData的序列化逻辑,利用[SerializeReference]实现多态序列化支持:

1. 修正MovementPatternData基类与子类

// ScriptableObject自带序列化支持,无需[Serializable]标签
public class MovementPatternData : ScriptableObject
{
    [SerializeField] protected float moveSpeed = 5f;
    public float MoveSpeed => moveSpeed;
}

// 添加菜单,方便在编辑器中直接创建数据资产
[CreateAssetMenu(fileName = "SinusMovementData", menuName = "Movement Patterns/Sinus")]
public class SinusMovementPatternData : MovementPatternData
{
    [SerializeField] public float amplitude = 1f;
    [SerializeField] public float frequency = 1f;
    [SerializeField] public Vector2 direction = Vector2.down;
}

2. 重构MovementPattern基类

[RequireComponent(typeof(Rigidbody2D))]
public class MovementPattern : MonoBehaviour
{
    protected Rigidbody2D rb;

    // [SerializeReference]支持多态序列化,允许编辑器中直接赋值子类对象
    [SerializeReference]
    [SerializeField] protected MovementPatternData movementPatternData;
            
    protected virtual void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        // 编辑器模式下自动补全数据对象
        #if UNITY_EDITOR
        if (movementPatternData == null)
        {
            AutoAssignData();
        }
        #endif
    }

    // 子类重写此方法,指定对应的数据类型
    protected virtual void AutoAssignData() { }

    public virtual void SetMovementPatternData(MovementPatternData newMovementPatternData)
    {
        movementPatternData = newMovementPatternData;
    }
    
    public virtual MovementPatternData GetMovementPatternData()
    {
        return movementPatternData;
    }
}

二、实现子类的自动数据分配

以SinusMovementPattern为例,重写AutoAssignData方法,在编辑器模式下自动创建并赋值对应的数据资产:

public class SinusMovementPattern : MovementPattern
{
    protected override void AutoAssignData()
    {
        #if UNITY_EDITOR
        // 创建对应的数据实例
        SinusMovementPatternData data = ScriptableObject.CreateInstance<SinusMovementPatternData>();
        data.name = $"SinusData_{gameObject.name}";
        movementPatternData = data;

        // 将资产保存到项目目录,避免退出编辑器后丢失
        UnityEditor.AssetDatabase.CreateAsset(data, $"Assets/MovementPatterns/{data.name}.asset");
        UnityEditor.AssetDatabase.SaveAssets();
        #endif
    }

    // 强类型Set方法,方便外部调用
    public void SetMovementPatternData(SinusMovementPatternData newData)
    {
        base.SetMovementPatternData(newData);
    }

    // 实现正弦移动逻辑
    protected void Update()
    {
        if (movementPatternData is SinusMovementPatternData sinusData)
        {
            Vector2 currentPos = rb.position;
            float offset = Mathf.Sin(Time.time * sinusData.frequency) * sinusData.amplitude;
            Vector2 newPos = currentPos + sinusData.direction * sinusData.MoveSpeed * Time.deltaTime;
            newPos.x += offset; // 横向正弦偏移,可按需调整
            rb.MovePosition(newPos);
        }
    }
}

三、自定义编辑器优化(可选)

创建自定义编辑器,提升组件添加时的操作体验:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MovementPattern), true)]
public class MovementPatternEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        MovementPattern pattern = (MovementPattern)target;
        if (pattern.GetMovementPatternData() == null)
        {
            if (GUILayout.Button("自动创建对应移动数据"))
            {
                pattern.AutoAssignData();
                EditorUtility.SetDirty(pattern);
            }
        }
    }

    // 添加组件菜单,自动创建数据
    [MenuItem("GameObject/Add Component/Movement Patterns/Sinus Movement")]
    public static void AddSinusMovement()
    {
        GameObject selected = Selection.activeGameObject;
        if (selected != null)
        {
            SinusMovementPattern pattern = selected.AddComponent<SinusMovementPattern>();
            pattern.AutoAssignData();
            EditorUtility.SetDirty(pattern);
        }
    }
}

注意事项

  1. [SerializeReference]需要Unity 2020.1及以上版本支持,请确保Unity版本符合要求。
  2. 自动创建的ScriptableObject资产必须保存到项目目录,否则退出编辑器后会丢失,代码中已包含保存逻辑。
  3. 也可手动通过右键菜单创建SinusMovementPatternData资产,再拖拽到组件的movementPatternData字段中使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 03:02:26