Cele lekcji
Po zakończeniu tej lekcji…
- Będziesz wiedzieć, czym jest klasa statyczna i jak ją utworzyć.
- Dowiesz się, kiedy używać i nie używać klas statycznych.
Czym jest klasa statyczna?
Dotychczas tworzyliśmy wyłącznie klasy publiczne, czyli klasy widoczne w obrębie całego programu. Jednak, aby korzystać z takich klas wymagane jest utworzenie obiektu klasy. który następnie jest opracowywany.
Natomiast klasa statyczna jest zawsze dostępna w programie i nie wymaga tworzenia obiektu na jej podstawie. Co więcej, z klasy statycznej nie można utworzyć obiektu; on tam po prostu jest.
W klasach statycznych przechowuje się zasoby współdzielone (zmienne i metody), ponieważ w ich przypadku istnieją tylko pojedyncze ich wystąpienia i wszystkie składniki w programie będą korzystać z tego pojedynczego wystąpienia, udostępnionego zasobu metod i zmiennych.
Do czego potrzebna jest klasa statyczna?
W naszej grze musimy przechowywać pewne dane, które będą używane w kilku miejscach w programie.
Jest to na przykład lista lokacji, typy przedmiotów, potworów i tak dalej. Te dane nie ulegają zmianie, ponieważ listy uzupełnimy na początku gry i nie będziemy ich modyfikować. List będziemy używać w wielu miejscach w naszej grze.
Klasa statyczna pozwala na przechowywanie wszystkich tych informacji i swobodne ich udostępnianie.
Do czego jeszcze można użyć klasy statycznej?
Klasa statyczna (i zmienna statyczna) mogą przechowywać wartości takie jak np. licznik systemowy.
Przypuśćmy, że potrzebny jest program generujący niepowtarzalne, kolejne liczby.
Aby go opracować, można użyć klasy statycznej NumberAssigner z metodą statyczną GetNextNumber, która pozwala śledzić zmienną statyczną _nextNumber.
namespace Engine { public static class NumberAssigner { static int _nextNumber = 0; public static int GetNextNumber() { _nextNumber = (_nextNumber + 1); return _nextNumber; } } }
Po rozpoczęciu programu wartością _nextNumber jest 0.
Wywołanie metody GetNextNumber powoduje dodanie wartości 1 do _nextNumber i zwrócenie wartości (w tym przypadku wartości 1) w programie. Kolejne wywołanie metody GetNextNumber powoduje dodanie wartości 1 do _nextNumber (czyli wynikiem jest 2) i zwrócenie wartości 2 w programie.
Jakie problemy może powodować klasa statyczna?
W przypadku metod i zmiennych statycznych niekiedy użytkownik nie chce dysponować zasobem udostępnionym, a wymagane jest raczej to, aby każdy użytkownik dysponował własną kopią obiektu lub zmiennej.
Tworzymy grę dla jednego gracza, więc problemy z wartościami statycznymi nie będą nas dotyczyć.
Jeśli jednak musielibyśmy stworzyć interfejs użytkownika dla gry wieloosobowej w trybie online, to jednocześnie mogłoby w nią grać kilka osób.
Przyjmijmy, że aktualną liczbę punktów wytrzymałości przechowujemy w zmienne statycznej CurrentHitPoints.
Gdy gracz A jest atakowany, program odejmuje obrażenia i zmienia wartość CurrentHitPoints. Jeśli jednak inny gracz wykonał inną akcję w grze (zaatakował potwora lub uleczył się miksturą), to ze względu na fakt, iż dysponujemy tylko pojedynczą, statyczną, udostępnioną zmienną CurrentHitPoints, użyta zostanie wartość pochodząca od gracza A, a nie rzeczywista, bieżąca wartość punktów wytrzymałości.
Tutaj widać, dlaczego klasy statyczne i zmienne mogą być niebezpieczne. Przechowując wartość w zmiennej statycznej, upewnij się, że rzeczywiście chcesz ją udostępniać wszystkim użytkownikom.
Wypełnianie świata gry w klasie statycznej
Wiesz już, do czego służą klasy statyczne i zmienne, więc stworzymy klasę World, w której umieścimy listy wszystkich składników gry: lokacji, przedmiotów, potworów i zadań.
Z tej klasy będziemy tylko odczytywać informacje, więc gdy uzupełnimy ją na początek wartościami, to będziemy ją mogli określić jako statyczną.
Etap 1: Uruchom aplikację Visual Studio i otwórz swoje rozwiązanie.
Etap 2: Utwórz nową klasę, klikając prawym przyciskiem myszy projekt Engine i wskazując pozycje Add i Class. Nadaj klasie nazwę World.cs.
Etap 3: Skopiuj kod klasy stąd: https://gist.github.com/ScottLilly/803df1021fbc404b38f5
Co robi ten kod?
Klasa World ma przechowywać wszystkie zasoby istniejące w grze. Będziemy w niej umieszczać potwory zamieszkujące określone lokacje, łupy otrzymywane po pokonaniu potworów i tak dalej. Dzięki niej będziemy również wiedzieć, w jaki sposób lokacje łączą się ze sobą, tworząc mapę.
Co zawiera klasa World?
Wiersze 11-14: Zmienne listy statycznej. Działają one podobnie do właściwości klasy. Uzupełnimy je wraz z innymi elementami w grze, a następnie inne składniki programu będą z nich odczytywać informacje.
Wiersze 16-42: Stałe. Stałe wyglądają jak zmienne i zachowują się jak one, ale jednym aspektem się różnią: możemy zmieniać ich wartości.
Ich nazwy są łatwe do zapamiętania dla człowieka, więc będziemy z nich korzystać, aby nie musieć zapamiętywać liczbowych ID różnych obiektów w grze. Gdybyśmy bez stałych chcieli stworzyć wielkiego potwora-pająka, to musielibyśmy pamiętać, że jego ID to 2. Stałe pozwalają nam używać ID potwora zapisanego w postaci MONSTER_ID_GIANT_SPIDER.
Jeśli nie rozumiesz, jak to działa, to wszystko wyjaśni się w lekcji, w której postać zacznie się poruszać po świecie gry i trzeba będzie określać lokacje, zadania, potwory i przedmioty.
Wiersze 44-50: To konstruktor statyczny. Możesz pomyśleć „Chwileczkę! Nie można utworzyć wystąpienia klasy statycznej, więc czemu ma konstruktora? Przecież do tego służy konstruktor — do tworzenia wystąpień obiektu!”
W przypadku klasy statycznej kod konstruktora jest uruchamiany po raz pierwszy, gdy dowolny element w klasie zostanie użyty. Więc jeśli uruchomimy grę i chcemy wyświetlić informacje o bieżącym położeniu gracza i usiłujemy uzyskać dane z klasy World, zostanie uruchomiona metoda konstruktora, a lista zostanie uzupełniona.
W konstruktorze wywoływane są cztery metody uzupełniające różne listy. Osobne metody nie są wymagane, a cały kod zapisany w wierszach 48-169 można umieścić w konstruktorze. Jednak rozbicie metod ułatwia ich odczytywanie i dalsze rozwijanie.
Wiersze 52-173: Tu znajdują się metody pozwalające tworzyć obiekty w grze i dodawać je do list statycznych.
Wywołanie metody Add() w zmiennej listy lub właściwości pozwala dodać obiekt do tej listy.
Przyjrzyjmy się wierszowi 54. Tutaj do listy Items dodajemy nowy obiekt Weapon. Po wywołaniu new Weapon() konstruktor klasy Weapon zwraca obiekt Weapon z podanymi parametrami. Wszystko to dzieje się w ramach Items.Add(), więc obiekt ten jest dodawany do listy Items.
Takie działanie nazywa się „zagnieżdzaniem” (inlining), ponieważ kilka operacji (tworzenie wartości i dodawanie jej do listy) jest wykonywanych w ramach jednego wiersza.
W wierszu 68 tworzymy nowy obiekt Monster i zapisujemy go w zmiennej rat. W wierszach 69 i 70 dodajemy elementy do właściwości (list) dla PotentialLootItems, czyli przedmioty, które mogą wypaść ze szczura. Następnie w wierszu 80 zmienną rat dodajemy do listy statycznej Monsters.
Wiersze 175-225: Te metody pozwalają nam uzyskać wartości z list statycznych. Dysponujemy bezpośrednim dostępem do list w wierszach 7-10, ponieważ są one publiczne, jednak takie metody opakowujące (wrapper) ułatwiają nam pracę.
Przekazujemy ID obiektu, który chcemy pobrać z listy (korzystając ze stałych w wierszach 16-42). Metoda sprawdza każdą pozycję na liście (pętla foreach) i określa, czy podany ID odpowiada ID obiektu. Jeśli tak, obiekt jest zwracany użytkownikowi. Jeśli po sprawdzeniu całej listy nie zostaną znalezione żadne odpowiadające wyniki (co nigdy nie powinno się zdarzyć), metoda zwraca wynik „null” – czyli nic.
Podsumowanie
Udało nam się zapełnić „świat” gry. Metod statycznych z tej klasy statycznej możemy używać w dowolnym miejscu w programie i otrzymywać wymagane informacje na temat obiektów w świecie gry.
Łącza do tej lekcji