ECS的Systems主要分为以下五类:
- InitializeSystem,需要继承IInitializeSystem接口,实现Initialize()方法,该方法在程序刚开始运行时触发一次,类似于Unity的Start方法。
public class InitPlayerSystem : IInitializeSystem
{
private GameContext game;
//获取GameContext
public InitPlayerSystem(Contexts contexts)
{
game = contexts.game;
}
//创建一个entity,并为entity添加某些component
public void Initialize()
{
var entity = game.CreateEntity();
entity.AddAssetName("Player");
entity.AddPosition(Vector3.zero);
entity.AddSpeed(0,0.03f);
entity.isSpecial = true;
}
}
- ExecuteSystem,需要继承IExecuteSystem接口,实现Execute()方法,在这个方法里的代码需要每帧执行,类似于Unity的Update方法。通常用于判断用户输入(Unity.Input)行为,主角移动等情况。
public class InputSystem : IExecuteSystem
{
private InputContext input;
//获取InputContext
public InputSystem(Contexts contexts)
{
input = contexts.input;
}
//每帧执行,此功能是按下鼠标左键
public void Execute()
{
input.isPlayerMoving = Input.GetMouseButton(0);
}
}
- CleanupSystem,需要继承ICleanupSystem接口,实现Cleanup()方法,这个方法里的代码会在每帧结束时执行,通常用于清理只存在一帧里的Entity等,用法与ExecuteSystem类似。
- TearDownSystem,需要继承ITearDownSystem接口,实现TearDown()方法,这个方法会在程序结束时执行一次,类似与Unity的OnDestroy方法,一般用于释放某些资源等等,用法与InitializeSystem类似。
- ReactiveSystem该System继承自IReactiveSystem接口,是一种特殊的ExecuteSystem,可以说是ExecuteSystem和Collector的结合体。
如何使用ReactiveSystem
下面是一个典型的ReactiveSystem使用
public class RemoveViewSystem : ReactiveSystem<GameEntity>
{
public RemoveViewSystem(Contexts contexts) : base(contexts.game)
{
}
protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
{
return context.CreateCollector(new TriggerOnEvent<GameEntity>(GameMatcher.Destroy,GroupEvent.Added));
}
protected override bool Filter(GameEntity entity)
{
return entity.hasView && entity.isDestroy;
}
protected override void Execute(List<GameEntity> entities)
{
foreach (var e in entities)
{
Object.DestroyImmediate(e.view.obj);
e.RemoveView();
}
}
}
其中GetTrigger,Filter,Execute是父类ReactiveSystem的抽象方法,子类继承是需要实现,同时父类还包含了一个带参数的构造函数,所以子类必须显示定义构造函数。 接下来看一下弗雷的代码:
public abstract class ReactiveSystem<TEntity> : IReactiveSystem, IExecuteSystem, ISystem where TEntity : class, IEntity
{
private readonly ICollector<TEntity> _collector;
private readonly List<TEntity> _buffer;
private string _toStringCache;
protected ReactiveSystem(IContext<TEntity> context)
{
this._collector = this.GetTrigger(context);
this._buffer = new List<TEntity>();
}
protected ReactiveSystem(ICollector<TEntity> collector)
{
this._collector = collector;
this._buffer = new List<TEntity>();
}
/// Specify the collector that will trigger the ReactiveSystem.
protected abstract ICollector<TEntity> GetTrigger(IContext<TEntity> context);
/// This will exclude all entities which don't pass the filter.
protected abstract bool Filter(TEntity entity);
protected abstract void Execute(List<TEntity> entities);
/// Activates the ReactiveSystem and starts observing changes
/// based on the specified Collector.
/// ReactiveSystem are activated by default.
public void Activate()
{
this._collector.Activate();
}
/// Deactivates the ReactiveSystem.
/// No changes will be tracked while deactivated.
/// This will also clear the ReactiveSystem.
/// ReactiveSystem are activated by default.
public void Deactivate()
{
this._collector.Deactivate();
}
/// Clears all accumulated changes.
public void Clear()
{
this._collector.ClearCollectedEntities();
}
/// Will call Execute(entities) with changed entities
/// if there are any. Otherwise it will not call Execute(entities).
public void Execute()
{
if (this._collector.count == 0)
return;
foreach (TEntity collectedEntity in this._collector.collectedEntities)
{
if (this.Filter(collectedEntity))
{
collectedEntity.Retain((object) this);
this._buffer.Add(collectedEntity);
}
}
this._collector.ClearCollectedEntities();
if (this._buffer.Count == 0)
return;
try
{
this.Execute(this._buffer);
}
finally
{
for (int index = 0; index < this._buffer.Count; ++index)
this._buffer[index].Release((object) this);
this._buffer.Clear();
}
}
public override string ToString()
{
if (this._toStringCache == null)
this._toStringCache = "ReactiveSystem(" + this.GetType().Name + ")";
return this._toStringCache;
}
~ReactiveSystem()
{
this.Deactivate();
}
}
- 父类的构造函数首先会调用GetTrigger方法,该方法是一个抽象函数,需要我们自行实现,并且返回值是一个Collecotr,关于Collector的使用,查看上一篇Collector的简介。
- 然后是非抽象函数Execute(),该方法应该是每帧都会调用,与ExecuteSystem类似,该方法首先遍历了Collector收集的额entities,然后清空了Collector收集的entities,最后执行了我们实现的抽象方法,带参数的Execute(List entities)。
- 所以ReactiveSystem和Collector结合使用非常强大。