Meccanica di piattaforma piattaforme mobili

Cosa starai creando

In questo tutorial imparerai come creare piattaforme mobili e assicurarti che gli oggetti che stanno cavalcando conservino la loro posizione relativa. Gestiremo anche il caso di essere schiacciati tra una piattaforma e il terreno.

Prerequisiti

Questo tutorial è basato sulla serie Basic Platformer Physics. In particolare, utilizzeremo il codice basato sull'ottava parte del tutorial come punto di partenza, con alcune modifiche. Controlla la serie di tutorial e in particolare l'ultima parte. I principi alla base dell'implementazione saranno validi anche se stai utilizzando una diversa soluzione fisica, ma il codice sarà compatibile con la versione presentata nella serie di tutorial.

dimostrazione

Puoi scaricare la demo dagli allegati tutorial. Utilizzare il WASD i tasti per spostare il personaggio, Spazio generare un carattere clone, e P generare una piattaforma mobile Il pulsante destro del mouse crea una tessera. È possibile utilizzare la rotella di scorrimento oi tasti freccia per selezionare una tessera che si desidera posizionare. I cursori cambiano le dimensioni del personaggio del giocatore.

La demo è stata pubblicata sotto Unity 2017.2b4, e il codice sorgente è anche compatibile con questa versione di Unity.

Implementazione

Piattaforme mobili

Prima di tutto, creiamo uno script per una piattaforma mobile.

Inizializzazione

Iniziamo creando la classe dell'oggetto.

public class MovingPlatform: MovingObject 

Ora inizializziamo alcuni parametri di base dell'oggetto nella funzione init.

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

Impostiamo la dimensione e la velocità e creiamo la cinematica del collisore, il che significa che non verrà spostato da oggetti normali. Abbiamo anche impostato il mSlopeWallHeight a 0, il che significa che la piattaforma non salirà le pendenze - le tratterà sempre come muri.

Comportamento

Il comportamento per questa particolare piattaforma mobile sarà proprio questo: inizia il movimento a destra, e ogni volta che incontri un ostacolo, cambia la direzione di 90 gradi in senso orario.

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 (); 

Ecco il modello visualizzato:

Incollare il personaggio sulla piattaforma

In questo momento, se un personaggio si trova su una piattaforma, la piattaforma scivolerà semplicemente da sotto, come se non ci fossero attriti tra gli oggetti. Cercheremo di porvi rimedio copiando l'offset della piattaforma.

Determina l'oggetto genitore

Prima di tutto, vogliamo essere consapevoli di quale oggetto, se presente, è il nostro personaggio in piedi. Dichiariamo un riferimento a quell'oggetto nel MovingObject classe.

public MovingObject mMountParent = null;

Ora, nel UpdatePhysicsResponse, se rileviamo che siamo in collisione con un oggetto sotto di noi, possiamo assegnare questo riferimento. Creiamo una funzione che assegnerà il riferimento per primo.

pubblico vuoto TryAutoMount (piattaforma MovingObject) if (mMountParent == null) mMountParent = platform; 

Ora usiamolo in luoghi appropriati, cioè ovunque diciamo che il nostro oggetto si scontra con un altro oggetto sottostante.

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

Il primo posto è quando controlliamo se gli oggetti stanno toccando.

se (sovrapposizione < 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); 

Il secondo posto è quando si sovrappongono.

Ora che abbiamo coperto questo, gestiamo il movimento per il nostro oggetto. Modifichiamo il UpdatePhysics funzione del tutorial precedente.

Dichiariamo una variabile di classe per l'offset di cui abbiamo bisogno per spostare il nostro personaggio.

pubblico Vector2 mOffset;

Ora sostituiamo il vecchio offset locale con quello di classe.

mOffset = mSpeed ​​* Time.deltaTime;

Nel caso in cui l'oggetto si trovi su una piattaforma, aggiungiamo il movimento della piattaforma all'offset.

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

Nota che stiamo anche controllando qui se siamo ancora in contatto con l'oggetto. Se questo non è il caso, impostiamo il mMountParent a nullo, per sottolineare che questo oggetto non sta più cavalcando su nessun altro.

Quindi, spostiamo la posizione del nostro oggetto per quell'offset. Non useremo il nostro Mossa funzione, ma semplicemente cambiare la posizione. Quindi nella verifica della collisione tra gli oggetti, che avviene subito dopo UpdatePhysics, otterremo il risultato per le posizioni in questo frame anziché il precedente.

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;

Ora passiamo al UpdatePhysicsP2, che viene chiamato dopo che le collisioni tra gli oggetti sono state risolte. Qui annulliamo il nostro movimento precedente, che non è stato verificato se è valido o meno.

pubblico vuoto UpdatePhysicsP2 () mPosition - = RoundVector (mOffset + mReminder); mAABB.Center = mPosition;

Successivamente, procederemo UpdatePhysicsResponse per applicare una mossa fuori dalla sovrapposizione con altri oggetti. Qui, in precedenza, stavamo modificando la posizione direttamente, ma ora invece modifichiamo il mOffset, quindi questo cambio di posizione viene risolto più tardi quando usiamo il nostro Mossa funzione.

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

Ora possiamo tornare al UpdatePhysicsP2, dove chiamiamo semplicemente il UpdatePhysicsResponse e Mossa funziona come abbiamo fatto prima, per ottenere lo stato di posizione corretto.

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

Correggere l'ordine di aggiornamento

A causa del modo in cui ordiniamo gli aggiornamenti di fisica, se l'oggetto figlio viene aggiornato prima del genitore, il bambino perde costantemente il contatto con la piattaforma quando viaggia su / giù.

Per risolvere questo problema, ogni volta che impostiamo il mMountParent, se la piattaforma è dietro il bambino nella coda di aggiornamento, scambiamo questi due, quindi l'oggetto genitore si aggiorna sempre per primo. Facciamo questa modifica nel TryAutoMount funzione.

pubblico vuoto TryAutoMount (piattaforma MovingObject) if (mMountParent == null) mMountParent = platform; if (platform.mUpdateId> mUpdateId) mGame.SwapUpdateIds (this, platform); 

Come puoi vedere, se l'id di aggiornamento dell'oggetto della piattaforma è maggiore del bambino, l'ordine di aggiornamento degli oggetti viene scambiato, rimuovendo il problema.

Questo è più o meno quando si tratta di incollare il personaggio alla piattaforma mobile.

Rileva di essere schiacciato

Rilevare essere schiacciati è piuttosto semplice. Nel UpdatePhysicsResponse, dobbiamo vedere se la sovrapposizione a un oggetto cinematico ci sposta in un muro. 

Prendiamoci prima l'asse X:

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

Se l'oggetto è dalla nostra parte destra e stiamo già spingendo contro una parete sinistra, chiamiamo a Schiacciare funzione, che implementeremo in seguito. Fai lo stesso per l'altro lato.

se (sovrapposizione.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); 

Ripetiamolo per l'asse Y..

se (sovrapposizione < 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); 

Il Schiacciare la funzione semplicemente sposterà il personaggio al centro della mappa per la demo.

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

Il risultato è che il personaggio viene teletrasportato quando viene schiacciato da una piattaforma.

Sommario

Questo è stato un breve tutorial perché l'aggiunta di piattaforme mobili non è una grande sfida, soprattutto se si conosce bene il sistema fisico. Prendendo in prestito da tutto il codice della serie di tutorial sulla fisica, si è trattato di un processo molto semplice. 

Questo tutorial è stato richiesto alcune volte, quindi spero che lo trovi utile! Grazie per la lettura, e ci vediamo la prossima volta!