Cykl życia skryptu, czyli w jakiej kolejności wykonują się funkcję domyślne dla Unity3d.

Znajomość podstawowych funkcji Unity3d jest wiedzą bardzo przydatną, wręcz podstawową. Warto wiedzieć, czy daną rzecz należy wykonać w funkcji Start, czy Update, czy może Awake. Aby wiedzieć, jak rozsądnie zarządzać pamięcią i mocą obliczeniową komputera, trzeba rozsądnie wykonywać obliczenia. Tzn. po co liczyć coś w każdej klatce, kiedy możemy to policzyć raz na początku. Czasami błędne umiejscowienie kodu jest źródłem wielu problemów.

Mógłbym się tutaj produkować i tworzyć swoje interpretację wielu funkcji, ale to byłoby nieco bez sensu. Dokładałbym ideologię do czegoś, co klarownie jest wyjaśnione w dokumentacji. Ale dokumentacja jest po Angielsku. Dlatego nie będzie tu cudów i fajerwerków, a porządnie przetłumaczony kawałek dokumentacji Unity3d, na temat znaczenia i zastosowania podstawowych funkcji, oraz ich kolejności wykonywania. Dokumentacja nazywa tę funkcję wydarzeniami (eventami), więc możemy stosować też taką nomenklaturę.

Warto tutaj zaznaczyć, że poza kolejnością wykonywania samych funkcji, mamy jeszcze kolejność kompilowania skryptów w języku C#, ale o tym już pisałem.

Edytor

  • Reset – Tutaj mamy tylko jedną funkcję, która wykonuje się w momencie dołączenie skryptu do obiektu w edytorze, oraz gdy uruchamiamy funkcję Reset ( (po kliknięciu w kółko zębate w okienku inspektor).
    Do czego wykorzystać? Np. możemy od razu przypisać zmiennej jakiś obiekt, który znajdziemy w scenie. Nie obciążamy sobie gry, a po dodaniu skryptu do wielu obiektów, nie musimy każdorazowo przeciągać tej zmiennej.

Pierwsze załadowanie sceny

Te funkcję wykonują się raz dla każdego obiektu, w momencie gdy ładuje się scena.

  • Awake – Funkcja, która wykonuje się jako pierwsze, zaraz gdy obiekt zostanie utworzony w scenie. Jednak wykonuje się tylko, gdy obiekt jest aktywny. Jeśli nie jest, to funkcja jest pierwszą, która się wykona, gdy zostanie aktywowany. Aktywacja to: gameObject.SetActive(true); Obiekt musi być aktywny, ale skrypt nie musi być włączony! Funkcja Awake wykonuje się jako pierwsza, gdy wszystkie obiekty zostaną umieszczone na scenie (więc można znajdować inne obiekty). Ale sama kolejność wykonywania Awake jest losowa (czyli nie przewidzimy, Awake którego obiektu wykona się jako pierwsze). Zaleca się stosowanie Awake jako konstruktora, ale bez tworzenia relacji między obiektami.
  • OnEnabled – Tutaj można się domyślić, że funkcja wykona się, gdy komponent zostanie aktywowany. Funkcja wykona się tylko dla komponentów aktywnych. Włączenie to: myLight.enabled = true; Ogólnie włączenie komponentu wykonuje się zaraz na samym początku, czyli gdy w scenie pojawi się instancja obiektu. Wykonuje się przed funkcja start i w przeciwieństwie do niej, wykonuje się przy każdej aktywacji komponentu. Dokładniej będzie to omówione, przy funkcji Start.
  • OnLevelWasLoaded – Funkcja wykonuje się gdy nowa scena została załadowana (wykorzystano Application.LoadLevel).

Przed aktualizacją pierwszej klatki

  • Start – Wykonuje się… przed aktualizacją pierwszej klatki. Jedyna uwaga: instancja obiektu musi być włączona, a skrypt aktywny. Gdy Awake traktujemy jako konstruktor, to funkcje Start powinniśmy wykorzystać do utworzenia relacji skryptu z pozostałymi.

Do przetestowania różnicy między Awake, Start i OnEnabled, można zastosować taki skrypt:

using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour 
{
	void Awake()
	{
		Debug.Log ("Awake");
	}

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

	void OnEnable()
	{
		Debug.Log ("Enabbled");
	}
}

Po wrzuceniu tego skryptu do np. obiektu kamery, należy się pobawić tymi opcjami:

Panel Inspector
Panel Inspector

Jak to powinno działać?

  • Jeżeli oba będą włączone, wyświetlą się wszystkie komunikaty
  • Jeśli włączony będzie skrypt (czerwone), a wyłączony obiekt (zielone), to po uruchomieniu gry nie wyświetli się nic. Ale gdy w trakcie gry uruchomimy obiekt, wyświetlą się wszystkie komunikaty.
  • Jeśli włączony będzie obiekt (zielone), a wyłączony skrypt (czerwone), to po uruchomieniu gry, wyświetli się komunikat Awake. Gdy w trakcie gry włączony skrypt (czerwone), to pojawi się komunikat ze Start i OnEnable.
  • Jeżeli w trakcie gry będziemy włączać i wyłączać skrypt (czerwone), to przy każdym włączeniu skryptu, pojawi się jedynie komunikat z OnEnable. Komunikat Start, pokazuje się tylko raz przy pierwszej aktywacji skryptu.

Pomiędzy klatkami

  • OnApplicationPause – Wykonany po klatce w której wykryto pauzowanie. Jednak pozwala się wykonać jeszcze jednej klatce, aby umożliwić wyświetlenie grafiki informującej o tym, że gra została zatrzymana. Zastosowanie? Oczywiste.

Kolejność aktualizacji klatek

Dokumentacja Unity3d zaleca, aby większość wykonywanych poleceń wrzucać do funkcji Update. Jednak są też inne opcje:

  • FixedUpdate – Po tym evencie wykonują się wszystkie obliczenia fizyki i inne update’y. Nie zależy ona od liczby klatek. Może się wykonać kilka razy w ciągu jednej klatki, gdy mamy niski framerate, albo ani razu w trakcie klatki, gdy framerate jest wysoki. Gdy tworzymy system poruszania postaci wewnątrz tej funkcji, nie trzeba wykorzystywać funkcji Time.delataTime, ponieważ FixedUpdate działa na zegarze, który nie jest zależny od liczby klatek. Wykonuje się w stałych odstępach czasu. Tą funkcję powinniśmy wykorzystywać, gdy chcemy przeprowadzać obliczenia związane z fizyką.
  • Update – Funkcja wykonująca się raz na klatkę. Tutaj powinniśmy umieszczać większość naszego kodu.
  • LateUpdate – Również wykonuje się raz na klatkę i robi to zaraz po zakończeniu funkcji Update. Gdy wykonuje się LateUpdate, mamy pewność, że wszystkie obliczenia wewnątrz Update zostały wykonane. Praktyczne zastosowanie? Gdy robimy system śledzenia jakiejś postaci za pomocą kamery, możemy ruch postaci wykonać w funkcji Update, a podążanie kamery wewnątrz LateUpdate. Dzięki temu, kamera namierzając pozycję postaci, ma pewność, że ta już zakończyła swój ruch.

Rendering

  • OnPreCull – Wywołana zanim kamera dokona Cullingu sceny. Czym jest Culling? Określeniem jakie obiekty mają być w kamerze widoczne, a jakie nie.
  • OnBecameVisible/OnBecameInvisible – Wywołana gdy obiekt staje się widoczny/niewidoczny w polu widzenia jakiejś kamery. (Dowolnej).
  • OnWillRenderObject: Wykonywany jednorazowo dla każdej kamery, jeśli obiekt jest widoczny w jej zasięgu.
  • OnPreRender – Wywołane zanim kamera zacznie renderować scenę.
  • OnRenderObject – Wywołana gdy standardowe procedury związane z renderowaniem sceny zostaną skończone. W tym miejscu można wykorzystać bibliotekę GL, albo Graphics.DrawMesh do dorysowania czegoś.
  • OnPostRender – Wywołuje się, gdy kamera zakończy renderowanie sceny.
  • OnRenderImage: Wykonuje się gdy zakończy się renderowanie sceny, aby umożliwić postprodukcję wyświetlanego obrazu. ImageEffects.
  • OnGUI – Wywoływana kilka razy na klatkę w odpowiedzi na zdarzenia (events) graficznego interfejsu użytkownika (GUI). Najpierw wykonuje zdarzenia przemalowania (Repaint), następnie odpowiada na zdarzenia klawiatury i myszki.
  • OnDrawGizmos – Wykorzystywana do ryzowania Gizm w widoku sceny, do wizualizacji. Czyli wykorzystywane w procesie produkcyjnym, ale nie w finalnej grze.

Coroutines (współprogramy)

Współprogramy to funkcję, które posiadają zdolność do wstrzymania swojej pracy na pewien czas. Normalnie są one wykonywane po tym gdy wykona się funkcja Update.

  • yield – Współprogram będzie kontynuowany gdy wszystkie funkcję Update zostaną wykonane w następnej klatce.
  • yield WaitForSeconds – Kontynuuje po pewnym czasie, oraz gdy wszystkie funkcję Update dla trwającej klatki zostaną wykonane.
  • yield WaitForFixedUpdate – Kontynuuje gdy FixedUpdate zostanie wywołany dla wszystkich skryptów.
  • yield WWW – Kontynuuje gdy pobieranie ze strony WWW zostanie zakończone.
  • yield StartCoroutine – Kolejkuje współprogramy i oczekuje aż wywołana funkcja się zakończy. (Przy wywołaniu StartCoruitine podajemy jako parametr funkcję, która ma się wykonać jako współprogram).

Gdy obiekt jest usuwany

  • OnDestroy – Funkcja wywołana po funkcji Update, dla ostatniej klatki istnienia obiektu. Obiekt może przestać istnieć przez użycie funkcji Destroy, albo przez zamknięcie obecnie wyświetlanej sceny. Wykorzystywany jako destruktor obiektu.

W czasie wyłączania

Poniższe funkcję są wywoływane dla każdego aktywnego obiektu w scenie.

  • OnApplicationQuit – Funkcja wywoływana dla wszystkich obiektów, zanim aplikacja zostanie zamknięta. W edytorze odpowiada temu wyjście z trybu grania, a w przypadku webplayera odpowiada za to moment zamknięcia okienka z grą.
  • OnDisable – Wywołane gdy obiekt posiadający skrypt stanie wyłączony (disabled) albo nieaktywny (inactive).

Diagram cyklu życia skryptu

Cykl Życia skryptu w Unity3d
Cykl Życia skryptu w Unity3d

I to tyle. Myślę, że ta wiedza pozwoli wam tworzyć bardziej optymalny kod.