Entitas CookBook 翻译 - 108_reacti
Reactive System 响应式系统
Reactive System是一个当有需要我们处理Entity才会被调用的system。Reactive System在内部使用了一个Collector
的实例来实现(更多的信息可以查看Collector这个章节)。你需要继承ReactiveSystem
这个抽象类来使用它,下面是一个MatchOne里面的ReactiveSystem例子:
using System.Collections.Generic;
using Entitas;
public sealed class DestroySystem : ReactiveSystem<GameEntity> {
public DestroySystem(Contexts contexts) : base(contexts.game) {
}
protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context) {
return context.CreateCollector(GameMatcher.Destroyed);
}
protected override bool Filter(GameEntity entity) {
return entity.isDestroyed;
}
protected override void Execute(List<GameEntity> entities) {
foreach (var e in entities) {
e.Destroy();
}
}
}
这个系统是用于销毁那些用Destroyed
Component标记的Entitiy。你能看到我们在GetTrigger
方法中返回了一个监测了Destroyed
Entity的Collector。在context.CreateCollector(GameMatcher.Destroyed)
中,我们不需要指定当一个Entity何时应当被收集的事件,因为默认就是会收集在Added
情况下被通知到的Entity。所以当我们增加一个Destroyed
组件到一个Entity上时,这个Entity会添加
到Destroyed
的group里面,并因此被对应的collector收集到对应的reactive system里面。
响应式的系统就像执行式系统一样,会每隔一段时间或是在每一个Update
中被触发,然而Execute(List<GameEntity> entities)
方法只会在收集器距离上一次Execute
收集到新的Entity才会被执行。
你可能会在猜想Filter
方法是用于干什么的。就像之前在收集器章节提到的那样,如果一个Entity被收集器收集了的话,即时有些事件像是第一次事件的复原,这个Entity依然会保持被收集。在我们上面说到的例子中,我们收集所有被销毁的entity。所即使Destroyed
组件别被移除了来复原被销毁这个状态,这个Entity已经会被收集着而且会传入Execute
方法中,除非我们把它过滤掉。在Filter
方法中,我们可以决定这个被收集的Entity是否可以被传入Execute
方法。如果你你没有特殊的规范,你可以直接返回true
,不然就像上面的例子,你应该检查收集到的entity是否依然拥有Destroyed
组件。
Careful with AnyOf based collector 小心使用AnyOf的收集器
当你创建了一个观察基于AnyOf
匹配器的组合的收集器时,你可能会得到一些意料之外的结果,就像你拥有组件A
和B
,并且你有一个AnyOf(A,B)
的组合。一个Entity只有当其中一个组件被添加的时候才会进入这个组合。当我们添加第二个组件时,这个entity因为已经在组合中了,所以不会触发Added
事件而被收集到。你可能不是你想要的情况。一般来说人们会想看见不管哪个组件被添加这个Entity都要能被收集到。在这种情况下,你应该设置一个分别针对这两种组件的组合而不是使用AnyOf
。这是在MatchOne中的例子:
using System.Collections.Generic;
using DG.Tweening;
using Entitas;
using Entitas.Unity;
using UnityEngine;
public sealed class RemoveViewSystem : ReactiveSystem<GameEntity> {
public RemoveViewSystem(Contexts contexts) : base(contexts.game) {
}
protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context) {
return context.CreateCollector(
GameMatcher.Asset.Removed(),
GameMatcher.Destroyed.Added()
);
}
protected override bool Filter(GameEntity entity) {
return entity.hasView;
}
protected override void Execute(List<GameEntity> entities) {
foreach (var e in entities) {
destroyView(e.view);
e.RemoveView();
}
}
void destroyView(ViewComponent viewComponent) {
var gameObject = viewComponent.gameObject;
var spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
var color = spriteRenderer.color;
color.a = 0f;
spriteRenderer.material.DOColor(color, 0.2f);
gameObject.transform
.DOScale(Vector3.one * 1.5f, 0.2f)
.OnComplete(() => {
gameObject.Unlink();
Object.Destroy(gameObject);
});
}
}
在这个响应式系统中,当我们移除Asset
组件或者添加Destroyed
组件到Entity上时,我们会移除掉这个Entity的视图,
然而这种解决方案也有要注意的地方。虽然收集器可以设置多个组合,但是这些组合必须基于同一个上下文中的组件。所以当你需要设置一个响应式系统收集来自不同上下文的Entity时,你需要扩展MultiReactiveSystem
类来达到这个目的。在这个类中GetTrigger
方法会返回一组收集器。