So erstellen Sie einen FPS mit der KI-Unterstützung in Unity

Ego-Shooter (FPS) ist ein Subgenre von Shooter-Spielen, bei dem der Spieler aus der Ego-Perspektive gesteuert wird.

Um ein FPS-Spiel in Unity zu erstellen, benötigen wir einen Spieler-Controller, eine Reihe von Gegenständen (in diesem Fall Waffen) und die Feinde.

Schritt 1: Erstellen Sie den Player-Controller

Hier erstellen wir einen Controller, der von unserem Spieler verwendet wird.

  • Erstellen Sie ein neues Spielobjekt (Spielobjekt -> Leer erstellen) und benennen Sie es "Player"
  • Erstellen Sie eine neue Kapsel (Spielobjekt -> 3D-Objekt -> Kapsel) und verschieben Sie sie in das "Player"-Objekt
  • Entfernen Sie die Capsule Collider-Komponente aus der Capsule und ändern Sie ihre Position in (0, 1, 0).
  • Bewegen Sie die Hauptkamera in das Objekt "Player" und ändern Sie ihre Position in (0, 1,64, 0).
  • Erstellen ein neues Skript, nennen Sie es "SC_CharacterController" und fügen Sie den folgenden Code ein:

SC_CharacterController.cs

using UnityEngine;

[RequireComponent(typeof(CharacterController))]

public class SC_CharacterController : MonoBehaviour
{
    public float speed = 7.5f;
    public float jumpSpeed = 8.0f;
    public float gravity = 20.0f;
    public Camera playerCamera;
    public float lookSpeed = 2.0f;
    public float lookXLimit = 45.0f;

    CharacterController characterController;
    Vector3 moveDirection = Vector3.zero;
    Vector2 rotation = Vector2.zero;

    [HideInInspector]
    public bool canMove = true;

    void Start()
    {
        characterController = GetComponent<CharacterController>();
        rotation.y = transform.eulerAngles.y;
    }

    void Update()
    {
        if (characterController.isGrounded)
        {
            // We are grounded, so recalculate move direction based on axes
            Vector3 forward = transform.TransformDirection(Vector3.forward);
            Vector3 right = transform.TransformDirection(Vector3.right);
            float curSpeedX = canMove ? speed * Input.GetAxis("Vertical") : 0;
            float curSpeedY = canMove ? speed * Input.GetAxis("Horizontal") : 0;
            moveDirection = (forward * curSpeedX) + (right * curSpeedY);

            if (Input.GetButton("Jump") && canMove)
            {
                moveDirection.y = jumpSpeed;
            }
        }

        // Apply gravity. Gravity is multiplied by deltaTime twice (once here, and once below
        // when the moveDirection is multiplied by deltaTime). This is because gravity should be applied
        // as an acceleration (ms^-2)
        moveDirection.y -= gravity * Time.deltaTime;

        // Move the controller
        characterController.Move(moveDirection * Time.deltaTime);

        // Player and Camera rotation
        if (canMove)
        {
            rotation.y += Input.GetAxis("Mouse X") * lookSpeed;
            rotation.x += -Input.GetAxis("Mouse Y") * lookSpeed;
            rotation.x = Mathf.Clamp(rotation.x, -lookXLimit, lookXLimit);
            playerCamera.transform.localRotation = Quaternion.Euler(rotation.x, 0, 0);
            transform.eulerAngles = new Vector2(0, rotation.y);
        }
    }
}
  • Hängen Sie das SC_CharacterController-Skript an das Objekt "Player" an (Sie werden feststellen, dass es auch eine weitere Komponente namens Character Controller hinzugefügt hat und deren Mittelwert in (0, 1, 0) geändert wurde.)
  • Weisen Sie die Hauptkamera der Player-Kamera-Variable in SC_CharacterController zu

Der Player-Controller ist jetzt bereit:

Schritt 2: Erstellen Sie das Waffensystem

Das Waffensystem des Spielers besteht aus drei Komponenten: einem Waffenmanager, einem Waffenskript und einem Bullet-Skript.

  • Erstellen ein neues Skript, nennen Sie es "SC_WeaponManager" und fügen Sie den folgenden Code ein:

SC_WeaponManager.cs

using UnityEngine;

public class SC_WeaponManager : MonoBehaviour
{
    public Camera playerCamera;
    public SC_Weapon primaryWeapon;
    public SC_Weapon secondaryWeapon;

    [HideInInspector]
    public SC_Weapon selectedWeapon;

    // Start is called before the first frame update
    void Start()
    {
        //At the start we enable the primary weapon and disable the secondary
        primaryWeapon.ActivateWeapon(true);
        secondaryWeapon.ActivateWeapon(false);
        selectedWeapon = primaryWeapon;
        primaryWeapon.manager = this;
        secondaryWeapon.manager = this;
    }

    // Update is called once per frame
    void Update()
    {
        //Select secondary weapon when pressing 1
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            primaryWeapon.ActivateWeapon(false);
            secondaryWeapon.ActivateWeapon(true);
            selectedWeapon = secondaryWeapon;
        }

        //Select primary weapon when pressing 2
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            primaryWeapon.ActivateWeapon(true);
            secondaryWeapon.ActivateWeapon(false);
            selectedWeapon = primaryWeapon;
        }
    }
}
  • Erstellen Sie ein neues Skript, nennen Sie es "SC_Weapon" und fügen Sie den folgenden Code ein:

SC_Weapon.cs

using System.Collections;
using UnityEngine;

[RequireComponent(typeof(AudioSource))]

public class SC_Weapon : MonoBehaviour
{
    public bool singleFire = false;
    public float fireRate = 0.1f;
    public GameObject bulletPrefab;
    public Transform firePoint;
    public int bulletsPerMagazine = 30;
    public float timeToReload = 1.5f;
    public float weaponDamage = 15; //How much damage should this weapon deal
    public AudioClip fireAudio;
    public AudioClip reloadAudio;

    [HideInInspector]
    public SC_WeaponManager manager;

    float nextFireTime = 0;
    bool canFire = true;
    int bulletsPerMagazineDefault = 0;
    AudioSource audioSource;

    // Start is called before the first frame update
    void Start()
    {
        bulletsPerMagazineDefault = bulletsPerMagazine;
        audioSource = GetComponent<AudioSource>();
        audioSource.playOnAwake = false;
        //Make sound 3D
        audioSource.spatialBlend = 1f;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0) && singleFire)
        {
            Fire();
        }
        if (Input.GetMouseButton(0) && !singleFire)
        {
            Fire();
        }
        if (Input.GetKeyDown(KeyCode.R) && canFire)
        {
            StartCoroutine(Reload());
        }
    }

    void Fire()
    {
        if (canFire)
        {
            if (Time.time > nextFireTime)
            {
                nextFireTime = Time.time + fireRate;

                if (bulletsPerMagazine > 0)
                {
                    //Point fire point at the current center of Camera
                    Vector3 firePointPointerPosition = manager.playerCamera.transform.position + manager.playerCamera.transform.forward * 100;
                    RaycastHit hit;
                    if (Physics.Raycast(manager.playerCamera.transform.position, manager.playerCamera.transform.forward, out hit, 100))
                    {
                        firePointPointerPosition = hit.point;
                    }
                    firePoint.LookAt(firePointPointerPosition);
                    //Fire
                    GameObject bulletObject = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
                    SC_Bullet bullet = bulletObject.GetComponent<SC_Bullet>();
                    //Set bullet damage according to weapon damage value
                    bullet.SetDamage(weaponDamage);

                    bulletsPerMagazine--;
                    audioSource.clip = fireAudio;
                    audioSource.Play();
                }
                else
                {
                    StartCoroutine(Reload());
                }
            }
        }
    }

    IEnumerator Reload()
    {
        canFire = false;

        audioSource.clip = reloadAudio;
        audioSource.Play();

        yield return new WaitForSeconds(timeToReload);

        bulletsPerMagazine = bulletsPerMagazineDefault;

        canFire = true;
    }

    //Called from SC_WeaponManager
    public void ActivateWeapon(bool activate)
    {
        StopAllCoroutines();
        canFire = true;
        gameObject.SetActive(activate);
    }
}
  • Erstellen Sie ein neues Skript, nennen Sie es "SC_Bullet" und fügen Sie den folgenden Code ein:

SC_Bullet.cs

using System.Collections;
using UnityEngine;

public class SC_Bullet : MonoBehaviour
{
    public float bulletSpeed = 345;
    public float hitForce = 50f;
    public float destroyAfter = 3.5f;

    float currentTime = 0;
    Vector3 newPos;
    Vector3 oldPos;
    bool hasHit = false;

    float damagePoints;

    // Start is called before the first frame update
    IEnumerator Start()
    {
        newPos = transform.position;
        oldPos = newPos;

        while (currentTime < destroyAfter && !hasHit)
        {
            Vector3 velocity = transform.forward * bulletSpeed;
            newPos += velocity * Time.deltaTime;
            Vector3 direction = newPos - oldPos;
            float distance = direction.magnitude;
            RaycastHit hit;

            // Check if we hit anything on the way
            if (Physics.Raycast(oldPos, direction, out hit, distance))
            {
                if (hit.rigidbody != null)
                {
                    hit.rigidbody.AddForce(direction * hitForce);

                    IEntity npc = hit.transform.GetComponent<IEntity>();
                    if (npc != null)
                    {
                        //Apply damage to NPC
                        npc.ApplyDamage(damagePoints);
                    }
                }

                newPos = hit.point; //Adjust new position
                StartCoroutine(DestroyBullet());
            }

            currentTime += Time.deltaTime;
            yield return new WaitForFixedUpdate();

            transform.position = newPos;
            oldPos = newPos;
        }

        if (!hasHit)
        {
            StartCoroutine(DestroyBullet());
        }
    }

    IEnumerator DestroyBullet()
    {
        hasHit = true;
        yield return new WaitForSeconds(0.5f);
        Destroy(gameObject);
    }

    //Set how much damage this bullet will deal
    public void SetDamage(float points)
    {
        damagePoints = points;
    }
}

Jetzt werden Sie feststellen, dass das SC_Bullet-Skript einige Fehler aufweist. Das liegt daran, dass wir noch eine letzte Sache zu tun haben, nämlich die IEntity-Schnittstelle zu definieren.

Schnittstellen in C# sind nützlich, wenn Sie sicherstellen müssen, dass in dem Skript, das sie verwendet, bestimmte Methoden implementiert sind.

Die IEntity-Schnittstelle verfügt über eine Methode namens ApplyDamage, die später verwendet wird, um Feinden und unserem Spieler Schaden zuzufügen.

  • Erstellen Sie ein neues Skript, nennen Sie es "SC_InterfaceManager" und fügen Sie den folgenden Code ein:

SC_InterfaceManager.cs

//Entity interafce
interface IEntity
{ 
    void ApplyDamage(float points);
}

Einrichten eines Waffenmanagers

Ein Waffenmanager ist ein Objekt, das sich unter dem Hauptkameraobjekt befindet und alle Waffen enthält.

  • Erstellen Sie ein neues GameObject und benennen Sie es "WeaponManager"
  • Bewegen Sie den WeaponManager in die Hauptkamera des Spielers und ändern Sie seine Position auf (0, 0, 0).
  • Hängen Sie das SC_WeaponManager-Skript an an "WeaponManager"
  • Weisen Sie die Hauptkamera der Variablen „Spielerkamera“ in SC_WeaponManager zu

Ein Gewehr einrichten

  • Ziehen Sie Ihr Waffenmodell per Drag-and-Drop in die Szene (oder erstellen Sie einfach einen Würfel und strecken Sie ihn, wenn Sie noch kein Modell haben).
  • Skalieren Sie das Modell so, dass seine Größe relativ zu einer Spielerkapsel ist

In meinem Fall verwende ich ein maßgeschneidertes Gewehrmodell (BERGARA BA13):

BERGARA BA13

  • Erstellen Sie ein neues GameObject, nennen Sie es "Rifle" und verschieben Sie dann das Gewehrmodell darin
  • Bewegen Sie das "Rifle"-Objekt innerhalb des "WeaponManager"-Objekts und platzieren Sie es wie folgt vor der Kamera:

Beheben Sie das Kamera-Clipping-Problem in Unity.

Um den Objektausschnitt zu korrigieren, ändern Sie einfach die Nahausschnittebene der Kamera auf etwas Kleineres (in meinem Fall habe ich sie auf 0,15 eingestellt):

BERGARA BA13

Viel besser.

  • Hängen Sie das SC_Weapon-Skript an ein Gewehrobjekt an (Sie werden feststellen, dass es auch eine Audioquellenkomponente hinzugefügt hat, diese wird benötigt, um das Feuer abzuspielen und Audios neu zu laden).

Wie Sie sehen können, muss SC_Weapon vier Variablen zuweisen. Sie können Fire-Audio- und Reload-Audio-Variablen sofort zuweisen, wenn Ihr Projekt über geeignete Audio-Clips verfügt.

Die Bullet Prefab-Variable wird später in diesem Tutorial erklärt.

Im Moment weisen wir einfach die Fire-Point-Variable zu:

  • Erstellen Sie ein neues GameObject, benennen Sie es in "FirePoint" um und verschieben Sie es in das Rifle Object. Platzieren Sie es direkt vor dem Lauf oder leicht innen, wie folgt:

  • Weisen Sie die FirePoint-Transformation einer Fire-Point-Variablen bei SC_Weapon zu
  • Weisen Sie das Gewehr einer Sekundärwaffenvariablen im SC_WeaponManager-Skript zu

Eine Maschinenpistole einrichten

  • Duplizieren Sie das Gewehrobjekt und benennen Sie es in Maschinenpistole um
  • Ersetzen Sie das darin enthaltene Waffenmodell durch ein anderes Modell (in meinem Fall verwende ich das maßgeschneiderte Modell von TAVOR X95)

TAVOR X95

  • Verschieben Sie die Fire Point-Transformation, bis sie zum neuen Modell passt

Einrichtung des Waffenfeuerpunktobjekts in Unity.

  • Weisen Sie Submachinegun einer Primärwaffenvariablen im SC_WeaponManager-Skript zu

Einrichten eines Bullet Prefab

Geschossfertigteile werden entsprechend der Feuerrate einer Waffe gespawnt und nutzen Raycast, um zu erkennen, ob sie etwas trifft und Schaden anrichtet.

  • Erstellen Sie ein neues GameObject und benennen Sie es "Bullet"
  • Fügen Sie die Trail-Renderer-Komponente hinzu und ändern Sie deren Zeitvariable auf 0,1.
  • Stellen Sie die Breitenkurve auf einen niedrigeren Wert ein (z. B. Start 0,1 Ende 0), um eine Spur hinzuzufügen, die spitz aussieht
  • Erstellen Sie ein neues Material, nennen Sie es „bullet_trail_material“ und ändern Sie seinen Shader in „Partikel/Additiv“.
  • Weisen Sie einem Trail-Renderer ein neu erstelltes Material zu
  • Ändern Sie die Farbe des Trail-Renderers in etwas anderes (z. B. Anfang: Helles Orange, Ende: Dunkleres Orange).

  • Speichern Sie das Bullet-Objekt unter Prefab und löschen Sie es aus der Szene.
  • Weisen Sie ein neu erstelltes Prefab (Drag & Drop aus der Projektansicht) der Prefab-Variablen Rifle und Submachinegun Bullet zu

Maschinenpistole:

Gewehr:

Die Waffen sind jetzt bereit.

Schritt 3: Erstellen Sie die Feind-KI

Die Feinde sind einfache Würfel, die dem Spieler folgen und angreifen, sobald sie nahe genug sind. Sie greifen in Wellen an, wobei jede Welle mehr Feinde zu eliminieren hat.

Feindliche KI einrichten

Unten habe ich zwei Varianten des Würfels erstellt (die linke ist für die lebendige Instanz und die rechte wird erzeugt, sobald der Feind getötet wird):

  • Fügen Sie sowohl toten als auch aktiven Instanzen eine Rigidbody-Komponente hinzu
  • Speichern Sie die tote Instanz in Prefab und löschen Sie sie aus der Szene.

Jetzt benötigt die lebendige Instanz ein paar weitere Komponenten, um durch das Spiellevel navigieren und dem Spieler Schaden zufügen zu können.

  • Erstellen Sie ein neues Skript, nennen Sie es "SC_NPCEnemy" und fügen Sie dann den folgenden Code ein:

SC_NPCEnemy.cs

using UnityEngine;
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshAgent))]

public class SC_NPCEnemy : MonoBehaviour, IEntity
{
    public float attackDistance = 3f;
    public float movementSpeed = 4f;
    public float npcHP = 100;
    //How much damage will npc deal to the player
    public float npcDamage = 5;
    public float attackRate = 0.5f;
    public Transform firePoint;
    public GameObject npcDeadPrefab;

    [HideInInspector]
    public Transform playerTransform;
    [HideInInspector]
    public SC_EnemySpawner es;
    NavMeshAgent agent;
    float nextAttackTime = 0;

    // Start is called before the first frame update
    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        agent.stoppingDistance = attackDistance;
        agent.speed = movementSpeed;

        //Set Rigidbody to Kinematic to prevent hit register bug
        if (GetComponent<Rigidbody>())
        {
            GetComponent<Rigidbody>().isKinematic = true;
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (agent.remainingDistance - attackDistance < 0.01f)
        {
            if(Time.time > nextAttackTime)
            {
                nextAttackTime = Time.time + attackRate;

                //Attack
                RaycastHit hit;
                if(Physics.Raycast(firePoint.position, firePoint.forward, out hit, attackDistance))
                {
                    if (hit.transform.CompareTag("Player"))
                    {
                        Debug.DrawLine(firePoint.position, firePoint.position + firePoint.forward * attackDistance, Color.cyan);

                        IEntity player = hit.transform.GetComponent<IEntity>();
                        player.ApplyDamage(npcDamage);
                    }
                }
            }
        }
        //Move towardst he player
        agent.destination = playerTransform.position;
        //Always look at player
        transform.LookAt(new Vector3(playerTransform.transform.position.x, transform.position.y, playerTransform.position.z));
    }

    public void ApplyDamage(float points)
    {
        npcHP -= points;
        if(npcHP <= 0)
        {
            //Destroy the NPC
            GameObject npcDead = Instantiate(npcDeadPrefab, transform.position, transform.rotation);
            //Slightly bounce the npc dead prefab up
            npcDead.GetComponent<Rigidbody>().velocity = (-(playerTransform.position - transform.position).normalized * 8) + new Vector3(0, 5, 0);
            Destroy(npcDead, 10);
            es.EnemyEliminated(this);
            Destroy(gameObject);
        }
    }
}
  • Erstellen Sie ein neues Skript, nennen Sie es "SC_EnemySpawner" und fügen Sie dann den folgenden Code ein:

SC_EnemySpawner.cs

using UnityEngine;
using UnityEngine.SceneManagement;

public class SC_EnemySpawner : MonoBehaviour
{
    public GameObject enemyPrefab;
    public SC_DamageReceiver player;
    public Texture crosshairTexture;
    public float spawnInterval = 2; //Spawn new enemy each n seconds
    public int enemiesPerWave = 5; //How many enemies per wave
    public Transform[] spawnPoints;

    float nextSpawnTime = 0;
    int waveNumber = 1;
    bool waitingForWave = true;
    float newWaveTimer = 0;
    int enemiesToEliminate;
    //How many enemies we already eliminated in the current wave
    int enemiesEliminated = 0;
    int totalEnemiesSpawned = 0;

    // Start is called before the first frame update
    void Start()
    {
        //Lock cursor
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;

        //Wait 10 seconds for new wave to start
        newWaveTimer = 10;
        waitingForWave = true;
    }

    // Update is called once per frame
    void Update()
    {
        if (waitingForWave)
        {
            if(newWaveTimer >= 0)
            {
                newWaveTimer -= Time.deltaTime;
            }
            else
            {
                //Initialize new wave
                enemiesToEliminate = waveNumber * enemiesPerWave;
                enemiesEliminated = 0;
                totalEnemiesSpawned = 0;
                waitingForWave = false;
            }
        }
        else
        {
            if(Time.time > nextSpawnTime)
            {
                nextSpawnTime = Time.time + spawnInterval;

                //Spawn enemy 
                if(totalEnemiesSpawned < enemiesToEliminate)
                {
                    Transform randomPoint = spawnPoints[Random.Range(0, spawnPoints.Length - 1)];

                    GameObject enemy = Instantiate(enemyPrefab, randomPoint.position, Quaternion.identity);
                    SC_NPCEnemy npc = enemy.GetComponent<SC_NPCEnemy>();
                    npc.playerTransform = player.transform;
                    npc.es = this;
                    totalEnemiesSpawned++;
                }
            }
        }

        if (player.playerHP <= 0)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Scene scene = SceneManager.GetActiveScene();
                SceneManager.LoadScene(scene.name);
            }
        }
    }

    void OnGUI()
    {
        GUI.Box(new Rect(10, Screen.height - 35, 100, 25), ((int)player.playerHP).ToString() + " HP");
        GUI.Box(new Rect(Screen.width / 2 - 35, Screen.height - 35, 70, 25), player.weaponManager.selectedWeapon.bulletsPerMagazine.ToString());

        if(player.playerHP <= 0)
        {
            GUI.Box(new Rect(Screen.width / 2 - 85, Screen.height / 2 - 20, 170, 40), "Game Over\n(Press 'Space' to Restart)");
        }
        else
        {
            GUI.DrawTexture(new Rect(Screen.width / 2 - 3, Screen.height / 2 - 3, 6, 6), crosshairTexture);
        }

        GUI.Box(new Rect(Screen.width / 2 - 50, 10, 100, 25), (enemiesToEliminate - enemiesEliminated).ToString());

        if (waitingForWave)
        {
            GUI.Box(new Rect(Screen.width / 2 - 125, Screen.height / 4 - 12, 250, 25), "Waiting for Wave " + waveNumber.ToString() + " (" + ((int)newWaveTimer).ToString() + " seconds left...)");
        }
    }

    public void EnemyEliminated(SC_NPCEnemy enemy)
    {
        enemiesEliminated++;

        if(enemiesToEliminate - enemiesEliminated <= 0)
        {
            //Start next wave
            newWaveTimer = 10;
            waitingForWave = true;
            waveNumber++;
        }
    }
}
  • Erstellen Sie ein neues Skript, nennen Sie es "SC_DamageReceiver" und fügen Sie dann den folgenden Code ein:

SC_DamageReceiver.cs

using UnityEngine;

public class SC_DamageReceiver : MonoBehaviour, IEntity
{
    //This script will keep track of player HP
    public float playerHP = 100;
    public SC_CharacterController playerController;
    public SC_WeaponManager weaponManager;

    public void ApplyDamage(float points)
    {
        playerHP -= points;

        if(playerHP <= 0)
        {
            //Player is dead
            playerController.canMove = false;
            playerHP = 0;
        }
    }
}
  • Hängen Sie das SC_NPCEnemy-Skript an eine aktive Feindinstanz an (Sie werden feststellen, dass es eine weitere Komponente namens NavMesh Agent hinzugefügt hat, die zum Navigieren im NavMesh benötigt wird)
  • Weisen Sie das kürzlich erstellte tote Instanz-Prefab der Variable „Npc Dead Prefab“ zu
  • Erstellen Sie für den Fire Point ein neues GameObject, verschieben Sie es in die lebende Feindinstanz, platzieren Sie es leicht vor der Instanz und weisen Sie es dann der Fire Point-Variablen zu:

  • Speichern Sie abschließend die lebendige Instanz in Prefab und löschen Sie sie aus der Szene.

Feind-Spawner einrichten

Kommen wir nun zu SC_EnemySpawner. Dieses Skript bringt Feinde in Wellen hervor und zeigt außerdem einige UI-Informationen auf dem Bildschirm an, z. B. Spieler-HP, aktuelle Munition, wie viele Feinde in einer aktuellen Welle übrig sind usw.

  • Erstellen Sie ein neues GameObject und benennen Sie es "_EnemySpawner"
  • Hängen Sie das SC_EnemySpawner-Skript daran an
  • Weisen Sie die neu erstellte Feind-KI der Enemy Prefab-Variable zu
  • Weisen Sie die Textur unten der Variablen „Crosshair Texture“ zu

  • Erstellen Sie ein paar neue GameObjects, platzieren Sie sie in der Szene und weisen Sie sie dann dem Spawn Points-Array zu

Sie werden feststellen, dass noch eine letzte Variable zugewiesen werden muss, nämlich die Player-Variable.

  • Hängen Sie das SC_DamageReceiver-Skript an eine Player-Instanz an
  • Ändern Sie das Player-Instanz-Tag in "Player"
  • Weisen Sie Player Controller- und Weapon Manager-Variablen in SC_DamageReceiver zu

  • Weisen Sie eine Player-Instanz einer Player-Variablen in SC_EnemySpawner zu

Und schließlich müssen wir das NavMesh in unserer Szene backen, damit die feindliche KI navigieren kann.

Vergessen Sie außerdem nicht, jedes statische Objekt in der Szene als Navigationsstatisch zu markieren, bevor Sie NavMesh backen:

  • Gehen Sie zum NavMesh-Fenster (Fenster -> KI -> Navigation), klicken Sie auf die Registerkarte Backen und dann auf die Schaltfläche Backen. Nachdem das NavMesh gebacken wurde, sollte es etwa so aussehen:

Jetzt heißt es Play drücken und testen:

Sharp Coder Videoplayer

Alles funktioniert wie erwartet!

Quelle
📁SimpleFPS.unitypackage4.61 MB
Empfohlene Artikel
Implementierung der KI eines Feindes in Unity
Unity Füge Feinde zu einem 2D-Plattformer hinzu
Wie man in Unity eine KI aus einem Hirsch macht
Erstelle einen NPC, der dem Spieler in Einheit folgt
Arbeiten mit NavMeshAgent in Unity
Überprüfung des Unity Asset Store-Pakets – Zombie AI System
So erstellen Sie ein Überlebensspiel in Unity