Tworzenie gry w C# z użyciem silnika Ogre - cz.6
12.05.2011 - Mateusz Osowski
Implementujemy konkretne drzewoPosiadamy już wszystkie typy składowych drzewa, więc możemy przejść do przekuwania koncepcji w rzeczywisty kod. Drzewo postaci utworzymy w osobnej klasieCharacterDecTree :
Klasa dziedziczy z DecTree.FirstSucc , ponieważ to alternatywa będzie korzeniem. Pole PickItemDownSeq będzie referencją na poddrzewo zawierające akcję podnoszenia przedmiotu nadające się do wielokrotnego użytku. TurnJob będzie zadaniem obracania postaci, jeżeli będzie taka potrzeba.
Na sekwencję podnoszenia przedmiotu zgodnie z rysunkiem składać się będą cztery podczynności. Intensywnie korzystamy z notacji lambda, dzięki czemu zapis drzewa jest zwięzły i stosukowo czytelny. Wszystkie funkcje podawane drzewu przyjmują argument typu Character , który my nazywamy ch .
Podczas wykonywania czynności 2 (schylania się po przedmiot) zabezpieczamy się na wypadek zniknięcia przedmiotu wstawiając dodatkową asercję (2.1), której niepowodzenie spowoduje porażkę całej czynności. Dzięki zgodności typów asercji wykorzystujemy funkcję Character->bool Czynność trzecia - dodanie przedmiotu do ekwipunku i usunięcie jego modelu ze sceny podobnie jak pierwsza wykonuje się w trybie natychmiastowym, zadanie zwraca prawdę (3.1), co będzie oczywiście zinterpretowane jako zakończenie zadania. Czynność czwarta - podnoszenie się - trwa do końca animacji. Posiadając wszystkie podczynności, możemy ułożyć je w sekwencję:
Sama akcja PickItemDownSeq jest niezależna od rozkazów. My chcielibyśmy dać możliwość wydania polecenia podniesienia rzeczy graczowi. W tym celu wprowadzamy do klasy postaci flagę PickItemOrder , którą będziemy ustawiać poprzez HumanController . Zadanie cleanUp czyszczące rozkaz jest wykorzystywane dwukrotnie - pierwszy raz, w przypadku podnoszenie przedmiotu się nie powiedzie (1) i drugi raz w przeciwnym wypadku (2).
Zadanie obracania postaci będzie wykorzystywane wielokrotnie. Wprowadzimy do klasy postaci pole TurnDelta , które będziemy ustawiać analogicznie do flagi PickItemOrder - w obiekcie HumanController . Zauważmy, że sprawdzenie wartości TurnDelta można przenieść do asercji i całe zadanie obudować w alternatywę. Stosując drzewa mamy pełną dowolność. Poprzez spychanie warunków do zadań możemy uprościć drzewo, jednak możemy również łatwo stracić modularność.
Ten węzeł bedzie drugą odnogą głównej alternatywy. Do sterowania chodzeniem postacią posłużymy się odpowiednią flagą ( MoveOrder ), tak jak we wcześniejszych sytuacjach. Podczas chodzenia obracanie się jest dozwolone, więc do ciągu zadań dodajemy TurnJob (1). Wykonanie zadania kończy się natychmiastowo sukcesem, ponieważ chcemy, by postać natychmiastowo reagowała na puszczenie strzałki.
Ostatnia alternatywa - bezczynność nie posiada już żadnych asercji. Aby nie powodować zgiełku w kodzie, do określania, czy postać może rozmawiać posłużymy się flagą-pozwoleniem TalkPerm , którą będziemy czyścić co klatkę. Podobnie robimy z ekwipunkiem. Wykonujemy też obrót.
Pozostaje tylko dodać trzy gałęzie alternatywy:
Klasa postaciZ klasyCharacter usuwamy wszystko, co jest związane ze starą implementacją stanów - pola enum , mapę przejść, metody zależne od stanów - i dodajemy pola-flagi oraz drzewo:
Zwróćmy uwagę na fakt, że z drzewem nie wiążemy żadnego wewnętrznego stanu, więc może być polem statycznym.
Metoda Update() uległa dużemu uproszczeniu - większość zadań została przeniesiona do drzewa.
Ta metoda również została skrócona. Pozbyliśmy się TrySwitchState() , wszelkie warunkowe czynności są teraz określane strukturą drzewa.
HumanControllerKlasę kontrolera również musimy dostosować do nowego rozwiązania. Zmiany następują tylko w metodzieHandleMovement() , jak można się domyślić we fragmentach kodu odpowiadających za sterowanie postacią:
Sytuacja również się uprościła. W przypadku obrotów nie obliczamy już żadnego kwaternionu, a określamy, jak bardzo chcielibyśmy obrócić postać ustawiając pole postaci. Używamy flagi TalkPerm do sprawdzenia, czy można rozpocząć rozmowę i flagi InventoryPerm do zezwalania na otwieranie ekwipunku.
Można odetchnąć... i skompilowaćUkończyliśmy wprowadzanie drzewa działań do naszego silnika gry. Teraz mamy zielone światło do implementacji sztucznej inteligencji. Nie określamy już wprost przejść między stanami prowadzących do pewnego efektu. Zamiast tego posługujemy się opisem celów wraz z ich pod-celami, które mogą być realizowane na różne sposoby dzięki przeszukiwaniu z nawrotami. Już w swojej obecnej, prostej formie nowa metoda jest skalowalna. Dobrym pomysłem na przyszłość byłoby stworzenie prostego języka opisującego tego typu drzewa lub napisanie wtyczki do graficznego edytora grafów typu yEd.W ramach ćwiczeń spróbuj zaprojektować język opisu drzew. Dla ambitnych: Możesz też pokusić się o implementację wczytywania takiego opisu albo generowania kodu C# na jego podstawie. O tworzeniu własnego języka poczytać możesz tu: Własny język programownia Aktualny kod źródłowy(2 ocen) |
Copyright © 2008-2010 Wrocławski Portal Informatyczny
design: rafalpolito.com