Comment déplacer un personnage dans un RPG avec Bullet Physics / Ogre3D?

9

Ces derniers temps, j'ai eu des problèmes avec le déplacement de mon personnage dans mon jeu Ogre3D. Fondamentalement, je déplace le personnage avec la RigidBody->translate()fonction de balle , mais lorsque je le fais et que je me cogne contre un mur, je le traverse légèrement puis je rebondis. Je me demande s'il existe un autre bon moyen de déplacer mon personnage (qui a une forme de collision de sphère) dans un monde simple de type Avion avec des murs?

Les bibliothèques que j'utilise qui sont relatives à cela sont «Ogre3D» et «Bullet Physics».

Molmasepic
la source

Réponses:

9

Bien que je n'aie pas travaillé spécifiquement avec le moteur physique des balles, j'ai fait quelque chose de très similaire dans un autre moteur physique. La façon dont je l'ai résolu était de définir la vitesse linéaire du corps rigide au lieu de la traduire directement. Les mouvements et les collisions ont ensuite été automatiquement gérés par la phase de mise à jour du moteur physique.

D'après la documentation, il semble y avoir une btRigidBody::setLinearVelocityméthode que vous pouvez utiliser. Ainsi, par exemple, si vous ne voulez pas d'accélérations, réglez simplement la vitesse linéaire sur une valeur appropriée chaque fois que le personnage se déplace et redéfinissez-la sur (0,0,0) lorsque le personnage est censé s'arrêter (c.-à-d. lorsque le joueur relâche la clé).

En ce qui concerne les valeurs à utiliser, l'approche habituelle serait de commencer à la vitesse souhaitée de votre personnage (en tant que flottant / scalaire), puis de la multiplier par un vecteur normalisé pointant dans la direction que vous souhaitez déplacer. D'après ce que je peux voir, la btVector3classe a déjà des méthodes pour tout cela.

Vous pouvez également envisager de traiter le personnage comme un objet physique complet et gérer les mouvements à l'aide des méthodes applyForceou applyImpulse. Cela entraînerait une accélération du corps, donc vos personnages auront de l'élan et les résultats seront probablement plus beaux de cette façon. Mais vous devez prendre des mesures supplémentaires, par exemple, en vous assurant que la vitesse linéaire ne dépasse jamais une certaine limite, soit en la serrant, soit en jouant avec l'amortissement / la friction. Il sera donc un peu plus difficile à mettre en œuvre et à affiner.

Expérimentez avec les deux approches, puis choisissez celle qui se comporte le plus près de vos besoins.

David Gouveia
la source
Très bien, merci pour la réponse rapide, je vais essayer tout de suite.
Molmasepic
L'astuce LinearVelocity a fonctionné comme prévu comme un charme! Il y avait quelques anomalies que j'ai dû corriger, mais cela fonctionne à 100% Merci beaucoup pour la réponse!
Molmasepic
9

Pour mémoire, mon expérience avec la physique utilise Chimpunk dans un moteur de jeu 2D, mais je suis presque sûr que ce concept se traduit très bien en 3D.

Je suppose que votre personnage est un corps physique avec du poids et autres. La meilleure façon de le faire est de faire une simulation très simplifiée de la marche. Pensez-y comme ceci: si vous êtes debout, vos pieds ont beaucoup de friction, donc vous ne glissez pas simplement. Lorsque vous bougez, cela équivaut à peu près à supprimer ce frottement (puisque vous ne résistez pas au mouvement avec vos pieds) et à appliquer une force de direction. Je ne dis pas que vous devez simuler individuellement chaque pied poussant sur le sol - un corps rigide est ce que vous voulez.

  • Lorsque votre personnage n'essaie pas activement de bouger, augmentez son amortissement de vitesse pour qu'il reste immobile.
  • Lorsque votre personnage se déplace, réduisez son amortissement de vitesse et appliquez une force dans le sens du mouvement. Réglez votre amortissement et votre force pour que le personnage se déplace à une vitesse raisonnable.
  • Si le personnage est dans l'air, réglez l'amortissement très, très bas. Si vous voulez être réaliste, ne leur permettez pas d'appliquer de force pour changer de direction en l'air. Vous pouvez trouver un juste milieu ici en leur permettant d'appliquer une force beaucoup plus petite, ce qui leur donne une capacité limitée d'ajuster leur trajectoire pendant la naissance.

Voici où obtenir devient un peu compliqué:

  • Si le personnage est déjà en mouvement et qu'il change de direction, vous devrez compenser votre élan en ajustant la direction dans laquelle vous appliquez la force. Par exemple, si votre personnage se déplace vers le nord et tourne vers l'est, vous devez appliquer votre force dans une direction à mi-chemin entre l'opposé de la direction de déplacement actuelle du personnage et sa direction de déplacement prévue. Au fur et à mesure que la direction du voyage change, ajustez la force afin qu'elle soit toujours à mi-chemin entre les deux, et votre personnage changera rapidement et en douceur de direction.

Si vous ajustez correctement votre force et votre amortissement, appliquer une force vous donnera toujours le résultat le plus réaliste, en particulier si le personnage va pousser des objets. La traduction va être la pire façon de le faire, car le moteur physique ne considère pas vraiment que c'est du mouvement. Régler directement la vitesse est un peu mieux, mais d'après mon expérience, les meilleurs résultats peuvent être obtenus en utilisant la force et l'amortissement.

J'espère que j'ai bien expliqué cela. N'hésitez pas à demander si vous avez besoin d'éclaircissements. :)

Lendrick
la source
0

Pour la puce 2.87, il semble que la méthode appropriée consiste à avoir un rappel de tick qui se met à jour au taux de mise à jour de la simulation interne (peut-être plusieurs centaines de Hz), et setWorldTransform () sur les corps cinématiques mettra à jour la position en douceur:

Cette partie est dans le manuel:

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

Cette partie était plus délicate à comprendre:

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

Il s'agissait d'une documentation utile dans btRigidBody.h https://github.com/bulletphysics/bullet3/blob/master/src/BulletDynamics/Dynamics/btRigidBody.h :

/// - C) Objets cinématiques, qui sont des objets sans masse, mais l'utilisateur peut les déplacer. Il y a une interaction à sens unique, et Bullet calcule une vitesse basée sur le pas de temps et la transformation du monde précédente et actuelle.

setLinearVelocity () ne fonctionne pas pour les objets cinématiques (peut-être que c'était le cas dans les versions antérieures?). Mais le monde de la dynamique comprendra setWorldTransform () et les appels à getLinearVelocity () sur l'objet cinématique retourneront la vitesse définie dans le rappel de tick (il retournera probablement une moyenne si ces vitesses devaient changer de tick interne à tick).

https://github.com/bulletphysics/bullet3/issues/1204 - l'affiche du problème a la bonne idée mais la réponse n'est pas utile.

Lucas W
la source