Wie würde ich einen Charakter in einem Rollenspiel mit Bullet Physics / Ogre3D bewegen?

9

In letzter Zeit hatte ich Probleme, meinen Charakter in meinem Ogre3D-Spiel zu bewegen. Grundsätzlich bewege ich den Charakter mit der RigidBody->translate()Funktion der Kugel , aber wenn ich das tue und gegen eine Wand stoße, gehe ich leicht durch und werde dann zurückgeworfen. Ich frage mich, ob es einen anderen guten Weg gibt, meinen Charakter (der eine Kugelkollisionsform hat) in einer einfachen Welt vom Typ Flugzeug mit Wänden zu bewegen.

Die Bibliotheken, die ich verwende, sind relativ dazu 'Ogre3D' und 'Bullet Physics'.

Molmasepic
quelle

Antworten:

9

Obwohl ich nicht speziell mit der Bullet-Physik-Engine gearbeitet habe, habe ich in einer anderen Physik-Engine etwas sehr Ähnliches gemacht. Die Art und Weise, wie ich es gelöst habe, bestand darin, die Lineargeschwindigkeit des starren Körpers einzustellen, anstatt sie direkt zu übersetzen. Bewegungen und Kollisionen wurden dann automatisch von der Aktualisierungsphase der Physik-Engine behandelt.

Aus der Dokumentation geht hervorbtRigidBody::setLinearVelocity , dass Sie eine Methode verwenden können. Wenn Sie beispielsweise keine Beschleunigungen wünschen, setzen Sie die Lineargeschwindigkeit einfach auf einen geeigneten Wert, wenn sich das Zeichen bewegt, und setzen Sie sie auf (0,0,0) zurück, wenn das Zeichen anhalten soll (dh wenn der Spieler den Schlüssel loslässt).

Was die zu verwendenden Werte betrifft, besteht der übliche Ansatz darin, mit der gewünschten Geschwindigkeit Ihres Charakters (als Float / Skalar) zu beginnen und diese dann mit einem normalisierten Vektor zu multiplizieren, der in die Richtung zeigt, in die Sie sich bewegen möchten. Soweit ich sehen kann, verfügt die btVector3Klasse bereits über Methoden für all dies.

Alternativ können Sie den Charakter als vollständiges Physikobjekt behandeln und die Bewegung entweder mit der Methode applyForceoder mit der applyImpulseMethode behandeln. Dies würde zu einer Körperbeschleunigung führen, sodass Ihre Charaktere Schwung haben und die Ergebnisse auf diese Weise wahrscheinlich besser aussehen. Sie müssen jedoch einige zusätzliche Maßnahmen ergreifen, indem Sie beispielsweise sicherstellen, dass die Lineargeschwindigkeit niemals einen bestimmten Grenzwert überschreitet, indem Sie sie entweder festklemmen oder mit Dämpfung / Reibung herumspielen. Es wird also etwas schwieriger zu implementieren und zu optimieren sein.

Experimentieren Sie mit beiden Ansätzen und wählen Sie dann den aus, der Ihren Anforderungen am nächsten kommt.

David Gouveia
quelle
Okay, danke für die schnelle Antwort, ich werde das sofort ausprobieren.
Molmasepic
Der LinearVelocity-Trick funktionierte wie erwartet wie ein Zauber! Es gab ein paar Knicke, die ich beheben musste, aber es funktioniert zu 100%. Vielen Dank für die Antwort!
Molmasepic
9

Meine Erfahrung mit Physik besteht darin, Chimpunk in einer 2D-Spiel-Engine zu verwenden, aber ich bin mir ziemlich sicher, dass dieses Konzept in 3D umgesetzt werden kann.

Ich gehe davon aus, dass dein Charakter ein physischer Körper mit Gewicht und so ist. Der beste Weg, dies zu tun, ist eine sehr vereinfachte Simulation des Gehens. Stellen Sie sich das so vor: Wenn Sie stehen, haben Ihre Füße viel Reibung, sodass Sie nicht einfach herumrutschen. Wenn Sie sich bewegen, entspricht dies in etwa dem Entfernen dieser Reibung (da Sie der Bewegung nicht mit Ihren Füßen widerstehen) und dem Aufbringen einer Richtungskraft. Ich sage nicht , dass Sie jeden Fuß, der auf den Boden drückt, einzeln simulieren sollten - ein starrer Körper ist das, was Sie wollen.

  • Wenn Ihr Charakter nicht aktiv versucht, sich zu bewegen, erhöhen Sie die Geschwindigkeit, damit er stationär bleibt.
  • Wenn sich Ihr Charakter bewegt, verringern Sie die Geschwindigkeitsdämpfung und üben Sie eine Kraft in Bewegungsrichtung aus. Stellen Sie Ihre Dämpfung und Kraft so ein, dass sich der Charakter mit einer angemessenen Geschwindigkeit bewegt.
  • Wenn der Charakter in der Luft ist, stellen Sie die Dämpfung sehr, sehr niedrig ein. Wenn Sie realistisch sein möchten, lassen Sie sie keine Kraft anwenden, um ihre Richtung in der Luft zu ändern. Sie können hier ein fröhliches Medium treffen, indem Sie ihnen erlauben, eine viel kleinere Kraft anzuwenden, was ihnen eine begrenzte Möglichkeit gibt, ihre Flugbahn während der Luftfahrt anzupassen.

Hier wird es etwas komplizierter:

  • Wenn sich der Charakter bereits bewegt und seine Richtung ändert, müssen Sie Ihren Schwung kompensieren, indem Sie die Richtung anpassen, in die Sie die Kraft anwenden. Wenn sich Ihr Charakter beispielsweise genau nach Norden bewegt und genau nach Osten dreht, müssen Sie Ihre Kraft in einer Richtung anwenden, die auf halbem Weg zwischen dem Gegenteil der aktuellen Fahrtrichtung des Charakters und seiner beabsichtigten Fahrtrichtung liegt. Wenn sich die Fahrtrichtung ändert, stellen Sie die Kraft so ein, dass sie immer auf halbem Weg zwischen den beiden liegt, und Ihr Charakter ändert schnell und reibungslos die Richtung.

Wenn Sie Ihre Kraft und Dämpfung richtig einstellen, erhalten Sie durch Anwenden einer Kraft immer das realistischste Ergebnis, insbesondere wenn der Charakter Objekte herumschiebt. Übersetzen wird der schlechteste Weg sein, da die Physik-Engine dies nicht wirklich als Bewegung betrachtet. Das direkte Einstellen der Geschwindigkeit ist etwas besser, aber meiner Erfahrung nach können die besten Ergebnisse durch Anwendung von Kraft und Dämpfung erzielt werden.

Hoffentlich habe ich das gut genug erklärt. Fragen Sie einfach, ob Sie eine Klärung benötigen. :) :)

Lendrick
quelle
0

Für Bullet 2.87 scheint es die richtige Methode zu sein, einen Tick-Rückruf zu haben, der mit der internen Simulationsaktualisierungsrate (möglicherweise viele 100 Hz) aktualisiert wird, und setWorldTransform () auf kinematischen Körpern aktualisiert die Position reibungslos:

Dieser Teil befindet sich im Handbuch:

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

Dieser Teil war schwieriger herauszufinden:

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

Dies war eine hilfreiche Dokumentation in btRigidBody.h https://github.com/bulletphysics/bullet3/blob/master/src/BulletDynamics/Dynamics/btRigidBody.h :

/// - C) Kinematische Objekte, die Objekte ohne Masse sind, aber vom Benutzer verschoben werden können. Es gibt eine Einweg-Interaktion, und Bullet berechnet eine Geschwindigkeit basierend auf dem Zeitschritt und der vorherigen und aktuellen Welttransformation.

setLinearVelocity () funktioniert nicht für kinematische Objekte (möglicherweise früher?). Die Dynamikwelt wird jedoch setWorldTransform () verstehen und Aufrufe von getLinearVelocity () für das kinematische Objekt geben die im Tick-Rückruf festgelegte Geschwindigkeit zurück (es wird wahrscheinlich ein Durchschnitt zurückgegeben, wenn sich diese Geschwindigkeiten von internem Tick zu Tick ändern würden).

https://github.com/bulletphysics/bullet3/issues/1204 - Das Themenposter hat die richtige Idee, aber die Antwort ist nicht hilfreich.

Lucas W.
quelle