听起来你要找的是ScriptableObject!
这些实例是 assets,因此它们不在场景中,而是在 Assets 文件夹中,并且基本上表现得有点像 prefabs,除了:它们已经存在,不再需要实例化。
大多数情况下,它们仅用作可配置的数据容器。他们有一个 Inspector,因此您可以轻松地用您想要的数据和对其他资产的引用(例如,您案例中的相关预制件)填充它们。
但除此之外,您还可以让它们实现类似于您的界面的行为,从而通过使用来自不同 ScriptableObjects 的方法的不同实现来改变场景对象的行为!
对于工厂,您只需要确定使用哪种方法即可使用哪个ScriptableObject 实例,例如通过使用不同的方法或通过Dictionary 填写您的 SO 参考。
作为一个例子,这可能是什么样子(确保每个 MonoBehaviour 和 ScriptableObject 都有其单独的脚本文件并具有匹配的名称)
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
[SerializeField] private ReactorComponentBehaviour _behaviourPrefab;
[SerializeField] private BaseReactorComponent[] _components;
public bool TrySpawn<T>(out T component, out ReactorComponentBehaviour componentBehaviour) where T : IReactorComponent
{
component = default(T);
componentBehaviour = default;
var foundComponent = components.FirstOrDefault(c => c.GetType() == typeof(T));
if(foundComponent == null)
{
Debug.LogError($"No component found of type {T.GetType().Name}!");
return false;
}
// Here Instantiate doesn't spawn anything into the scene but
// rather creates a copy of the ScriptableObject asset
// This is just to avoid that any changes in the fields during the game
// would change the original ScriptableObject asset and thereby ALL related behavior instances
component = Instantiate ( (T) foundComponent);
// This now indeed spawns the related MonoBehaviour + GameOver
componentBehaviour = Instantiate (behaviourPrefab);
componentBehaviour.Init(component);
return true;
}
}
BaseReactorComponent.cs
public abstract class BaseReactorComponent : ScriptableObject, IReactorComponent
{
public abstract void WhateverIReactorComponentNeeds();
// Common fields and methods e.g.
public Sprite Icon;
}
HeatVent.cs
[CreateAssetMenu]
public class HeatVent : BaseReactorComponent
{
public int CoolAmount;
public int HeatCapacity;
public override void WhateverIReactorComponentNeeds ()
{
// Do something
}
}
UraniumCell.cs
[CreateAssetMenu]
public class UraniumCell : BaseReactorComponent
{
public int FuelAmount;
public int HeatProduce;
public int PowerProduce;
public override void WhateverIReactorComponentNeeds ()
{
// Do something
}
}
最后你只需要一个带有
的基础预制件
ReactorComponentBehavior.cs
public class ReactorComponentBehavior : MonoBehaviour
{
[SerializeField] private Image _image;
private IReactorComponent _component;
public void Init(IReactorComponent component)
{
_componemt = component;
// Do other stuff like e.g. adjust visuals according to the component etc
_image.sprite = component.Icon;
}
// And then use whatever this behavior should do with the assigned component
}
所以最后你会像这样使用它
if(spawManagerReference.TrySpawn<HeatVent>(out var component, out var componentBehaviour)
{
// Do something with the behavior e.g. set its position, parent etc
}
else
{
Debug.LogError($"Failed to get a {nameof(HeatVent)}!");
}
如果在某些时候你仍然想要不同的附加行为,你可以让它们从常见的 ReactorComponentBehavior 继承,而是引用 BaseReactorComponent 本身内部的预制件.. 然后每个组件都可以带来自己的预制件,但仍然有一个共同的核心行为