首页 > 解决方案 > 将 ScriptableObjects 加载到单个预制件/多个预制件的最佳实践是什么?

问题描述

(ScriptableObject = SO)

我最近一直在尝试了解 ScriptableObjects,并在 Unity 中建立了一个项目,我只是想射击生成的传入敌人。我已经设置好了,所以我可以通过 SO:s 创建不同的敌人,但注意到我仍然需要为每个敌人创建一个预制件。因此,在没有太多运气谷歌搜索之后,我想问是否有办法为每个生成的敌人加载特定的 SO:s?

假设我有一个 spawnmanager,它根据生成的百分比来生成不同的敌人。是否可以在运行时通过脚本检查生成哪个敌人的概率,然后加载/查找通过 SO:s 创建的特定数据?

标签: c#unity3d

解决方案


TLDR : 使用 Monobehvaiour 来实现敌人。使用 SO 只是为了设置其数据或实现其部分逻辑。所以最好不要为每个敌人生成一个 SO。您将敌人生成为预制件,在他们的预制件中您可以添加一个或多个 SO 以实现组合。

为了理解这一点,我可以解释 SO 的历史,这样你就可以找到所有常用的实现。

阶段 1 - 数据容器

最初 SO 用于存储(非可变)数据。他们可以作为模板工作。

IE,你有一个武器 SO,你为每个武器实例化一个。通过这种方式,您可以将武器附加到一个单位。

    public class Attacker : MonoBehaviour
    {
        public Weapon weapon;

        public void Attack(IDamageGetter target) => target.GetDamage(weapon.damage);
    }

    public interface IDamageGetter
    {
       void GetDamage(int Damage);
    }

    [CreateAssetMenu(menuName = "Base Weapon")]
    public class Weapon : ScriptableObject
    {
        public int damage = 5;
    }

以下是一些关于 SO 用于数据的不错的教程: Customizing UI With Scriptable Objects

第 2 阶段 - 组合的 SO 逻辑

在第一阶段之后,有人指出SO 可以用于许多不同的事情。如此多的开发人员开始在 SO 中添加逻辑。

    public class AttackerWithLogic : MonoBehaviour
    {
        public WeaponWithLogic weapon;

        public void Attack(IDamageGetter target) => weapon.Attack(target);
    }

    public abstract class WeaponWithLogic : ScriptableObject
    {
        public int damage = 5;

        public abstract void Attack(IDamageGetter target);
    }

这样做可以将大量逻辑与单一行为分离,并将任何逻辑附加到 SO 本身。你不会有一个单一行为的敌人,它有 2 个派生类enemyWithAxe 和enemyWithSword。您将有一个 Monobehaviour 敌人,您可以附加武器斧或武器剑。

了解此逻辑并将事件应用于 AI 的一个重要来源是本系列教程。 具有可编写脚本对象的可插拔 Ai

第 3 阶段 - 反应式可编写脚本的对象

最后,我们有了这种使用Unite Austin 2017中描述的 Scriptable 对象的新方法。

这比其他想法更高级,并且需要了解观察者模式和 C# 事件系统,但它是使用可脚本化对象的好方法。

这可以用作生成器。

    [CreateAssetMenu(menuName = "EnemySpawner")]
    public class EnemySpawner : ScriptableObject
    {
        public event Action<Enemy> OnSpawn;

        public Enemy enemyPrefab;

        public void SpawnEnemyWithSword()
        {
            var enemy = Instantiate(enemyPrefab);
            //..
            //code to setup sword 
            //..
            OnSpawn?.Invoke(enemy);
        }

        public void SpawnEnemyWithAxe()
        {
            //any other logic with the same event
        }
    }

实现 OOP 模式非常棒我仍然不确定新 ECS 的未来会怎样,但我希望他们也能有一席之地。

综上所述

我是 SO 的忠实粉丝,但在某些情况下,您想为敌人生成一个 SO。无论如何,SO不应该在任何地方取代MonoBehaviours。我会使用 MonoBehaviour 来代表敌人。

如果您仍然需要为每个单一行为创建一个 SO,您可以在清醒时使用 ScrictableObject.CreateInstance 创建它们,但在这种情况下,它们与基本可序列化类没有太大区别。

您仍然可以实现 SO 来设置其数据(例如生命值或武器伤害),处理其部分逻辑(例如为每个武器添加效果),或处理生成机制的事件。我强烈建议看看我在这里分享的链接。根据您的经验选择从哪里开始,第 1 阶段的视频更容易,而第 3 阶段的视频最先进。(反正都很简单,只有最后一个需要了解c#的事件)


推荐阅读