Feindliche Charaktere
In der Unreal Engine sind NPCs und Feinde in der Regel sehr ähnlich wie der Spieler eingerichtet, da ein Feind einen Controller und einen Pawn hat. Tatsächlich haben die Feinde und der Spieler einige gemeinsame Funktionen in einer gemeinsamen Basis-Klasse, AParrotCharacterBase. Für das „Parrot“-Spiel werden die Verhaltensbäume verwendet, um das Verhalten der Gegner zu steuern. Daher wird die KI-Controller-Klasse verwendet, um unsere gegnerischen Controller zu erstellen. Wir beginnen mit dem Animationsblueprint, da diese Konfiguration für alle Feinde gleich ist.
Die Animationsblueprints des Feindes
Animationsblueprint-Vorlagen
Für Fälle, in denen du mehrere Skelett-Meshs hast, die ansonsten identische Anforderungen an die Animation haben, kannst du eine so genannte Animationsblueprint-Vorlage erstellen (siehe Animation Blueprint Linking). In diesem Fall gibt es vier verschiedene Typen von Gegnern – ein kopfloses Skelett, ein Skelett, einen Haifisch und einen Boss-Hai. Da sie alle ein identisches Skelett und eine identische Konfiguration für die Animation haben, kann eine Vorlage, die das Animationsdiagramm und den Event Graph implementiert, für alle wiederverwendet werden. Wie diese Vorlage eingerichtet wird, kannst du hier sehen: Blueprints > Enemy > EnemyBase > ABT_EnemyBase. Er sieht dem Animationsblueprint des Spielers sehr ähnlich, da eine Vorlage fast genauso eingerichtet ist wie der Animationsblueprint.
Animationsblueprints
In diesem Fall befindet sich die gesamte Implementierung in der Vorlage, während die Animationen selbst nicht vorhanden sind, da die Vorlage keine Referenzen auf ein bestimmtes Skelett enthält. Jeder Feind besitzt einen eigenen Animationsblueprint, der von der Vorlage abgeleitet ist, und jeder Feind hat die Animationen des Skeletts im Slot der Animationsdiagramm-Overrides. Ein Beispiel dafür kannst du hier sehen: Blueprints > Enemy > HeadlessSkeleton > ABP_Enemy_HeadlessSkeleton.
Nachfolgend haben wir für das Skelett-Mesh des kopflosen Skeletts für jeden Animationszustand die passenden Animationen ausgewählt.
Der feindliche Pawn
Die feindlichen Charaktere von „Parrot“ haben viele Funktionen mit dem Spieler gemeinsam, insbesondere die Trefferpunkte und ähnliche Funktionen (wie den Tod). Diese gemeinsame Implementierung ist in AParrotCharacterBase zu sehen. Für die Feind-spezifische Implementierung haben wir eine Subklasse AParrotEnemyCharacterBase. Diese handhabt die gesamte Implementierung des Patrouillensystems, wie der Kampf funktioniert und mehr. Mehr darüber, wie der Kampf funktioniert, erfährst du in der Dokumentation Kampf in „Parrot“.
In dieser Konfiguration werden die Kampftreffer- und -Verletzungsprüfungen beim Feind durchgeführt, wobei eine Implementierung zum Auslösen von Volumen in der Blueprint-Basis-Klasse hier zu finden ist: Blueprints > Enemy > EnemyBase > BP_EnemyCharacter_Base.
Diese Implementierung wird hier vorgenommen und nicht in der nativen C++-Klasse gehandhabt, da die Trigger-Volumen für jeden Feind aufgrund der unterschiedlichen Formen, Größen und Komplexität seiner Meshs unterschiedlich sein müssen. Dies ist notwendig, da ein geerbtes Trigger-Volumen in der abgeleiteten Klasse nicht geändert werden kann.
Der feindliche KI-Controller
Wie bereits erwähnt, werden die Feinde mit Verhaltensbäumen gesteuert, daher gibt es in AParrotEnemyAIControllerBase eine von AIController abgeleitete Basis-Klasse. Hier siehst du eine Reihe von BlueprintImplementableEvents, die zum Senden von Daten an die Tafel verwendet werden, um von den Verhaltensbäumen genutzt zu werden.
Wie der KI-Controller Daten an den Verhaltensbaum weitergibt und wie er die Spieler-Präsenz erkennt, kannst du in Blueprints > KI > EnemyBase > BP_EnemyController_Base sehen.
Erstellen von KI mit Verhaltensbäumen
Die Unreal Engine bietet eine leistungsstarke und flexible Infrastruktur zum Erstellen von KI unter Verwendung von Verhaltensbäumen. Eine Schnellstart-Anleitung zur Einrichtung des grundlegenden Boilerplate-Codes mit einigen Verhaltensweisen findest du in der Behavior Tree Quick Start Guide. Die Ergebnisse dieser Anleitung werden als Grundlage für die feindliche KI verwendet und um Änderungen vorzunehmen, um die erforderlichen Verhaltensweisen einzurichten.
Wir haben zwei Verhaltensbaum-Aufgaben, Blueprints > AI > EnemyBase > BTT_FindNextPatrol und Blueprints > AI > EnemyBase > BTT_AttackPlayer, um die grundlegende Funktionalität für Patrouillen und Angriffe auf den Spieler bereitzustellen. Es gibt eine gemeinsame Tafel, Blueprints > AI > EnemyBase > BB_Enemy_Base, da die Infrastruktur zur Unterstützung aller Funktionen bei jedem Feind vorhanden ist. Es liegt an der Implementierung des Verhaltensbaums zu entscheiden, welche Funktionalitäten verwendet werden sollen, um das Verhalten der Feinde anzupassen.
Jeder der Feinde hat eine andere Konfiguration des Verhaltensbaums, so dass sich jeder von ihnen anders verhält. Du kannst dir alle vier an den folgenden Positionen ansehen:
Blueprints> AI > HeadlessSkeleton > BT_HeadlessSkeleton
Blueprints > AI/Skeleton > BT_Skeleton
Blueprints > AI > Sharky > BT_Sharky
Blueprints > AI > BossShark > BT_Boss_Shark
Erstellen eines bearbeitbaren Wegpunkt-Systems für Patrouillen
Für die Feinde möchte „Parrot“ nicht die gleiche Funktionalität wie in der Anleitung zum Verhaltensbaum gezeigt, bei dem die feindliche KI alle 4 Sekunden zufällig einen navigierbaren Punkt innerhalb eines Radius auswählt. Gewünscht sind Feinde, die aktiv einer Patrouillenroute hin und her folgen, wie in vielen anderen Plattformspielen.
Dafür hat „Parrot“ ein eigenes System erstellt, das die benötigten Funktionen bietet. Dieses besteht aus der UParrotEnemyPatrolRigComponent, die in der Szene platziert werden kann, um eine Patrouille zu erstellen. Sie verwendet Standard-Teilobjekte der Klasse, um einen Spline zu instanzieren. Der Spline wird für die Erstellung der Patrouillenwegpunkte, zwei Volumen (zum Auslösen des KI-Verhaltens) und einen Editor-exklusiven Visualisierer verwendet, der die Reihenfolge der Wegpunkte zeichnet. Die Reihenfolge der Wegpunkte erlaubt es, zum Zeitpunkt der Bearbeitung die Richtung des Patrouillenweges zu sehen. Die Details der C++-Implementierung findest du in UParrotEnemyPatrolRigComponent.
Bei dieser Implementierung handelt es sich um eine Komponente, mit der ein Patrouillen-Rig an einen beliebigen Actor in einer Szene angekoppelt werden kann. Dies erlaubt es, ein Rig auf einem sich bewegenden Objekt zu platzieren und bedeutet, dass die Patrouille korrekt im lokalen Raum platziert bleibt. Um ein Patrouillen-Rig zu platzieren, das nicht an einen bestehenden Actor in einer Szene angekoppelt ist, gibt es den AParrotEnemyPatrolRigActor, einen Actor, den du überall in einer Szene platzieren kannst und der eine UParrotEnemyPatrolRigComponent als Standard-Teilobjekt auf sich selbst spawnen lässt.
Diese Implementierung erlaubt es, einen Feind auszuwählen, der zur Laufzeit spawnen soll, um dem Patrouillenweg zu folgen. Dieser Prozess nutzt eine Funktion der Unreal Engine, die als deferred actor spawning bezeichnet wird und das Spawnen eines Actors in zwei Stufen erlaubt – in der ersten wird das Actor-Objekt konstruiert, ohne dass eine AActor-Initialisierung wie BeginPlay ausgeführt wird. Dies gibt dir die Möglichkeit, jede Konfiguration vorzunehmen, die der Actor haben muss, bevor er initialisiert wird. Nachdem du diese Konfiguration durchgeführt hast, rufe die zweite Phase auf, um den Spawnvorgang abzuschließen und den Actor zu initialisieren. Dies geschieht für das Patrouillen-Rig, damit beim Spawnen des feindlichen Actors die Splines und Auslöser-Volumen in den Actor geleitet werden können, damit sie während der Initialisierung des Actors verarbeitet werden und die Patrouillensequenz automatisch beginnen kann.
Du kannst den Code, der das zurückgestellte Spawnen des Actors durchführt, in UParrotEnemyPatrolRigComponent sehen. Eine ähnliche Funktion gibt es in Blueprint, wo du Eigenschaften eines Blueprint-Actors als Bei Spawnen bereitstellen kennzeichnen kannst. Dies erlaubt es dir, Argumente an den Spawn-Knoten des Blueprints zu übergeben, die vor der Initialisierung des Actors gesetzt werden.
In der Abbildung unten siehst du ein Beispiel dafür, wie eines dieser Patrouillen-Rigs in Maps > Level_1 > Level_1 mit einem Zwei-Punkt-Patrouillen-Rig eingerichtet wird, beginnend bei Punkt 0 rechts. Dieses Patrouillen-Rig ist so eingerichtet, dass es ein BP_EnemyCharacter_Skeleton spawnen kann.
Darunter befindet sich ein Menü zum Umschalten von Flagge anzeigen, um die Wegpunkte des Patrouillen-Rigs zu kennzeichnen, damit du sie während der Bearbeitung leichter identifizieren kannst. Die meisten Kontrollkästchen zum Anzeigen von Flaggen wurden aktiviert. Details zur Implementierung dieser Funktion findest du in einer Editor-exklusiven Visualisierungskomponente, UParrotPatrolRigDebugVisualizer.