czwartek, 31 marca 2016

Zmienne statyczne

Opadanie żetonów

Gdy ostatnio pisałem o zmianach w tworzeniu żetonów zapomniałem omówić 2 elementy: żetony pojawiają się teraz wysoko nad planszą (poza widokiem kamery) i przypisywany jest do nich stworzony przeze mnie skrypt który sprawia, że opadają one na planszę. Spadające żetony sprawiają, że gra wydaje się być odrobinę żywsza, choć nie ma to wpływu na samą mechanikę rozgrywki. Zawartość skryptu:

using UnityEngine;
using System.Collections;

public class FallingScript : MonoBehaviour {

 void Update () {

  if (transform.localPosition.z < -0.01f) {
   transform.Translate (0, 0, Time.deltaTime * 7.5f / 0.25f);
  } 

  if (transform.localPosition.z >= -0.01f) {
   transform.localPosition = new Vector3 (0, 0, -0.01f);
   Destroy (GetComponent<FallingScript> ());
  }
 }

}

Funkcja Update jest funkcją która aktywuje się automatycznie co klatkę gry, a więc jest wykonywana cały czas. Pierwszą rzeczą którą wykonujemy w tej funkcji jest sprawdzenie, czy pozycja lokalna (względem ojca obiektu) na osi z jest mniejsza od -0.01f (im mniejsza wartość, tym bardziej obiekt jest oddalony od planszy w kierunku kamery). Jeśli warunek if jest prawdziwy, to oznacza, że żeton jest nad planszą i musi dalej przemieszczać się w jej kierunku. Z pomocą przychodzi nam funkcja transform.Translate, która do lokalnej pozycji obiektu dodaje wartości podanych przez nas argumentów - jeśli tylko trzeci argument będzie niezerowy, to obiekt przesunie się względem osi z. Warto tu pamiętać, że odstęp pomiędzy klatkami bywa różny - jeśli jakiś gracz ma 2x mniej klatek na sekundę niż drugi gracz, to u takiej osoby funkcja update będzie się wykonywała 2x rzadziej. Aby żetony spadały z tą samą szybkością u wszystkich graczy korzystamy z funkcji Time.deltaTime, która zwraca czas pomiędzy klatkami - jeśli jakiś gracz będzie miał opóźnienia, to dzięki tej funkcji obiekt przesunie się o większą wartość. Do tego czas pomiędzy klatkami mnożymy przez drogę którą żeton ma pokonać, oraz dzielimy przez czas w którym obiekt ten ma pokonać tą odległość. W rezultacie czas opadania żetonu będzie zawsze taki sam.

Drugi warunek sprawdza, czy lokalna pozycja na osi z jest większa od -0.01f, co oznaczałoby, że żeton dotarł już do planszy, lub znalazł się pod nią. Jeśli do tego dojdzie, to lokalna pozycja obiektu jest ustalana na konkretną wartość, aby mieć pewność, że będzie znajdował się minimalnie nad polem, a następnie nasz skrypt zostaje usunięty z obiektu, pozbywając się zbędnego balastu.

Tutaj pojawia się jednak kolejne przemyślenie: droga którą musi pokonać żeton będzie wykorzystywana w różnych miejscach kodu, np. w trakcie tworzenia żetonów. Podobnie wygląda sytuacja z czasem opadania żetonów: ich umiejętności będą aktywowane dopiero wtedy, gdy opadną na planszę. Gdybyśmy teoretycznie użyli tej wartości w 100 miejscach, a następnie doszlibyśmy do wniosku, że lepsza byłaby minimalnie inna wartość, to stracilibyśmy relatywnie sporo czasu poprawiając nasz kod. Aby temu zapobiec warto jest stworzyć zmienne, do których będziemy mogli odwołać się ze wszystkich skryptów.

Zmienne statyczne

Zmienne statyczne są to zmienne do których możemy się odwołać w każdej chwili z dowolnej sceny, dowolnego obiektu i dowolnego skryptu. Aby to zrobić, musimy tylko podać nazwę klasy w której się ta zmienna znajduje, a po kropce napisać nazwę tej zmiennej. Od razu można zauważyć, że jest to wygodniejsze niż odwoływanie się do zmiennych niestatycznych, ponieważ nie musimy podawać nazwy obiektu w którym znajduje się skrypt z tą zmienną. Dzieje się tak dlatego, że zmienna ta jest wspólna dla wszystkich instancji tej klasy. Co więcej nasz skrypt może nie być przypisany do żadnego obiektu na scenie a i tak będziemy mieli dostęp do jego zmiennych statycznych. Warto też pamiętać, że zmienne te są deklarowane przy starcie programu i pozostają niezmienione podczas zmiany sceny. To wszystko sprawia, że zmienne statyczne są okropnym narzędziem do przechowywania informacji o stanie rozgrywki (wyobraźcie sobie, że w jakiejś grze wy i wasi przeciwnicy macie wspólne zdrowie, które nie odnawia się po ponownym wczytaniu rozgrywki), ale za to mogą świetnie się spisywać w roli ustawień gry (np. głośności muzyki).

Utworzyłem więc nowy skrypt:

 using UnityEngine;
 using System.Collections;

 public class GameData : MonoBehaviour {

  static public float FallingDuration = 0.25f;
  static public float FallingDiestance = 7.5f;
 }


Zmienne statyczne można tu rozpoznać po wyrazie static znajdującym się przed deklaracją zmiennej. Skoro dodałem do gry dwie nowe zmienne, mogę wstawić je we wcześniej wymienionym skrypcie zamiast stałych wartości:

  transform.Translate (0, 0, Time.deltaTime * GameData.FallingDiestance / GameData.FallingDuration);

Stworzenie skryptu z danymi/opcjami gry jest świetną okazją, aby przenieść do niego informacje o żetonach o których pisałem kilka wpisów temu. Jest ku temu kilka dobrych powodów:
- informacje te są zawsze takie same,
- chcemy mieć do nich dostęp z wielu skryptów i wielu scen (nie tylko ze scen online klienta i serwera, ale także z poziomu menu),
- nie ma potrzeby wczytywania tych samych informacji kilkukrotnie w trakcie działania programu.

Tutaj pojawia się jednak nowy problem: dotychczas jeśli chcieliśmy, aby jakiś fragment kodu był wykonany tylko na starcie czegoś, to umieszczaliśmy go w funkcji Start, a cały skrypt był doczepiany do jakiegoś obiektu na scenie. My jednak chcemy, aby skrypt nie był doczepiany do żadnego obiektu. W takiej sytuacji pomocny będzie tzw. konstruktor, czyli funkcja która wywołuje się automatycznie po utworzeniu się klasy, w naszym przypadku konstruktor statyczny będzie się wykonywał tylko podczas wczytywania gry. Aby zadeklarować konstruktor statyczny, musimy najpierw napisać wyraz static, a następnie musimy napisać nazwę klasy w której ten konstruktor się znajduje. Przykład:

 using UnityEngine;
 using System.Collections;

 public class GameData : MonoBehaviour {

  static public float FallingDuration = 5.25f;
  static public float FallingDiestance = 7.5f;

  static GameData() {
   FallingDuration = 0.25f;
  }
 }


Po uruchomieniu gry wartość zmiennej FallingDuration będzie ustawiona na 0.25f, mimo iż przy deklaracji zmiennej była podana inna wartość, co oznacza, że nasz konstruktor zadziałał. Jeśli ktoś chciałby się upewnić, że uruchomi się on tylko raz, to można dodać do klasy zmienną statyczną typu int o wartości 0, którą będziemy zwiększać o 1 w konstruktorze. Bez względu na to ile razy zmienimy scenę w grze wartość tego inta będzie zawsze wynosiła 1, co rozwieje wszelkie wątpliwości.

Brak komentarzy:

Prześlij komentarz