Как мне переместить персонажа в RPG с Bullet Physics / Ogre3D?

9

В последнее время у меня были проблемы с перемещением моего персонажа в моей игре Ogre3D. По сути, я перемещаю персонажа с помощью RigidBody->translate()функции пули , но когда я наталкиваюсь на него и врезаюсь в стену, я немного прохожу его, а затем отскакиваю назад. Я задаюсь вопросом, есть ли другой хороший способ перемещать моего персонажа (у которого есть форма столкновения сферы) в простом мире типа Плоскости со стенами?

Относительно этого я использую библиотеки Ogre3D и Bullet Physics.

Molmasepic
источник

Ответы:

9

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

Из документации, кажется, есть btRigidBody::setLinearVelocityметод, который вы можете использовать. Так, например, если вы не хотите, чтобы происходило какое-либо ускорение, просто установите линейную скорость на соответствующее значение всякий раз, когда персонаж движется, и установите ее обратно в (0,0,0), когда персонаж должен остановиться (т.е. когда игрок отпускает ключ).

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

В качестве альтернативы, вы можете рассматривать персонажа как целостный физический объект и обрабатывать движения, используя методы applyForceили applyImpulse. Это приведет к ускорению тела, поэтому у ваших персонажей будет импульс, и результаты, вероятно, будут выглядеть лучше. Но вам нужно предпринять некоторые дополнительные меры, например, убедившись, что линейная скорость никогда не превышает определенного предела, либо путем ее ограничения или игры с демпфированием / трением. Так что это будет немного сложнее в реализации и настройке.

Поэкспериментируйте с обоими подходами, а затем выберите тот, который наиболее соответствует вашим потребностям.

Дэвид Гувея
источник
Хорошо, спасибо за быстрый ответ, я собираюсь попробовать это прямо сейчас.
Molmasepic
Трюк LinearVelocity сработал так же, как ожидалось, как шарм! Было несколько перегибов, которые я должен был исправить, но он работал на 100%. Большое спасибо за ответ!
Molmasepic
9

Напомним, что мой опыт работы с физикой - это использование Chimpunk в движке 2D-игр, но я уверен, что эта концепция очень хорошо переводится в 3D.

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

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

Вот где получить немного сложнее:

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

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

Надеюсь, я объяснил это достаточно хорошо. Не стесняйтесь спрашивать, если вам нужны разъяснения. :)

Lendrick
источник
0

Для маркера 2.87 кажется, что правильный метод должен иметь обратный вызов галочки, который обновляется с частотой обновления внутренней симуляции (возможно, много сотен Гц), и setWorldTransform () для кинематических тел будет плавно обновлять позицию:

Эта часть находится в руководстве:

// set the rigid body as kinematic
rigid_body->setCollisionFlags(
    rigid_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
rigid_body->setActivationState(DISABLE_DEACTIVATION);
...

Эту часть было сложнее понять:

void externalTickCallback(btDynamicsWorld *world, btScalar timeStep)
{
  // get object passed into user data point
  Foo* foo = static_cast<Foo*>(world->getWorldUserInfo());
  ... loop through all the rigid bodies, maybe foo has them
  {
    if (rigid_body->getCollisionFlags() & btCollisionObject::CF_KINEMATIC_OBJECT)
    {
      btVector3 kinematic_linear_vel = ... // get velocity from somewhere
      btTransform trans;
      rigid_body->getMotionState()->getWorldTransform(trans);
      trans.setOrigin(trans.getOrigin() + kinematic_linear_vel * time_step);
      // TODO support angular velocity
      rigid_body_->getMotionState()->setWorldTransform(trans);
    }
  }
}
...
my_dynamics_world->setInternalTickCallback(tickCallback, static_cast<void*>(this), true);

Это была полезная документация в btRigidBody.h https://github.com/bulletphysics/bullet3/blob/master/src/BulletDynamics/Dynamics/btRigidBody.h :

/// - C) Кинематические объекты, которые являются объектами без массы, но пользователь может перемещать их. Существует одностороннее взаимодействие, и Bullet рассчитывает скорость на основе временного шага и предыдущего и текущего преобразования мира.

setLinearVelocity () не работает для кинематических объектов (возможно, раньше в более ранних версиях?). Но динамический мир поймет setWorldTransform () и вызовы getLinearVelocity () для кинематического объекта вернут скорость, установленную в обратном вызове тика (он, вероятно, возвращает среднее значение, если эти скорости изменятся от внутреннего тика к тику).

https://github.com/bulletphysics/bullet3/issues/1204 - у автора проблемы есть правильная идея, но ответ не поможет.

Лукас W
источник