¿Cómo movería un personaje en un juego de rol con Bullet Physics / Ogre3D?

9

Últimamente he tenido problemas para mover mi personaje en mi juego Ogre3D. Básicamente, estoy moviendo al personaje con la RigidBody->translate()función de bala , pero al hacerlo y chocar contra una pared, lo atravieso ligeramente y luego me recupero. Me pregunto si hay otra buena manera de mover a mi personaje (que tiene una forma de colisión de esfera) en un mundo simple de tipo Plano con paredes.

Las bibliotecas que estoy usando que son relativas a esto son 'Ogre3D' y 'Bullet Physics'.

Molmasepic
fuente

Respuestas:

9

Aunque no he trabajado específicamente con el motor de física de bala, he hecho algo muy similar en otro motor de física. La forma en que lo resolví fue establecer la velocidad lineal del cuerpo rígido en lugar de traducirlo directamente. El movimiento y las colisiones fueron manejados automáticamente por la fase de actualización del motor de física.

De la documentación parece haber un btRigidBody::setLinearVelocitymétodo que puede usar. Entonces, por ejemplo, si no desea que ocurran aceleraciones, simplemente configure la velocidad lineal en un valor apropiado cada vez que el personaje se mueva, y vuelva a establecerlo en (0,0,0) cuando se supone que el personaje se detiene (es decir, cuando el jugador suelta la llave).

En cuanto a qué valores usar, el enfoque habitual sería comenzar con la velocidad deseada de su personaje (como flotante / escalar) y luego multiplicarlo por un vector normalizado que apunte en la dirección que desea mover. Por lo que puedo ver, la btVector3clase ya tiene métodos para todo esto.

Alternativamente, podría considerar tratar al personaje como un objeto físico completo y manejar el movimiento utilizando los métodos applyForceo applyImpulse. Esto daría como resultado una aceleración del cuerpo, por lo que tus personajes tendrán impulso y los resultados probablemente se verán mejor de esta manera. Pero debe tomar algunas medidas adicionales, por ejemplo, asegurándose de que la velocidad lineal nunca exceda un cierto límite, ya sea sujetándola o jugando con amortiguación / fricción. Por lo tanto, será un poco más difícil de implementar y afinar.

Experimente con ambos enfoques y luego elija el que mejor se adapte a sus necesidades.

David Gouveia
fuente
Muy bien, gracias por la respuesta rápida, voy a probar esto de inmediato.
Molmasepic
¡El truco LinearVelocity funcionó como se esperaba como un encanto! Hubo algunos problemas que tuve que arreglar, pero está funcionando al 100%. ¡Muchas gracias por la respuesta!
Molmasepic
9

Para el registro, mi experiencia con la física es usar Chimpunk en un motor de juego 2D, pero estoy bastante seguro de que este concepto se traduce en 3D muy bien.

Supongo que tu personaje es un cuerpo de física con peso y tal. La mejor manera de hacer esto es hacer una simulación muy simplificada de caminar. Piénselo de esta manera: si está de pie, sus pies tienen mucha fricción, por lo que no solo se desliza. Cuando te mueves, es más o menos equivalente a eliminar esa fricción (ya que no estás resistiendo el movimiento con los pies) y aplicar una fuerza de dirección. Estoy no decir que usted debe simular de forma individual cada pie empujando en el suelo - un cuerpo rígido es lo que desea.

  • Cuando tu personaje no esté intentando moverse activamente, aumenta la velocidad de su velocidad para que permanezca inmóvil.
  • Cuando tu personaje se esté moviendo, baja la amortiguación de velocidad y aplica una fuerza en la dirección del movimiento. Establece tu amortiguación y fuerza para que el personaje se mueva a una velocidad razonable.
  • Si el personaje está en el aire, establece la amortiguación muy, muy baja. Si quieres ser realista, no permitas que apliquen ninguna fuerza para cambiar su dirección mientras están en el aire. Puedes golpear a un medio feliz aquí permitiéndoles aplicar una fuerza mucho más pequeña, dándoles una capacidad limitada para ajustar su trayectoria mientras nacen en el aire.

Aquí es donde get se vuelve un poco complicado:

  • Si el personaje ya se está moviendo y cambian de dirección, deberás compensar tu impulso ajustando la dirección en la que aplicas la fuerza. Por ejemplo, si tu personaje se mueve hacia el norte y gira hacia el este, debes aplicar tu fuerza en una dirección que esté a medio camino entre el opuesto de la dirección de viaje actual del personaje y su dirección de viaje prevista. A medida que cambia la dirección de viaje, ajusta la fuerza para que siempre esté a medio camino entre los dos, y tu personaje cambiará de dirección rápida y suavemente.

Si ajusta su fuerza y ​​la amortiguación correctamente, la aplicación de una fuerza siempre le dará el resultado más realista, especialmente si el personaje va a empujar objetos. Traducir será la peor forma de hacerlo, ya que el motor de física realmente no considera que eso sea movimiento. Establecer la velocidad directamente es algo mejor, pero en mi experiencia los mejores resultados se pueden obtener utilizando la fuerza y ​​la amortiguación.

Espero haber explicado esto lo suficientemente bien. No dude en preguntar si necesita aclaraciones. :)

Lendrick
fuente
0

Para la viñeta 2.87, parece que el método adecuado es tener una devolución de llamada que se actualice a la velocidad de actualización de la simulación interna (posiblemente muchos 100s de Hz), y setWorldTransform () en cuerpos cinemáticos actualizará sin problemas la posición:

Esta parte está en el manual:

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

Esta parte fue más difícil de entender:

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);

Esta fue una documentación útil en btRigidBody.h https://github.com/bulletphysics/bullet3/blob/master/src/BulletDynamics/Dynamics/btRigidBody.h :

/// - C) Objetos cinemáticos, que son objetos sin masa, pero el usuario puede moverlos. Hay interacción unidireccional, y Bullet calcula una velocidad basada en el paso de tiempo y la transformación del mundo anterior y actual.

setLinearVelocity () no funciona para objetos cinemáticos (¿tal vez solía hacerlo en versiones anteriores?). Pero el mundo dinámico comprenderá setWorldTransform () y las llamadas a getLinearVelocity () en el objeto cinemático devolverán la velocidad establecida en la devolución de llamada de tick (probablemente devuelva un promedio si esas velocidades cambian de tick interno a tick).

https://github.com/bulletphysics/bullet3/issues/1204 : el póster del problema tiene la idea correcta pero la respuesta no es útil.

Lucas W
fuente