Проблема
Я строю систему компонентов сущности для игрового движка, который пытаюсь создать, и я не совсем уверен, как действовать на языке строгой типизации, в данном случае, 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)
В приведенном выше примере сущности могут содержать компоненты любых типов, и системы, которые их обрабатывают, могут извлекать определенные c компоненты, от которых они зависят, напрямую получать доступ к своим конкретным свойствам и затем изменять компоненты. Внешний код может также добавить совершенно неизвестный компонент к одной из сущностей, и физический компонент не нужно будет менять, чтобы приспособить его. Это то же самое поведение, которое я хочу иметь в своем ржавом ECS.
Как примечание: игры требуют более высокопроизводительного решения, чем мой пример javascript, я бы хотел свести к минимуму косвенность указателя, поиск таблиц, распределение памяти и максимально оптимизировать локальность данных, насколько это возможно, но оптимизация является второй по функциональности. Мой пример кода игнорирует эти оптимизации.
То, что я пробовал
Я рассмотрел создание хэш-карты ComponentStorage<T>
, где ComponentStorage
- это черта, которая позволяет мне абстрагировать базовая структура данных, используемая для хранения компонентов. State
структура будет содержать HashMap<ComponentStorage<std::any::TypeId, T>>
. Указанное хранилище c может быть найдено с помощью TypeId ha sh, затем, используя черту ComponentStorage, я могу извлечь Option<T>
из этого хранилища по идентификатору сущности, а затем получить доступ к любым свойствам T
.
Это не работает, однако, потому что тип T будет отличаться для каждого элемента в HashMap
, и я не могу стереть параметр типа, создав отдельную черту для реализации для каждого варианта параметра типа (как предложено в этом похожем вопросе: Vector of Generi c Structs in Rust ), потому что мне нужен доступ к конкретному типу T в системах, которые обрабатывают объекты.
Я мог бы потенциально реализовать что-то похожее на мой пример JavaScript, используя Any
для хранения компонентов, но я понимаю, что использование Any
на произвольных пользовательских структурах означает отсутствие смежного хранилища и излишнюю косвенность указателя. Я не хочу преждевременно оптимизировать, но я не решаюсь go пойти по этому пути, чтобы прототипировать его, потому что не похоже, что эти ограничения Any
могут быть преодолены без полного переписывания его позже.
Поскольку косвенное указание должно , очевидно, произойдет, чтобы не вводить жесткие типы кода в эту систему, я надеюсь, что коллекции компонентов, а не сами компоненты, будут полиморфными c, в то время как в то же время я могу получить доступ к предметам, содержащимся в данной коллекции, по их конкретным типам.
За любую помощь, которую вы можете предложить мне здесь, я был бы вечно благодарен. Спасибо!