различная геометрия столкновений в игровом движке на основе компонентов

Я пишу простой игровой движок и после долгого переосмысления / рефакторинга остановился на архитектуре, основанной на компонентах (не строго на ECS, но она больше не основана на наследовании). Итак, все в моем мире - это сущность, и каждая сущность имеет набор компонентов. Каждая система / подсистема в моей игре сканирует объект на предмет ряда компонентов, которые его интересуют, и выполняет некоторые соответствующие вычисления.

Все идет нормально. Базовую архитектуру движка можно увидеть здесь :

Теперь каждый объект, с которым сталкивается, имеет компонент столкновения (вместе с компонентами положения / движения / твердого тела), поэтому физическая система должна получить этот компонент и использовать его для подачи своих алгоритмов обнаружения столкновений, чтобы сгенерировать контактные данные, которые будут используется для разрешения коллизии.

Я застрял в следующей проблеме: алгоритмы обнаружения столкновений имеют дело с разной геометрией: прямоугольниками, сферами, плоскостями и лучами (на данный момент), но я не хочу иметь компонент столкновения сфер и компонент столкновения ящиков, по крайней мере, я этого не делаю. Я не хочу, чтобы они не были связаны друг с другом, но я бы хотел, чтобы у них был общий базовый класс.

class Sphere 
{
public:
    Sphere(float radius);
    ~Sphere();
    float GetRadius() { return mRadius; }

private:
    float mRadius;
};

class Box  : public BoundingVolume
{
public:
    Box(const XMFLOAT3 &halfSize);
    ~Box();
    XMFLOAT3 const &GetHalfSize() const { return mHalfSize; }
private:
    XMFLOAT3 mHalfSize;
};

Очевидно, что у каждого компонента свой интерфейс (блоки имеют половинные размеры, сферы имеют радиус и т. Д.), И различные функции обнаружения столкновений работают с каждым из них по-разному (ящик-ящик, ящик-сфера, сфера-сфера ...).

void CollisionSystem::BoxAndBoxCollision(const Box &box1, const Box &box2)
{
    // contact data
    XMFLOAT3 contactPoint;
    XMFLOAT3 contactNormal;
    float minOverlap = 100.0f;

    // get axes for SAT test
    std::vector<XMFLOAT3> axes = GetSATAxes(box1, box2);

    int axisIndex = 0;
    int index = 0;

    for (XMFLOAT3 axis : axes)
    {
        if (XMVectorGetX(XMVector3Length(XMLoadFloat3(&axis))) < 0.01f)
        {
            index++;
            continue;
        }

        float overlap = PerformSAT(axis, box1, box2);

        if (overlap < 0)  // found separating axis - early out
            return;

        if (overlap < minOverlap)
        {
            minOverlap = overlap;
            axisIndex = index;
        }

        index++;
    }

    // other collision detection/generation code.....


    // store contact
    mContacts.push_back(new Contact(box1->GetRigidBody(), box2->GetRigidBody(), contactPoint, contactNormal, minOverlap, coefficientOfRestitution));
}

Итак, как я могу решить эту проблему элегантным и надежным способом?


person Luca    schedule 06.04.2019    source источник
comment
Единственное решение, которое я нашел элегантным и надежным, - это использовать объединение с тегами для геометрии, позволяющее легко проверить, какой тип геометрии у вас есть, и вызвать соответствующую функцию обнаружения столкновений.   -  person UnholySheep    schedule 06.04.2019
comment
@UnholySheep, если я использую анонимное объединение с тегом в своем классе, разве мне не нужна другая функция-член GetCollidable () для каждого типа геометрии? Может быть, я должен сделать член данных общедоступным? что я бы хотел избежать   -  person Luca    schedule 06.04.2019
comment
Вы можете просто вернуть весь союз, вам не нужно перегружать геттер. Логика коллизий - единственное место, где она вам все равно понадобится, так что вы можете проверить тип там.   -  person UnholySheep    schedule 06.04.2019