Benötigen Sie einen effizienten Weg, um zu verhindern, dass der Feind mehrmals von derselben Quelle getroffen wird


7

Mein Spiel ist ein einfaches 2D-Spiel, aber dies gilt wahrscheinlich für viele Arten von Szenarien.

Angenommen, mein Spieler hat ein Schwert oder eine Waffe, die auf ein Projektil schießt, das mehrere Feinde durchdringen und treffen kann.

Während das Schwert schwingt, gibt es eine Zeitspanne, in der ich nach dem Schwert suche, das in jedem Bild Kontakt mit einem Feind aufnimmt. Aber sobald ein Feind von diesem Schwert getroffen wird, möchte ich nicht, dass er immer wieder getroffen wird, während das Schwert durchkommt. (Ich möchte, dass das Schwert weiterhin prüft, ob es andere Feinde trifft.)

Ich habe mir ein paar verschiedene Ansätze überlegt (siehe unten), aber sie scheinen mir nicht gut zu sein. Ich suche nach einem Weg, der keine Querverweise erzwingt (ich möchte nicht, dass der Feind eine Nachricht an das Schwert / Projektil zurücksenden muss). Und ich möchte vermeiden, bei jedem Angriff mehrere Array-Listen zu generieren / zurückzusetzen.

  • Jedes Mal, wenn das Schwert schwingt, generiert es eine eindeutige ID (möglicherweise durch Inkrementieren einer globalen statischen Länge). Jeder Feind führt eine Liste mit IDs von Schlägen oder Projektilen, die ihn bereits getroffen haben, damit der Feind weiß, dass er nicht mehrmals von etwas verletzt wird. Nachteil ist, dass jeder Feind eine große Liste haben kann, mit der er vergleichen kann. Projektile und Schwertkämpfe müssten also ihr Lebensende an alle Feinde senden und eine Suche und Entfernung auf der Array-Liste jedes Feindes veranlassen. Scheint irgendwie langsam.
  • Jeder Schwertschlag oder jedes Projektil führt eine eigene Liste von Feinden, die es bereits getroffen hat, damit es keinen Schaden anrichtet. Nachteile: Jedes Mal, wenn ein Schwert geschwungen oder ein Projektil abgefeuert wird, muss eine neue Liste erstellt werden (wahrscheinlich aus einem Pool ziehen und eine löschen). Dies beeinträchtigt auch die Modularität, da das Schwert nun eine Nachricht an den Feind senden muss und der Feind eine Nachricht an das Schwert zurücksenden muss. Mir scheint, dass solche Einbahnstraßen eine großartige Möglichkeit sind, sehr schwer zu findende Fehler zu verursachen.

Antworten:


8

Die Auflösung hierfür könnte dieselbe sein, die Sie für die Verarbeitung gehaltener Tasten auf der Tastatur verwenden würden.

colliding = checkCollision(this, otherObject);
if(colliding && !previousFrameColliding) {
    //trigger hit with otherObject
} else if(colliding && previousFrameColliding) {
    //still in contact, do what you will here, or nothing
} else if(!colliding && previousFrameColliding) {
    //we just finished a collision
} else {
    //not colliding
}
previousFrameColliding = colliding;

Wenn Sie wahrscheinlich immer nur mit einem Objekt gleichzeitig kollidieren, funktioniert dies. Wenn Sie mit mehreren Objekten gleichzeitig kollidieren möchten, müssen Sie die "aktuell kollidierenden" Objekte verfolgen und dies überprüfen.


Leider fliegen überall Projektile aus verschiedenen Quellen herum, sodass ich mit dieser Methode zu meiner ersten Idee zurückkomme.
TenFour04

Das Obige würde im Projektil oder Schwert aufbewahrt. Die Idee ist jedoch dieselbe, wie auch immer Sie sie implementieren. Es geht darum zu erkennen, wann Sie von nicht kollidierend zu kollidierend wechseln. Es geht nicht darum zu überprüfen, ob ich gerade kollidiere
MichaelHouse

Okay, ich verstehe jetzt. Jeder Feind führt eine Liste der Dinge, die ihn zuvor getroffen haben, kann jedoch ein Objekt aus der Liste selbst entfernen, sobald dieses Objekt nicht mehr mit ihm kollidiert. Benötigt dieses Objekt nicht, um seinen Tod zu übertragen.
TenFour04

4

Bei so etwas wie einer Schwertschaukel interessiert dich die Frage "Mit wem kollidiere ich gerade?" Nicht so sehr. Was Sie interessiert, ist nicht der Zustand, sondern die Änderung des Zustands , dh Sie möchten wissen, wann sich der Kollisionszustand zwischen Schwert und Feind von falsenach ändert true. Die einzige Möglichkeit, Statusänderungen zu messen, besteht darin, Ihren aktuellen Status mit Ihrem vorherigen Status zu vergleichen.

So hat jedes Objekt hält zwei Sätze: die Menge von dem, was ich mit bin kollidiert jetzt (dh dieses Rahmen), und die Menge von dem, was ich mit wurde kollidiert zuvor (dh letzte Frame). Der Unterschied zwischen diesen beiden Sätzen liefert Ihnen die Menge von Dingen, mit denen ich jetzt kollidiere, mit denen ich vorher nicht kollidiert habe .

Ich sehe keinen Grund, warum dies notwendigerweise das Hin- und Herleiten von Nachrichten zwischen den kollidierenden Einheiten beinhalten würde. Dein Schwert weiß, dass es etwas getroffen hat; es muss keine Genehmigung von dem Ding bekommen, das es getroffen hat! Lassen Sie das Schwert dem Feind sagen, dass es getroffen wurde, und lassen Sie den Feind angemessen reagieren, oder was auch immer in Ihrem Szenario Sinn macht.


2

Sie brauchen nicht unbedingt eine große Liste. Wenn Sie beispielsweise auf nur vier Spieler beschränkt wären, könnten Sie für jeden Gegner nur vier kleine Timer verwenden (einen für jeden möglichen Spieler). Diese Timer können so klein wie ein paar Bits sein, wobei alle Timer etwa alle 1/5 Sekunde dekrementiert werden.

Wenn Sie eine größere Anzahl von Spielern haben, können Sie sich wieder nur um eine kleine Anzahl von Treffern gleichzeitig sorgen. Selbst wenn Sie 1.000 Spieler haben, ist es unwahrscheinlich, dass alle 1.000 denselben Feind angreifen. Der Feind kann einen kleinen Ringpuffer mit fester Größe der jüngsten Angreifer und Zeitstempel aufbewahren. Wenn zufällig zu viele Spieler einen bestimmten Feind angreifen, können Sie einfach die Angriffe ignorieren, die den Puffer "überlaufen" würden (es ist unwahrscheinlich, dass dies für die Spieler eine große Sache ist).

Eine weitere Option besteht darin, einen einzigen globalen Satz von Timern für Angriffe auf Feinde beizubehalten. Wenn Sie zu einem bestimmten Zeitpunkt eine kleine Anzahl aktiv aktivierter Gegner haben, kann dieser Ansatz die Speichernutzung erheblich senken und die Leistung nur minimal beeinträchtigen (möglicherweise sogar besser, da die Dinge besser im Cache bleiben; Profil sicher, natürlich) und ist ziemlich einfach zu implementieren. Auf diese Weise verfolgen viele der Minecraft-ähnlichen Spiele den vorübergehenden Status pro Block (wie z. B. das Mining von "Schaden"), da das Speichern des Status in Blöcken aus Speichergründen nicht möglich ist und es im Allgemeinen nur eine winzige Handvoll Blöcke gibt in einem Übergangszustand zu einem bestimmten Zeitpunkt, obwohl es so etwas wie 2x10 ^ 17 Blöcke gibt.

Für größere High-Multiplayer-Welten können Sie den gemeinsam genutzten Timer-Satz für Angriffe auf Feinde verwenden, ihn jedoch nicht global pro Zone (oder Block oder Bereich oder einer von Ihnen verwendeten verarbeitungsorientierten räumlichen Unterteilung) speichern.

Denken Sie daran, dass Sie bei Spielen nicht 100% korrekt sein müssen. Sie müssen nur korrekt genug sein, um die Spieler bei Laune zu halten. Sie nicht wirklich brauchen , um einen Feind zu nehmen Schaden von allen Angriffen aber nur einmal zu ermöglichen; Sie müssen nur sicherstellen, dass sich der Kampf lustig und unterhaltsam anfühlt. Wenn Sie auf ein algorithmisches Problem stoßen, ändern Sie in Spielen häufig nur das Spiel, um das Problem vollständig zu beseitigen, oder führen es zu einem leichter zu lösenden Problem zurück, anstatt zu versuchen, tatsächlich etwas zu lösen, das möglicherweise keine akzeptablen Lösungen hat.


0

Eine Variable am Schwert:

Wenn Sie das Schwert schwingen, klären Sie es.

Wenn es auf etwas trifft, speichern Sie die ID des Objekts, auf das es trifft.

Wenn Sie feststellen, dass es mit dem letzten getroffenen Objekt kollidiert, ignorieren Sie den Treffer.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.