Platformer Mechanics Moving Platforms

Wat je gaat creëren

In deze zelfstudie leert u hoe u bewegende platforms maakt en zorgt u ervoor dat de objecten die erop rijden hun relatieve positie behouden. We zullen ook het geval behandelen van verpletterd raken tussen een platform en de grond.

voorwaarden

Deze tutorial is gebaseerd op de Basic Platformer Physics-serie. In het bijzonder zullen we de code gebaseerd op het achtste deel van de tutorial als uitgangspunt gebruiken, met een paar aanpassingen. Bekijk de tutorialseries, en met name het laatste deel. De principes achter de implementatie blijven bestaan, zelfs als u een andere fysica-oplossing gebruikt, maar de code is compatibel met de versie die wordt gepresenteerd in de zelfstudieserie.

demonstratie

Je kunt de demo downloaden van de tutorialbijlagen. Gebruik de WASD toetsen om het personage te verplaatsen, Ruimte om een ​​kloonkarakter te spawnen, en P om een ​​bewegend platform te spaaien. De rechtermuisknop maakt een tegel. U kunt het scrollwiel of de pijltoetsen gebruiken om een ​​tegel te selecteren die u wilt plaatsen. De schuifregelaars veranderen de grootte van het personage van de speler.

De demo is gepubliceerd onder Eenheid 2017.2b4, en de broncode is ook compatibel met deze versie van Unity.

Implementatie

Platforms verplaatsen

Allereerst, laten we een script maken voor een bewegend platform.

initialisatie

Laten we beginnen met het maken van de klasse van het object.

public class MovingPlatform: MovingObject 

Laten we nu enkele basisparameters van het object initialiseren in de init-functie.

public void Init () mAABB.HalfSize = new Vector2 (32.0f, 8.0f); mSlopeWallHeight = 0; mMovingSpeed ​​= 100.0f; mIsKinematic = true; mSpeed.x = mMovingSpeed; 

We stellen de grootte en snelheid in, en we maken de collider kinematisch, wat betekent dat hij niet door reguliere objecten wordt verplaatst. We hebben ook de mSlopeWallHeight tot 0, wat betekent dat het platform de hellingen niet zal beklimmen - het zal ze altijd als muren behandelen.

Gedrag

Het gedrag voor dit specifieke bewegende platform is precies dit: begin met rechts te gaan en wanneer je een obstakel tegenkomt, verander je de richting 90 graden met de klok mee.

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 is het patroon gevisualiseerd:

Het personage aan het platform lijmen

Op dit moment, als een personage op een platform staat, schuift het platform eenvoudig van onderaf, alsof er geen wrijving tussen de objecten is. We zullen proberen dat te verhelpen door de offset van het platform te kopiëren.

Bepaal het bovenliggende object

Allereerst willen we ons bewust zijn van welk object, indien aanwezig, ons karakter staat. Laten we een verwijzing naar dat object in de MovingObject klasse.

openbaar MovingObject mMountParent = null;

Nu, in de UpdatePhysicsResponse, als we detecteren dat we botsen met een object onder ons, kunnen we deze referentie toewijzen. Laten we een functie maken die eerst de referentie toekent.

openbare leegte TryAutoMount (MovingObject-platform) if (mMountParent == null) mMountParent = platform; 

Laten we het nu op de juiste plaatsen gebruiken, dat is waar we ook zeggen dat ons object botst met een ander object eronder.

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 (anders); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max (mSpeed.y, 0.0f);  doorgaan met; 

De eerste plaats is wanneer we controleren of de objecten elkaar raken.

als (overlapping.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); 

De tweede plaats is wanneer ze elkaar overlappen.

Nu we dit hebben afgedekt, laten we de beweging van ons object afhandelen. Laten we het aanpassen UpdatePhysics functie van de vorige zelfstudie.

Laten we een klassenvariabele declareren voor de offset die we nodig hebben om ons karakter te verplaatsen.

openbare Vector2 mOffset;

Laten we nu de oude lokale offset vervangen door de eerste.

mOffset = mSpeed ​​* Time.deltaTime;

Als het object zich op een platform bevindt, voegen we de beweging van het platform toe aan de offset.

mOffset = mSpeed ​​* Time.deltaTime; if (mMountParent! = null) if (HasCollisionDataFor (mMountParent)) mOffset + = mMountParent.mPosition - mMountParent.mOldPosition; else mMountParent = null; 

Merk op dat we hier ook controleren of we nog steeds contact hebben met het object. Als dat niet het geval is, hebben we de mMountParent naar nul, om aan te geven dat dit object niet meer op een ander rijdt.

Laten we vervolgens de positie van ons object met die verschuiving verplaatsen. We zullen onze niet gebruiken verhuizing functie, maar verander gewoon de positie. Dus in de botsingcontrole tussen de objecten, die plaatsvindt direct na de UpdatePhysics, we krijgen het resultaat voor de posities in dit frame in plaats van de vorige.

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;

Laten we nu gaan naar de UpdatePhysicsP2, die wordt genoemd nadat de botsingen tussen de objecten zijn opgelost. Hier maken we onze eerdere beweging ongedaan, maar er is niet gecontroleerd of deze geldig is of niet.

public void UpdatePhysicsP2 () mPosition - = RoundVector (mOffset + mReminder); mAABB.Center = mPosition;

Vervolgens gaan we verder UpdatePhysicsResponse een beweging toepassen uit overlapping met andere objecten. Hier hebben we eerder de positie direct gewijzigd, maar laten we nu de mOffset, dus deze positieverandering wordt later opgelost wanneer we onze verhuizing functie.

if (smallestOverlap == Mathf.Abs (overlapping.x)) float-offsetX = overlapping.x * speedRatioX; mOffset.x + = offsetX; offsetSum.x + = offsetX; if (overlapping.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);  

Nu kunnen we teruggaan naar de UpdatePhysicsP2, waar we gewoon de UpdatePhysicsResponse en verhuizing werkt zoals we eerder deden, om de juiste positiestatus te krijgen.

mPosition - = RoundVector (mOffset + mReminder); mAABB.Center = mPosition; UpdatePhysicsResponse (); if (mOffset! = Vector2.zero) Move (mOffset, mSpeed, ref mPosition, ref mReminder, mAABB, ref mPS);

Herstel de updateorder

Vanwege de manier waarop we de fysica-updates bestellen en het kindobject vóór de ouder wordt bijgewerkt, verliest het kind voortdurend contact met het platform wanneer het op en neer reist.

Om dit op te lossen, telkens wanneer we de mMountParent, Als het platform achter het kind in de updatewachtrij staat, wisselen we die twee om, zodat het bovenliggende object altijd als eerste wordt bijgewerkt. Laten we die modificatie doen in de TryAutoMount functie.

openbare leegte TryAutoMount (MovingObject-platform) if (mMountParent == null) mMountParent = platform; if (platform.mUpdateId> mUpdateId) mGame.SwapUpdateIds (this, platform); 

Zoals je kunt zien, als de update-id van het platformobject groter is dan die van het kind, wordt de updatebestelling van de objecten geruild, waardoor het probleem wordt verwijderd.

Dat is ongeveer het geval als het personage aan het bewegende platform wordt gelijmd.

Detecteren wordt verpletterd

Het detecteren van verpletterd worden is vrij eenvoudig. In de UpdatePhysicsResponse, we moeten zien of de overlap tegen een kinematisch object ons in een muur beweegt. 

Laten we eerst voor de X-as zorgen:

if (smallestOverlap == Mathf.Abs (overlapping.x)) float-offsetX = overlapping.x * speedRatioX; mOffset.x + = offsetX; offsetSum.x + = offsetX; if (overlapping.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);  

Als het object zich aan onze rechterkant bevindt en we al tegen een linkermuur duwen, laten we dan a bellen Verpletteren functie, die we later zullen implementeren. Doe hetzelfde voor de andere kant.

if (overlapping.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); 

Laten we dat herhalen voor de Y-as.

als (overlapping.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); 

De Verpletteren functie verplaatst het personage eenvoudig naar het midden van de kaart voor de demo.

public void Crush () mPosition = mMap.mPosition + new Vector3 (mMap.mWidth / 2 * Map.cTileSize, mMap.mHeight / 2 * Map.cTileSize); 

Het resultaat is het personage dat wordt geteleporteerd wanneer het door een platform wordt verpletterd.

Samenvatting

Dit was een korte tutorial omdat het toevoegen van bewegende platforms geen grote uitdaging is, vooral als je het fysische systeem goed kent. Lenen van alle code in de serie natuurkunde-tutorials, het was eigenlijk een zeer soepel proces. 

Deze tutorial is een paar keer opgevraagd, dus ik hoop dat je het nuttig vindt! Bedankt voor het lezen en tot de volgende keer!