C ++ разработка игр и вопрос полиморфизма - PullRequest
3 голосов
/ 23 апреля 2010

Я пытаюсь реализовать какой-то игровой движок "просто для меня", и сюжет проблемы выглядит следующим образом:

Предположим, у меня есть какой-то абстрактный интерфейс для визуализируемой сущности, например IRenderable.

И это объявлено следующим образом:

interface IRenderable {
  // (...)
  // Suppose that Backend is some abstract backend used
  // for rendering, and it's implementation is not important
  virtual void Render(Backend& backend) = 0;
};

Сейчас я делаю что-то вроде объявления разных классов, таких как

class Ball : public IRenderable {

  virtual void Render(Backend& backend) {
    // Rendering implementation, that is specific for
    // the Ball object
    // (...)
  }
};

И затем всевыглядит хорошо.Я могу легко сделать что-то вроде std::vector<IRenderable*> items, вставить некоторые элементы, такие как new Ball() в этот вектор, а затем сделать вызов, похожий на foreach (IRenderable* in items) { item->Render(backend); }

Хорошо, я думаю, что это «полиморфный» способ, но чтоесли я хочу иметь разные типы объектов в своей игре и возможность манипулировать их состоянием, где каждый объект может управляться через собственный интерфейс?

Я мог бы сделать что-то вроде

struct GameState {
  Ball ball;
  Bonus bonus;
  // (...)
};

и затем легко изменять состояние объектов с помощью своих собственных методов, например ball.Move(...) или bonus.Activate(...), где Move(...) характерно только для Ball и Activate(...) - только для Bonus экземпляров.

Но в этом случае я теряю возможность писать foreach IRenderable* просто потому, что храню эти шары и бонусы как экземпляры их производных, а не базовых классов.И в этом случае процедура рендеринга превращается в беспорядок, подобный

ball.Render(backend);
bonus.Render(backend);
// (...)

, и это плохо, потому что мы фактически теряем наш полиморфизм таким образом (нет реальной необходимости делать виртуальную функцию Render и т. Д.

Другой подход означает вызов downcasting через dynamic_cast или что-то с typeid, чтобы определить тип объекта, которым вы хотите манипулировать, и это выглядит еще хуже для меня, и это также разрушает эту «полиморфную» идею.

Итак, мой вопрос - существует ли какой-то (вероятно) альтернативный подход к тому, что я хочу сделать, или мой текущий шаблон каким-то образом может быть изменен так, чтобы я фактически сохранил IRenderable* для своей игры?объекты (чтобы я мог вызывать виртуальный Render метод для каждого из них), сохраняя при этом возможность легко изменять состояние этих объектов?

Может быть, я делаючто-то абсолютно неправильно с самого начала, если да, пожалуйста, укажите это:)

Заранее спасибо!

Ответы [ 3 ]

3 голосов
/ 23 апреля 2010

Тот факт, что вы храните различные объекты как члены класса, не должен помешать вам хранить указатели на них в векторе IRenderable*.

struct GameState {
  Ball ball;
  Bonus bonus;
  vector<IRenderable*> m_items;

  GameState() // constructor
  {
    m_items.push_back(&ball);
    m_items.push_back(&bonus);
  }

};

Таким образом, вам не нужно беспокоиться об освобождении указателей в m_items. Они будут автоматически уничтожены при уничтожении GameState.

Кроме того, ваш синтаксис foreach фанк.

2 голосов
/ 23 апреля 2010

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

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

1 голос
/ 23 апреля 2010

Я не программист на С ++, но, надеюсь, искусственный код поможет ...

Класс Renderable имеет функцию Render

Класс Movable наследуется от Renderable, а также имеет такие свойства, как x, y, z и такие функции, как Move

Класс Ball наследуется от Movable

Теперь в вашем игровом состоянии может быть список объектов Renderable и, когда придет время Render, он может просто проходить по ним.

Другие действия, такие как отметка мировых часов, могут циклически искать объекты, которые являются экземплярами Moveable, и сообщать им об обновлении.

Если ваш бонус подлежит визуализации (я не могу точно сказать по вашему вопросу), то он может подкласс Collectable, какие подклассы Moveable.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...