Реализация компонентов в ECS со структурами

Цель:

Я пытаюсь реализовать систему компонентов сущности, и я застрял в реализации своего компонента. Я намерен иметь общий тип компонента, который содержит статический бит для каждого компонента, например компоненты типа Position имеют Bit = 0, компоненты типа Sprite имеют Bit = 1 и т. д. Компонент должен состоять только из значений, которые он хранит, поэтому я начал со структур. Ниже приводится моя попытка (не рабочий код):

    struct Position : Component<Position> //this does not work in c#
    {
        public int x, y;
    }

    internal struct ComponentBit
    {
        public static int bitCounter = 0;

    };

    public struct Component<T>
    {
        public static readonly int bit;

        static Component()
        {
            bit = ComponentBit.bitCounter++;
        }

        public int GetBit()
        {
            return bit;
        }
    }

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

Вопрос:

Есть ли способ реализовать эти функции с помощью структур, как это было бы возможно в C ++ (шаблонах)? Я хотел бы сохранить их как типы значений, а не ссылочные типы в целях реализации / производительности позже.

Редактировать1:

Предполагаемое использование, которое я хотел бы иметь:

Position pos = new Position(x, y);
int bit1 = pos.GetBit(); // or
int bit2 = Position.bit; // both should be possible

person Jan    schedule 10.12.2019    source источник
comment
Только не используйте наследование. Изменить: похоже, что Component<T> предназначен для работы как тип для словаря int. Вы можете сделать это. Нет необходимости наследовать от него.   -  person Theraot    schedule 10.12.2019
comment
@Theraot Я немного смущен вашим комментарием. Это означало бы, что мне нужно продублировать код для каждого компонента, который у меня есть, не так ли? С другой стороны, как тогда я смогу идентифицировать переменную как Компонент? Изменить: я думаю, я мог бы использовать интерфейс IComponent для второго вопроса.   -  person Jan    schedule 10.12.2019
comment
Не вижу необходимости в дублировании. Что касается определения того, что является компонентом ... просто рассматривайте каждую структуру как возможный компонент. Если программист пытается добавить структуру к сущности, любую структуру к любой сущности (с любым API, который вы для этого создаете), то это компонент. Изменить: затем вы запрашиваете бит, используя тип.   -  person Theraot    schedule 10.12.2019
comment
@Theraot Если я правильно вас понял, это означает, что мне нужно вручную зарегистрировать каждую структуру как компонент, например Компонент ‹Position›, чтобы установить бит или иметь отдельную логику для обработки этого. Моя первоначальная идея заключалась в том, что все это делается автоматически при создании экземпляра нового компонента. См. Мой Edit1 в вопросе для этого. Думаю, тогда мне придется сменить дизайн. Если вы хотите, вы можете написать ответ, который я могу принять (хотя я немного подожду дальнейших предложений).   -  person Jan    schedule 10.12.2019


Ответы (1)


Я предлагаю не пытаться использовать наследование.

Возможным допустимым компонентом является любая структура, например:

struct Position
{
    public int X, Y;

    public Position(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Теперь у вас все еще может быть Component<T> для работы в качестве типа для словаря int (Dictionary<Type, int>). Хотя я собираюсь внести минимум изменений, чтобы эта работа работала:

internal static class ComponentBit
{
    public static int bitCounter = 0;

};

public static class Component<T>
{
    public static readonly int bit; // This is static, it has a value per type T

    static Component()
    {
        bit = ComponentBit.bitCounter++;
    }

    public static int GetBit()
    {
        // This method only accesses static members, it can be static
        // Thus, no instance of T is needed
        return bit;
    }
}

И вы бы использовали это так:

Position pos = new Position(x, y);
int bit1 = Component<Position>.GetBit();
int bit2 = Component<Position>.bit;

Понятия не имею, почему вам нужны два способа сделать это, но готово.


Хорошо, теперь я позволю себе внести большие изменения ... помня об этом, если вам нужен вывод типа, вы можете сделать это:

public static class Component
{
    internal static int bitCounter = 0;

    public static int GetBit<T>()
        where T : struct // We only accept structs here
    {
        return Component<T>.bit;
    }

    public static int GetBit<T>(this ref T value)
        where T : struct // We only accept structs here
    {
        // This is an extension method.
        // It will appear as a method on any valid T (which is all structs)
        // The type T will be infered from the instance.
        // Passing the struct as a reference to avoid a copy
        _ = value; // discard value
        return GetBit<T>();
    }
};

internal static class Component<T>
    where T : struct // We only accept structs here
{
    internal static readonly int bit;

    static Component()
    {
        bit = Component.bitCounter++;
    }
}

Использование:

var pos = new Position(x, y);
var bit1 = Component.GetBit<Position>();
var bit2 = pos.GetBit();

Да, приведенный выше код компилируется и запускается. Смотрите в SharpLab.

Примечание: я бы рассмотрел возможность вложения Component<T> в Component.

person Theraot    schedule 10.12.2019