Нашел полезный сайт с вопросами/ответами по обработке столкновений. Прошу любить и жаловать: Collision Detection FAQ.
Почему он не встретился мне на пару-тройку лет раньше, когда он был очень нужен.
Нашел полезный сайт с вопросами/ответами по обработке столкновений. Прошу любить и жаловать: Collision Detection FAQ.
Почему он не встретился мне на пару-тройку лет раньше, когда он был очень нужен.
Вы никогда не задумывались почему Новгород называют Новгородом? То есть «Новый Город»? А старый тогда где?
Не поленился, полез в википедию читать про Новгород и нашел такую информацию:
На роль такого «Старого города», иногда отождествляемого с эпическим Словенском, предполагают несколько поселений, среди которых самые вероятные — Рюриково городище и поселение на месте будущего Славенского конца.
А «Гардарики» — это в переводе со скандинавского «страна городов». Википедия говорит, что, возможно, так называли Новгородскую землю.
Вы знали об этом? Мне как-то ни разу в голову не пришло, почему именно «Нов-город», а ведь слышал много раз. Даже бывал там полтора-два года назад проездом. Вот такие дела, век живи — век учись.
Движок Box2D используется для симуляции физики в двумерном пространстве. Из фигур он поддерживает сферу (круг), бокс (прямоугольник) и выпуклый полигон (с внутренними углами больше 1-10 градусов, иначе будет падение с ошибкой).
Что же делать, если надо нарисовать этот полигон от руки и скормить Box2D, чтоб он не рухнул?
Можно нарисовать составной полигон в виде множества сфер и боксов. Так, например, сделана фигура танка для демки движка NeoAxis Game Engine (этот движок трехмерный, но проблемы с физикой такие же)
Но у меня такой возможности не было, поэтому я скармливал физике предварительно триангулированный полигон, алгоритм решения очень простой:
Третий пункт — это просто оптимизация. Делать его необязательно, реализуется простым перебором всех треугольников.
И еще один момент: если угол между сторонами полигона равен 180 градусам, можно удалить точку, общую для обеих сторон.
Все мы видели как камера плавно перелетает с места на место в различных играх. Попробуем повторить хотя бы минимум этого функционала, а именно: ограничим скорость полета камеры и дистанции текущей позиции камеры до вычисленной (чтобы камера не опаздывала за быстрым перемещением персонажа).
Итак, обозначения:
Алгоритм:
А вот, собственно, код этого безобразия:
void updateCameraPosition( float dt ) { // Скорость камеры в метрах в секунду const float speed = 0.5f; // Максимальная дистанция от текущей позиции камеры до будущей const float maxDist = 1.0f; // Текущая позиция камеры const Ogre::Vector3 currCameraPos = camera->getPosition(); // Будущая позиция камеры const Ogre::Vector3 nextCameraPos = calculateCameraPosition(); // Направление от текущей позиции камеры до будущей Ogre::Vector3 direction = nextCameraPos - currCameraPos; // Длина смещения камеры float dist = direction.length(); // Нормализуем направление direction.normalise(); // Проверим дистанцию if (dist > maxDist) { camera->setPosition(nextCameraPos - direction * maxDist); } // Иначе применим скорость else { // Ограничим максимальное смещение, // чтобы не улететь дальше nextCameraPos; const float realSpeed = Ogre::Math::Clamp(dt * speed, 0.0f, dist); camera->setPosition(currCameraPos + direction * realSpeed); } } |
Примечание для специалистов: да, я в курсе, что в играх применяется более плавное движение камеры с учетом инерции. Об этом как-нибудь в другой раз.
Хочу предложить более совершенный алгоритм проверки коллизии камеры с землей, чем опубликованный ранее (ссылка). Теперь все как «у взрослых» (так сделано, например, в Perfect World). Задача: камера не должна проходить сквозь стены.
Оговорим обозначения:
В общих чертах, алгоритм такой:
А теперь выразим это в коде (код писался прямо в блокноте, поэтому не обессудьте)
bool checkCameraCollides( const Ogre::Vector3 & cameraPos, const Ogre::Vector3 & targetPos, Ogre::Vector3 & newCameraPos ) { // Сохраняем позицию и направление камеры const Ogre::Vector3 lastCameraPos = camera->getPosition(); const Ogre::Vector3 lastCameraDir = camera->getDirection(); // Установим новую позицию и направление камеры для корректной // работы метода Ogre::Camera::getCameraToViewportRay() camera->setPosition(cameraPos); camera->setDirection(targetPos - cameraPos); // Вычисляем крайние точки std::vector<Ogre::Vector3> points; { Ogre::Ray ray; // Верхний левый угол camera->getCameraToViewportRay(0, 0, &ray); points.push_back(ray.getOrigin()); // Нижний левый угол camera->getCameraToViewportRay(0, 1, &ray); points.push_back(ray.getOrigin()); // Верхний правый угол camera->getCameraToViewportRay(1, 0, &ray); points.push_back(ray.getOrigin()); // Нижний правый угол camera->getCameraToViewportRay(1, 1, &ray); points.push_back(ray.getOrigin()); } // Вернем камере исходные позицию и направление camera->setPosition(lastCameraPos); camera->setDirection(lastCameraDir); // Минимальная длина проекции float minProj = 0.0f; // Есть столкновение bool collideDetected = false; // Луч от цели до камеры const Ogre::Vector3 targetToCameraDir = (cameraPos - targetPos).normalisedCopy(); for (size_t = 0; i < points.size(); ++i) { // Начало луча в позиции цели const btVector3 from = OgreBtConverter::to(targetPos); // Окончание луча крайней точке const btVector3 to = OgreBtConverter::to(points[i]); // Настроим стуктуру для обнаружение столкновения btDynamicsWorld::ClosestRayResultCallback rayCallback(from, to); // Сталкиваться только с землей rayCallback.m_collisionFilterGroup = CG_GROUND; rayCallback.m_collisionFilterMask = CM_GROUND; // Пускаем луч dynamicsWorld->rayTest(from, to, rayCallback); // Есть контакт if (rayCallback.hasHit()) { // Точка столкновения const Ogre::Vector3 hitPoint = BtOgreConverter::to(rayCallback.m_hitPointWorld); // Длина проекции на луч от targetPos до cameraPos float proj = (hitPoint - targetPos).dotProduct(targetToCameraDir); // Сохраним минимальную длину проекции if (!collideDetected || minProj > proj) { minProj = proj; collideDetected = true; } } } // Если столкновение произошло if (collideDetected) { // Новая позиция камеры - это targetPos смещенная в сторону cameraPos на minProj newCameraPos = targetPos + targetToCameraDir * minProj; return true; } return false; } |
И для «самых маленьких» пример использования:
void updateCameraPosition() { Ogre::Vector3 cameraPos, targetPos, newCameraPos; // Вычисляем позицию камеры и цели (решение о том, как это делается, // остается за вами, у меня позиция и цель камеры зависит от режима камеры) calculateCameraPosition(cameraPos, targetPos); // Проверим столкновение if (checkCameraCollides(cameraPos, targetPos, newCameraPos)) { camera->setPosition(newCameraPos); } else { camera->setPosition(cameraPos); } camera->setDirection(targetPos - camera->getPosition()); } |