Gegner-KI
Teil des Abschnitts Spielarchitektur in Unity der Unity-Journey von Coddy. Lektion 39 von 64.
Das Gehirn eines Gegners ist eine Zustandsmaschine mit einem Zustand pro Verhalten. Dieses Skelett läuft zwischen seinen Posten hin und her, verfolgt einen Spieler, der sich ihm auf weniger als 6 Einheiten nähert, und schwingt einmal pro Sekunde mit seinem Schwert, wenn es nahe genug ist. Jeder Zustand entscheidet selbst, wie er verlassen wird, und das Skelett behält die Helfer, die sie gemeinsam verwenden:
public class Skeleton : MonoBehaviour
{
public Transform player;
public Transform[] posts;
public float speed = 2f;
public StateMachine machine = new StateMachine();
public IState patrol, chase, attack;
void Start()
{
patrol = new PatrolState(this);
chase = new ChaseState(this);
attack = new AttackState(this);
machine.ChangeState(patrol);
}
void Update() { machine.Tick(); }
public float PlayerDistance() { return Vector3.Distance(transform.position, player.position); }
public void WalkTo(Vector3 target)
{
target.y = transform.position.y;
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
}
public class PatrolState : IState
{
private Skeleton skel;
private int next;
public PatrolState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() < 6f) { skel.machine.ChangeState(skel.chase); return; }
Vector3 post = skel.posts[next].position;
post.y = skel.transform.position.y;
skel.WalkTo(post);
if (skel.transform.position == post) next = (next + 1) % skel.posts.Length;
}
}
public class ChaseState : IState
{
private Skeleton skel;
public ChaseState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
float d = skel.PlayerDistance();
if (d > 6f) { skel.machine.ChangeState(skel.patrol); return; }
if (d < 1.5f) { skel.machine.ChangeState(skel.attack); return; }
skel.WalkTo(skel.player.position);
}
}
public class AttackState : IState
{
private Skeleton skel;
private float cooldown;
public AttackState(Skeleton skel) { this.skel = skel; }
public void Enter() { cooldown = 0f; }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() > 1.5f) { skel.machine.ChangeState(skel.chase); return; }
cooldown -= Time.deltaTime;
if (cooldown <= 0f) { Debug.Log("Swing!"); cooldown = 1f; }
}
}Die Zustände werden einmal erstellt und bleiben so lange bestehen wie das Skelett, daher bleiben ihre Felder zwischen den Besuchen erhalten. next von PatrolState ist auch nach einer Verfolgung noch gesetzt, und das Skelett läuft weiter zu dem Posten, auf den es zuging. Enter setzt nur zurück, was neu beginnen soll: Das Enter von AttackState setzt cooldown auf 0, sodass der erste Schwerthieb erfolgt, sobald das Skelett den Spieler erreicht.
Die gesamte Bewegung läuft über WalkTo, hier MoveTowards auf dem Transform, wodurch das Skelett durch Wände läuft. Um das Skelett stattdessen mit einem Rigidbody zu bewegen, setzt WalkTo die Geschwindigkeit des Körpers, und das Skelett aktualisiert die Maschine in FixedUpdate, wo Time.deltaTime dem Physik-Zeitschritt entspricht. Die drei Zustände ändern sich nicht: Sie geben an, wohin es gehen soll, der Besitzer gibt an, wie.
Aufgabe
MittelGib dem Ritter in KnightStates.cs die Zustände PatrolState und ChaseState, denen jeweils der Ritter übergeben wird.
- Patrouille: Gehe zu
waypoints[next]; wenn du näher als0.3bist, erhöhenextzyklisch und behalte den Wert zwischen den Besuchen bei. Wenn du näher alssightRangebist, verfolge den Spieler. - Verfolgung: Gehe zum Spieler; wenn du weiter als
loseRangeentfernt bist, patrouilliere. - Erstelle in
Knightdie Zustandsmaschine und die Zustände in Awake, starte mit der Patrouille in Start und rufe Tick in FixedUpdate auf.
Drücke „Spielen“, nähere dich dann und renne los. Drücke danach „Prüfen“.
Probier es selbst
using UnityEngine;
// PatrolState und ChaseState
Alle Lektionen in Spielarchitektur in Unity
1Komponenten statt Hierarchien
Kleine KomponentenEin Basis-GegnerskriptAbstrakte KomponentenZusammenwirkende KomponentenRückblick: Modulares GeschützRückblick: Zwillingskanone4Serialisierte Daten
Serialisierbare KlassenListen im InspectorEnums im InspectorRückblick: Loot-TabelleRückblick: Beute auswürfeln7Zustandsautomaten
Zustände als KlassenDer AlarmzustandEin ZustandsautomatÜbergängeGegner-KIDer Ritter greift anRückblick: Türzustände2Schnittstellen in Unity
IDamageableSchnittstellen und TryGetComponentGetComponentsZusammenfassung: InteraktionstasteZusammenfassung: Nächstes in Reichweite5ScriptableObjects
Daten-AssetsWaffenwerteGemeinsame EinstellungenDen Wind speichernWellen als DatenRückblick: Item-DatenbankRückblick: Im Shop einkaufen8Wiederverwendbarer Code
ErweiterungsmethodenEin generisches SingletonGenerische HilfsmethodenRückblick: FindNearestÜbe selbstständig: Unity-Playground