Wie implementiere ich einen Traktorstrahl?

8

Ich arbeite an einem Spiel, bei dem der Spieler Objekte mit einem Traktorstrahl aufnehmen und herumtragen kann.

Das Objekt in Richtung Strahlmitte anzuziehen ist nicht schwierig. Aber sobald das Objekt nahe genug an der Mitte ist, muss ich es dort halten, während sich der Spieler bewegt, womit ich Probleme habe. Ich kann mir zwei Möglichkeiten vorstellen, und beide haben Probleme:

  1. Aktualisieren Sie die Position des Objekts, wenn sich die Position des Spielers ändert, und halten Sie es auf dem Strahl zentriert.

  2. Aktualisieren Sie die Geschwindigkeit des Objekts so, dass sie direkt auf die Mitte des Strahls zeigt. Je weiter entfernt, desto höher die Geschwindigkeit.

Das Bewegen und Drehen funktioniert bei beiden Ansätzen einwandfrei, aber die Physik ist falsch, wenn das getragene Objekt mit anderen Objekten kollidiert:

Beim ersten Ansatz wird die Physik völlig ignoriert. Das getragene Objekt schiebt einfach alles aus dem Weg. Das liegt daran, dass Positionsänderungen nur als Teil der Weltphysik basierend auf der Geschwindigkeit vorgenommen werden sollen.

Beim zweiten Ansatz verhält sich die Physik grundsätzlich so, wie sie sollte, reagiert jedoch übermäßig. Das Problem ist: Um das getragene Objekt auch beim Drehen und Bewegen in der Mitte des Strahls zu halten, muss ich hohe Geschwindigkeitswerte verwenden. Sobald das getragene Objekt ein anderes berührt, wird die Kollision viel zu schnell.

Wie kann ich das richtig umsetzen? Meine derzeit beste Vermutung ist, den zweiten Ansatz zu wählen und der Weltphysik eine spezielle Handhabung für getragene Objekte hinzuzufügen, um die Geschwindigkeit auf vernünftige Werte für Kollisionen zu reduzieren oder wenn der Spieler aufhört, sie zu tragen. Aber das scheint eine ziemlich unelegante Problemumgehung zu sein.

Bearbeiten: Hinzufügen von Pseudocode, um zu veranschaulichen, wie es jetzt funktioniert (das wäre der zweite Ansatz oben)

void attract_object(object, ticks) {
    Vector distance = beam_center - object.center;
    // If the object is not close to the beam center, attract it slowly
    if (magnitude(distance) > 10) {
        object.velocity += distance.normalized() * ticks * 0.1;
        return;
    }

    // Here comes the part we're talking about. That magic 0.5 is just high enough
    // that the object isn't lost while moving around. But it's still so high that
    // other objects are repelled with way too much force.
    object.velocity = distance * ticks * 0.5;
}

Soweit ich weiß, geschieht dies, wenn das getragene Objekt ein anderes Objekt wegschiebt:

  1. Das getragene Objekt kollidiert mit einem anderen Objekt
  2. Die Geschwindigkeiten der Objekte sind richtig verteilt, so dass das getragene Objekt dabei von der Mitte des Strahls weggeschoben wird
  3. Der obige Code bewirkt, dass das getragene Objekt mit so viel Geschwindigkeit in die Mitte des Strahls zurückkehrt, dass es schnell dorthin zurückkehrt
  4. Wenn sich das getragene Objekt zur Mitte des Strahls zurückbewegt, wird die Hälfte seiner hohen Geschwindigkeit auf das andere Objekt übertragen, wodurch es heftig abgestoßen wird. Da die Anfangsgeschwindigkeit des getragenen Objekts vernünftig zu sein scheint, kann ich mir vorstellen, dass die Schritte 2 bis 4 mehrmals wiederholt werden, wodurch eine so hohe Geschwindigkeit entsteht.

Das scheint die Ursache zu sein. Ich kann mir keinen guten Weg vorstellen, das Problem zu beheben :(

Futlib
quelle
1
Willkommen in der Komplexität eines Tokomak-Fusionsreaktors. Sie haben den Vorteil, dass Sie nur ein funktionierendes mathematisches Modell erstellen müssen, nicht die funktionierende Magnetflasche, aber die Mathematik ist identisch und nicht trivial. Was Sie versuchen, ist machbar, aber Sie müssen Ihr mathematisches Modell vor dem Codieren genau durchdenken.
Pieter Geerkens

Antworten:

1

Im Wesentlichen suchen Sie, dass sich das "gestrahlte" Objekt genau so verhält, als würden Sie es mit Ihren Händen greifen.
Eine Möglichkeit wäre, es die Beschleunigungs-A / O-Geschwindigkeiten der 'Hand' teilen zu lassen, die es hält, anstatt seine Geschwindigkeit anzupassen, um die Lücke mit der Mitte des Strahls zu füllen.

Angenommen, die Mitte des Strahls ist die haltende Hand. Wenn sich Ihr Charakter in 1 Sekunde um 90 Grad nach links dreht, beträgt die Geschwindigkeit der Hand:

If R = length of the arm: which is the radius of the rotation circle
R^2 *PI /4 would be the distance traveled over a second.
Machen Sie es mal die verstrichene Zeit des Rahmens, um die Geschwindigkeit zu finden, die Sie auf Ihr Objekt anwenden müssen. Finden Sie die horizontale Normale zum Strahl, um seinen Richtungsvektor zu finden.

Mein Punkt ist, dass Sie keine Probleme lösen müssen, wenn Sie andere Implementierungen ausprobieren, die dies überhaupt nicht verursachen.

Ich würde mit der Schwerkraftkanone in HL2 spielen gehen, um mich von dem Problem inspirieren zu lassen, aber ich habe heute andere Pläne.

EDIT: Es tut mir leid, ich dachte, es wäre für eine 3D-Strahlpistole, aber es ist im Wesentlichen das gleiche mit 2D, außer dass die Achsen unterschiedlich sind (und es keine komplexe Geometrie gibt).

icosamuel
quelle
Nachdem ich verschiedene Hacks ausprobiert habe, hat mich das auf den richtigen Weg gebracht. Das Ergebnis sieht gut aus. Die Kollisionsgeschwindigkeit ist immer noch etwas zu hoch, aber ich denke, ich kann das herausfinden. Möglicherweise nur, indem keine Objekte mit hoher Geschwindigkeit in eine andere Richtung angezogen werden.
Futlib
Wenn die 'Hand' mit einer Wand kollidiert, können Sie möglicherweise die nächste plausible Position (die nicht kollidiert) für die Hand berechnen und diese als 'temporäre Hand' verwenden, während eine Kollision auftritt. Ich beschäftige mich oft gerne mit Problemen aus einer anderen Perspektive. Ich bin froh, dass ich dabei helfen konnte;).
icosamuel
4

Wie wäre es, wenn Sie eine Federverbindung hinzufügen, dh das getragene Objekt zwingen, sich basierend auf der Entfernung in die Trageposition zurückzubewegen, es aber dennoch von festen Gegenständen (wie Wänden) wegschieben zu lassen.

Passen Sie die Geschwindigkeit des getragenen Objekts ständig an (indem Sie die Beschleunigung basierend auf Position / Entfernung ändern), um auf die Position des Traktorstrahls zu zeigen (dh auf Ihren zweiten Ansatz). Wenn das Objekt zu weit weggeschoben wird, trennen Sie die Verbindung (und das Objekt).

Ich bin mir nicht sicher, warum Sie hohe Geschwindigkeiten benötigen würden. Insbesondere der Fall "Spieler lässt los" weist darauf hin, dass Ihre Rotationsgeschwindigkeit möglicherweise zu hoch oder unrealistisch ist. Vergessen Sie auch nicht Dinge wie Luftwiderstand und Schwerkraft.


Bearbeiten: In Anbetracht des aktualisierten Codes ist das Problem eher trivial zu finden:

if (magnitude(distance) > 10) {
    object.velocity += distance.normalized() * ticks * 0.1;
    return;
}

Das Problem ist hier der Fall, in dem der Abstand des Objekts zu seiner Zielposition ständig zu groß ist (dh > 10). Solange diese Bedingung erfüllt ist, nimmt die Geschwindigkeit einfach immer wieder zu (dh auf unbestimmte Zeit).

Zwei mögliche Lösungen hierfür:

Definieren Sie eine maximale Geschwindigkeit:

object.velocity = min(object.velocity + distance.normalized() * ticks * 0.1, max_velocity);

Wenden Sie eine feste Geschwindigkeit an, anstatt zu beschleunigen:

object.velocity = distance.normalized() * ticks * magic_factor;

Nur zu beschleunigen, während man zu weit weg ist, ist hier definitiv ein falscher Ansatz. Eine Feder oder ein Gummiband ziehen: Es spielt keine Rolle, ob Sie es eine Sekunde oder eine Minute lang halten. Am Ende wird es auf die gleiche Weise beschleunigen (wenn man bedenkt, dass es nicht in Bewegung war und keine anderen Kräfte angewendet werden).

Mario
quelle
Das habe ich in Methode 2 beschrieben, nicht wahr? Das ist die grundlegende Frühlingsphysik AFAIK. Ich werde der obigen Frage Code hinzufügen, um sie zu veranschaulichen.
Futlib
Auch das Teil über falsche Geschwindigkeit oben entfernt, es ist eigentlich in Ordnung, nur getestet. Es sind also nur die Kollisionen mit anderen Objekten, die durcheinander gebracht werden.
Futlib
Ja, es ist im Wesentlichen Ihr zweiter Ansatz. Aktualisiere meine Antwort.
Mario
Versuchte beide Ansätze, aber es hilft nicht. Es sieht so aus, als ob der Fall der Größe (Entfernung)> 10 hier nicht der Schuldige ist. Ich habe versucht, die Geschwindigkeit für den Fall <= zu begrenzen, aber es ist das übliche Problem: Die Geschwindigkeit ist entweder so niedrig, dass das Objekt fallen gelassen wird, oder so hoch, dass es andere gewaltsam abstößt.
Futlib
"so hoch, dass es andere heftig abstößt": Sie sollten andere Objekte basierend auf der effektiven Geschwindigkeit bewegen, nicht auf der Geschwindigkeit hinter den Kulissen (dh Geschwindigkeit aufgrund von Kollision zurücksetzen).
Mario