tot*_*ner 7 c# entity-component-system
使用Entity-Component-System模式我想要将一些系统与事件连接起来.所以有些系统不应该在循环中运行,它们应该只是按需运行.
鉴于Health系统的示例,死亡系统应仅在组件低于1健康时运行.
我想过有两种类型的系统.第一种类型是周期性系统.这将运行每帧一次中,例如一个渲染或运动系统.另一种类型是基于事件的系统.作为之间的连接之前提到的健康和死亡.
首先,我创建了两种系统类型使用的基本接口.
internal interface ISystem
{
    List<Guid> EntityCache { get; } // Only relevant entities get stored in there
    ComponentRequirements ComponentRequirements { get; } // the required components for this system
    void InitComponentRequirements();
    void InitComponentPools(EntityManager entityManager);
    void UpdateCacheEntities(); // update all entities from the cache
    void UpdateCacheEntity(Guid cacheEntityId); // update a single entity from the cache
}
我进一步创建了接口
internal interface IReactiveSystem : ISystem
{
// event based
}
和
internal interface IPeriodicSystem : ISystem
{
// runs in a loop
}
但我不确定他们是否有必要.使用没有问题
foreach (ISystem system in entityManager.Systems)
{
    system.UpdateCacheEntities();
}
但如果不需要,我不想运行系统.
有两种类型的事件,a ChangeEvent和a ExecuteEvent.当组件中的值发生更改时,会触发第一个.当应该使用特定实体执行某些操作时,会触发第二个触发器.
如果你需要或想要你可以看一下 EntityManager
该 ComponentRequirements
以及ECS的用法
一个示例系统就是这样的
internal class HealthSystem : IReactiveSystem
{
    public HealthSystem(EntityManager entityManager)
    {
        InitComponentRequirements();
        InitComponentPools(entityManager);
    }
    private Dictionary<Guid, HealthComponent> healthComponentPool;
    public List<Guid> EntityCache { get; } = new List<Guid>();
    public ComponentRequirements ComponentRequirements { get; } = new ComponentRequirements();
    public void InitComponentRequirements()
    {
        ComponentRequirements.AddRequiredType<HealthComponent>();
    }
    public void InitComponentPools(EntityManager entityManager)
    {
        healthComponentPool = entityManager.GetComponentPoolByType<HealthComponent>();
    }
    public void UpdateCacheEntities()
    {
        for (int i = 0; i < EntityCache.Count; i++)
        {
            UpdateCacheEntity(EntityCache[i]);
        }
    }
    public void UpdateCacheEntity(Guid cacheEntityId)
    {
        Health healthComponent = healthComponentPool[cacheEntityId];
        healthComponent.Value += 10; // just some tests
        // update UI 
    }
}
我如何创建ChangeEvents和ExecuteEvents使用不同的系统?
编辑
有没有办法将组件事件委托添加到组件中,以便在更改事件正在侦听时为此实体运行特定系统,或者在执行事件正在侦听时按需运行?
通过提及ChangeEvent,ExecuteEvent我只是指事件代表.
目前我可以做这样的事情
internal class HealthSystem : IReactiveSystem
{
    //… other stuff
    IReactiveSystem deathSystem = entityManager.GetSystem<Death>(); // Get a system by its type
    public void UpdateCacheEntity(Guid cacheEntityId)
    {
        // Change Health component
        // Update UI
        if(currentHealth < 1) // call the death system if the entity will be dead
        {
            deathSystem.UpdateCacheEntity(cacheEntityId);
        }
    }
}
但是我希望通过使用事件委托来实现系统之间的通信和共享数据,从而实现更好的架构.
我不是这种设计模式的专家,但我读过一些相关内容,我的建议是:尽量不要忘记这种模式的真正目的。这次我发现维基百科上的文章非常有趣。基本上是说(至少是我所理解的)这种模式的“设计”是为了避免创建太多的依赖关系,从而失去解耦。这是我从文章中摘取的一个例子:
假设有一个绘图函数。这将是一个“系统”,它迭代具有物理和可见组件的所有实体,并绘制它们。可见组件通常可以具有一些有关实体外观的信息(例如,人类、怪物、四处飞舞的火花、飞翔的箭头),并使用物理组件来知道在哪里绘制它。另一个系统可能是碰撞检测。它将迭代具有物理组件的所有实体,因为它不关心实体是如何绘制的。例如,该系统将检测与怪物碰撞的箭头,并在发生这种情况时生成一个事件。它不需要理解什么是箭,以及当另一个物体被箭击中时意味着什么。另一个组成部分可能是健康数据和管理健康的系统。健康组件将附加到人类和怪物实体,但不附加到箭头实体。健康管理系统将订阅碰撞产生的事件并相应地更新健康状况。该系统还可以不时地遍历具有健康组件的所有实体,并重新生成健康状况。
我认为您的架构过于复杂,失去了这种模式可以给您带来的优势。
首先:为什么需要EntityManager?我再引用一遍:
ECS架构以非常安全且简单的方式处理依赖关系。由于组件是简单的数据桶,因此它们没有依赖性。
相反,您的组件是通过EntityManager注入的依赖项构建的:
entityManager.AddSystem(new Movement(entityManager));
结果是一个相对复杂的内部结构来存储实体和相关组件。
解决这个问题后,问题是:如何与ISystems“沟通”?同样,答案就在文章中:观察者模式。本质上,每个组件都有一组附加系统,每次发生特定操作时都会通知这些系统。
| 归档时间: | 
 | 
| 查看次数: | 314 次 | 
| 最近记录: |