Система компонентов сущностей с динамической типизацией в Rust - PullRequest
1 голос
/ 09 февраля 2020

Проблема

Я строю систему компонентов сущности для игрового движка, который пытаюсь создать, и я не совсем уверен, как действовать на языке строгой типизации, в данном случае, 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, в то время как в то же время я могу получить доступ к предметам, содержащимся в данной коллекции, по их конкретным типам.

За любую помощь, которую вы можете предложить мне здесь, я был бы вечно благодарен. Спасибо!

1 Ответ

0 голосов
/ 10 февраля 2020

Существует только один способ поместить объекты с разными типами внутри одного контейнера: извращение указателя в изобилии.

На самом деле JavaScript движок делает то же самое для вас за сценой.

...