Повышение эффективности компонентного игрового движка - PullRequest
0 голосов
/ 29 сентября 2018

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

void PhysicsSystem::update(float dt) {
std::vector<GameEngine::GameObject> moveObjects = manager->getAllObjectsWithComponent("move");
std::vector<GameEngine::GameObject> physicsObjects = manager->getAllObjectsWithComponent("physics");

for (int i = 0; i < moveObjects.size(); i++) {
    MoveComponent* mComponent = static_cast<MoveComponent*>(manager->getComponentByType("move", moveObjects[i]));
    PhysicsComponent* pComponent = static_cast<PhysicsComponent*>(manager->getComponentByType("physics", moveObjects[i]));
    RenderComponent* rComponent = static_cast<RenderComponent*>(manager->getComponentByType("render", moveObjects[i]));

    if (pComponent == nullptr || mComponent == nullptr || rComponent == nullptr) {
        continue;
    }

    if (!pComponent->isSolid()) {
        continue;
    }

    glm::vec4 coords1 = rComponent->getRenderCoords();


    for (int j = 0; j < physicsObjects.size(); j++) {
        PhysicsComponent* pComponent2 = static_cast<PhysicsComponent*>(manager->getComponentByType("physics", physicsObjects[j]));
        RenderComponent* rComponent2 = static_cast<RenderComponent*>(manager->getComponentByType("render", physicsObjects[j]));
        if (pComponent2 == nullptr || rComponent2 == nullptr) {
            continue;
        }

        if (!pComponent2->isSolid()) {
            continue;
        }


        glm::vec4 coords2 = rComponent2->getRenderCoords();



        int dist = sqrt(pow((coords1.x - coords2.x), 2) + pow((coords1.y - coords2.y), 2));
        if (dist > pComponent->getCollisionRadius()) {
            continue;
        }

        if (GameEngine::Physics::checkCollision(coords1, coords2)) {
            pComponent->addCollision(coords2);
        }
    }
}

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

PhysicsComponent* pComponent2 = static_cast<PhysicsComponent*>(manager->getComponentByType("physics", physicsObjects[j]));
RenderComponent* rComponent2 = static_cast<RenderComponent*>(manager->getComponentByType("render", physicsObjects[j]));

Они вызывают функцию в моем классе GameObjectManager.Вот код этой функции:

Component* GameObjectManager::getComponentByType(std::string type, GameObject object) {
    std::unordered_map<std::string, std::unordered_map<GLuint, Component*>>::iterator it = componentsByType.find(type);
    if (it == componentsByType.end()) {
        return nullptr;
    }
    std::unordered_map<GLuint, Component*>::iterator it2 = it->second.find(object.getGameObjectID());
    if (it2 == it->second.end()) {
        return nullptr;
    }

    return it2->second;
}

Если я уберу эти две строки, игра значительно ускорится.Есть что-то, что я делаю не так?Я думал, что поиск объекта в unordered_map - это операция с постоянным временем, поэтому я не уверен, как увеличить скорость.Есть ли более эффективный способ обработки моих компонентов?Любая помощь будет принята с благодарностью, спасибо!

Ответы [ 2 ]

0 голосов
/ 29 сентября 2018

Вы запрашиваете компоненты во внутреннем цикле moveObjects.size() раз, что означает большую избыточную работу.

Вы должны поместить цикл предварительной обработки перед основным циклом, который собирает компоненты:

for (int j = 0; j < physicsObjects.size(); j++) {
    PhysicsComponent* pComponent2 = static_cast<PhysicsComponent*>(manager->getComponentByType("physics", physicsObjects[j]));
    RenderComponent* rComponent2 = static_cast<RenderComponent*>(manager->getComponentByType("render", physicsObjects[j]));
    if (pComponent2 == nullptr || rComponent2 == nullptr) {
        continue;
    }

    if (!pComponent2->isSolid()) {
        continue;
    }

    // add pComponent2 and rComponent2 into an array here
}

А затем во внутреннем цикле используйте собранные данные вместо того, чтобы запрашивать их у менеджера.

Обратите внимание, что если у вас много объектов, вы можете поместить ихв некоторую структуру данных разделения пространства (октри / Kd-дерево / BSP-дерево), чтобы избежать O(n^2) времени выполнения.

0 голосов
/ 29 сентября 2018

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

  1. moveObjects и physicsObjects являются векторами и являются копиями исходных данных.Это должны быть копии, или они могут быть const & векторами, хранящимися в игровом менеджере?
  2. У вас есть несколько проверок с pComponent, mComponent и rComponent.Если какая-либо проверка не удалась, вы переходите к следующему объекту.Получите одно из этих значений, проверьте его на nullptr, затем получите следующее.Сначала выполните pComponent и проверьте, является ли он твердым, перед двумя другими.
  3. Сделайте то же самое с циклом j для pComponent2 и rComponent2.
  4. Когдапроверяя расстояние, не вычисляйте квадратный корень.Вместо этого сравните квадрат расстояния.
  5. Передайте параметр object в getComponentByType на const &, чтобы вы не делали его копию.Вы получаете доступ только к одному его полю.
  6. Используйте основанные на диапазоне значения для циклов.Если это невозможно, попробуйте сохранить размер ваших векторов в переменной, а не вызывать size каждую итерацию цикла.Компилятор может оптимизировать избыточные вызовы, но единственный способ узнать наверняка - проверить сгенерированный оптимизированный код.
...