Bei jeder Kollision sind zwei GameObjects beteiligt, oder? Ich möchte wissen, wie ich entscheide, welches Objekt mein enthalten sollOnCollision*
?
Angenommen, ich habe ein Player-Objekt und ein Spike-Objekt. Mein erster Gedanke ist, ein Skript auf den Player zu schreiben, das folgenden Code enthält:
OnCollisionEnter(Collision coll) {
if (coll.gameObject.compareTag("Spike")) {
Destroy(gameObject);
}
}
Die exakt gleiche Funktionalität kann natürlich erreicht werden, indem stattdessen ein Skript auf dem Spike-Objekt vorhanden ist, das Code wie den folgenden enthält:
OnCollisionEnter(Collision coll) {
if (coll.gameObject.compareTag("Player")) {
Destroy(coll.gameObject);
}
}
Während beide gültig sind, war es für mich sinnvoller, das Skript auf dem Player zu haben, da in diesem Fall, wenn die Kollision auftritt, eine Aktion auf dem Player ausgeführt wird .
Ich bezweifle jedoch, dass Sie in Zukunft weitere Objekte hinzufügen möchten, die den Player bei einer Kollision töten, z. B. einen Feind, eine Lava, einen Laserstrahl usw. Diese Objekte haben wahrscheinlich unterschiedliche Tags. Dann würde das Skript auf dem Player folgendermaßen aussehen:
OnCollisionEnter(Collision coll) {
GameObject other = coll.gameObject;
if (other.compareTag("Spike") || other.compareTag("Lava") || other.compareTag("Enemy")) {
Destroy(gameObject);
}
}
In dem Fall, in dem sich das Skript auf dem Spike befand, müssten Sie nur dasselbe Skript zu allen anderen Objekten hinzufügen, die den Player beenden und das Skript so benennen können KillPlayerOnContact
.
Wenn Sie eine Kollision zwischen dem Spieler und einem Feind haben, möchten Sie wahrscheinlich eine Aktion auf beiden ausführen . Welches Objekt sollte in diesem Fall mit der Kollision umgehen? Oder sollten beide die Kollision bewältigen und unterschiedliche Aktionen ausführen?
Ich habe noch nie ein Spiel mit einer vernünftigen Größe gebaut und frage mich, ob der Code unübersichtlich und schwierig zu warten sein kann, wenn Sie anfangs so etwas falsch verstehen. Oder sind vielleicht alle Wege gültig und es ist nicht wirklich wichtig?
Jeder Einblick wird sehr geschätzt! Vielen Dank für Ihre Zeit :)
quelle
Tag.SPIKE
stattdessen?Antworten:
Ich persönlich bevorzuge es, Systeme so zu entwerfen, dass kein an einer Kollision beteiligtes Objekt damit umgeht, und stattdessen kann ein kollisionsbehandelnder Proxy mit einem Rückruf wie diesem umgehen
HandleCollisionBetween(GameObject A, GameObject B)
. Auf diese Weise muss ich nicht darüber nachdenken, welche Logik wo sein soll.Wenn dies keine Option ist, wenn Sie beispielsweise nicht die Kontrolle über die Kollisionsreaktionsarchitektur haben, wird es etwas unübersichtlich. Im Allgemeinen lautet die Antwort "es kommt darauf an".
Da beide Objekte Kollisionsrückrufe erhalten, würde ich in beide nur Code einfügen, der für die Rolle des Objekts in der Spielwelt relevant ist , und gleichzeitig versuchen, die Erweiterbarkeit so weit wie möglich aufrechtzuerhalten. Das bedeutet, wenn eine Logik in beiden Objekten gleich sinnvoll ist, ziehen Sie es vor, sie in das Objekt einzufügen, das auf lange Sicht wahrscheinlich zu weniger Codeänderungen führt, wenn neue Funktionen hinzugefügt werden.
Anders ausgedrückt, zwischen zwei Objekttypen, die kollidieren können, hat einer dieser Objekttypen im Allgemeinen die gleichen Auswirkungen auf mehr Objekttypen als der andere. Das Einfügen von Code für diesen Effekt in den Typ, der mehr andere Typen betrifft, bedeutet auf lange Sicht weniger Wartung.
Zum Beispiel ein Spielerobjekt und ein Kugelobjekt. Wahrscheinlich ist es als Teil des Spielers logisch, "Schaden durch Kollision mit Kugeln zu erleiden". "Schaden an dem Ding anrichten, auf das es trifft" ist als Teil der Kugel logisch sinnvoll. Eine Kugel kann jedoch mehr verschiedene Arten von Objekten beschädigen als ein Spieler (in den meisten Spielen). Ein Spieler erleidet keinen Schaden durch Geländeblöcke oder NPCs, aber eine Kugel kann diesen dennoch Schaden zufügen. In diesem Fall würde ich es vorziehen, die Kollisionsabwicklung für das Verursachen von Schaden in die Kugel zu stecken.
quelle
TL; DR Der beste Ort, um den Kollisionsdetektor zu platzieren, ist der Ort, an dem Sie das aktive Element in der Kollision anstelle des passiven Elements in der Kollision betrachten, weil Sie möglicherweise zusätzliches Verhalten benötigen, um in einer solchen aktiven Logik ausgeführt zu werden (und nach guten OOP-Richtlinien wie SOLID liegt es in der Verantwortung des aktiven Elements).
Detailliert :
Wir werden sehen ...
Wenn ich unabhängig von der Spiel-Engine den gleichen Zweifel habe, gehe ich so vor, dass ich feststelle, welches Verhalten aktiv und welches passiv in Bezug auf die Aktion ist, die ich beim Sammeln auslösen möchte.
Bitte beachten Sie: Ich benutze unterschiedliche Verhaltensweisen als Abstraktionen der verschiedenen Teile unserer Logik, um den Schaden und - als weiteres Beispiel - das Inventar zu definieren. Beides sind unterschiedliche Verhaltensweisen, die ich später zu einem Spieler hinzufügen würde, und nicht dazu, meinen benutzerdefinierten Spieler mit unterschiedlichen Verantwortlichkeiten zu überfrachten, die schwerer zu pflegen wären. Mit Anmerkungen wie RequireComponent würde ich sicherstellen, dass mein Player-Verhalten auch das Verhalten aufweist, das ich jetzt definiere.
Dies ist nur meine Meinung: Vermeiden Sie es, Logik abhängig vom Tag auszuführen, sondern verwenden Sie stattdessen unterschiedliche Verhaltensweisen und Werte wie Aufzählungen zur Auswahl .
Stellen Sie sich folgendes Verhalten vor:
Verhalten, um ein Objekt als Stapel auf dem Boden anzugeben. RPG-Spiele verwenden dies für Gegenstände im Boden, die aufgenommen werden können.
Verhalten, um ein Objekt anzugeben, das Schaden verursachen kann. Dies kann Lava, Säure, eine giftige Substanz oder ein fliegendes Projektil sein.
Betrachten Sie diesbezüglich
DamageType
undInventoryObject
als Aufzählungen.Diese Verhaltensweisen sind nicht aktiv , da sie auf dem Boden liegen oder herumfliegen und nicht von selbst handeln. Die machen nichts. Wenn Sie beispielsweise einen Feuerball in einem leeren Raum fliegen lassen, ist er nicht bereit, Schaden anzurichten (möglicherweise sollten andere Verhaltensweisen in einem Feuerball als aktiv angesehen werden, z. B. wenn der Feuerball auf Reisen seine Kraft verbraucht und dabei seine Schadenskraft verringert, aber sogar Wenn ein potenzielles
FuelingOut
Verhalten entwickelt werden könnte, ist es ein anderes Verhalten und nicht dasselbe DamageProducer-Verhalten. Wenn Sie ein Objekt im Boden sehen, werden nicht alle Benutzer überprüft und es wird nachgedacht, ob sie mich auswählen können.Dafür brauchen wir aktives Verhalten. Die Gegenstücke wären:
Ein Verhalten, um Schaden zu nehmen (Es würde ein Verhalten erfordern, um mit Leben und Tod umzugehen, da das Verringern des Lebens und das Empfangen von Schaden normalerweise getrennte Verhaltensweisen sind und weil ich diese Beispiele so einfach wie möglich halten möchte) und vielleicht dem Schaden widerstehen .
Dieser Code ist nicht getestet und sollte als Konzept funktionieren . Was ist der Zweck? Schaden ist etwas, das jemand empfindet und das sich auf das Objekt (oder die lebende Form) bezieht, das beschädigt wird, wie Sie meinen. Dies ist ein Beispiel: In normalen RPG-Spielen fügt Ihnen ein Schwert Schaden zu , während Sie in anderen RPGs (z. B. Summon Night Swordcraft Story I und II ) Schaden erleiden können, wenn Sie einen harten Teil treffen oder die Rüstung blockieren Reparaturen . Da sich die Schadenslogik auf den Empfänger und nicht auf den Absender bezieht, werden nur relevante Daten in den Absender und die Logik in den Empfänger eingefügt.
Gleiches gilt für einen Inventarinhaber (ein weiteres erforderliches Verhalten ist dasjenige, das sich auf das Objekt bezieht, das sich auf dem Boden befindet, und die Fähigkeit, es von dort zu entfernen). Vielleicht ist dies das richtige Verhalten:
quelle
Wie Sie anhand der Vielzahl der Antworten hier sehen können, sind diese Entscheidungen mit einem angemessenen Maß an persönlichem Stil und einem Urteilsvermögen verbunden, das auf den Bedürfnissen Ihres jeweiligen Spiels basiert - kein absolut bester Ansatz.
Ich empfehle, darüber nachzudenken, wie sich Ihr Ansatz skalieren lässt, wenn Sie Ihr Spiel erweitern , Funktionen hinzufügen und wiederholen. Führt das Platzieren der Kollisionsbehandlungslogik später zu komplexerem Code? Oder unterstützt es modulareres Verhalten?
Sie haben also Stacheln, die den Spieler beschädigen. Frag dich selbst:
Offensichtlich wollen wir uns nicht mitreißen lassen und ein überentwickeltes System aufbauen, das eine Million Fälle verarbeiten kann, anstatt nur den einen Fall, den wir benötigen. Aber wenn es so oder so gleich ist (dh diese 4 Zeilen in Klasse A gegen Klasse B stecken), eine aber besser skaliert, ist es eine gute Faustregel, um die Entscheidung zu treffen.
Speziell für dieses Beispiel in Unity, ich würde neigen zu der Putten auf Schaden Logik in den Spitzen , in Form von so etwas wie eine
CollisionHazard
Komponente, die bei einer Kollision einen ruft Schaden Mitnahmen Methode in einerDamageable
Komponente. Hier ist der Grund:Damageable
Komponente darauf (wenn Sie einige benötigen, die nur gegen Spikes immun sind, können Sie hinzufügen) Schadensarten & lassen Sie dieDamageable
Immunität Komponente)quelle
Es ist wirklich egal, wer mit der Kollision umgeht, aber es ist wichtig, mit wem zusammenzuarbeiten.
In meinen Spielen versuche ich,
GameObject
dasjenige zu wählen, das etwas mit dem anderen Objekt "tut", als dasjenige, das die Kollision kontrolliert. IE Spike beschädigt den Spieler, so dass er die Kollision handhabt. Wenn beide Objekte sich gegenseitig etwas antun, lassen Sie sie jeweils ihre Aktion auf dem anderen Objekt ausführen.Außerdem würde ich vorschlagen, dass Sie versuchen, Ihre Kollisionen so zu gestalten, dass die Kollisionen von dem Objekt verarbeitet werden, das auf Kollisionen mit weniger Dingen reagiert. Ein Spike muss nur über Kollisionen mit dem Player Bescheid wissen, wodurch der Kollisionscode im Spike-Skript schön und kompakt wird. Das Spielerobjekt kann mit vielen anderen Dingen kollidieren, wodurch ein aufgeblähtes Kollisionsskript entsteht.
Versuchen Sie schließlich, Ihre Kollisionsbehandlungsroutinen abstrakter zu gestalten. Ein Spike muss nicht wissen, dass er mit einem Spieler kollidiert ist, er muss nur wissen, dass er mit etwas kollidiert ist, an dem er Schaden anrichten muss. Nehmen wir an, Sie haben ein Skript:
Sie können eine Unterklasse
DamageController
in Ihrem Spieler-GameObject erstellen, um den Schaden zu beheben, und dann in dessenSpike
Kollisionscodequelle
Ich hatte das gleiche Problem, als ich anfing, also hier ist es: In diesem speziellen Fall benutze den Enemy. Normalerweise möchten Sie, dass die Kollision von dem Objekt behandelt wird, das die Aktion ausführt (in diesem Fall vom Feind, aber wenn Ihr Spieler eine Waffe hatte, sollte die Waffe die Kollision behandeln), denn wenn Sie Attribute optimieren möchten (zum Beispiel den Schaden) des Feindes) Sie möchten es auf jeden Fall im Feindeskript und nicht im Spielerskript ändern (besonders wenn Sie mehrere Spieler haben). Wenn Sie den Schaden einer vom Spieler verwendeten Waffe ändern möchten, möchten Sie dies auf keinen Fall bei jedem Gegner Ihres Spiels tun.
quelle
Beide Objekte würden die Kollision bewältigen.
Einige Objekte würden durch die Kollision sofort deformiert, zerbrochen oder zerstört. (Kugeln, Pfeile, Wasserballons)
Andere würden einfach abprallen und zur Seite rollen. (Felsen) Diese könnten immer noch Schaden nehmen, wenn das Ding, auf das sie treffen, hart genug wäre. Steine nehmen keinen Schaden von einem Menschen, der sie schlägt, aber sie können von einem Vorschlaghammer stammen.
Einige matschige Gegenstände wie Menschen würden Schaden nehmen.
Andere würden aneinander gebunden sein. (Magnete)
Beide Kollisionen würden in eine Ereignisbehandlungswarteschlange für einen Akteur gestellt, der zu einem bestimmten Zeitpunkt und an einem bestimmten Ort (und mit einer bestimmten Geschwindigkeit) auf ein Objekt einwirkt. Denken Sie daran, dass der Handler sich nur mit dem befassen sollte, was mit dem Objekt passiert. Es ist sogar möglich, dass ein Handler einen anderen Handler in die Warteschlange stellt, um zusätzliche Auswirkungen der Kollision auf der Grundlage der Eigenschaften des Akteurs oder des Objekts, auf das gewirkt wird, zu behandeln. (Bombe explodiert und die resultierende Explosion muss nun auf Kollisionen mit anderen Objekten getestet werden.)
Verwenden Sie beim Skripten Tags mit Eigenschaften, die von Ihrem Code in Schnittstellenimplementierungen übersetzt werden. Verweisen Sie dann auf die Schnittstellen in Ihrem Bearbeitungscode.
Beispielsweise könnte ein Schwert ein Tag für Nahkampf haben, das in eine Schnittstelle mit dem Namen Nahkampf übersetzt wird, die eine DamageGiving-Schnittstelle erweitern könnte, die Eigenschaften für den Schadenstyp und eine Schadensmenge aufweist, die entweder mit einem festen Wert oder mit Reichweiten usw. definiert wurde ein Explosive-Tag, dessen Explosive-Schnittstelle auch die DamageGiving-Schnittstelle erweitert, die eine zusätzliche Eigenschaft für den Radius und möglicherweise für die Ausbreitung des Schadens aufweist.
Ihre Spiel-Engine würde dann gegen die Schnittstellen codieren, nicht gegen bestimmte Objekttypen.
quelle