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.
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.
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.
Allereerst, laten we een script maken voor een bewegend platform.
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.
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:
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.
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);
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.
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.
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!