Entitas Cookbook

Entitas CookBook 翻译 - 108_reacti

2018-04-24  本文已影响0人  SkyrimWu

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();
        }
    }
}

这个系统是用于销毁那些用DestroyedComponent标记的Entitiy。你能看到我们在GetTrigger方法中返回了一个监测了DestroyedEntity的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匹配器的组合的收集器时,你可能会得到一些意料之外的结果,就像你拥有组件AB,并且你有一个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方法会返回一组收集器。

上一篇下一篇

猜你喜欢

热点阅读