Przeciwnicy
W silniku Unreal Engine postacie niezależne (NPC) i przeciwnicy są zazwyczaj skonfigurowani bardzo podobnie do gracza, ponieważ przeciwnik posiada kontroler i pionek. W rzeczywistości przeciwnicy i gracz mają pewne wspólne funkcjonalności w klasie bazowej AParrotCharacterBase. W grze Parrot drzewa zachowań służą do sterowania zachowaniem przeciwników, więc do tworzenia kontrolerów przeciwników używana jest klasa AI Controller (kontrolera SI). Zaczniemy od szablonu animacji, ponieważ ta konfiguracja jest wspólna dla wszystkich przeciwników.
Blueprinty animacji przeciwnika
Szablony blueprintu animacji
W przypadku wielu siatek szkieletowych, które mają identyczne wymagania dotyczące animacji, można utworzyć tak zwany szablon blueprintu animacji (patrz: Animation Blueprint Linking). W tym przypadku istnieją cztery różne typy przeciwników – szkielet bez głowy, szkielet, sharky (człowiek-rekin) i rekin-boss. Ponieważ wszystkie mają identyczny szkielet i konfigurację animacji, jeden szablon implementujący graf animacji i graf zdarzeń może być wykorzystany dla wszystkich z nich. Możesz zobaczyć, jak ten szablon jest skonfigurowany tutaj: Blueprints (Blueprinty) > Enemy (Przeciwnik) > EnemyBase > ABT_EnemyBase. Wygląda bardzo podobnie do blueprintu animacji gracza, ponieważ szablon jest skonfigurowany w niemal identyczny sposób jak ten blueprint animacji.
Blueprinty animacji
W tym przypadku cała implementacja znajduje się w szablonie, natomiast same animacje są nieobecne, ponieważ szablon nie odwołuje się do konkretnego szkieletu. Każdy z przeciwników ma własny blueprint animacji pochodzący z szablonu, a każdy z nich ma animacje tego szkieletu umieszczone w zastąpieniach grafów animacji. Przykład można znaleźć tutaj: Blueprints (Blueprinty) > Enemy (Przeciwnik) > HeadlessSkeleton > ABP_Enemy_HeadlessSkeleton.
Poniżej wybrano odpowiednie animacje dla siatki szkieletowej bezgłowego szkieletu dla każdego stanu animacji.
Pionek przeciwnika
Przeciwnicy w grze Parrot mają wiele wspólnych funkcjonalności z graczem, w szczególności punkty wytrzymałości i podobne funkcje (takie jak śmierć). Tę wspólną implementację można zobaczyć w AParrotCharacterBase. W przypadku implementacji specyficznej dla przeciwników mamy podklasę AParrotEnemyCharacterBase. Obsługuje ona wszystkie implementacje działania systemu patrolowania, walki i nie tylko. Więcej informacji na temat działania systemu walki zawiera dokumentacja Walka w Parrot.
W tej konfiguracji sprawdzanie trafień i obrażeń w walce jest wykonywane na przeciwniku za pomocą implementacji sposobu wyzwalania woluminów w klasie bazowej blueprintu, którą można znaleźć tutaj: Blueprints (Blueprinty) > Enemy (Przeciwnik) > EnemyBase > BP_EnemyCharacter_Base.
Implementacja ta jest wykonywana tutaj, a nie w natywnej klasie C++, ponieważ woluminy wyzwalania muszą być różne dla każdego przeciwnika ze względu na różne kształty, rozmiary i złożoność ich siatek. Jest to konieczne, ponieważ wolumin wyzwalania, który jest dziedziczony, nie może być modyfikowany w klasie pochodnej.
Kontroler SI przeciwnika
Jak wspomniano wcześniej, przeciwnicy są kontrolowani za pomocą drzew zachowań, więc istnieje klasa bazowa pochodząca od AIController w AParrotEnemyAIControllerBase. Tutaj zobaczysz różne BlueprintImplementableEvents używane do wysyłania danych do tablicy, które będą wykorzystywane przez drzewa zachowań.
Możesz zobaczyć, w jaki sposób kontroler SI przekazuje dane do drzewa zachowań i jak wykrywa obecność gracza w Blueprints (Blueprinty)> AI (SI)> EnemyBase > BP_EnemyController_Base.
Tworzenie SI za pomocą drzew zachowań
Unreal Engine oferuje potężną i elastyczną infrastrukturę do tworzenia sztucznej inteligencji przy użyciu drzew zachowań. Skrócony poradnik zawierający podstawowe informacje o konfiguracji i kilka przykładowych zachowań można znaleźć w dokumencie Behavior Tree Quick Start Guide. Wyniki przedstawione w tym poradniku służą jako podstawa do tworzenia sztucznej inteligencji przeciwników oraz wprowadzania zmian w celu skonfigurowania wymaganych zachowań.
Mamy dwa zadania drzewa zachowań: Blueprints (Blueprinty) > AI (SI) > EnemyBase > BTT_FindNextPatrol oraz Blueprints (Blueprinty) > AI (SI) > EnemyBase > BTT_AttackPlayer, które zapewniają podstawowe funkcje potrzebne do patrolowania i atakowania gracza. Dostępna jest jedna wspólna tablica, Blueprints (Blueprinty) > AI (SI) > EnemyBase > BB_Enemy_Base, ponieważ infrastruktura obsługująca wszystkie funkcje jest obecna w każdym przeciwniku. To implementacja drzewa zachowań decyduje, które funkcje zostaną użyte do dostosowania zachowań przeciwników.
Każdy z przeciwników ma inną konfigurację drzewa zachowań, dzięki czemu każdy z nich zachowuje się w inny sposób. Możesz zapoznać się z wszystkimi czterema w następujących lokalizacjach:
Blueprints (Blueprinty) > AI (SI) > HeadlessSkeleton > BT_HeadlessSkeleton
Blueprints (Blueprinty) > AI/Skeleton (SI/Szkielet) > BT_Skeleton
Blueprints (Blueprinty) > AI (SI) > Sharky > BT_Sharky
Blueprints (Blueprinty) > AI (SI) > BossShark > BT_Boss_Shark
Tworzenie systemu punktów nawigacyjnych patrolu z możliwością wyznaczania tras
W przypadku przeciwników w grze Parrot nie chcemy tej samej funkcjonalności patrolowania, którą przedstawia poradnik dotyczący drzewa zachowań, gdzie sztuczna inteligencja przeciwnika co 4 sekundy losowo wybiera punkt nawigacyjny w określonym promieniu. Chcemy, żeby przeciwnicy aktywnie podążali trasą patrolowania tam i z powrotem, jak w wielu innych grach platformowych.
W tym celu w Parrot stworzono własny system, który zapewnia potrzebną funkcjonalność. Składa się on z komponentu UParrotEnemyPatrolRigComponent, który można umieścić w scenie w celu utworzenia patrolu. Wykorzystuje on domyślne podobiekty klasy do tworzenia instancji krzywej sklejanej. Krzywa sklejana służy do tworzenia punktów nawigacyjnych patrolu, dwóch woluminów wyzwalania (używanych do wyzwalania zachowań SI) oraz wizualizatora dostępnego tylko w edytorze, który rysuje kolejność punktów nawigacyjnych. Kolejność punktów nawigacyjnych pozwala zobaczyć kierunek ścieżki patrolu podczas edycji. Szczegóły implementacji w C++ można znaleźć w UParrotEnemyPatrolRigComponent.
Ta implementacja to komponentem, dzięki któremu do dowolnego aktora w scenie można dołączyć system patrolowania. Pozwala to umieścić system patrolowania na poruszającym się obiekcie, dzięki czemu patrol pozostanie poprawnie umiejscowiony w przestrzeni lokalnej. Do umieszczania systemu patrolowania, który nie jest przypięty do istniejącego aktora w scenie, służy aktor AParrotEnemyPatrolRigActor, którego można umieścić w dowolnym miejscu w scenie – spawnuje on komponent UParrotEnemyPatrolRigComponent jako domyślny podobiekt na sobie.
Ta implementacja umożliwia wybranie przeciwnika, który będzie spawnowany w czasie wykonywania, aby podążał ścieżką patrolu. Proces ten wykorzystuje funkcję Unreal Engine o nazwie deferred actor spawning (odroczone spawnowanie aktora), która pozwala na spawnowanie aktora w dwóch etapach – w pierwszym etapie tworzony jest obiekt aktora bez wykonywania żadnej inicjalizacji AActor, takiej jak BeginPlay. Daje to możliwość wykonania wszelkich konfiguracji lub ustawień niezbędnych dla aktora przed jego inicjalizacją. Po wykonaniu tej konfiguracji wywołuje się drugi etap, aby sfinalizować spawnowanie i zainicjować aktora. Jest to wykonywane dla systemu patrolowania, aby po spawnowaniu aktora przeciwnika krzywa sklejana patrolu i woluminy wyzwalania mogły zostać przekazane do aktora, tak aby można je było przetworzyć podczas inicjalizacji aktora, a sekwencja patrolowania mogła rozpocząć się automatycznie.
Kod odpowiedzialny za odroczone spawnowanie aktora można wyświetlić w UParrotEnemyPatrolRigComponent. Podobna funkcja istnieje w blueprintach, gdzie można oznaczyć właściwości aktora blueprintu jako Expose on Spawn (Odkryj w momencie spawnu), co pozwala na przekazanie argumentów do węzła blueprintu spawnu, które są ustawiane przed zainicjowaniem aktora.
Na przykładzie poniżej można zobaczyć, jak jeden z takich systemów patrolowania jest skonfigurowany w Maps (Mapy) > Level_1 > Level_1 z dwupunktowym systemem patrolowania, zaczynającym się w punkcie 0 po prawej stronie. Ten system patrolowania jest skonfigurowany tak, aby spawnować BP_EnemyCharacter_Skeleton.
Poniżej znajduje się menu przełączające Show Flag (Flaga wyświetlania), które pozwala zidentyfikować punkty nawigacyjne systemu patrolowania, aby ułatwić ich identyfikację podczas edycji. Większość pól wyboru flag wyświetlania została zaznaczona. Szczegóły dotyczące implementacji tej funkcji można zobaczyć w komponencie wizualizatora dostępnym tylko w edytorze, UParrotPatrolRigDebugVisualizer.