Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Platformer

Grundlegende 2D-Platformer-Physik, Teil 8: Steigungen

by
Difficulty:IntermediateLength:LongLanguages:

German (Deutsch) translation by Władysław Łucyszyn (you can also view the original English article)

Final product image
What You'll Be Creating

Demo

Die Demo zeigt das Endergebnis der Pistenimplementierung. Verwenden Sie WASD, um den Charakter zu verschieben. Mit der rechten Maustaste wird eine Kachel erstellt. Sie können das Scrollrad oder die Pfeiltasten verwenden, um eine Kachel auszuwählen, die Sie platzieren möchten. Die Schieberegler ändern die Größe des Charakters des Spielers.

Die Demo wurde unter Unity 5.5.2f1 veröffentlicht, und der Quellcode ist auch mit dieser Version von Unity kompatibel.

Bevor wir anfangen...

Wie bei den vorherigen Teilen der Serie werden wir unsere Arbeit dort fortsetzen, wo wir im letzten Teil aufgehört haben. Letztes Mal haben wir die Daten berechnet und zwischengespeichert, die erforderlich sind, um die Objekte aus der Hangkollision zu entfernen, und die Art und Weise geändert, wie die Kollisionen mit der Tilemap verglichen werden. In diesem Teil benötigen wir das gleiche Setup vom Ende des letzten Teils.

Sie können die Projektdateien aus dem vorherigen Teil herunterladen und den Code zusammen mit diesem Tutorial schreiben.

In diesem Teil implementieren wir die Kollision mit Hängen oder anderen benutzerdefinierten Kacheln, fügen Einweg-Hänge hinzu und ermöglichen es dem Spielobjekt, sich reibungslos entlang der Hänge zu bewegen.

Pistenimplementierung

Vertikale Steigungsprüfung

Wir können endlich Pisten erreichen! Zunächst versuchen wir zu behandeln, wenn sich die Unterkante des Objekts innerhalb einer Hangkachel befindet.

Schauen wir uns unsere CollidesWithTileBottom-Funktion an, insbesondere den Teil, in dem wir die Kacheln bearbeiten.

Um zu sehen, ob unser Objekt mit der Steigung kollidiert, müssen wir zuerst die Offsets von der zuvor erstellten Funktion abrufen, die den größten Teil unserer Arbeit erledigt.

Da wir ein Pixel unter unserem Zeichen prüfen, müssen wir den Versatz anpassen.

Die Bedingung für die Kollision ist, dass der freeUp-Versatz größer oder gleich 0 ist, was bedeutet, dass wir entweder das Zeichen nach oben bewegen oder das Zeichen auf der Steigung steht.

Wir sollten den Fall nicht vergessen, wenn wir wollen, dass der Charakter am Hang bleibt. Dies bedeutet, dass der Charakter, obwohl er den Hang verlässt, sich sowieso so verhalten soll, als wäre er am Hang. Dazu müssen wir eine neue Konstante hinzufügen, die den Wert enthält, wie steil eine Steigung sein muss, um als vertikale Wand anstelle einer Steigung betrachtet zu werden.

Wenn der Versatz unter dieser Konstante liegt, sollte es dem Objekt möglich sein, sich reibungslos entlang der Steigungskurve zu bewegen. Wenn es gleich oder größer ist, sollte es als Wand behandelt werden, und zum Klettern wäre ein Springen erforderlich.

Jetzt müssen wir unserer Aussage eine weitere Bedingung hinzufügen. Diese Bedingung prüft, ob das Zeichen an Steigungen haften soll, ob es sich im letzten Frame einer Steigung befand und ob es um weniger Pixel als unsere cSlopeWallHeight-Konstante nach unten oder oben gedrückt werden muss.

Wenn die Bedingung erfüllt ist, müssen wir diese Kachel als potenzielle Kollision mit dem Objekt speichern. Wir müssen noch alle anderen Kacheln entlang der X-Achse durchlaufen. Erstellen Sie zunächst die Variablen, die die X-Koordinate und den Versatzwert für die kollidierende Kachel enthalten.

Speichern Sie nun die Werte, wenn die definierte Bedingung erfüllt ist. Wenn wir bereits ein kollidierendes Plättchen gefunden haben, müssen wir die Offsets vergleichen, und das endgültige kollidierende Plättchen ist dasjenige, für das der Charakter am meisten versetzt werden muss.

Nachdem wir alle Kacheln durchlaufen und eine Kachel gefunden haben, mit der das Objekt kollidiert, müssen wir das Objekt versetzen.

Das ist so ziemlich alles für den unteren Check, also lass uns jetzt den oberen machen. Dieser wird ein bisschen einfacher sein, da wir nicht einmal mit dem Kleben fertig werden müssen.

Das ist es.

Horizontale Hangprüfung

Die horizontale Prüfung wird etwas komplizierter sein, da wir hier die schwierigsten Fälle behandeln werden.

Beginnen wir mit dem Umgang mit den Pisten auf der rechten Seite. Es gibt ein paar Dinge, die wir beachten müssen, hauptsächlich in Bezug auf das Aufsteigen. Betrachten wir die folgenden Situationen.

Different shaped slopes

Wir müssen diese Fälle mit besonderer Sorgfalt behandeln, da wir irgendwann, wenn wir uns am Hang entlang bewegen, an die Decke stoßen werden. Um dies zu verhindern, müssen wir weitere Überprüfungen durchführen, falls sich der Charakter horizontal bewegt.

Für die vertikalen Überprüfungen haben wir das Objekt von der Kachel nach oben verschoben, aber im Allgemeinen werden wir diese Funktionalität dort nicht verwenden. Da wir immer ein Pixel überprüfen, das sich gerade außerhalb der Objektgrenzen befindet, werden wir ein Hindernis nie wirklich überlappen. Bei den horizontalen Überprüfungen ist dies etwas anders, da dies der Ort ist, an dem wir uns entlang des Abhangs bewegen. Daher wird die Höheneinstellung natürlich hauptsächlich hier stattfinden.

Um die richtige Kollisionsreaktion für die oben dargestellten Fälle zu erzielen, ist es einfacher zu überprüfen, ob wir horizontal in einen Raum eintreten können, und wenn dies möglich ist, prüfen Sie, ob sich das Objekt nicht mit festen Pixeln überlappt, wenn dies erforderlich ist vertikal bewegt aufgrund der Bewegung entlang eines Abhangs. Wenn wir den Platz nicht finden, wissen wir, dass es unmöglich ist, sich in die überprüfte Richtung zu bewegen, und wir können die horizontale Wandflagge setzen.

Wechseln wir zur Funktion CollidesWithTileRight, zu dem Teil, in dem wir die Steigungen behandeln.

Wir erhalten den Versatz auf ähnliche Weise wie für die vertikalen Prüfungen, aber der Versatz, der uns wichtig ist, ist derjenige, der größer ist.

Nun wollen wir sehen, ob unser Charakter das markierte Plättchen als Wand behandeln soll. Wir tun dies, wenn entweder der Steigungsversatz größer oder gleich unserer cSlopeWallHeight-Konstante ist oder wenn wir aus der Kollision herauskommen möchten, müssen wir das Zeichen nach oben oder unten versetzen, während wir bereits mit einer Kachel in derselben Richtung kollidieren, was bedeutet, dass unsere Das Objekt wird zwischen die oberen und unteren Kacheln gedrückt.

Wenn dies nicht der Fall ist und der Versatz größer als 0 ist, treffen wir eine Steigung. Ein Problem hierbei ist, dass wir nicht wissen, ob wir auf anderen Kacheln, die wir noch nicht überprüft haben, gegen eine Wand stoßen. Daher speichern wir vorerst nur den Neigungsversatz und den Kachelkollisionstyp, falls wir sie später verwenden müssen.

Anstatt zu sehen, ob der Steigungsversatz größer als Null ist, vergleichen wir ihn jetzt mit dem Steigungsversatz einer anderen Kachel, falls wir bereits in früheren Iterationen eine kollidierende Steigung gefunden haben.

Behandeln Sie das Zusammendrücken zwischen den Fliesen

Nachdem wir alle interessierenden Kacheln durchlaufen haben, wollen wir sehen, ob wir das Objekt verschieben müssen. Lassen Sie uns den Fall behandeln, in dem der Steigungsversatz ungleich Null war.

Wir müssen hier zwei Fälle behandeln, und wir müssen etwas unterschiedliche Dinge tun, je nachdem, ob wir unser Objekt nach oben oder unten versetzen müssen.

Zunächst müssen wir prüfen, ob wir nach dem Versetzen des Objekts in den Raum passen können. Wenn dies der Fall ist, behandeln wir einen der oben dargestellten Fälle. Wenn der Charakter versucht, sich nach rechts zu bewegen, ist der Versatz positiv. Wenn wir das Objekt jedoch versetzen, wird es in die obere Wand geschoben. Stattdessen markieren wir nur, dass es mit der Wand auf der rechten Seite kollidiert, um das zu blockieren Bewegung in diese Richtung.

Wenn wir in den Raum passen, markieren wir, dass wir mit der unteren Kachel kollidieren und versetzen die Position des Objekts entsprechend.

Wir behandeln den Fall, in dem das Objekt versetzt werden muss, auf ähnliche Weise.

Objekt in Kollisionsprüfung verschieben

Jetzt versetzt diese Funktion das Objekt nach oben oder unten, wenn dies erforderlich ist, wenn wir auf die Kachel nach rechts treten möchten. Was ist jedoch, wenn wir diese Funktion nur als Kontrolle verwenden möchten und den Charakter nicht wirklich verschieben möchten? Indem Sie das nennen? Um dieses Problem zu lösen, fügen wir eine zusätzliche Variable mit dem Namen 'move' hinzu, um zu markieren, ob die Funktion das Objekt verschieben kann oder nicht.

Verschieben Sie das Objekt nur, wenn dieses neue Flag auf true gesetzt ist.

Handle Slope Sticking

Nun wollen wir uns an Hängen festhalten. Es ist ziemlich einfach, aber wir müssen alle Eckfälle richtig behandeln, damit der Charakter ohne Schluckauf am Hang bleibt.

Bevor wir jedoch die Eckfälle behandeln, können wir bei der vertikalen Kollisionsprüfung sehr leicht das Anhaften von Steigungen innerhalb einer einzelnen Kachel behandeln. Es reicht aus, wenn wir die folgende Bedingung in die CollidesWithTileBottom-Funktion einfügen.

Diese Bedingung bewirkt, dass, wenn der Abstand zwischen der Position des Objekts und dem nächstgelegenen Boden zwischen 0 und cSlopeWallHeight liegt, das Zeichen zusätzlich zur ursprünglichen Bedingung auch nach unten gedrückt wird. Dies funktioniert leider nur innerhalb einer einzigen Kachel; Die folgende Abbildung zeigt das Problem, das wir lösen müssen.

Slope with three squares marked on it

Der Eckfall, über den wir sprechen, ist genau dieser: Der Charakter bewegt sich von Kachel Nummer eins nach unten und nach links von Kachel Nummer zwei. Kachel Nummer zwei ist leer, daher müssen wir die Kachel darunter überprüfen und prüfen, ob der Versatz vom Zeichen zu Kachel Nummer 3 angemessen ist, um dort weiter den Hang entlang zu gehen.

Behandeln Sie die Eckkoffer

Es wird einfacher sein, diese Eckfälle bei den horizontalen Kollisionsprüfungen zu behandeln. Kehren wir also zur Funktion CollidesWithTileRight zurück. Gehen wir zum Ende der Funktion und behandeln hier die problematischen Fälle.

Zunächst muss das Flag mSticksToSlope gesetzt werden, um das Steigen der Steigung zu bewältigen, das Objekt muss sich im vorherigen Frame auf dem Boden befunden haben und das Flag zum Verschieben muss aktiviert sein.

Jetzt müssen wir die Kachel finden, an der wir festhalten sollen. Da diese Funktion die Kollision am rechten Rand des Objekts überprüft, werden wir die Steigung für die untere linke Ecke des Charakters behandeln.

Jetzt müssen wir einen Weg finden, um die Höhe des Objekts mit der zu vergleichen, auf die es treten möchte. Wenn die nächste Höhe niedriger als die aktuelle ist, aber immer noch höher als unsere cSlopeWallHeight-Konstante, drücken wir unser Objekt auf den Boden.

Steigungshöhe erhalten

Kehren wir zu unserer Slope-Klasse zurück, um eine Funktion zu erstellen, die die Höhe einer Steigung an einer bestimmten Position zurückgibt.

Die Parameter für die Funktion sind der x-Wert für die Steigung und der Steigungstyp. Wenn die Steigung leer ist, können wir sofort 0 zurückgeben, und wenn sie voll ist, geben wir die Kachelgröße zurück.

Mit unseren zwischengespeicherten Offsets können wir leicht die Höhe eines Abhangs ermitteln. Wenn die Kachel in keiner Weise transformiert wird, erhalten wir nur einen Versatz für ein Objekt, das an der Position x ein Pixel breit ist und dessen Höhe der Kahelgröße entspricht.

Lassen Sie uns dies für verschiedene Transformationen behandeln. Wenn eine Steigung auf der X-Achse gespiegelt wird, müssen wir nur das x-Argument spiegeln.

Wenn die Steigung auf der Y-Achse umgedreht ist, müssen wir den collidingTop anstelle des collidingBottom-Offsets zurückgeben. Da collidingTop in diesem Fall negativ ist, müssen wir auch das Vorzeichen dafür umdrehen.

Wenn die Kachel um 90 Grad gedreht wird, müssen wir die collidingLeft oder collidingRight Offsets zurückgeben. Abgesehen davon müssen wir die x- und y-Positionen und die Größe vertauschen, um einen korrekten zwischengespeicherten Versatz zu erhalten.

Das ist die letzte Funktion.

Zurück zu den Eckkoffern

Kehren wir zur Funktion CollidesWithTileRight zurück, genau dort, wo wir die Steigungstypen für die Kacheln bestimmt haben, zwischen denen sich der Charakter bewegt.

Um die soeben erstellte Funktion verwenden zu können, müssen wir die Position bestimmen, an der die Höhe einer Kachel ermittelt werden soll.

Berechnen wir nun die Höhe zwischen diesen beiden Punkten.

Wenn der Versatz zwischen 0 und der Konstante cSlopeWallHeight liegt, werden wir das Objekt nach unten drücken, aber zuerst müssen wir prüfen, ob wir das Objekt tatsächlich nach unten drücken können. Dies ist genau die gleiche Routine, die wir zuvor durchgeführt haben.

Alles in allem sollte die Funktion so aussehen.

Jetzt müssen wir alles analog für die CollidesWithTileLeft-Funktion tun. Die endgültige Version sollte die folgende Form haben.

Das ist es. Der Code sollte in der Lage sein, alle Arten von nicht übersetzten Steigungen zu verarbeiten.

Animation of character moving on slope

Übersetzungsarten behandeln

Bevor wir mit der Behandlung übersetzter Kacheln beginnen, wollen wir einige Funktionen erstellen, die zurückgeben, ob ein bestimmter TileCollisionType auf eine bestimmte Weise übersetzt wird. Unsere Aufzählung vom Kollisionstyp ist folgendermaßen strukturiert:

Wir können diese Muster verwenden, um anhand des Werts der Aufzählung zu erkennen, wie ein bestimmter Kollisionstyp übersetzt wird. Beginnen wir mit der Identifizierung des Flip auf der X-Achse.

Lassen Sie uns zuerst die Pisten-ID ermitteln. Dazu berechnen wir den Versatz von der ersten definierten Steigungskachel zu der Kachel, die wir identifizieren möchten.

Wir haben acht Arten von Übersetzungen. Jetzt brauchen wir nur noch den Rest, um die typeId durch 8 zu teilen.

Jetzt haben die Übersetzungen eine zugewiesene Nummer für sie.

Der Flip auf der X-Achse ist in den Typen 1, 3, 5 und 7 vorhanden. Wenn er also einem dieser Typen entspricht, sollte die Funktion true zurückgeben, andernfalls false.

Auf die gleiche Weise erstellen wir eine Funktion, die angibt, ob ein Typ auf der Y-Achse gespiegelt wird.

Und schließlich, wenn der Kollisionstyp gedreht wird.

Das ist alles was wir brauchen.

Transformiere den Offset

Kehren wir zur Slopes-Klasse zurück und lassen Sie unsere GetOffset-Funktion die übersetzten Kacheln unterstützen.

Da wir keine Daten für übersetzte Steigungen zwischengespeichert haben, übersetzen wir wie üblich die Position und Größe des Objekts, sodass das Ergebnis identisch ist, als ob die Kachel übersetzt worden wäre. Beginnen wir mit dem Flip auf der X-Achse. Alles was wir hier tun müssen, ist das Objekt entlang der Mitte der Kachel zu drehen.

Ähnliches gilt für den Flip auf der Y-Achse.

Falls wir nun die Kachel auf der y-Achse umgedreht haben, werden die erhaltenen Offsets tatsächlich vertauscht. Lassen Sie uns sie so übersetzen, dass sie tatsächlich genauso funktionieren wie die Offsets der nicht übersetzten Kachel, was bedeutet, dass oben oben und unten unten ist!

Lassen Sie uns nun die 90-Grad-Drehung behandeln.

Hier sollte alles um 90 Grad gedreht werden. Anstatt unsere posX und sizeX auf den linken und rechten Rand des Objekts zu stützen, werden wir sie auf den oberen und unteren Rand stützen.

Jetzt müssen wir etwas Ähnliches tun wie zuvor, wenn die Kachel auf der Y-Achse gedreht wurde, aber diesmal müssen wir dies sowohl für die 90-Grad-Drehung als auch für die Y-Drehung tun.

Das ist es. Da unsere endgültigen Auf- und Ab-Offsets so angepasst werden, dass sie im Weltraum sinnvoll sind, funktionieren unsere Anpassungen außerhalb der Kachelgrenzen immer noch ordnungsgemäß.

Das war's - jetzt können wir auch übersetzte Pisten verwenden.

Animation of character moving on slope

In der obigen Animation gibt es Steigungen von 45, 22, 15 und 11 Grad. Dank der 90-Grad-Drehung können wir auch 79-, 75- und 68-Grad-Neigungen erhalten, ohne zusätzliche Neigungskacheln zu definieren. Sie können auch sehen, dass die 79-Grad-Neigung zu steil ist, um mit unserem Wert von cSlopeWallHeight reibungslos weiterzumachen.

Einwegplattformen handhaben

In all diesen Schwierigkeiten haben wir unsere Unterstützung für Einwegplattformen gebrochen. Wir müssen das beheben und die Funktionalität auch auf Steigungen ausweiten. Einwegplattformen sind genauso wichtig oder oft sogar wichtiger als die festen Kacheln, daher können wir es uns nicht leisten, sie zu verpassen.

Fügen Sie die Einwegarten hinzu

Als erstes müssen wir neue Kollisionstypen für Einwegplattformen hinzufügen. Wir werden sie über die Nicht-Einweg-Kollisionstypen hinaus hinzufügen und auch markieren, wo sie beginnen, damit wir später leicht feststellen können, ob ein bestimmter Kollisionstyp Einweg ist oder nicht.

Jetzt befinden sich alle Einwegplattformen zwischen den Aufzählungen OneWayStart und OneWayEnd, sodass wir problemlos eine Funktion erstellen können, die diese Informationen zurückgibt.

Die Einwegvarianten von Steigungen sollten auf dieselben Daten verweisen wie die Nicht-Einwegplattformen, sodass Sie sich hier keine Sorgen machen müssen, den Speicherbedarf weiter zu erweitern.

Decken Sie die zusätzlichen Daten ab

Fügen wir nun Variablen hinzu, mit denen ein Objekt Einwegplattformen ignorieren kann. Eine davon ist eine Objektflagge, die im Wesentlichen dazu dient, das permanente Ignorieren von Einwegplattformen festzulegen. Dies ist nützlich für fliegende Monster und andere Objekte, bei denen die Plattformen nicht verwendet werden müssen, und eine andere Flagge, um die Kollision vorübergehend zu deaktivieren Einwegplattformen, nur um durch sie zu fallen.

Die erste Variable befindet sich in der MovingObject-Klasse.

Der zweite befindet sich in der PositionState-Struktur.

Wir werden hier auch eine weitere Variable hinzufügen, die die Y-Koordinate der Plattform enthält, die wir überspringen möchten.

Damit Einwegplattformen funktionieren, ignorieren wir einfach eine einzelne horizontale Ebene von Plattformen. Wenn wir eine andere Ebene betreten, dh die Y-Position unseres Charakters hat sich in den Kartenkoordinaten geändert, setzen wir den Charakter so, dass er wieder mit den Einwegplattformen kollidiert.

Ändern Sie die Kollisionsprüfungen

Gehen wir zu unserer CollidesWithTileBottom-Funktion. Lassen Sie uns zunächst beim Durchlaufen der Kacheln prüfen, ob es sich um eine Einwegplattform handelt, und wenn ja, ob wir überhaupt in Betracht ziehen sollten, mit dieser Kachel zu kollidieren oder nicht.

Wir sollten nur dann mit Einwegplattformen kollidieren, wenn der Abstand zur Oberseite der Plattform geringer als der cSlopeWallHeightConstant ist, damit wir tatsächlich darauf kommen können. Fügen wir dies zu der bereits festgelegten Bedingung hinzu, und wir müssen state.onOneWay und state.oneWayY auch die richtigen Werte zuweisen.

Für die CollidesWithTileTop-Funktion ignorieren wir einfach Einwegplattformen.

Für die horizontale Kollisionsprüfung wird etwas mehr Arbeit anfallen. Zunächst erstellen wir zu Beginn zwei zusätzliche Boolesche Werte, die als Information darüber dienen, ob es sich bei der aktuell verarbeiteten Kachel um eine Einwegplattform handelt und ob die Kachel aus der vorherigen Iteration eine Einwegplattform war.

Jetzt sind wir daran interessiert, eine Einwegplattform zu durchlaufen, wenn wir uns darauf bewegen. Wir können nicht wirklich mit Einwegplattformen von rechts oder links kollidieren, aber wenn sich der Charakter entlang eines Abhangs bewegt, der auch eine Einwegplattform ist, muss er genauso behandelt werden wie ein normaler Abhang.

Stellen Sie jetzt sicher, dass wir nicht mit einem Hang kollidieren können, als wäre es eine Wand.

Und wenn dies nicht der Fall ist und der Versatz klein genug ist, um ihn zu besteigen, denken Sie daran, dass wir uns jetzt auf einer Einbahnstraße bewegen.

Jetzt müssen wir sicherstellen, dass jedes Mal, wenn wir den Positionsstatus ändern, auch die Variable onOneWay aktualisiert werden muss.

Nach unten springen

Wir müssen aufhören, die Einwegplattformen zu ignorieren, sobald wir die Y-Position in den Kartenkoordinaten ändern. Wir werden unseren Zustand nach der Bewegung auf der Y-Achse in der Verschieben-Funktion einrichten. Wir müssen es am Ende des zweiten Falls hinzufügen.

Und auch am Ende des dritten Falles.

Das sollte es tun. Jetzt müssen wir nur noch einen tmpIgnoresOneWay auf true setzen, damit ein Charakter von einer Einwegplattform entfernt wird.

Mal sehen, wie das in Aktion aussieht.

New animation of character moving on slope

Zusammenfassung

Puh, das war viel Arbeit, aber es hat sich gelohnt. Das Ergebnis ist sehr flexibel und robust. Dank unserer Handhabung von Kollisionsbitmaps können wir jede Art von Neigung definieren, die Kacheln übersetzen und sie in Einwegplattformen verwandeln.

Diese Implementierung ist immer noch nicht optimiert, und ich bin sicher, ich habe viele Möglichkeiten dafür verpasst, die unsere neue Ein-Pixel-Integrationsmethode bietet. Ich bin mir auch ziemlich sicher, dass viele zusätzliche Kollisionsprüfungen übersprungen werden könnten. Wenn Sie also diese Implementierung verbessern, lassen Sie es mich im Kommentarbereich wissen!

Vielen Dank, dass Sie so weit bei mir geblieben sind, und ich hoffe, dieses Tutorial ist für Sie von Nutzen!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.