Эта проблема
Я создаю систему компонентов сущности для игрового движка, который пытаюсь построить, и я не совсем уверен, как действовать на языке со строгой типизацией, в данном случае Rust.
Я бы хотел, чтобы типы компонентов были произвольными структурами, которые могли бы содержать любой тип состояния объекта, но не знали о его поведении. Таким образом, например, объект может содержать компоненты Position
, Hitbox
и Velocity
, но физическая подсистема может быть изменена или заменена отдельно, без необходимости изменять что-либо в этих компонентах.
Я также хотел бы сделать возможным добавление новых типов компонентов извне модуля. Это позволит новому игровому моду добавлять собственный пользовательский компонент к существующим объектам без изменения основного кода игры.
Я новичок в Rust и проделал ограниченную работу на C ++, поэтому, возможно, я придерживаюсь неправильного подхода, и если да, то был бы признателен за совет о более эффективных способах решения этой проблемы.
На языке без строгой системы типов (и с которым я более знаком), например в JavaScript, я могу иметь массив сущностей, которые содержат набор компонентов произвольного типа, а затем выполнять проверку типов во время выполнения для получения данных. :
class Position {
constructor(x = 0, y = 0, z = 0) {
this.x = x;
this.y = y;
this.z = z;
}
}
class Velocity {
constructor(x = 0, y = 0, z = 0) {
this.x = x;
this.y = y;
this.z = z;
}
}
const world = [
[
Position(0, 0, 0),
Velocity(0.25, 0.1, 1.2)
]
]
const physicsSystem = (world = []) => world.map((entity = []) => {
const velocity = entity.find((component) => component instanceof Velocity)
return velocity != null ? entity.map((component) => component instanceof Position
? Position(component.x + velocity.x, component.y + velocity.y, component.z + velocity.z)
: component
) : component
})
window.setInterval(() => world = physicsSystem(world), 100)
В приведенном выше примере объекты могут содержать любые типы компонентов, а системы, которые их обрабатывают, могут извлекать определенные компоненты, от которых они зависят, напрямую обращаться к их конкретным свойствам, а затем изменять компоненты. Внешний код также может добавить совершенно неизвестный компонент к одной из сущностей, и физический компонент не нужно будет изменять, чтобы приспособить его. Это то же самое поведение, которое я хочу иметь в моем ржавом ECS.
В качестве примечания, поскольку игры требуют более высокопроизводительного решения, чем мог бы предоставить мой пример javascript, я хотел бы минимизировать косвенный указатель, поиск по хэш-таблице, выделение памяти и максимально оптимизировать локальность данных, но, естественно, оптимизацию уступает по функциональности. Мой примерный код игнорирует эти оптимизации.
Что я пробовал
Я подумал о создании хэш-карты ComponentStorage<T>
, где ComponentStorage
- это признак, который позволяет мне абстрагироваться от базовой структуры данных, используемой для хранения компонентов. Структура State
будет содержать HashMap<ComponentStorage<std::any::TypeId, T>>
. Конкретное хранилище можно было найти с помощью хэша TypeId, а затем, используя черту ComponentStorage, я мог получить Option<T>
из этого хранилища по идентификатору объекта, а затем получить доступ к любым свойствам, которые содержит T
.
Однако это не работает, потому что тип T будет отличаться для каждого элемента в HashMap
, и я не могу стереть параметр типа, создав единственную характеристику для реализации для каждого варианта параметра типа (как предлагается в этом аналогичном вопросе : Вектор общих структур в Rust), потому что мне нужен доступ к конкретному типу T в системах, обрабатывающих сущности.
Я потенциально мог бы реализовать что-то похожее на мой пример JavaScript, используя Any
для хранения компонентов, но я понимаю, что использование Any
в произвольных настраиваемых структурах означает отсутствие смежного хранилища и изобилие косвенных указателей. Я не хочу преждевременно оптимизировать, но я не решаюсь идти по этому пути, чтобы создать его прототип, потому что не похоже, что эти ограничения Any
можно преодолеть, не переписывая его полностью позже.
Поскольку косвенное обращение к указателю должно, очевидно, происходить, чтобы не вводить жесткие типы кода в эту систему, я надеюсь, что коллекции компонентов, а не сами компоненты, будут полиморфными, и в то же время возможность доступа к элементам, содержащимся в данной коллекции, по их конкретным типам.
Я буду бесконечно благодарен за любую помощь, которую вы можете мне здесь предложить. Спасибо!