Z jakich elementów składa się postać w Unreal Engine?
Jak wspomniano w sekcji Tryb gry / Stan gry, struktura rozgrywki Unreal Engine pomaga zrozumieć, jak działają postacie w tym silniku. Koncepcyjnie gracz składa się z pionka reprezentującego fizyczną obecność w świecie gry oraz kontrolera, który określa zachowanie. To rozdzielenie jest przydatne zarówno w przypadku sztucznej inteligencji, jak i replikacji sieciowej. Dla porównania hierarchia wygląda następująco:
Zacznijmy od zaimportowania zasobów, utworzenia pionka i przejścia do kontrolera gracza. W przypadku Parrot naszym bohaterem sterowanym przez gracza będzie Kapitan Barbarossa (odwiedź stronę internetową Quaternius, aby pobrać zestaw Pirate Kit udostępniony przez Quaternius).
Importowanie zasobów graficznych
W wybranym narzędziu do modelowania 3D wyeksportuj siatkę/animacje do formatu zgodnego z silnikiem. W tym przypadku twórcy użyli formatu .fbx. Zasoby Barbarossy znajdują się w folderze Content/Assets/Quaternius/PirateKit/Characters/Barbarossa. Plik .fbx można zaimportować, klikając prawym przyciskiem myszy, by wywołać menu kontekstowe lub przeciągając i upuszczając plik w przeglądarce zawartości. Pojawi się okno ustawień importu pliku .fbx. Wybierz odpowiednie opcje importu. W tym przypadku upewnij się, że nie jest wybrany żaden istniejący szkielet i że opcja import animations (importuj animacje) jest zaznaczona. Konieczne jest również utworzenie nowego materiału atlasu, który może być indeksowany przez siatkę. Materiał ten może być ponownie wykorzystany w różnych zasobach zestawu Pirate Kit. Na tym etapie będziesz mieć trzy pliki oraz wszelkie animacje zawarte w pliku .fbx.
Dla przejrzystości pliki sekwencji animacji znajdują się w folderze Animations (Animacje) obok wspomnianych trzech plików:
Szkielet, siatka szkieletowa i zasoby fizyki
Szkielet to hierarchia służąca do definiowania kości (czasami nazywanych przegubami) w siatce szkieletowej. Kości te powinny odpowiadać sztywnemu modelowi w narzędziu do modelowania 3D. Szkielety mogą być ponownie wykorzystane w różnych siatkach szkieletowych, o ile sztywne modele są kompatybilne. Zauważysz również utworzony zasób fizyki. Zasoby fizyki definiują fizykę i kolizje używane przez siatkę szkieletową. Zawierają one sztywne korpusy i ograniczenia dla symulacji. Możesz mieć tylko jeden zasób fizyki na siatkę szkieletową i można je warunkowo włączać lub wyłączać. Możesz dostosować zasoby fizyki podczas ich importowania lub utworzyć nowe zasoby na podstawie siatki szkieletowej.
Blueprint animacji
Teraz, gdy masz już gotową siatkę szkieletową, możesz rozpocząć animowanie za pomocą blueprintu animacji. Blueprint animacji jest podobny do Mecanim Animation System (system animacji Mecanim) w Unity. Graf animacji w Blueprintach animacji powinien wyglądać znajomo i działać w podobny sposób. Oba systemy posiadają warunkowy przepływ logiki, który może przekształcać stany animacji w końcową pozę wyjściową. W folderze utwórz nowy blueprint animacji z menu kontekstowego i wybierz szkielet. Nasz blueprint animacji będzie nosił nazwę ABP_Captain_Barbarossa i będzie znajdował się w folderze Content/Blueprints/Player.
Po lewej stronie okna inspektora Blueprintu znajdują się dwa grafy: graf zdarzeń (Event Graph) i graf animacji (Animation Graph). Graf animacji to maszyna stanów, która kontroluje końcową pozę wyjściową. W grafie zdarzeń można zdefiniować dowolną logikę związaną z animacją. W przeciwieństwie do Unity, gdzie trzeba przekazywać dane do komponentu Mecanim, blueprinty animacji mogą pobierać potrzebne dane w określonym zdarzeniu. Na przykład, możesz chcieć sprawdzić prędkość postaci i sterować animacją na podstawie tej wartości. Dobrym sposobem na to jest użycie węzła Event Blueprint Initialize Animation w grafie zdarzeń, pobranie Owning Actor (aktora będącego posiadaczem) i zapisanie jego komponentu ruchu w zmiennej. Następnie, przy każdym tyknięciu BlueprintThreadSafeUpdateAnimation, można sprawdzić prędkość bezpośrednio z komponentu ruchu. Zmienne mogą być również współdzielone między grafem zdarzeń a grafem animacji.
Skonfigurowany blueprint animacji dla Barbarossy warto przeanalizować samodzielnie. Dwukrotne kliknięcie w węzły pozwala zobaczyć, jak skonfigurowano maszyny stanów, stany i reguły stanów dla końcowej pozy wyjściowej. Istnieją również przykłady wykorzystania odtwarzacza sekwencji i przestrzeni mieszania. Analogicznie, graf zdarzeń pokazuje, w jaki sposób animacja jest sterowana przez dane postaci – zależność ta stanie się bardziej zrozumiała w dalszej części artykułu.
Pionek
Teraz, gdy masz już siatkę szkieletową, którą można animować, możesz zacząć tworzyć pionek, który będzie używany w trybie gry. Twórcy zdecydowali się wykorzystać domyślną postać Unreal Engine, która jest klasą pochodną domyślnej klasy pionka C++. Klasa postaci posiada komponent ruchu postaci, który jest szczególnie przydatny w tej grze i stanowi kolejną klasę, na której można budować.
Zarówno pionki gracza, jak i przeciwnika mają pewne wspólne funkcjonalności, takie jak punkty życia, otrzymywanie obrażeń i śmierć. Ta funkcjonalność jest udostępniona poprzez utworzenie klasy C++ AParrotCharacterBase, która dziedziczy po klasie ACharacter. Klasa ta stanowi dobrą podstawę do rozbudowy, ponieważ pionki przeciwnika i gracza różnią się pod względem zachowania.
Następnie utwórz klasę C++ AParrotPlayerCharacter, która będzie obsługiwać logikę specyficzną dla gracza. Na koniec utwórz blueprint, który będzie obsługiwał wizualną reprezentację twojej postaci. Blueprint Parrot znajduje się w BP_ParrotPlayerCharacter w folderze Content/Blueprints/Player. Hierarchia dziedziczenia wygląda następująco:
Po otwarciu zasobu BP_ParrotPlayerCharacter, po lewej stronie okna edytora, w inspektorze komponentów, powinny pojawić się pewne komponenty. Komponent siatki (Mesh Component) to miejsce, w którym można ustawić zasób siatki szkieletowej (Skeletal Mesh Asset), a także blueprint animacji pod opcją klasy animacji (Anim Class). Jeśli siatka nie jest prawidłowo ustawiona, można dostosować wartości przekształcenia, aby dopasować ją do komponentu kapsuły. Powinno to wyglądać mniej więcej tak:
Następnie można przypisać domyślną klasę pionka do blueprintu w zasobie trybu gry.
Teraz w grze dostępny jest pionek z animacją!
Kontroler gracza
Dzięki gotowemu do użycia pionkowi możesz teraz stworzyć kontroler gracza. Kontrolery gracza zasadniczo reprezentują wolę gracza. Podczas tworzenia gry warto zastanowić się, gdzie zdefiniować zdarzenia wejściowe. W przypadku prostych gier, w których pionki się nie zmieniają, pionek w zupełności wystarczy. Jednak gdy zachowanie zaczyna być bardziej złożone, lepszym rozwiązaniem jest kontroler gracza. Pionki są przejściowe i mogą być przejmowane lub zwalniane przez kontrolery gracza. Pionki mogą być generowane lub niszczone, podczas gdy kontrolery gracza pozostają w grze przez cały czas.
W kontrolerze gracza nie jest potrzebna żadna logika C++, więc wystarczy utworzyć blueprint, który wykona niezbędne czynności. BP_ParrotPlayerController znajduje się w folderze Content/Blueprints/Player. Hierarchia dziedziczenia wygląda następująco:
Wewnątrz blueprintu zwróć uwagę na węzły zdarzeń wejściowych, które prowadzą do podstawowej logiki rozgrywki. Te zdarzenia wejściowe są ściśle powiązane z Enhanced Input. Więcej informacji na temat konfiguracji Enhanced Input zawiera dokumentacja Enhanced Input. Zapisując w pamięci podręcznej pionek postaci gracza w grze Parrot na początku gry, możemy użyć go później do wywołania podstawowych funkcji ruchu. Na przykład Add Movement Input (Dodaj dane wejściowe ruchu), Jump (Skok) i StopJump (Zatrzymaj skok).
Czas na test! W skrypcie blueprintu trybu gry ustaw nową klasę kontrolera gracza, a następnie sprawdź, czy kontroler działa w grze:
Z tego menu rozwijanego w edytorze poziomów sprawdź, czy wybrany jest właściwy tryb gry:
Na koniec upewnij się, że mapa zawiera aktora PlayerStart (pkt. startowy gracza) oraz kamerę, którą można wyświetlić. Po naciśnięciu przycisku „Play” powinna pojawić się postać, którą można się poruszać i animować:
Komponent ruchu postaci w grze Parrot
Klasa bazowa ACharacter posiada wiele wbudowanych przydatnych funkcjonalności. Przykładem może być komponent aktora UCharacterMovement. Komponent ruchu postaci obsługuje całą logikę poruszania się postaci w świecie. Obsługuje chodzenie, bieganie, spadanie, pływanie, latanie oraz niestandardowe tryby ruchu. Zdecydowanie warto zapoznać się z nim we własnym zakresie, aby zrozumieć podstawy działania ruchu postaci, a także przekonać się o potężnych możliwościach komponentów aktorów.
W przypadku Parrot podstawowy komponent ruchu postaci nie zapewniał takich wrażeń z gry, jakich oczekiwali twórcy. Stanowił jednak solidną podstawę do dalszego rozwoju. Klasa C++ UParrotCharacterMovementComponent jest pochodną klasy UCharacterMovementComponent. Następnie możemy ustawić komponent ruchu w komponencie ruchu CDO w BP_ParrotPlayerCharacter w następujący sposób:
Ale co właściwie robi komponent ruchu Parrot? Zasadniczo komponent ten pozwala zdefiniować skok w stylu platformówki poprzez nadpisanie funkcji związanych ze skakaniem z podstawowego komponentu ruchu. Korzystając z pewnych wartości, które można dostosować do projektu, w momencie skoku można zmodyfikować skalę grawitacji postaci gracza i prędkość skoku, aby osiągnąć najwyższy punkt skoku w określonym czasie. Po osiągnięciu najwyższego punktu można następnie zacząć stosować skalę grawitacji spadku. Jeśli gracz przedwcześnie zwolni przycisk skoku, można zastosować mnożnik, aby zwiększyć grawitację.
W rezultacie skok „wydaje się” bardziej zbliżony do tego, czego można oczekiwać w platformówce, w przeciwieństwie do zwykłego zastosowania impulsu w osi Z.
Opierając się na ciężkiej pracy podstawowego komponentu ruchu postaci, można skupić logikę komponentu ruchu Parrot wyłącznie na skoku. Warto również przyjrzeć się wartościom w panelu Inspector dla BP_ParrotPlayerCharacter w komponencie ruchu, aby uzyskać pełny obraz tego, jak wszystkie elementy komponentu ruchu współpracują ze sobą. Logika ruchu może zostać rozszerzona, aby dopasować się do różnych przypadków użycia.