Entwicklung

Hindernisberspringende Gewehrkugeln
von SI|SuperSebas 01.03.2012 23:38 Uhr

Während der Devmania lief der Top Down Shooter Prototyp einige Male auf recht belasteten Laptops, die zudem wenig Grafikleistung bieten. Das Spiel lief mit einer niedrigen Framerate von ca. 20-30 FPS. Leider zeigte sich dabei, dass die Kollision mancher Gewehrkugeln mit Gegnern oder Hindernissen nicht erkannt wird. Ein bekanntes Problem bei der Kollisionsbestimmung schnell bewegter Objekte, das auch in Unity und damit in unserem Top Down Shooter auftritt. Um auch bei niedriger Framerate in gleicher Geschwindigkeit zu fliegen muss die Kugel mit sinkender Framerate immer größere "Sprünge" zwischen zwei Frames machen. Überspringt sie dabei von einem Frame auf den nächsten ein Hinderniss komplett, so wird die Kollission nicht erkannt. Vor dem Aufzeigen unserer Lösung möchte ich zunächst erklären wie sich die Kollissionabfrage der Kugeln bisher entwickelt hat und welche Eigenschaften jeweils damit verbunden sind. Kugeln in unserem Spiel fliegen mit einer gewissen, relativ langsamen Geschwindigkeit. In vielen First-Person Shootern fliegen Geschosse unendlich schnell, so dass bereits beim Abfeuern feststeht was von ihnen getroffen wird. Dies ist bei uns nicht so. Fliegenden Kugeln kann noch ausgewichen werden. Es ist also eine genaue Kollisionsabfrage zwischen Kugel und Hinderniss nötig.

Im ersten Prototypen waren Kugeln mittels Rigidbody-Komponenten voll durch die Physikengine von Unity simuliert. Kollisionen wurden mit einer Kollisionsbox erkannt, die Kugel wurde mittels einer konstanten Kraft bewegt. Vorteil dieser Methode ist, dass sie sehr einfach umzusetzen und anzuwenden ist. Um die Kugel zu bewegen muss lediglich eine Kraft auf sie definiert werden. Weitere Scripte sind nicht nötig. Für jedes durch eine Kugel getroffene Objekt muss lediglich ein Collider, z.B. eine Kollisionsbox, erstellt werden und die OnCollisionEnter() Methode implementiert werden um einem Spieler beispielsweise Helathpoints abzuziehen und Blutspritzer anzuzeigen. Zudem erhält man einen durch die Physikengine automatisch berechneten Rückschlag Effekt auf andere Rigidbody-Objekte, z.B. die Gegner.

Das Problem dieser voll durch die Physik-Engine berechneten Kugeln ist ihre Performance. Insbesondere wenn Kollisionen durch die Physikengine verarbeitet werden müssen, drücken sie stark auf die Ausführungsgeschwindigkeit. Es ist daher zu vermeiden viele Kollissionen durch die Physikengine erkennen und verarbeiten zu lassen. Ein zweites Problem trat auf, nachdem wir die Funkeneffekte beim Treffen eines festen Hindernisses, z.B. eines Hauses, eingefügt haben. Bei Kollisionserkennung durch Kollisionsboxen ist es nicht einfach den genauen Auftreffpunkt der Kugel auf das Objekt zu bestimmen. Splitter wurden teilweise zu weit im Inneren des getroffenen Objektes ausgelöst, wenn die Kugel seit dem letzten Frame bereits "in" das Objekt gesprungen ist.

Die Lösung dieser Probleme stellt gleichzeitig den Stand der Kollisionsverarbeitung dar, wie er auf der Devmania eingesetzt und gezeigt wurde. Die Kollision mit Gegnern, die selbst durch die Physikengine berechnet werden, wurden beibehalten. Die Rigidbody-Objekte der Kugeln wurden jedoch als "Kinematic" markiert. Das heißt es wird zwar eine Kollisionserkennung durchgeführt, die Bewegung des Objekts wird aber nicht beeinflusst. Die Bewegung der Kugel wurde durch ein manuelles verschieben der Position der Kugel ersetzt. Gegner können trotzdem noch über die Kollisionsabfrage getroffen werden und auch die Reaktion darauf erfolgt weiterhin im automatisch ausgelösten OnCollisionEnter-Event.

Die Kollisionsabfrage gegen die statische Levelgeometrie wurde zunächst durch einen ersten Raycast-Ansatz ersetzt. Hindernisse wurden zunächst markiert, indem sie in einen Layer "Obstacles" zusammengefasst wurden. Es gibt auch Hindernisse, die nicht getroffen werden können, z.B. "unsichtbare" Barrieren wenn ein Spieler einen Fluss nicht überqueren kann, oder bei einem Zaun der zwar nicht passiert jedoch durch- bzw. überschossen werden kann. Gegen diesen Hindernis-Layer wird nun von der Kugel in jedem Frame ein kurzer Strahl in Flugrichtung nach vorne abgeschossen, der auf Kollision mit einem markierten Hinderniss testet. Trifft der Raycast ein Hinderniss, werden an der Auftreffstelle Funken eingeblendet. Die Kugel selbst wird gelöscht. Ein Treffer des Strahls wird unmittelbar erkannt, nicht erst wenn die Kugel das Hinderniss tatsächlich berührt. Deshalb darf die Strahllänge nicht zu groß gewählt werden. Durch die Fluggeschwindigkeit und Länge der Kugel fällt es jedoch nicht auf, dass die Kollision möglicherweise zu früh erkannt wird.

Bei langsamer Framerate kann es jetzt zwar nicht mehr passieren, dass die Kugel in ein Objekt hineinspringt, sie kann jedoch immernoch komplett über das Hinderniss springen. In diesem Fall wird auch jetzt keine Kollision erkannt.

Dieses Problem wird nun durch eine Modifizierung der Raycast-Erkennung behoben. Die Kollisionsabfrage mit Kollisionsboxen wird komplett fallen gelassen. Der Raycast wird nicht mehr als Ersatz für die Kollissionsbox nach vorne ausgestrahlt, sondern zwischen der aktuellen Position der Kugel und der Position aus dem letzten Frame. Das heißt eine Kugel speichert immer ihre letzte Position und benutzt diese als Ausgangspunkt für den Raycast zur Kollisionserkennung. Alle Objekte die von der Kugel während der Zeit zwischen aktuellem und letztem Frame berührt wurden, werden durch den Raycast erkannt. Das erste Hinderniss, das auf dem Weg zur aktuellen Position vom Raycast getroffen wurde, wurde tatsächlich durch die Kugel getroffen. Alle nachfolgenden nicht mehr, da die Kugel ja bereits durch das erste Hinderniss abgefangen wurde.

Handelt es sich um ein Hinderniss im Level, kann wie bisher verfahren werden. Die Kugel selbst wird entfernt, an der Auftreffstelle werden Funken eingeblendet. Anders verhält es sich jedoch mit getroffenen Gegnern. Diese reagierten bisher auf das OnCollisionEnter-Event, das nun jedoch nicht mehr aufgerufen wird. Für jeden Gegner muss nun ein eigenes Event erstellt werden, das durch die Raycast-Kollisionsabfrage aufgerufen wird. Dieses Event bietet den Vorteil, dass relativ einfach Parameter von der Kugel an das getroffene Objekt weitergegeben werden können. Zum Beispiel die Art des Geschosses und welche Durchschlagkraft es besitzt.

Auch diese Methode ist leider nicht 100% genau, nämlich dann wenn die Kollision einer Kugel mit einem Objekt berechnet wird, das sich ebenfalls (schnell) bewegt. Denn dieses vollzieht dann ebenso einen Sprung von einem Frame auf den nächsten wie die Kugel selbst. Bewegte Objekte die abgeschossen werden können bewegen sich in der Regel jedoch deutlich langsamer als die Kugel selbst. Das heißt dieser Effekt wird sich kaum merkbar auswirken.



Kommentar hinzufgen

Teilen



Kommentare



      gehostet mit 100% Ökostrom von all-inkl.com