Padroneggiare le fotocamere 2D in Unity: un tutorial per gli sviluppatori di giochi

Pubblicato: 2022-03-11

Per uno sviluppatore, la fotocamera è una delle pietre miliari del processo di sviluppo del gioco. Dal semplice mostrare la visuale del gioco in un'app di scacchi al dirigere magistralmente il movimento della telecamera in un gioco 3D AAA per ottenere effetti cinematografici, le telecamere sono fondamentalmente utilizzate in qualsiasi videogioco mai realizzato, anche prima di essere effettivamente chiamate "telecamere".

In questo articolo spiegherò come progettare un sistema di telecamere per giochi 2D e spiegherò anche alcuni punti su come implementarlo in uno dei motori di gioco più popolari in circolazione, Unity.

Dal 2D al 2.5D: un sistema di telecamere estensibile

Il sistema di telecamere che progetteremo insieme è modulare ed estensibile. Ha un nucleo di base composto da diversi componenti che garantiranno la funzionalità di base, e quindi vari componenti/effetti che possono essere utilizzati opzionalmente, a seconda della situazione in questione.

Il sistema di telecamere che stiamo costruendo qui è destinato ai giochi di piattaforma 2D, ma può essere facilmente esteso ad altri tipi di giochi 2D, giochi 2.5D o persino giochi 3D.

Padroneggiare la fotocamera 2D in Unity: un tutorial per gli sviluppatori di giochi

Padroneggiare la fotocamera 2D in Unity: un tutorial per gli sviluppatori di giochi
Twitta

Dividerò la funzionalità della fotocamera in due gruppi principali: rilevamento della fotocamera ed effetti della fotocamera.

Tracciamento

La maggior parte del movimento della telecamera che faremo qui sarà basato sul tracciamento. Questa è la capacità di un oggetto, in questo caso la telecamera, di seguire altri oggetti mentre si muovono nella scena del gioco. I tipi di tracciamento che implementeremo risolveranno alcuni scenari comuni riscontrati nei giochi di piattaforma 2D, ma possono essere estesi con nuovi tipi di tracciamento per altri scenari particolari che potresti avere.

Effetti

Implementeremo alcuni fantastici effetti come il movimento della fotocamera, lo zoom della fotocamera, la dissolvenza della fotocamera e la sovrapposizione del colore.

Iniziare

Crea un nuovo progetto 2D in Unity e importa risorse standard, in particolare il personaggio RobotBoy. Quindi, crea una casella di terra e aggiungi un'istanza di carattere. Dovresti essere in grado di camminare e saltare con il tuo personaggio nella scena attuale. Assicurati che la fotocamera sia impostata sulla modalità ortografica (è impostata su Prospettiva per impostazione predefinita).

Tracciare un bersaglio

Il seguente script aggiungerà il comportamento di tracciamento di base alla nostra fotocamera principale. Lo script deve essere allegato come componente alla telecamera principale nella scena ed espone un campo per l'assegnazione di un oggetto target da tracciare. Quindi lo script assicura che le coordinate xey della telecamera siano le stesse con l'oggetto che traccia. Tutta questa elaborazione viene eseguita durante la fase di aggiornamento.

 [SerializeField] protected Transform trackingTarget; // ... void Update() { transform.position = new Vector3(trackingTarget.position.x, trackingTarget.position.y, transform.position.z); }

Trascina il personaggio RobotBoy dalla tua gerarchia di scene sul campo "Tracking Target" esposto dal nostro seguente comportamento per abilitare il tracciamento del personaggio principale.

Aggiunta di offset

Tutto bene, ma si nota subito un limite: il personaggio è sempre al centro della nostra scena. Possiamo vedere molto dietro il personaggio, che di solito è roba che non ci interessa, e vediamo troppo poco di ciò che ha davanti al nostro personaggio, il che potrebbe essere dannoso per il gameplay.

Per risolvere questo problema, stiamo aggiungendo allo script alcuni nuovi campi che consentiranno il posizionamento della telecamera a un offset dal suo target.

 [SerializeField] float xOffset; [SerializeField] float yOffset; // ... void Update() { transform.position = new Vector3(trackingTarget.position.x + xOffset, trackingTarget.position.y + yOffset, transform.position.z); }

Di seguito puoi vedere una possibile configurazione per i due nuovi campi:

Appianare le cose

Il movimento della telecamera è piuttosto rigido e produrrà anche vertigini in alcuni giocatori a causa del costante movimento percepito dell'ambiente. Per risolvere questo problema, aggiungeremo un certo ritardo nel tracciamento della telecamera utilizzando l'interpolazione lineare e un nuovo campo per controllare la velocità con cui la telecamera si posiziona dopo che il personaggio ha iniziato a cambiare posizione.

 [SerializeField] protected float followSpeed; // ... protected override void Update() { float xTarget = trackingTarget.position.x + xOffset; float yTarget = trackingTarget.position.y + yOffset; float xNew = Mathf.Lerp(transform.position.x, xTarget, Time.deltaTime * followSpeed); float yNew = Mathf.Lerp(transform.position.y, yTarget, Time.deltaTime * followSpeed); transform.position = new Vector3(xNew, yNew, transform.position.z); } 

Ferma le vertigini: blocco dell'asse

Dal momento che non è piacevole per il tuo cervello guardare la telecamera andare su e giù tutto il tempo insieme al personaggio, stiamo introducendo il blocco dell'asse. Ciò significa che possiamo limitare il tracciamento a un solo asse. Quindi separeremo il nostro codice di tracciamento in tracciamento indipendente dall'asse e terremo conto dei nuovi flag di blocco.

 [SerializeField] protected bool isXLocked = false; [SerializeField] protected bool isYLocked = false; // ... float xNew = transform.position.x; if (!isXLocked) { xNew = Mathf.Lerp(transform.position.x, xTarget, Time.deltaTime * followSpeed); } float yNew = transform.position.y; if (!isYLocked) { yNew = Mathf.Lerp(transform.position.y, yTarget, Time.deltaTime * followSpeed); } 

Sistema di corsie

Ora che la telecamera segue il giocatore solo orizzontalmente, siamo limitati all'altezza di uno schermo. Se il personaggio sale una scala o salta più in alto, dobbiamo seguirlo. Il modo in cui lo stiamo facendo è utilizzare un sistema di corsie.

Immagina il seguente scenario:

Il personaggio è inizialmente sulla corsia inferiore. Mentre il personaggio rimane entro i confini di questa corsia, la telecamera si muoverà solo orizzontalmente su un offset di altezza specifico della corsia che possiamo impostare.

Non appena il personaggio entra in un'altra corsia, la telecamera passerà a quella corsia e continuerà a muoversi orizzontalmente da lì in poi fino al successivo cambio di corsia.

È necessario prestare attenzione alla progettazione della corsia per evitare il cambio di corsia rapido durante azioni come i salti, che possono creare confusione per il giocatore. Una corsia dovrebbe essere cambiata solo se il personaggio del giocatore vi rimarrà per un po'.

I livelli delle corsie possono cambiare durante il livello di gioco in base alle esigenze specifiche del progettista, oppure possono essere interrotti del tutto e un altro sistema di tracciamento della telecamera può prendere il loro posto. Pertanto, abbiamo bisogno di alcuni limitatori per specificare le zone di corsia.

Implementazione

Una possibile implementazione consiste nell'aggiungere corsie come semplici oggetti nella scena. Useremo la loro coordinata di posizione Y accoppiata con l'offset Y nello script di tracciamento sopra per implementare il sistema. Pertanto, il loro posizionamento sulle coordinate X e Z non ha importanza.

Aggiungi la classe LaneSystem alla telecamera, insieme alla classe di rilevamento, e assegna gli oggetti corsia all'array fornito. Assegna anche il personaggio del giocatore al campo Riferimento. Poiché il riferimento è posizionato tra una corsia e un'altra corsia, quella inferiore delle due verrà utilizzata per posizionare la telecamera.

E la classe LaneSystem si occupa di spostare la telecamera tra le corsie, in base alla posizione di riferimento. Il followSpeed ​​viene utilizzato anche qui per l'interpolazione della posizione, per evitare che il cambio di corsia sia troppo brusco:

 [SerializeField] Transform reference; [SerializeField] List<Transform> lanes; [SerializeField] float followSpeed = 5f; // ... void Update() { float targetYCoord = transform.position.y; if (lanes.Count > 1) { int i = 0; for (i = 0; i < lanes.Count - 1; ++i) { if ((reference.position.y > lanes[i].position.y) && (reference.position.y <= lanes[i + 1].position.y)) { targetYCoord = lanes[i].position.y; break; } } if (i == lanes.Count - 1) targetYCoord = lanes[lanes.Count - 1].position.y; } else { targetYCoord = lanes[0].position.y; } float yCoord = Mathf.Lerp(transform.position.y, targetYCoord, Time.deltaTime * followSpeed); transform.position = new Vector3(transform.position.x, yCoord, transform.position.z); }

Questa implementazione non è WYSIWYG e viene lasciata come un esercizio per il lettore.

Sistema di nodi di blocco

Avere la telecamera in movimento sulle corsie è fantastico, ma a volte abbiamo bisogno che la telecamera sia bloccata su qualcosa, un punto di interesse (POI) nella scena del gioco.

Ciò può essere ottenuto configurando tali POI nella scena e allegandovi un collisore trigger. Ogni volta che il personaggio entra in quel collisore di innesco, spostiamo la telecamera e rimaniamo sul POI. Quando il personaggio si muove e poi lascia il collisore di innesco del POI, torniamo a un altro tipo di tracciamento, di solito il comportamento di inseguimento standard.

Il passaggio del rilevamento della telecamera a un nodo di blocco e viceversa può essere effettuato tramite un semplice interruttore o tramite un sistema di stack, su cui le modalità di rilevamento vengono attivate e attivate.

Implementazione

Per configurare un nodo di blocco, basta creare un oggetto (può essere vuoto o come nello screenshot qui sotto, uno sprite) e allegare un grande componente Circle Collider 2D ad esso in modo che segni l'area in cui si troverà il giocatore quando la telecamera lo farà focalizzare il nodo. Puoi scegliere qualsiasi tipo di collisore, sto scegliendo Circle come esempio qui. Crea anche un tag che puoi facilmente controllare, come "CameraNode" e assegnalo a questo oggetto.

Aggiungi la seguente proprietà allo script di monitoraggio sulla videocamera:

 public Transform TrackingTarget { get { return trackingTarget; } set { trackingTarget = value; } }

Quindi allega il seguente script al giocatore, che gli consentirà di cambiare temporaneamente il bersaglio della telecamera sul nodo di blocco che hai impostato. Lo script ricorderà anche il suo obiettivo precedente in modo che possa tornare ad esso quando il giocatore è fuori dall'area di attivazione. Puoi andare avanti e trasformarlo in uno stack completo se ne hai bisogno, ma per il nostro scopo poiché non sovrapponiamo più nodi di blocco, questo andrà bene. Inoltre, tieni presente che puoi modificare la posizione del Circle Collider 2D, o ancora aggiungere qualsiasi altro tipo di collisore per attivare il blocco della fotocamera, questo è solo un semplice esempio.

 public class LockBehavior : MonoBehaviour { #region Public Fields [SerializeField] Camera camera; [SerializeField] string tag; #endregion #region Private private Transform previousTarget; private TrackingBehavior trackingBehavior; private bool isLocked = false; #endregion // Use this for initialization void Start() { trackingBehavior = camera.GetComponent<TrackingBehavior>(); } void OnTriggerEnter2D(Collider2D other) { if (other.tag == tag && !isLocked) { isLocked = true; PushTarget(other.transform); } } void OnTriggerExit2D(Collider2D other) { if (other.tag == tag && isLocked) { isLocked = false; PopTarget(); } } private void PushTarget(Transform newTarget) { previousTarget = trackingBehavior.TrackingTarget; trackingBehavior.TrackingTarget = newTarget; } private void PopTarget() { trackingBehavior.TrackingTarget = previousTarget; } } 

Zoom della fotocamera

Lo zoom della telecamera può essere eseguito sia sull'input dell'utente che come animazione quando vogliamo concentrarci su qualcosa come un POI o un'area più ristretta all'interno di un livello.

Lo zoom della telecamera 2D in Unity 3D può essere ottenuto manipolando la dimensione ortografica della telecamera. Allegare lo script successivo come componente a una telecamera e utilizzare il metodo SetZoom per modificare il fattore di zoom produrrà l'effetto desiderato. 1.0 significa nessuno zoom, 0.5 significa zoom avanti due volte, 2 significa zoom indietro due volte e così via.

 [SerializeField] float zoomFactor = 1.0f; [SerializeField] float zoomSpeed = 5.0f; private float originalSize = 0f; private Camera thisCamera; // Use this for initialization void Start() { thisCamera = GetComponent<Camera>(); originalSize = thisCamera.orthographicSize; } // Update is called once per frame void Update() { float targetSize = originalSize * zoomFactor; if (targetSize != thisCamera.orthographicSize) { thisCamera.orthographicSize = Mathf.Lerp(thisCamera.orthographicSize, targetSize, Time.deltaTime * zoomSpeed); } } void SetZoom(float zoomFactor) { this.zoomFactor = zoomFactor; }

Scuotere lo schermo

Ogni volta che abbiamo bisogno di mostrare un terremoto, un'esplosione o qualsiasi altro effetto nel nostro gioco, l'effetto di vibrazione della fotocamera è utile.

Un'implementazione di esempio di come farlo è disponibile su GitHub: gist.github.com/ftvs/5822103. L'implementazione è abbastanza semplice. A differenza degli altri effetti che abbiamo trattato finora, si basa su un po' di casualità.

Dissolvenza e sovrapposizione

Quando il nostro livello inizia o finisce, un effetto di dissolvenza in entrata o in uscita è piacevole. Possiamo implementarlo aggiungendo una texture dell'interfaccia utente non interagibile in un pannello che si estende su tutto il nostro schermo. Inizialmente trasparente, possiamo riempirlo con qualsiasi colore e opacità o animarlo per ottenere l'effetto desiderato.

Ecco un esempio di tale configurazione, si prega di notare che l'oggetto UI Panel è assegnato al figlio "Camera Overlay" dell'oggetto Main Camera. Camera Overlay espone uno script chiamato Overlay che presenta quanto segue:

 [SerializeField] Image overlay; // ... public void SetOverlayColor(Color color) { overlay.color = color; } 

Per avere un effetto dissolvenza, cambia il tuo script Overlay aggiungendo un'interpolazione a un colore target che hai impostato con SetOverlayColor come nello script successivo, e imposta il colore iniziale del nostro pannello su Nero (o Bianco) e il colore target al colore finale della tua sovrapposizione. Puoi cambiare la velocità di dissolvenza in base alle tue esigenze, penso che 0.8 sia una buona scelta per cominciare. Il valore di fadeSpeed ​​funziona come un modificatore di tempo. 1.0 significa che accadrà su più fotogrammi, ma entro un intervallo di tempo di 1 secondo. 0,8 significa che ci vorranno effettivamente 1/0,8 = 1,25 secondi per essere completato.

 public class Overlay : MonoBehaviour { #region Fields [SerializeField] Image overlay; [SerializeField] float fadeSpeed = 5f; [SerializeField] Color targetColor; #endregion void Update() { if (overlay.color != targetColor) { overlay.color = Color.Lerp(overlay.color, targetColor, Time.deltaTime * fadeSpeed); } } #region Public public void SetOverlayColor(Color color) { targetColor = color; } #endregion }

Incartare

In questo articolo ho cercato di dimostrare i componenti di base necessari per avere un sistema di telecamere 2D modulare in atto per il tuo gioco, e anche qual è la mentalità necessaria per progettarlo. Naturalmente, tutti i giochi hanno le loro esigenze particolari, ma con il tracciamento di base e i semplici effetti qui descritti puoi fare molta strada e avere anche un progetto per implementare i tuoi effetti. Quindi puoi andare ancora oltre e imballare tutto in un pacchetto Unity 3D riutilizzabile che puoi trasferire anche ad altri progetti.

I sistemi di telecamere sono molto importanti per trasmettere la giusta atmosfera ai tuoi giocatori. Un buon confronto che mi piace usare è quando pensi alla differenza tra teatro classico e film. Le telecamere e i filmati stessi hanno portato così tante possibilità alla scena che alla fine si è evoluta in un'arte a sé stante, quindi se non hai intenzione di implementare un altro gioco "Pong", le telecamere avanzate dovrebbero essere il tuo strumento preferito in qualsiasi progetto di gioco tu mi impegnerò d'ora in poi.

Correlati: Unity with MVC: come aumentare di livello lo sviluppo del tuo gioco