W Unity nie ma czegoś takiego jak główna pętla gry, w której moglibyśmy po kolei zdefiniować co ma się dziać w każdej klatce.

Silnik oparty jest na zdarzeniach, które obsługiwane są w konkretnej kolejności. Znajomość reguł tego mechanizmu jest kluczowa jeśli chcemy tworzyć gry przyjazne dla procesora i pamięci docelowego urządzenia.

Oto lista zdarzeń dla klasy MonoBehaviour – głównej klasy jeśli chodzi o cykl życia naszej gry.

Start gry, załadowanie sceny:

Awake() – metoda, o której pisałem już tutaj. Wołana jest jak tylko zainicjowany zostanie obiekt, który zawiera komponent ze skryptem przeciążającym tą metodę. NIE zostanie jednak wywołana, jeśli wspomniany obiekt jest nieaktywny.

OnEnable() – Wołana w momencie aktywowania obiektu (GameObject), jeśli obiekt od samego początku jest na scenie i jest aktywny, metoda zostanie wywołana zaraz po Awake().

OnLevelWasLoaded() – Metoda była używana do poinformowania obiektów, że scena została w pełni załadowana. Obecnie jest oznaczona jako przestarzała (deprecated) i najprawdopodobniej zostanie usunięta w przyszłych wersjach silnika.

Przed pierwszą klatką:

Start() – wołana dla wszystkich aktywnych obiektów będących na scenie od początku. Naturalnie jeśli zainicjujemy jakiś obiekt już w trakcie gry, to nie mamy szans, aby metoda była wykonana przed pierwszą klatką gry. Ale zostanie ona wykonana przed pierwszym wywołaniem Update() dla tego obiektu.

Pomiędzy klatkami:

OnApplicationPause() – przed każdą następną klatką, silnik sprawdza czy gra została zastopowana. Nie chodzi tu jednak o pause w sensie logiki gry, ale np przełączenie się w telefonie na inną aplikację. W takiej sytuacji silnik zawsze wykona jeszcze jedną klatkę, co daje możliwość wyświetlenia na ekranie np. ekranu pausy.

Na tym przykładzie widać, że po wykonaniu kodu z OnApplicationPause(), został jeszcze raz wykonany kod z Update()

void Update(){
    Debug.Log ("Update");
}

void OnApplicationPause(){
   Debug.Log ("OnApplicationPause");
}

W trakcie każdej klatki:

FixedUpdate() – o tym również pisałem. Jest to metoda, w której powinniśmy umieszczać wszelkie interakcje z fizyką (np aplikowanie siły). Ponieważ jest ona wykonywana ze stałym interwałem, może zostać wykonana zero, raz lub wiele razy w trakcie trwania jednej klatki.

Update() – Można powiedzieć, że główna funkcja gry. Wykonywana raz na każdą klatkę. To w niej powinniśmy obsługiwać większość interakcji z graczem.

LateUpdate() – również wołane raz na klatę, ale dopiero gdy Update() zakończy swój przebieg. Daje nam to możliwość skorzystania z ewentualnych obliczeń wykonywanych w Update(). Dokumentacja podaje, że sztandarowym wykorzystaniem tej metody jest poruszanie kamerą, która śledzi gracza.

Tutaj możemy zobaczyć, że FixedUpdate() zdążył wykonać się kilkukrotnie, zanim wykonał się Update() i zaraz po nim LateUpdate()

void Update(){
   Debug.Log ("Update, czas wykonania klatki = " + Time.deltaTime + " czas symulacji = " + Time.time );
}

void FixedUpdate(){
   Debug.Log ("FixedUpdate, czas wykonania klatki = " + Time.deltaTime + " czas symulacji = " + Time.time );
}

void LateUpdate(){
   Debug.Log ("LateUpdate, czas wykonania klatki = " + Time.deltaTime + " czas symulacji = " + Time.time );
}

Renderowanie obrazu:

OnPreCull() – nie wiem jak przetłumaczyć na polski ‚Culling’ (słownik diki mówi, że może to być zbieranie informacji),  jest to proces, który determinuje, jakie obiekty są widoczne dla kamery gry. Metoda OnPreCull() jest wywołana przed uruchomieniem tego procesu.

OnBecameVisible()/OnBecameInvisible() – wołane odpowiednio gry obiekt zacznie i przestanie być widoczny dla dowolnej kamery.

OnPreRender() – metoda wołana tuż przed faktycznym wyrysowaniem obiektów na scenie.

OnRenderObject() – wołane gdy renderowanie sceny jest już zakończone, ale w tym miejscu możemy jeszcze wyrenderować własne obiekty przy pomocy biblioteki GL.

OnPostRender() – wołane gdy renderowanie sceny jest zakończone, ale tej metody możemy użyć tylko w skrypcie przypiętym jako komponent kamery.

OnRenderImage() – wołane po zakończeniu renderowania sceny, w tej metodzie możemy nałożyć post-processing. (rozmycie, głębia itp).

OnGUI() – metoda może być wołana wielokrotnie w czasie jednej klatki – raz, dla każdego zdarzenia związanego z GUI, np kliknięcia w przycisk.

OnDrawGizmos() – znowu brakuje mi słowa, diki tłumaczy „Gizmo” jako „ustrojstwo” 🙂 W tej metodzie możemy wyrysować pomocnicze obiekty, które widoczne będą tylko w edytorze, w podglądzie sceny. Typowym „gizmosem” jest prostokąt obrazujący zakres collidera.

Tutaj króliczkowi dorysowuję prostokąt obrazujący zasięg jego „stóp”, przy pomocy tego prostokąta wykrywam kolizje z platformami.

void OnDrawGizmos(){
    Gizmos.DrawWireCube(feet.position, new Vector3(feetCheckWidth,feetCheckHeight));
}

Korutyny:

Korutyny (coroutines) to temat, który raczej wymaga osobnego wpisu, ale dla zachowania porządku umieszczam tutaj opis związanych z nimi metod. W dużym uogólnieniu, korutyny pozwalają na wstrzymanie wykonywania metody, do póki nie zostanie spełniony określony warunek. Można w ten sposób zrobić typowego waita na kilka sekund.

yield – kontynuuj dopiero po zakończeniu wszystkich metod Update() w następnej klatce.

yield WaitForSeconds() – kontynuuj po tym jak minie podana ilość sekund. Liczone od momentu zakończenia wszystkich metod Update() w tej klatce.

yield WaitForFixedUpdate() – kontynuuj po zakończeniu następnego cyklu wołania metod FixedUpdate().

yield WaitForEndOfFrame() – kontynuuj po zakończeniu bieżącej klatki.

yield WWW – WWW to klasa do podstawowej obsługi połączeń poprzez URL. Yield WWW czeka, aż klasa zakończy pobieranie zasobu, do którego prowadzi URL.

yield StartCouroutine() – startuje korutynę.

Tutaj yield wykorzystany do wstrzymania wykonania metody na jedną sekundę. Nie zatrzymuje to symulacji, w między czasie normalnie wykonują się inne metody jak np. Update().

void Awake(){
  StartCoroutine(YieldTest ());
}

private IEnumerator YieldTest(){
  Debug.Log("Before yield, time = " + Time.time);
  yield return new WaitForSeconds(1);
  Debug.Log("After yield, time = " + Time.time);
}

Niszczenie obiektu:

OnDestroy() – wywołana na zakończenie ostatniej klatki życia obiektu. Obiekt można zniszczyć na dwa sposoby – poprzez zamknięcie sceny lub wołając Destroy(obiekt).

Wyjście z gry:

OnApplicationQuit() – metoda wołana tuż przed wyjściem z gry.

OnDisable() – Wołana za każdym razem gdy obiekt przestanie być aktywny lub przed jego zniszczeniem (wtedy będzie wywołana wcześniej niż OnDestroy() ).

Poniżej rezultat zakończenia symulacji dla danego kodu. Warto zwrócić uwagę, że OnApplicationQuit() wykona się najpierw, dopiero potem wykonywane są OnDisable() i OnDestroy():

void OnApplicationQuit(){
    Debug.Log ("OnApplicationQuit");
}

void OnDisable(){
    Debug.Log ("OnDisable");
}

void OnDestroy(){
    Debug.Log ("OnDestroy");
} 

Kolejność wołania tych wszystkich metod jest bardzo ładnie zobrazowana na diagramie z oficjalnej dokumentacji Unity.