связать системы с событиями - PullRequest
0 голосов
/ 27 января 2019

Используя шаблон Entity-Component-System, я хочу связать некоторые системы с событиями.Поэтому некоторые системы не должны работать в цикле, они должны просто запускаться по требованию.

Учитывая пример системы Health , система Death должна запускаться только тогда, когдакомпонент получает менее 1 здоровья.

Я думал о наличии двух типов систем.Первый тип - это периодическая система.Это выполняется один раз за кадр, например Render или Movement System.Другой тип - это система, основанная на событиях.Как упоминалось ранее, связь между Health и Death .

Сначала я создал базовый интерфейс, используемый обоими типами систем.

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

, но я не хочу запускать систему, если она не нужна.

Существует два типа событий: ChangeEvent и ExecuteEvent.,Первый срабатывает при изменении значения компонента.Второй срабатывает, когда что-то должно быть сделано с определенной сущностью.

Если вам нужно или хотите, вы можете взглянуть на EntityManager

https://pastebin.com/NnfBc0N9

ComponentRequirements

https://pastebin.com/xt3YGVSv

и использование ECS

https://pastebin.com/Yuze72xf

Пример системы будет примерно таким

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 Iпросто имею в виду делегатов событий.

В настоящее время я мог бы сделать что-то вроде этого

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

Но я надеялся добиться лучшей архитектуры, используя делегатов событий, чтобы системы взаимодействовали и обменивались данными между собой.

Ответы [ 2 ]

0 голосов
/ 05 февраля 2019

Я не эксперт по этому шаблону проектирования, но я кое-что прочитал, и мой совет: постарайтесь не забывать истинное назначение этого шаблона.На этот раз я нашел статью в Википедии действительно интересной.Это в основном говорит (по крайней мере, это то, что я понял), что этот шаблон был «разработан», чтобы избежать создания слишком большого количества зависимостей, потеряв развязку.Вот пример, который я взял из статьи:

Предположим, есть функция рисования.Это будет «Система», которая перебирает все объекты, которые имеют как физический, так и видимый компоненты, и рисует их.Видимый компонент может обычно иметь некоторую информацию о том, как должен выглядеть объект (например, человек, монстр, искры, летающие стрелы) и использовать физический компонент, чтобы знать, где его нарисовать.Другая система может быть обнаружение столкновений.Он будет перебирать все сущности, которые имеют физический компонент, так как ему будет все равно, как рисуется сущность.Эта система будет, например, обнаруживать стрелы, которые сталкиваются с монстрами, и генерировать событие, когда это происходит.Он не должен понимать, что такое стрелка, и что это значит, когда стрелка поражает другой объект.Еще одним компонентом могут быть данные о здоровье и система, которая управляет здоровьем.Компоненты здоровья будут прикреплены к сущностям человека и монстра, но не к сущностям стрелок.Система управления работоспособностью подпишется на событие, созданное в результате столкновений, и соответствующим образом обновит работоспособность.Эта система также может время от времени проходить через все объекты с компонентом здоровья и восстанавливать здоровье.

Я думаю, что вы слишком усложнили свою архитектуру, потеряв преимущества, которые может дать вам этот шаблон.

Прежде всего: зачем вам EntityManager?Цитирую еще раз:

Архитектура ECS обрабатывает зависимости очень безопасным и простым способом.Поскольку компоненты представляют собой простые блоки данных, они не имеют зависимостей.

Вместо этого компоненты создаются с введенной зависимостью EntityManager:

entityManager.AddSystem(new Movement(entityManager));

В результате получается относительно сложный внутреннийструктура для хранения сущностей и связанных с ними компонентов.

После исправления этого вопроса возникает вопрос: как вы можете "общаться" с ISystem s?Опять же, ответ в статье: Шаблон наблюдателя.По сути, каждый компонент имеет набор подключенных систем, которые уведомляются каждый раз, когда происходит определенное действие.

0 голосов
/ 04 февраля 2019

Что бы я ни понял, вы хотите иметь повторяющееся, одноразовое событие тикового типа наряду с событием типа раз в год (преувеличенное, но ясное), вы можете сделать это с помощью функции обратного вызова делегата IE:

public delegate void Event(object Sender, EventType Type, object EventData);
public event Event OnDeath;
public event Event OnMove;
public void TakeDamage(int a)
{
    Health-=a;
    if(Health<1)
        OnDeath?.Invoke(this,EventType.PlayerDeath,null);
}
public void ThreadedMovementFunction()
{
    while(true)
    {
        int x,y;
        (x,y) = GetMovementDirection(); 
        if(x!=0||y!=0)
            OnMove?.Invoke(this,EventType.PlayerMove,(x,y));
    }
}

Вы можете реализовать это в интерфейсе, а затем сохранить класс объекта и получить доступ только к необходимым вещам, таким как события и так далее.но я не совсем понимаю, что вы ищете, так что если бы вы могли подробно остановиться на конкретной проблеме или том, что вам нужно решить, это было бы очень признательно!

...