2010-08-09 11 views
10

Ho ottenuto questo metodo (all'interno di Unity C# Script), ma non capisco come funzioni la parte "yield".Come funziona questa funzione con una "resa" in dettaglio?

So da MSDN che la funzione restituirà un IEnumerator che potrei scorrere attraverso, ma questo codice attende 1,5 secondi e non viene iterato perché ciò significherebbe che gli oggetti creati all'interno sono stati creati più volte. Chiunque qui può spiegarmi come funziona questo codice?

IEnumerator DestroyShip() 
{ 
    // create new gameobject 
    Instantiate(ExplosionPrefab, transform.position, transform.rotation); 
    // make current gameobject invisible 
    gameObject.renderer.enabled = false; 
    // set new position for the current gameobject 
    transform.position = new Vector3(0f, transform.position.y, transform.position.z); 
    // wait for 1,5 seconds 
    yield return new WaitForSeconds(1.5f); 
    // make the current gameobject visible again 
    gameObject.renderer.enabled = true; 
} 
+0

Il titolo della domanda potrebbe essere più descrittivo. In qualche modo, Google ha ancora raccolto le parole chiave, comunque. – ftvs

risposta

21

l'enumeratore il compilatore genera per voi viene iterato. Una volta.

Il compilatore genererà una classe che implementa IEnumerator, che ha una funzione MoveNext() e una proprietà Current. La classe avrà tutti i membri necessari per memorizzare lo stato della funzione tra le chiamate. I dettagli esatti possono essere considerati "Compiler Magic".

L'oggetto di questa classe generata verrà gestito e gestito da Unity3d Engine. Il motore Unity3d chiamerà MoveNext() su ciascuna coroutine attiva una volta ogni frame (salvo istruzioni contrarie).

Ciò ha permesso al programmatore Unity3d di scrivere script che vengono riprodotti un fotogramma alla volta. Una combinazione di magia del compilatore C# e magia del motore Unity3d si traduce in uno scripting molto potente, ma facile da usare.

Per rispondere alla tua domanda: il codice nella tua funzione verrà eseguito una volta, ma si fermerà alla dichiarazione "rendimento".

Come detto sopra, un oggetto speciale che implementa IEnumerator viene creato dal compilatore C#.

Alla prima chiamata a MoveNext(), la funzione crea un'esplosione e imposta l'oggetto corrente su "new WaitForSeconds (1.5f)".

Il motore Unity3d ispeziona questo oggetto, vede che è un'istanza della classe speciale "WaitForSeconds", quindi mette l'enumeratore su una coda in attesa e non chiederà il secondo elemento fino a che non sia passato 1,5 secondi. Nel frattempo, molti fotogrammi saranno renderizzati e l'esplosione verrà riprodotta.

Dopo 1,5 sec, Unity eseguirà l'estrazione di enumeratore dalla coda e richiamerà nuovamente MoveNext(). La seconda parte della tua funzione verrà eseguita ora, ma non riuscirà a generare un secondo oggetto. MoveNext() restituirà false per indicare che non è riuscito a ottenere un nuovo elemento, che è il segnale di Unity3d per eliminare questo enumeratore. Il Garbage Collector recupererà la memoria ad un certo punto nel tempo.

Come detto: un sacco di compilatore e magia Unity3d sta succedendo. Finché ti ricordi che la tua funzione sarà messa in attesa fino al prossimo frame ad ogni dichiarazione di rendimento, saprai abbastanza per trarre beneficio da quelle funzioni speciali.

+0

Scrivere quel codice in un progetto C# standalone e osservare l'assembly .net generato in ILSPy è un rapido eyeopener su come funziona tutta la magia. –

5

Se yield viene usato una volta, è come se si stesse restituendo un IEnumerator con un elemento, questo è il motivo per cui si ha l'impressione che non iterare.

È un uso piuttosto strano della parola chiave yield, è difficile capire perché è stato implementato così senza vedere l'intero contesto.

+1

Viene chiamato in una funzione chiamata "StartCoroutine": Unity.StartCoroutine (DestroyShip), penso che questo sia molto specifico per il motore di Unity http://unity3d.com –

+0

Riferimento: http://unity3d.com/support/documentation /ScriptReference/MonoBehaviour.StartCoroutine.html –

+1

Vedo ... beh, questo ha sicuramente senso nel contesto del motore Unity. –

1

È preferibile restituire IEnumerable anziché IEnumerator in quanto è più flessibile e facile da usare. È ancora meglio usare IEnumerable<T>. Il rendimento restituito è solo zucchero sintattico per l'implementazione di un blocco iteratore, il compilatore genera il codice pertinente in quanto è essenzialmente un piatto caldaia standard che può essere facilmente generato in modo programmatico. Si utilizzano in genere dei rendimenti quando si vuole fare una serie di azioni singole apear essere una raccolta, ad es .:

public IEnumerable<Thing> GetThings() 
{ 
    yield return GetNextThing(); 
} 
+1

Tranne che il motore di Unity3d è l'unico che utilizza quel componente IEnumerator e che il motore richiede che sia un oggetto IEnumerable. Si può sostenere che IEnumrable è migliore, ma si deve anche giocare con le design desissions di Unity3d. – Sjoerd

Problemi correlati