• Home
  • Corsi

    Informazioni sui corsi

    • Tutti i corsi
    • Diventa un insegnante
    • Profilo insegnante
    Fondamenti di programmazione: le basi del linguaggio C++

    Fondamenti di programmazione: le basi del linguaggio C++

    €29.99
    Read More
    D4F Games
  • Develop4fun
    • Chi siamo
    • Portfolio
    • Eventi
    • Galleria
    • Negozio
    • FAQ
  • Articoli
  • Contattaci
    • Categorie

      • Sviluppo
        • Linguaggio di programmazione
          • C#
          • C++
          • Javascript
          • Lua
        • Videogiochi
          • LOVE2D
          • Phaser 3
          • RayLib
          • Unity3D
          • Unreal Engine
        • Web
          • Backend
            • PHP e MySQL
          • CMS
          • Frontend
            • CSS
            • Html
            • Javascript
    • Login

Cerca

Hai qualche domanda?
contact@develop4fun.com
Develop4fun
  • Home
  • Corsi

    Informazioni sui corsi

    • Tutti i corsi
    • Diventa un insegnante
    • Profilo insegnante
    Fondamenti di programmazione: le basi del linguaggio C++

    Fondamenti di programmazione: le basi del linguaggio C++

    €29.99
    Read More
    D4F Games
  • Develop4fun
    • Chi siamo
    • Portfolio
    • Eventi
    • Galleria
    • Negozio
    • FAQ
  • Articoli
  • Contattaci
    • Categorie

      • Sviluppo
        • Linguaggio di programmazione
          • C#
          • C++
          • Javascript
          • Lua
        • Videogiochi
          • LOVE2D
          • Phaser 3
          • RayLib
          • Unity3D
          • Unreal Engine
        • Web
          • Backend
            • PHP e MySQL
          • CMS
          • Frontend
            • CSS
            • Html
            • Javascript
    • Login

C#

Home » Articoli » [Unity] Programmare in C# – Coroutine

[Unity] Programmare in C# – Coroutine

  • Pubblicato da Antonella De Chiara
  • Categorie C#, Corso Unity, Linguaggi di Programmazione, Mac, PC, Programmazione Videogiochi, Unity
  • Data 28 Agosto 2019
  • Commenti 0 commenti
C#
(Ultimo aggiornamento: 21 Settembre 2021)

Coroutine di Unity3D in dettaglio

Innanzitutto è essenziale capire che i motori di gioco (come Unity 3D) lavorano su un paradigma “frame based“.

Questo significa che il codice viene eseguito durante ogni frame.

Quando si pensa a questi, è importante capire che è complicato sapere quando i frame sono eseguiti. Non sono eseguiti secondo un ordine ben preciso. Gli intervalli tra i frame potrebbero essere, ad esempio, 0,02632 dopo 0,021167, poi 0,029778 e così via. Negli esempi sono tutti a “circa” 1/50 di secondo, ma sono tutti diversi. In ogni instante, si può chiedere più o meno tempo; e il codice può essere eseguito in qualsiasi momento all’interno del frame.

Tenendo questo a mente, potreste chiedervi: come si accede a questi frame nel codice di Unity?

Molto semplicemente, si usa la funzione Update () o una coroutine. Sono esattamente la stessa cosa: permettono al codice di essere eseguire “un qualcosa ad ogni fotogramma”.

Lo scopo di una coroutine è:

  • eseguire un po’ di codice e poi “fermarsi e attendere” fino ad alcuni frame futuri.

Si può aspettare fino al fotogramma successivo, un numero di fotogrammi , oppure un po’ di tempo approssimativo (ad esempio in secondi).

Infatti, si può aspettare “circa un secondo”, il che significa che il programma aspetterà per circa un secondo, e quindi metterà il codice in qualche fotogramma approssimativamente di un secondo. All’interno di quel frame, il codice potrebbe essere eseguito in qualsiasi momento. Lo ribadiamo: non si tratterà esattamente di un secondo. La precisione non esiste, purtroppo, in un motore di gioco.

Per aspettare un frame all’interno di una coroutine:

// do something
yield return null;  // wait until next frame
// do something

Per aspettare tre frame:

// do something
yield return null;  // wait until three frames from now
yield return null;
yield return null;
// do something

Per attendere circa mezzo secondo:

// do something
yield return new WaitForSeconds (0.5f); // wait for a frame in about .5 seconds
// do something

Far qualcosa ogni singolo fotogramma:

while (true)
{
    // do something
    yield return null;  // wait until the next frame
}

Questo esempio è identico al semplice inserimento di “un qualcosa” all’interno della funzione “Update” di Unity: il codice di “fare qualcosa” verrà eseguito ogni frame.

Un altro esempio

Per questo esempio bisognerà allegare lo script “Ticker” a un oggetto GameObject. Mentre quell’oggetto di gioco è attivo, verrà eseguito il codice seguente. Bisogna notare che quando lo script blocca la coroutine l’oggetto del gioco diventa inattivo. Questo, di solito, è un aspetto importante per un corretto utilizzo della coroutine.

using UnityEngine;
using System.Collections;

public class Ticker:MonoBehaviour {

    void OnEnable()
    {
        StartCoroutine(TickEverySecond());
    }

    void OnDisable()
    {
        StopAllCoroutines();
    }

    IEnumerator TickEverySecond()
    {
        var wait = new WaitForSeconds(1f); // REMEMBER: IT IS ONLY APPROXIMATE
        while(true)
        {
            Debug.Log("Tick");
            yield return wait;  // wait for a frame, about 1 second from now
        }
    }
}

Coroutine a catena

Le coroutine possono far parte di una “reazione a catena” e attendere l’esecuzione di altre coroutine .

Quindi le coroutine verranno eseguite “una dopo l’altra”.

Questo concetto è molto semplice, ed è una tecnica di base, fondamentale in Unity.

È assolutamente naturale che, nei giochi , certe cose debbano accadere “in ordine”. Quasi ogni “scena” di un gioco inizia con una serie di eventi, in un certo lasso di tempo, in un certo ordine. Ecco come potreste iniziare, ad esempio, un gioco di corse automobilistiche:

IEnumerator BeginRace()
{
  yield return StartCoroutine(PrepareRace());
  yield return StartCoroutine(Countdown());
  yield return StartCoroutine(StartRace());
}

Quindi, quando chiamiamo BeginRace …

 StartCoroutine(BeginRace());

Il framework eseguirà prima “PrepareRace“. Forse, facendo un po’ di effetti di luci e rumori di folla, azzerando i punteggi e così via. Quando l’azione sarà finita, Unity eseguirà il “Countdown“, dove, forse, potremo animare un conto alla rovescia sull’interfaccia utente. Al termine, verrà eseguito il codice di partenza della gara, durante la quale si potrebbero aggiungere degli effetti sonori, spostare la telecamera in un determinato modo e così via.

Per chiarezza, è importante capire che le tre funzioni

  yield return StartCoroutine(PrepareRace());
  yield return StartCoroutine(Countdown());
  yield return StartCoroutine(StartRace());

devono essere in una coroutine. Vale a dire, devono essere in una funzione del tipo IEnumerator . Quindi nel nostro esempio è IEnumerator BeginRace . Nel codice, l’azione si esegue con la chiamata StartCoroutine .

 StartCoroutine(BeginRace());

Per comprendere meglio il concatenamento, ecco una funzione che esegue tutte le coroutine. Con un ciclo che recupera le coroutine e le avvia. La funzione esegue tutte le coroutine, in ordine, una dopo l’altra.

// run various routines, one after the other
IEnumerator OneAfterTheOther( params IEnumerator[] routines ) 
{
    foreach ( var item in routines ) 
    {
        while ( item.MoveNext() ) yield return item.Current;
    }

    yield break;
}

Diciamo che ci sono tre funzioni. Ricordiamo che devono essere tutte IEnumerator :

IEnumerator PrepareRace() 
{
    // codesay, crowd cheering and camera pan around the stadium
    yield break;
}

IEnumerator Countdown() 
{
    // codesay, animate your countdown on UI
    yield break;
}

IEnumerator StartRace() 
{
    // codesay, camera moves and light changes and launch the AIs
    yield break;
}

Potremo chiamarle in questo modo:

StartCoroutine( MultipleRoutines( PrepareRace(), Countdown(), StartRace() ) );

o anche così:

IEnumerator[] routines = new IEnumerator[] {
     PrepareRace(),
     Countdown(),
     StartRace() };
StartCoroutine( MultipleRoutines( routines ) );

Per ripetere, uno dei requisiti di base dei giochi è che certe cose accadono una dopo l’altra “in una sequenza” nel tempo. In Unity si fa molto semplicemente, ad esempio in questo modo:

  yield return StartCoroutine(PrepareRace());
  yield return StartCoroutine(Countdown());
  yield return StartCoroutine(StartRace());

Metodi MonoBehaviour che possono essere Coroutine

Ci sono dei metodi in MonoBehaviour che possono includere una coroutine.

  1. OnBecameVisible ()
  2. OnLevelWasLoaded ()

Questi vengono utilizzati, ad esempio, per eseguire lo script solo quando l’oggetto è visibile a una telecamera.

using UnityEngine;
using System.Collections;

public class RotateObject : MonoBehaviour
{
    IEnumerator OnBecameVisible()
    {
        var tr = GetComponent<Transform>();
        while (true)
        {
            tr.Rotate(new Vector3(0, 180f * Time.deltaTime));
            yield return null;
        }
    }
    
    void OnBecameInvisible()
    {
        StopAllCoroutines();
    }
}

Modi per cedere

Si può aspettare fino al fotogramma successivo.

yield return null; // wait until sometime in the next frame

È possibile avere più chiamate “in fila”, e attendere semplicemente il numero di frame desiderato.

//wait for a few frames
yield return null;
yield return null;

Attendere circa per “n” secondi. È estremamente importante capire che questo è solo del tempo approssimativo.

yield return new WaitForSeconds(n);

Non è assolutamente possibile utilizzare la chiamata “WaitForSeconds” per qualsiasi forma di sincronizzazione accurata.

Spesso si usa per concatenare delle azioni. Quindi, “fai qualcosa, e quando questo è finito allora fai qualcos’altro, e quando questo è finito fai qualcos’altro“. Per riuscirci, aspetta un’altra coroutine:

yield return StartCoroutine(coroutine);

Si può chiamare una coroutine anche in questo modo:

StartCoroutine(Test());

È da qui si avvia una coroutine con un pezzo di codice “normale”.

Quindi, all’interno della coroutine in esecuzione:

Debug.Log("A");
StartCoroutine(LongProcess());
Debug.Log("B");

Ciò mostrerà A, poi avviando il LongProcess e rimostrerà immediatamente B. L’azione non finirà con LongProcess. D’altro canto:

Debug.Log("A");
yield return StartCoroutine(LongProcess());
Debug.Log("B");

Ciò mostrerà A, avvierà il LongProcess , attenderà fino a quando non sarà finito , e quindi mostrerà B.

Vale sempre la pena ricordare che le coroutine non hanno assolutamente alcuna connessione fra di loro, in alcun modo. Con questo codice:

Debug.Log("A");
StartCoroutine(LongProcess());
Debug.Log("B");

è facile pensare ad un thread in background con l’avvio di LongProcess. Ma è assolutamente scorretto. È solo una coroutine. I motori di gioco sono basati su frame e “coroutines” e, in Unity, permettono semplicemente di accedere ai frame.

È anche molto semplice aspettare che una richiesta web venga completata.

void Start() {
    string url = "http://google.com";
    WWW www = new WWW(url);
    StartCoroutine(WaitForRequest(www));
}

IEnumerator WaitForRequest(WWW www) {
    yield return www;
    
    if (www.error == null) {
        //use www.data);
    }
    else {
        //use www.error);
    }
}

Per completare l’argomento, c’è una funzione WaitForFixedUpdate(), anche se in realtà non viene mai utilizzata. E’ una funzione specifica ( WaitForEndOfFrame() nella versione corrente di Unity) che viene usata in determinate situazioni durante lo sviluppo. (Il meccanismo esatto cambia di volta in volta con gli aggiornamenti di Unity, quindi guardate su Google per le ultime informazioni pertinenti).

Terminare una coroutine

Spesso le coroutine possono terminare quando vengono raggiunti determinati obiettivi.

IEnumerator TickFiveSeconds()
{
    var wait = new WaitForSeconds(1f);
    int counter = 1;
    while(counter < 5)
    {
        Debug.Log("Tick");
        counter++;
        yield return wait;
    }
    Debug.Log("I am done ticking");
}

Per stoppare una coroutine da “dentro” la coroutine, non si può semplicemente “tornare indietro” come succede in una funzione ordinaria. Così si usa yield break .

IEnumerator ShowExplosions()
{
    ... show basic explosions
    if(player.xp < 100) yield break;
    ... show fancy explosions
}

Si possono anche “forzare” tutte le coroutine lanciate dallo script a fermarsi prima di finire l’azione svolta.

void OnDisable()
{
    // Stops all running coroutines
    StopAllCoroutines();
}

Se abbiamo avviato una coroutine con una stringa:

StartCoroutine("YourAnimation");

si può stoppare chiamando la funzione StopCoroutine con parametro lo stesso nome della stringa:

StopCoroutine("YourAnimation");

In alternativa:

public class SomeComponent : MonoBehaviour 
{
    Coroutine routine;

    void Start () {
        routine = StartCoroutine(YourAnimation());
    }

    void Update () {
        // later, in response to some input...
        StopCoroutine(routine);
    }

    IEnumerator YourAnimation () { /* ... */ }
}
Visualizzazioni post: 1.309

Tag:cicli, frame, framerate, iterazioni, loop, multithreading, path-finding, pathfinder, programmare videogiochi, Programmatore, timeslicing, trigger, Tutorial Unity, Unity3D, Videogiochi

  • Share:

ABOUT INSTRUCTOR

Antonella De Chiara
Antonella De Chiara

Appassionata di giochi di ruolo giapponesi, genere che ha scoperto grazie alla saga Final Fantasy e approfondito con i capolavori usciti negli ultimi anni, s’interessa al retrogaming, rigiocando e programmando vecchie glorie come Monkey Island, Prince of Persia o vecchi capolavori marchiati Nintendo.
Antonella comincia il suo percorso giornalistico in Italia, lavorando con Gamerepublic, PS Mania e Pokémon Mania. Si trasferisce in Francia per studiare la programmazione web e gamedev: grazie a questo percorso anomalo nasce Develop4fun.

Articolo precedente

La storia dei videogiochi: gli anni ’80 - Parte 2
28 Agosto 2019

Articolo successivo

Simboli e caratteri speciali: ASCII, ISO, HTML
30 Agosto 2019

Ti potrebbe anche piacere

unity3d
Chi sono gli sviluppatori nel mondo dei videogiochi?
22 Novembre, 2021
Steam
Come pubblicare un videogioco su Steam?
21 Settembre, 2021
Money Game
Sviluppare Videogiochi: come fare un’indagine sul mercato
9 Settembre, 2021

Lascia un commento Annulla risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Cerca

Impara a programmare videogiochi web con Phaser 3

Impara a programmare videogiochi web con Phaser 3

€49.99
HTML e CSS: Corso di programmazione per principianti

HTML e CSS: Corso di programmazione per principianti

€69.99
[Lua & LOVE2D] Introduzione alla programmazione dei videogiochi

[Lua & LOVE2D] Introduzione alla programmazione dei videogiochi

€49.99
D4F Games

Ultimi articoli pubblicati

unity3d
Chi sono gli sviluppatori nel mondo dei videogiochi?
22Nov2021
[Unreal Engine] Un nuovo fantastico asset GRATUITO!
[Unreal Engine] Slay, un nuovo fantastico asset GRATUITO!
22Set2021
Steam
Come pubblicare un videogioco su Steam?
21Set2021

Develop4fun.it by Develop4fun.

Gestisci Consenso Cookie
Usiamo cookie per ottimizzare il nostro sito web ed i nostri servizi.
Funzionale Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici. L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.
Gestisci opzioni Gestisci servizi Gestisci fornitori Per saperne di più su questi scopi
Preferenze
{title} {title} {title}