Ora che l'astronave è in grado di volare e sparare rendiamo più realistica l'animazione facendo scorrere lo sfondo di gioco.
La tecnica che adopereremo è chiamata Parallax Scrolling. Concettualmente è molto semplice. Gli oggetti più vicini, nel nostro caso i pianeti, scorrono più velocemente rispetto a quelli più lontani, le stelle. Questo da l'impressione di muoversi in un ambiente molto realistico. Praticamente è quello che succede quando da un treno osserviamo dal finestrino. Gli alberi al bordo dei binari sfrecciano velcemente, mentre e le montagne in lontananza si muovono lentamente.
In un gioco 3d l'effetto è automatico semplicemente adottando l'accorgimento di impostare opportunamente la coordinata z degli oggetti. Tuttavia nel nostro gioco 2D stiamo utilizzando una camera ortografica, quindi ci dovremo occupare manualmente di creare l'effetto di scorrimento tramite script.
Un' altra funzionalità che includeremo nello script, è quella di riposozionare lo sfondo con tutti gli oggetti contenuti in esso in maniera ciclica. In questo modo continuerà a scorre come se fosse infinito.
using System.Collections.Generic; using System.Linq; using UnityEngine; ////// Parallax scrolling script that should be assigned to a layer /// public class ScrollingScript : MonoBehaviour { ////// Scrolling speed /// public Vector2 speed = new Vector2(1, 1); ////// Moving direction /// public Vector2 direction = new Vector2(-1, 0); ////// Movement should be applied to camera /// public bool isLinkedToCamera = false; ////// 1 - Background is infinite /// public bool isLooping = false; ////// 2 - Looping Gap /// public float loopingGap = 0; private float screenWidth = 0 ; ////// 2 - List of children with a renderer. /// private ListbackgroundPart; // 3 - Get all the children void Start() { // For infinite background only if (isLooping) { // Get all the children of the layer with a renderer backgroundPart = new List (); for (int i = 0; i < transform.childCount; i++) { Transform child = transform.GetChild(i); // Add only the visible children if (child.renderer != null) { backgroundPart.Add(child); } } // Sort by position. // Note: Get the children from left to right. // We would need to add a few conditions to handle // all the possible scrolling directions. backgroundPart = backgroundPart.OrderBy( t => t.position.x ).ToList(); Transform firstChild = backgroundPart.FirstOrDefault(); Transform lastChild = backgroundPart.LastOrDefault(); screenWidth = lastChild.renderer.bounds.max.x - firstChild.renderer.bounds.min.x; } } void Update() { // Movement Vector3 movement = new Vector3( speed.x * direction.x, speed.y * direction.y, 0); movement *= Time.deltaTime; transform.Translate(movement); // Move the camera if (isLinkedToCamera) { Camera.main.transform.Translate(movement); } // 4 - Loop if (isLooping) { // Get the first object. // The list is ordered from left (x position) to right. Transform firstChild = backgroundPart.FirstOrDefault(); if (firstChild != null) { // Check if the child is already (partly) before the camera. // We test the position first because the IsVisibleFrom // method is a bit heavier to execute. if (firstChild.position.x < Camera.main.transform.position.x) { // If the child is already on the left of the camera, // we test if it's completely outside and needs to be // recycled. if (firstChild.renderer.IsVisibleFrom(Camera.main) == false) { // Set the position of the recyled one to be AFTER // the last child. // Note: Only work for horizontal scrolling currently. firstChild.position = new Vector3(screenWidth + firstChild.position.x + loopingGap, firstChild.position.y, firstChild.position.z); // Set the recycled child to the last position // of the backgroundPart list. backgroundPart.Remove(firstChild); backgroundPart.Add(firstChild); } } } } } }
using UnityEngine;
public static class RendererExtensions
{
public static bool IsVisibleFrom(this Renderer renderer, Camera camera)
{
Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera);
return GeometryUtility.TestPlanesAABB(planes, renderer.bounds);
}
}
Aggiungiamo al background ed al middleground lo script ScrollingScript. Per il Background impostiamo i seguenti valori:
- Speed X: 0.2
- SpeedY: 0
- Direction X: -1
- Direction Y: 0
- Is Looping: Selezionato
- Looping Gap : 0
Per il Middleground invece
- Speed X: 0.2
- SpeedY: 0
- Direction X: -1
- Direction Y: 0
- Is Looping: Selezionato
- Looping Gap : 0
Lo script posiziona lo sprite appena fuoriscito dalla sinistra dello schermo direttamente dopo l'ultimo sprite alla destra. Se impostiamo LoopingGap a 0 i vari sprite combacieranno perfettamente l'uno all'altro, in sequenza, come un unico sprite. Quindi imposteremo LoopingGap a 0 nel caso delle immagini di sfondo come lo spazio, mentre imposteremo un LoopingGap a piacimento per il Middleground dei pianeti.
Per funzionare correttamente lo script necessita che le immagini di Background siano almeno due. Occorrerà quindi duplicare lo sprite space posizionando un secondo sfondo accanto al primo.
Per far funzionare lo ScrollingScript necessita uno script di utilità che abbiamo chiamato RendererHelper. Non occorre aggiungere questo script agli oggetti di gioco.
Se vogliamo rendere il nostro gioco ancora più realistico, possiamo creare un ulteriore livello tra il Foreground ed il Middleground. Nel Middleground posizioneremo i pianeti scalati di un fattore tra lo 0.2 e lo 0.4. mentre nel nuovo livello invece lasceremo i pianeti della dimensione originale o scalati tra 0.8 e 1.2.
Regoliamo quindi le velocità dei tre livelli in maniera tale che i pianeti più grandi e vicini scorrano più velocemente rispetto a quelli lontani.
L'effetto finale dovrebbe essere all'incirca il seguente.
Grazie mille per il tutorial dettagliato! Ora proverò qualcosa, poi magari se posso vorrei farti qualche domanda su come creare un terreno unendo perfettamente gli sprite (il cosiddetto pixel perfect), grazie ancora!
RispondiEliminaGrazie mille per il tutorial dettagliato! Ora proverò qualcosa, poi magari se posso vorrei farti qualche domanda su come creare un terreno unendo perfettamente gli sprite (il cosiddetto pixel perfect), grazie ancora!
RispondiEliminaottimo tutorial peccato che ormai risulta datato e parte del codice non funziona più
RispondiElimina