I denne opplæringen lærer du hvordan du oppretter bevegelige plattformer og sørger for at objekter som kjører dem, bevare deres relative posisjon. Vi skal også håndtere tilfellet om å bli knust mellom en plattform og bakken.
Denne opplæringen er basert på Basic Platformer Physics serien. Spesielt bruker vi koden basert på 8. del av opplæringen som utgangspunkt, med noen få modifikasjoner. Sjekk ut opplæringsserien, og spesielt den siste delen. Prinsippene bak implementeringen vil stå selv om du bruker en annen fysikkløsning, men koden vil være kompatibel med versjonen som presenteres i opplæringsserien.
Du kan laste ned demoen fra opplæringsvedleggene. Bruke WASD nøkler for å flytte tegnet, Rom å gyte en klon karakter, og P å gyte en bevegelig plattform. Den høyre museknappen lager en flis. Du kan bruke rullehjulet eller piltastene til å velge en flis du vil plassere. Skyvekontrollene endrer størrelsen på spillerens karakter.
Demoen har blitt publisert under Enhet 2017.2b4, og kildekoden er også kompatibel med denne versjonen av Unity.
Først av alt, la oss lage et skript for en bevegelig plattform.
La oss begynne med å lage objektets klasse.
offentlig klasse MovingPlatform: MovingObject
La oss nå initialisere noen grunnleggende parametere for objektet i init-funksjonen.
offentlig tomrom Init () mAABB.HalfSize = ny Vector2 (32.0f, 8.0f); mSlopeWallHeight = 0; mMovingSpeed = 100,0f; mIsKinematisk = sant; mSpeed.x = mMovingSpeed;
Vi setter størrelsen og hastigheten, og vi gjør kollematiske collider, noe som betyr at den ikke vil bli flyttet av vanlige objekter. Vi satte også mSlopeWallHeight
til 0, noe som betyr at plattformen ikke vil klatre i bakkene, det vil alltid behandle dem som vegger.
Oppførselen for denne bestemte flyttbare plattformen vil være akkurat slik: Start bevegelsen rett, og når du møter et hinder, må du endre retningen 90 grader med klokken.
offentlig tomgang CustomUpdate () if (mPS.pushesRightTile &&! mPS.pushesBottomTile) mSpeed.y = -mMovingSpeed; ellers hvis (mPS.pushesBottomTile &&! mPS.pushesLeftTile) mSpeed.x = -mMovingSpeed; ellers hvis (mPS.pushesLeftTile &&! mPS.pushesTopTile) mSpeed.y = mMovingSpeed; ellers hvis (mPS.pushesTopTile &&! mPS.pushesRightTile) mSpeed.x = mMovingSpeed; UpdatePhysics ();
Her er mønsteret visualisert:
Akkurat nå, hvis et tegn står på en plattform, vil plattformen bare skyve fra under den, som om det ikke var friksjon mellom objektene. Vi vil prøve å rette opp det ved å kopiere offseten til plattformen.
Først og fremst vil vi være oppmerksomme på hvilket objekt, hvis noe er vår karakter som står på. La oss erklære en referanse til det objektet i MovingObject
klasse.
offentlig MovingObject mMountParent = null;
Nå, i UpdatePhysicsResponse
, Hvis vi oppdager at vi kolliderer med et objekt under oss, kan vi tildele denne referansen. La oss lage en funksjon som vil tildele referansen først.
offentlig tomrom TryAutoMount (MovingObject plattform) if (mMountParent == null) mMountParent = plattform;
La oss nå bruke det på passende steder, det er hvor vi sier at vårt objekt kolliderer med et annet objekt under det.
ellers hvis (overlap.y == 0.0f) if (other.mAABB.Center.y> mAABB.Center.y) mPS.pushesTopObject = true; mSpeed.y = Mathf.Min (mSpeed.y, 0.0f); ellers TryAutoMount (annet); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max (mSpeed.y, 0,0f); Fortsette;
Det første stedet er når vi sjekker om objektene berører.
hvis (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);
Det andre stedet er når de er overlappende.
Nå som vi har dette dekket, la oss håndtere bevegelsen for objektet vårt. La oss endre UpdatePhysics
Funksjon fra foregående opplæring.
La oss erklære en klassevariabel for forskyvningen som vi trenger for å flytte vår karakter.
offentlig Vector2 mOffset;
La oss nå erstatte det gamle lokale offsetet med klassen.
mOffset = mSpeed * Time.deltaTime;
Hvis objektet er på en plattform, la vi legge til plattformens bevegelse til forskyvningen.
mOffset = mSpeed * Time.deltaTime; hvis (mMountParent! = null) if (HasCollisionDataFor (mMountParent)) mOffset + = mMountParent.mPosition - mMountParent.mOldPosition; ellers mMountParent = null;
Vær oppmerksom på at vi også sjekker her hvis vi fortsatt har kontakt med objektet. Hvis det ikke er tilfelle, setter vi inn mMountParent
til null
, for å markere at dette objektet ikke lenger kjører på noen andre.
Neste, la oss flytte posisjonen til vårt objekt med den forskyvningen. Vi skal ikke bruke vår Bevege seg
funksjon, men bare endre posisjonen. Så i kollisjonskontrollen mellom objektene, som finner sted rett etter UpdatePhysics
, vi får resultatet for stillingene i denne rammen i stedet for den forrige.
mOffset = mSpeed * Time.deltaTime; hvis (mMountParent! = null) if (HasCollisionDataFor (mMountParent)) mOffset + = mMountParent.mPosition - mMountParent.mOldPosition; ellers mMountParent = null; mPosition + = RoundVector (mOffset + mReminder); mAABB.Center = mPosition;
La oss nå flytte til UpdatePhysicsP2
, som kalles etter at kollisjonene mellom objektene er løst. Her unngår vi vår forrige bevegelse, som ikke er sjekket for om den er gyldig eller ikke.
offentlig ugyldig UpdatePhysicsP2 () mPosition - = RoundVector (mOffset + mReminder); mAABB.Center = mPosition;
Deretter fortsetter vi videre til UpdatePhysicsResponse
å bruke et trekk ut av overlapping med andre objekter. Her har vi tidligere endret posisjonen direkte, men nå i stedet la oss endre mOffset
, så denne posisjonendringen blir løst senere når vi bruker vår Bevege seg
funksjon.
hvis (smallestOverlap == Mathf.Abs (overlap.x)) float offsetX = overlap.x * speedRatioX; mOffset.x + = offsetX; offsetSum.x + = offsetX; hvis (overlap.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);
Nå kan vi gå tilbake til UpdatePhysicsP2
, hvor vi bare ringer til UpdatePhysicsResponse
og Bevege seg
Fungerer som tidligere, for å få riktig posisjonstilstand.
mPosition - = RoundVector (mOffset + mReminder); mAABB.Center = mPosition; UpdatePhysicsResponse (); hvis (mOffset! = Vector2.zero) Flytt (mOffset, mSpeed, ref mPosition, ref mReminder, mAABB, ref mPS);
På grunn av måten vi bestiller fysikkoppdateringene på, hvis barnobjektet blir oppdatert for foreldre, vil barnet hele tiden miste kontakten med plattformen når du reiser opp / ned.
For å fikse dette, når vi setter mMountParent
, Hvis plattformen ligger bak barnet i oppdateringskøen, bytter vi disse to, slik at foreldreobjektet alltid oppdateres først. La oss gjøre den endringen i TryAutoMount
funksjon.
offentlig tomrom TryAutoMount (MovingObject plattform) if (mMountParent == null) mMountParent = plattform; hvis (platform.mUpdateId> mUpdateId) mGame.SwapUpdateIds (denne plattformen);
Som du kan se, hvis oppdaterings-ID for plattformobjektet er større enn barnet, blir objekternes oppdateringsordre byttet, fjerner problemet.
Det er ganske mye det når det gjelder liming av tegnet til den bevegelige plattformen.
Det er ganske enkelt å oppdage å bli knust. I UpdatePhysicsResponse
, Vi må se om overlappingen mot en kinematisk gjenstand beveger oss inn i en vegg.
La oss ta vare på X-aksen først:
hvis (smallestOverlap == Mathf.Abs (overlap.x)) float offsetX = overlap.x * speedRatioX; mOffset.x + = offsetX; offsetSum.x + = offsetX; hvis (overlap.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);
Hvis objektet er på høyre side og vi allerede skyver mot en venstre mur, så la oss ringe en Crush
funksjon, som vi skal implementere senere. Gjør det samme for den andre siden.
hvis (overlap.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);
La oss gjenta det for Y-aksen.
hvis (overlap.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 Crush
funksjonen vil ganske enkelt flytte tegnet til midten av kartet for demoen.
offentlig tomrom Crush () mPosition = mMap.mPosition + ny Vector3 (mMap.mWidth / 2 * Map.cTileSize, mMap.mHeight / 2 * Map.cTileSize);
Resultatet er at karakteren blir teleportert når den blir knust av en plattform.
Dette var en kort opplæring fordi det å legge til bevegelige plattformer ikke er en stor utfordring, spesielt hvis du kjenner fysikksystemet godt. Låner fra all koden i fysikkopplæringsserien, det var faktisk en veldig jevn prosess.
Denne opplæringen er blitt forespurt et par ganger, så jeg håper du finner det nyttig! Takk for at du leser, og ser deg neste gang!