In diesem Lernprogramm erfahren Sie, wie Sie bewegliche Plattformen erstellen und sicherstellen, dass Objekte, auf denen sie sich befinden, ihre relative Position beibehalten. Wir werden auch den Fall behandeln, dass er zwischen einer Plattform und dem Boden gequetscht wird.
Dieses Tutorial basiert auf der Basic Platformer Physics-Serie. Im Einzelnen verwenden wir den Code, der auf dem 8. Teil des Tutorials basiert, als Ausgangspunkt mit einigen Änderungen. Schauen Sie sich die Tutorialserie an, insbesondere den letzten Teil. Die Prinzipien der Implementierung gelten auch dann, wenn Sie eine andere Physiklösung verwenden. Der Code ist jedoch mit der in der Lernreihe dargestellten Version kompatibel.
Sie können die Demo aus den Anhängen des Tutorials herunterladen. Verwenden Sie die WASD Tasten zum Bewegen des Zeichens, Platz einen Klon-Charakter zu erzeugen, und P eine bewegte Plattform hervorbringen. Die rechte Maustaste erstellt eine Kachel. 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 veröffentlicht Einheit 2017.2b4, Der Quellcode ist auch mit dieser Unity-Version kompatibel.
Zunächst erstellen wir ein Skript für eine sich bewegende Plattform.
Beginnen wir mit der Erstellung der Objektklasse.
öffentliche Klasse MovingPlatform: MovingObject
Initialisieren wir nun einige grundlegende Parameter des Objekts in der init-Funktion.
public void Init () mAABB.HalfSize = new Vector2 (32.0f, 8.0f); mSlopeWallHeight = 0; mMovingSpeed = 100.0f; mIsKinematic = true; mSpeed.x = mMovingSpeed;
Wir legen die Größe und Geschwindigkeit fest und machen den Collider kinematisch, dh er wird von normalen Objekten nicht bewegt. Wir setzen auch die mSlopeWallHeight
auf 0, was bedeutet, dass die Plattform die Hänge nicht besteigen wird - sie behandelt sie immer als Wände.
Das Verhalten für diese bestimmte Plattform ist genau dies: Starten Sie die Bewegung nach rechts und ändern Sie die Richtung um 90 Grad im Uhrzeigersinn, wenn Sie auf ein Hindernis stoßen.
public void CustomUpdate () if (mPS.pushesRightTile &&! mPS.pushesBottomTile) mSpeed.y = -mMovingSpeed; else if (mPS.pushesBottomTile &&! mPS.pushesLeftTile) mSpeed.x = -mMovingSpeed; else if (mPS.pushesLeftTile &&! mPS.pushesTopTile) mSpeed.y = mMovingSpeed; else if (mPS.pushesTopTile &&! mPS.pushesRightTile) mSpeed.x = mMovingSpeed; UpdatePhysics ();
Hier ist das visualisierte Muster:
Wenn sich ein Charakter auf einer Plattform befindet, rutscht die Plattform einfach darunter, als gäbe es keine Reibung zwischen den Objekten. Wir werden versuchen, Abhilfe zu schaffen, indem wir den Versatz der Plattform kopieren.
Zuallererst möchten wir wissen, auf welchen Gegenstand, wenn überhaupt, unser Charakter steht. Lassen Sie uns einen Verweis auf dieses Objekt in der MovingObject
Klasse.
public MovingObject mMountParent = null;
Jetzt in der UpdatePhysicsResponse
, Wenn wir feststellen, dass wir mit einem Objekt unter uns kollidieren, können wir diese Referenz zuweisen. Lassen Sie uns eine Funktion erstellen, die die Referenz zuerst zuweist.
public void TryAutoMount (MovingObject-Plattform) if (mMountParent == null) mMountParent = Plattform;
Jetzt verwenden wir es an geeigneten Stellen, wo wir sagen, dass unser Objekt mit einem anderen Objekt darunter kollidiert.
else if (overlap.y == 0.0f) if (other.mAABB.Center.y> mAABB.Center.y) mPS.pushesTopObject = true; mSpeed.y = Mathf.Min (mSpeed.y, 0.0f); else TryAutoMount (andere); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max (mSpeed.y, 0.0f); fortsetzen;
Der erste Ort ist, wenn wir prüfen, ob sich die Objekte berühren.
if (überlappen.y < 0.0f) mPS.pushesTopObject = true; mSpeed.y = Mathf.Min(mSpeed.y, 0.0f); else TryAutoMount(other); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max(mSpeed.y, 0.0f);
Der zweite Ort ist, wenn sie sich überlappen.
Jetzt, da wir dies abgedeckt haben, lasst uns die Bewegung für unser Objekt handhaben. Ändern wir das UpdatePhysics
Funktion aus dem vorherigen Tutorial.
Lassen Sie uns eine Klassenvariable für den Versatz deklarieren, den wir benötigen, um unseren Charakter zu verschieben.
public Vector2 mOffset;
Jetzt ersetzen wir den alten lokalen Offset durch die Klasse Eins.
mOffset = mSpeed * Time.deltaTime;
Wenn sich das Objekt auf einer Plattform befindet, fügen Sie dem Versatz die Bewegung der Plattform hinzu.
mOffset = mSpeed * Time.deltaTime; if (mMountParent! = null) if (HasCollisionDataFor (mMountParent)) mOffset + = mMountParent.mPosition - mMountParent.mOldPosition; else mMountParent = null;
Beachten Sie, dass wir hier auch prüfen, ob wir noch mit dem Objekt in Kontakt sind. Wenn das nicht der Fall ist, setzen wir das mMountParent
zu Null
, um zu zeigen, dass dieses Objekt nicht mehr auf einem anderen reitet.
Als Nächstes verschieben wir die Position unseres Objekts um diesen Versatz. Wir werden unser nicht verwenden Bewegung
Funktion, aber ändern Sie einfach die Position. Also bei der Kollisionsprüfung zwischen den Objekten, die direkt nach dem erfolgt UpdatePhysics
, Wir erhalten das Ergebnis für die Positionen in diesem Frame anstelle der vorherigen.
mOffset = mSpeed * Time.deltaTime; if (mMountParent! = null) if (HasCollisionDataFor (mMountParent)) mOffset + = mMountParent.mPosition - mMountParent.mOldPosition; else mMountParent = null; mPosition + = RoundVector (mOffset + mReminder); mAABB.Center = mPosition;
Gehen wir jetzt zum UpdatePhysicsP2
, die aufgerufen wird, nachdem die Kollisionen zwischen den Objekten gelöst wurden. Hier machen wir unsere vorherige Bewegung rückgängig, bei der nicht geprüft wurde, ob sie gültig ist oder nicht.
public void UpdatePhysicsP2 () mPosition - = RoundVector (mOffset + mReminder); mAABB.Center = mPosition;
Als nächstes fahren wir mit fort UpdatePhysicsResponse
eine Bewegung außerhalb der Überlappung mit anderen Objekten anwenden. Hier haben wir vorher die Position direkt geändert, aber jetzt ändern wir stattdessen die Position mOffset
, so wird diese Positionsänderung später gelöst, wenn wir unsere verwenden Bewegung
Funktion.
if (kleinsteOverlap == Mathf.Abs (overlap.x)) float offsetX = overlap.x * speedRatioX; mOffset.x + = offsetX; offsetSum.x + = offsetX; if (Überlappung.x < 0.0f) mPS.pushesRightObject = true; mSpeed.x = Mathf.Min(mSpeed.x, 0.0f); else mPS.pushesLeftObject = true; mSpeed.x = Mathf.Max(mSpeed.x, 0.0f); else float offsetY = overlap.y * speedRatioY; mOffset.y += offsetY; offsetSum.y += offsetY; if (overlap.y < 0.0f) mPS.pushesTopObject = true; mSpeed.y = Mathf.Min(mSpeed.y, 0.0f); else TryAutoMount(other); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max(mSpeed.y, 0.0f);
Jetzt können wir zum zurückkehren UpdatePhysicsP2
, wo wir einfach anrufen UpdatePhysicsResponse
und Bewegung
funktioniert wie zuvor, um den korrekten Positionszustand zu erhalten.
mPosition - = RoundVector (mOffset + mReminder); mAABB.Center = mPosition; UpdatePhysicsResponse (); if (mOffset! = Vector2.zero) Move (mOffset, mSpeed, ref mPosition, ref mReminder, mAABB, ref mPS);
Aufgrund der Art und Weise, wie wir die Physikaktualisierungen anordnen, verliert das untergeordnete Objekt vor dem übergeordneten Element ständig den Kontakt mit der Plattform, wenn es auf / ab fährt.
Um dies zu beheben, wann immer wir das einstellen mMountParent
, Wenn sich die Plattform in der Aktualisierungswarteschlange hinter dem untergeordneten Element befindet, tauschen wir diese beiden aus, sodass das übergeordnete Objekt immer zuerst aktualisiert wird. Lassen Sie uns diese Änderung in der TryAutoMount
Funktion.
public void TryAutoMount (MovingObject-Plattform) if (mMountParent == null) mMountParent = Plattform; if (platform.mUpdateId> mUpdateId) mGame.SwapUpdateIds (this, Plattform);
Wenn Sie sehen, dass die Aktualisierungs-ID des Plattformobjekts größer als das untergeordnete ist, wird die Aktualisierungsreihenfolge der Objekte vertauscht, wodurch das Problem beseitigt wird.
Das ist so ziemlich alles, wenn es darum geht, den Charakter auf die bewegliche Plattform zu kleben.
Das Zerquetschen zu entdecken ist ziemlich einfach. In dem UpdatePhysicsResponse
, Wir müssen sehen, ob die Überlappung eines kinematischen Objekts uns in eine Wand führt.
Kümmern wir uns zuerst um die X-Achse:
if (kleinsteOverlap == Mathf.Abs (overlap.x)) float offsetX = overlap.x * speedRatioX; mOffset.x + = offsetX; offsetSum.x + = offsetX; if (Überlappung.x < 0.0f) mPS.pushesRightObject = true; mSpeed.x = Mathf.Min(mSpeed.x, 0.0f); else mPS.pushesLeftObject = true; mSpeed.x = Mathf.Max(mSpeed.x, 0.0f);
Wenn sich das Objekt auf unserer rechten Seite befindet und wir bereits gegen eine linke Wand drücken, dann nennen wir a Zerquetschen
Funktion, die wir später implementieren. Machen Sie dasselbe für die andere Seite.
if (Überlappung.x < 0.0f) if (other.mIsKinematic && mPS.pushesLeftTile) Crush(); mPS.pushesRightObject = true; mSpeed.x = Mathf.Min(mSpeed.x, 0.0f); else if (other.mIsKinematic && mPS.pushesRightTile) Crush(); mPS.pushesLeftObject = true; mSpeed.x = Mathf.Max(mSpeed.x, 0.0f);
Wiederholen wir das für die Y-Achse.
if (überlappen.y < 0.0f) if (other.mIsKinematic && mPS.pushesBottomTile) Crush(); mPS.pushesTopObject = true; mSpeed.y = Mathf.Min(mSpeed.y, 0.0f); else if (other.mIsKinematic && mPS.pushesTopTile) Crush(); TryAutoMount(other); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max(mSpeed.y, 0.0f);
Das Zerquetschen
Mit der Funktion wird der Charakter für die Demo einfach in die Mitte der Karte verschoben.
public void Crush () mPosition = mMap.mPosition + neuer Vector3 (mMap.mWidth / 2 * Map.cTileSize, mMap.mHeight / 2 * Map.cTileSize);
Das Ergebnis ist, dass der Charakter teleportiert wird, wenn er von einer Plattform zerquetscht wird.
Dies war ein kurzes Tutorial, da das Hinzufügen von beweglichen Plattformen keine große Herausforderung ist, insbesondere wenn Sie das Physiksystem gut kennen. Es war ein sehr reibungsloser Prozess, der sich sämtlichen Code aus der Physik-Lehrserie entlehnte.
Dieses Tutorial wurde einige Male angefragt. Ich hoffe, Sie finden es nützlich! Danke fürs Lesen und bis zum nächsten Mal!