Plattformsmekanik Flytta plattformar

Vad du ska skapa

I denna handledning lär du dig att skapa rörliga plattformar och se till att objekt som rider dem bevarar deras relativa position. Vi hanterar även om det krossas mellan en plattform och marken.

förutsättningar

Denna handledning bygger på Basic Platformer Physics serien. Specifikt använder vi koden baserat på 8: e delen av handledningen som utgångspunkt, med några ändringar. Kolla in handledningsserien, och särskilt sista delen. Principerna bakom implementeringen kommer att stå även om du använder en annan fysiklösning, men koden kommer att vara kompatibel med den version som presenteras i handledningsserien.

demo

Du kan ladda ner demo från bilagorna. Använd WASD nycklar för att flytta karaktären, Rymden att gissa en klontecken, och P att koka en rörlig plattform. Den högra musknappen skapar en kakel. Du kan använda rullhjulet eller piltangenterna för att välja en kakel du vill placera. Glidarna ändrar storleken på spelarens karaktär.

Demon har publicerats under Enhet 2017.2b4, och källkoden är också kompatibel med den här versionen av Unity.

Genomförande

Flytta plattformar

Låt oss först skapa ett manus för en rörlig plattform.

initiering

Låt oss börja med att skapa objektets klass.

allmän klass MovingPlatform: MovingObject 

Låt oss nu initiera några grundläggande parametrar för objektet i init-funktionen.

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

Vi ställer in storlek och hastighet, och vi gör kollematikens collider, vilket innebär att det inte kommer att flyttas av vanliga föremål. Vi ställer också in mSlopeWallHeight till 0, vilket innebär att plattformen inte kommer att klättra på sluttningarna - det kommer alltid att behandlas som väggar.

Beteende

Beteendet för den här rörliga plattformen kommer att vara just detta: starta rörelsen rätt, och när du möter ett hinder, ändra riktningen 90 grader medsols.

public void CustomUpdate () if (mPS.pushesRightTile &&! mPS.pushesBottomTile) mSpeed.y = -mMovingSpeed; annars om (mPS.pushesBottomTile &&! mPS.pushesLeftTile) mSpeed.x = -mMovingSpeed; annars om (mPS.pushesLeftTile &&! mPS.pushesTopTile) mSpeed.y = mMovingSpeed; annars om (mPS.pushesTopTile &&! mPS.pushesRightTile) mSpeed.x = mMovingSpeed; UpdatePhysics (); 

Här är mönstret visualiserat:

Limmar karaktären till plattformen

Just nu, om ett tecken står på en plattform, kommer plattformen enkelt att glida från under den, som om det inte fanns någon friktion mellan föremålen. Vi ska försöka åtgärda det genom att kopiera plattformens kompensation.

Bestäm modermålet

Först och främst vill vi vara medvetna om vilket föremål, om någon, vår karaktär står på. Låt oss förklara en hänvisning till det objektet i MovingObject klass.

allmänhet MovingObject mMountParent = null;

Nu, i UpdatePhysicsResponse, om vi upptäcker att vi kolliderar med ett objekt under oss, kan vi tilldela denna referens. Låt oss skapa en funktion som kommer att tilldela referensen först.

public void TryAutoMount (MovingObject-plattform) if (mMountParent == null) mMountParent = plattform; 

Låt oss nu använda det på lämpliga platser, det är där vi säger att vårt objekt kolliderar med ett annat objekt under det.

annars om (överlapp.y == 0,0f) om (other.mAABB.Center.y> mAABB.Center.y) mPS.pushesTopObject = true; mSpeed.y = Mathf.Min (mSpeed.y, 0.0f);  annars TryAutoMount (andra); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max (mSpeed.y, 0,0f);  Fortsätta; 

Första platsen är när vi kontrollerar om föremålen rör sig.

om (överlapp.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); 

Den andra platsen är när de överlappar varandra.

Nu när vi har täckt det här, låt oss hantera rörelsen för vårt objekt. Låt oss ändra UpdatePhysics funktion från föregående handledning.

Låt oss förklara en klassvariabel för offset som vi behöver flytta vår karaktär.

offentliga Vector2 mOffset;

Låt oss nu ersätta den gamla lokala offseten med klass ett.

mOffset = mSpeed ​​* Time.deltaTime;

Om objektet ligger på en plattform, låt oss lägga till plattformens rörelse till offset.

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

Observera att vi också kontrollerar här om vi fortfarande har kontakt med objektet. Om så inte är fallet ställer vi in mMountParent till null, för att markera att det här objektet inte längre rider på någon annan.

Låt oss sedan flytta positionen av vårt objekt med den förskjutningen. Vi ska inte använda vår Flytta funktion, men helt enkelt ändra positionen. Så i kollisionskontrollen mellan föremålen, som äger rum strax efter UpdatePhysics, vi får resultatet för positionerna i denna ram istället för den tidigare.

mOffset = mSpeed ​​* Time.deltaTime; om (mMountParent! = null) if (HasCollisionDataFor (mMountParent)) mOffset + = mMountParent.mPosition - mMountParent.mOldPosition; annars mMountParent = null;  mPosition + = RoundVector (mOffset + mReminder); mAABB.Center = mPosition;

Låt oss nu flytta till UpdatePhysicsP2, som kallas efter kollisionerna mellan föremålen har lösts. Här ångrar vi vår tidigare rörelse, som inte har kontrollerats om det är giltigt eller inte.

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

Därefter fortsätter vi vidare till UpdatePhysicsResponse att tillämpa ett drag utöver överlappning med andra objekt. Här har vi tidigare ändrat positionen direkt, men nu istället låt oss ändra mOffset, så denna position förändring blir löst senare när vi använder vår Flytta fungera.

om (minstaOverlap == Mathf.Abs (overlap.x)) float offsetX = overlap.x * speedRatioX; mOffset.x + = offsetX; offsetSum.x + = offsetX; om (överlappning.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 kan vi gå tillbaka till UpdatePhysicsP2, där vi bara ringa UpdatePhysicsResponse och Flytta fungerar som vi gjorde tidigare, för att få rätt positionstillstånd.

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

Fix uppdateringsordern

På grund av hur vi beställer fysikuppdateringarna, om barnobjektet uppdateras före föräldern, kommer barnet ständigt att förlora kontakten med plattformen när de reser upp / ner.

För att åtgärda detta, när vi ställer in mMountParent, Om plattformen ligger bakom barnet i uppdateringskön byter vi dessa två, så förälderobjektet uppdaterar alltid först. Låt oss göra den ändringen i TryAutoMount fungera.

public void TryAutoMount (MovingObject-plattform) if (mMountParent == null) mMountParent = plattform; om (platform.mUpdateId> mUpdateId) mGame.SwapUpdateIds (den här plattformen); 

Som du kan se, om uppdaterings-id för plattformsobjektet är större än barnet, ändras objektets uppdateringsorder, vilket tar bort problemet.

Det är ganska mycket när det gäller att limma karaktären till den rörliga plattformen.

Upptäcka att krossas

Det är ganska enkelt att upptäcka att krossas. I UpdatePhysicsResponse, vi behöver se om överlappningen mot ett kinematiskt objekt rör oss till en vägg. 

Låt oss ta hand om X-axeln först:

om (minstaOverlap == Mathf.Abs (overlap.x)) float offsetX = overlap.x * speedRatioX; mOffset.x + = offsetX; offsetSum.x + = offsetX; om (överlappning.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);  

Om objektet är på vår högra sida och vi redan trycker mot en vänster vägg, låt oss ringa en Krossa funktion, som vi ska implementera senare. Gör detsamma för andra sidan.

om (överlappning.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); 

Låt oss upprepa det för Y-axeln.

om (överlapp.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 Krossa funktionen kommer helt enkelt att flytta karaktären till mitten av kartan för demo.

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

Resultatet är att karaktären teleporteras när den krossas av en plattform.

Sammanfattning

Detta var en kort handledning eftersom det inte är en stor utmaning att lägga till rörliga plattformar, särskilt om du känner till fysiksystemet bra. Låna från hela koden i fysikhandledningsserien var det faktiskt en mycket smidig process. 

Denna handledning har begärts ett par gånger, så jag hoppas att du tycker att den är användbar! Tack för att du läste och vi ses nästa gång!