Я разрабатываю базу для 2D-игры. Мой общий дизайн выглядит примерно так:
class Entity:
Every object class (like a wall, an enemy, floor etc.) derives
from this class. Most of the functions are pure virtual. There is
a hitbox as well.
class Scene:
Contains pointers to Entity-objects. When an Entity-pointer is added,
it will be given a Scene-pointer to its parent so it may access that.
Сцена также имеет функцию обнаружения столкновения:
getIntersections<T> (Entity *)
getIntersections<T...> (Entity *)
(both return a std::vector<Entity *>)
Это в основном получает все Entity *
пересекающие parameter- Entity *
(путем проверки hitbox), а затем пытается использовать dynamic_cast<T *>
. Затем возвращаются все соответствующие Entity *
(а не литые). Вариационный шаблон используется для проверки более одного пересекающегося класса.
Моя основная идея заключалась в том, что если бы у меня был Player
-class (который явно представляет игрока) и некоторые другие классы, такие как Enemy
, Wall
и т.д., Было бы легкой задачей проверить, был ли Player
-object сталкиваясь с одним (или более) из них:
// (inside Player::tick(); quick and dirty)
{
if ( (this->parentScene->getIntersections<Wall>(this)).empty() )
// player does not collide with a wall, just move.
else
// player does collide with a wall, do whatever.
}
Однако у меня есть два вопроса:
dynamic_cast<T *>
качестве замены для instanceof
(например, в Java)Scene
основном проходит через каждую содержащуюся в ней Entity *
, проверяет, сталкивается ли она с данным Entity *
и, наконец, бросает ее, чтобы проверить, вытекает ли она из другого данного класса. Если это не работает, какие изменения должны были сделать в моем дизайне, чтобы сделать его работоспособным?В части производительности было бы лучше отделить объекты в отдельных векторах примитивным типом. Не только вы можете специально протестировать плоскость и сферу, например, это избавляет от необходимости иметь dynamic_cast полностью (-> дополнительное ускорение). Кроме того, поскольку вы уже разделили типы в векторах, вы можете просто игнорировать виртуальные функции и перейти на не виртуальный вызов, обеспечивающий дополнительное повышение производительности; Таким образом, ваша сцена будет выглядеть примерно так:
class scene
{
std::vector<PlaneEntity> m_planes;
std::vector<CircleEntity> m_circles;
};
И в отношении дизайна этого гораздо проще выбрать правильный алгоритм при пересечении примитивов: это пример того, как будет выглядеть базовая проверка столкновения на основе этого дизайна:
void colide(const PlaneEntity & e)
{
for each plane
call plane vs plane collision
for each circle
call plane vs circle collision;
};
void colide(const CircleEntity & e)
{
for each plane
call plane vs circle collision;
for each circle
call circle vs circle collision;
};
Поэтому, чтобы ответить на ваш вопрос:
1: это не правило погоды использовать или не dynamic_cast, известно, что не использовать его лучше для производительности.
2: Конструкция, описанная выше, вообще не работает. Также ваш дизайн с динамическим приводом еще медленнее. Чтобы улучшить это, вам нужно изучить структуры ускорений (это огромная тема, поэтому я не могу все объяснить здесь), чтобы ускорить обнаружение столкновений. Они в основном уменьшают количество проверок столкновения для каждого примитива, что дает значительное улучшение производительности. Основными структурами являются KD-Tree, Quad-Trees, Spacial-Hash, Grid и т.д. Вы можете найти много кода для каждого, просто выполнив их.
Надеюсь, это поможет, Раксван.