Unity'de Yapay Zeka Desteğiyle FPS Nasıl Yapılır?

Birinci şahıs nişancı (FPS), oyuncunun birinci şahıs bakış açısıyla kontrol edildiği, nişancı oyunlarının bir alt türüdür.

Unity'de bir FPS oyunu yapmak için bir oyuncu kontrolörüne, bir dizi öğeye (bu durumda silahlar) ve düşmanlara ihtiyacımız olacak.

1. Adım: Oynatıcı Denetleyicisini Oluşturun

Burada oynatıcımızın kullanacağı bir kontrolcü oluşturacağız.

  • Yeni bir Oyun Nesnesi oluşturun (Oyun Nesnesi -> Boş Oluştur) ve onu adlandırın "Player"
  • Yeni bir Kapsül oluşturun (Oyun Nesnesi -> 3D Nesne -> Kapsül) ve onu "Player" Nesnesinin içine taşıyın
  • Kapsül Çarpıştırıcısı bileşenini Kapsül'den kaldırın ve konumunu (0, 1, 0) olarak değiştirin
  • Ana Kamerayı "Player" Nesnesinin içine taşıyın ve konumunu (0, 1,64, 0) olarak değiştirin
  • yeni bir komut dosyası oluşturun, onu "SC_CharacterController" olarak adlandırın ve içine aşağıdaki kodu yapıştırın:

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);
        }
    }
}
  • SC_CharacterController betiğini "Player" Object'e ekleyin (Karakter Denetleyicisi adında başka bir bileşen de eklediğini ve merkez değerini (0, 1, 0) olarak değiştirdiğini fark edeceksiniz)
  • Ana Kamerayı SC_CharacterController'daki Oyuncu Kamerası değişkenine atayın

Oynatıcı denetleyicisi artık hazır:

Adım 2: Silah Sistemini Oluşturun

Oyuncu silah sistemi 3 bileşenden oluşacaktır: Silah yöneticisi, Silah betiği ve Mermi betiği.

  • yeni bir komut dosyası oluşturun, onu "SC_WeaponManager" olarak adlandırın ve içine aşağıdaki kodu yapıştırın:

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;
        }
    }
}
  • Yeni bir komut dosyası oluşturun, onu "SC_Weapon" olarak adlandırın ve içine aşağıdaki kodu yapıştırın:

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);
    }
}
  • Yeni bir komut dosyası oluşturun, onu "SC_Bullet" olarak adlandırın ve içine aşağıdaki kodu yapıştırın:

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;
    }
}

Artık SC_Bullet betiğinin bazı hatalar içerdiğini fark edeceksiniz. Çünkü yapmamız gereken son bir şey var, o da IEntity arayüzünü tanımlamak.

C#'taki arayüzleri, onu kullanan komut dosyasında belirli yöntemlerin uygulandığından emin olmanız gerektiğinde kullanışlıdır.

IEntity arayüzünün ApplyDamage adında bir yöntemi olacak, bu daha sonra düşmanlara ve oyuncumuza hasar vermek için kullanılacak.

  • Yeni bir komut dosyası oluşturun, onu "SC_InterfaceManager" olarak adlandırın ve içine aşağıdaki kodu yapıştırın:

SC_InterfaceManager.cs

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

Silah Yöneticisi Kurma

Silah yöneticisi, Ana Kamera Nesnesi altında yer alacak ve tüm silahları içerecek bir Nesnedir.

  • Yeni bir GameObject oluşturun ve adlandırın "WeaponManager"
  • WeaponManager'ı Oyuncu Ana Kamerasının içine taşıyın ve konumunu (0, 0, 0) olarak değiştirin
  • SC_WeaponManager komut dosyasını şuraya ekleyin: "WeaponManager"
  • Ana Kamerayı SC_WeaponManager'daki Oyuncu Kamerası değişkenine atayın

Tüfek Kurma

  • Silah modelinizi sahneye sürükleyip bırakın (veya henüz bir modeliniz yoksa bir Küp oluşturup onu uzatın).
  • Modeli, boyutu Player Capsule'e göre olacak şekilde ölçeklendirin

Benim durumumda özel yapım bir Tüfek modeli (BERGARA BA13) kullanacağım:

BERGARA BA13

  • Yeni bir GameObject oluşturun ve onu "Rifle" olarak adlandırın, ardından tüfek modelini bunun içine taşıyın
  • "Rifle" Nesnesini "WeaponManager" Nesnesinin içine taşıyın ve Kameranın önüne şu şekilde yerleştirin:

Unity'de Kamera Kırpma Sorununu Düzeltin.

Nesne kırpmasını düzeltmek için Kameranın yakın kırpma düzlemini daha küçük bir şeyle değiştirmeniz yeterlidir (benim durumumda bunu 0,15'e ayarladım):

BERGARA BA13

Çok daha iyi.

  • SC_Weapon komut dosyasını bir Tüfek Nesnesine ekleyin (Aynı zamanda bir Ses Kaynağı bileşeni de eklediğini fark edeceksiniz, bu, ateşi oynatmak ve sesleri yeniden yüklemek için gereklidir).

Gördüğünüz gibi SC_Weapon'un atanacak 4 değişkeni var. Projenizde uygun Ses Klipleriniz varsa, Fire audio ve Reload ses değişkenlerini hemen atayabilirsiniz.

Bullet Prefabrik değişkeni bu eğitimin ilerleyen kısımlarında açıklanacaktır.

Şimdilik sadece Ateş noktası değişkenini atayacağız:

  • Yeni bir GameObject oluşturun, onu "FirePoint" olarak yeniden adlandırın ve Tüfek Nesnesinin içine taşıyın. Namlunun tam önüne veya biraz içine şu şekilde yerleştirin:

  • FirePoint Dönüşümünü SC_Weapon'daki bir Ateşleme noktası değişkenine atayın
  • SC_WeaponManager betiğinde Tüfeği İkincil Silah değişkenine atayın

Hafif Makineli Tüfek Kurma

  • Tüfek Nesnesini çoğaltın ve onu Hafif Makineli Tüfek olarak yeniden adlandırın
  • İçerisindeki silah modelini farklı bir modelle değiştirin (Benim durumumda TAVOR X95'in özel yapım modelini kullanacağım)

TAVOR X95

  • Ateş Noktası dönüşümünü yeni modele uyacak şekilde taşıyın

Unity'de Silah Ateşleme Noktası nesne kurulumu.

  • SC_WeaponManager betiğinde Hafif Makineli Tüfeği Birincil Silah değişkenine atayın

Bullet Prefabrik Kurulumu

Mermi prefabrik Silahın atış hızına göre oluşturulacak ve bir şeye çarpıp hasar vermediğini tespit etmek için Raycast'i kullanacak.

  • Yeni bir GameObject oluşturun ve adlandırın "Bullet"
  • Trail Renderer bileşenini buna ekleyin ve Time değişkenini 0,1 olarak değiştirin.
  • Sivri görünümlü bir iz eklemek için Genişlik eğrisini daha düşük bir değere ayarlayın (örneğin Başlangıç ​​0,1 ve 0)
  • Yeni Malzeme oluşturun ve onu bullet_trail_material olarak adlandırın ve Gölgelendiricisini Parçacıklar/Katkı Maddesi olarak değiştirin
  • Yeni oluşturulan bir materyali Trail Renderer'a atayın
  • İz Oluşturucunun Rengini farklı bir şeyle değiştirin (örn. Başlangıç: Parlak Turuncu Bitiş: Daha Koyu Turuncu)

  • Madde İşareti Nesnesini Prefab'e kaydedin ve Sahneden silin.
  • Yeni oluşturulan bir Prefabrik'i Tüfek ve Hafif Makineli Tüfek Mermi Prefabrik değişkenine atayın (Proje görünümünden sürükleyip bırakın)

Hafif makineli tüfek:

Tüfek:

Artık silahlar hazır.

3. Adım: Düşman Yapay Zekasını Oluşturun

Düşmanlar, Oyuncuyu takip eden ve yeterince yaklaştığında saldıran basit Küpler olacaktır. Dalgalar halinde saldıracaklar ve her dalgada ortadan kaldırılacak daha fazla düşman olacak.

Düşman Yapay Zekasını Ayarlama

Aşağıda Küpün 2 varyasyonunu oluşturdum (Soldaki canlı örnek içindir ve Sağdaki ise düşman öldürüldüğünde ortaya çıkacaktır):

  • Hem ölü hem de canlı örneklere bir Rigidbody bileşeni ekleyin
  • Ölü Örneği Prefab'a kaydedin ve Scene'den silin.

Artık canlı örneğin oyun seviyesinde gezinebilmesi ve Oyuncuya hasar verebilmesi için birkaç bileşene daha ihtiyacı olacak.

  • Yeni bir komut dosyası oluşturun ve onu "SC_NPCEnemy" olarak adlandırın, ardından aşağıdaki kodu içine yapıştırın:

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);
        }
    }
}
  • Yeni bir komut dosyası oluşturun, "SC_EnemySpawner" olarak adlandırın ve içine aşağıdaki kodu yapıştırın:

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++;
        }
    }
}
  • Yeni bir komut dosyası oluşturun, "SC_DamageReceiver" olarak adlandırın ve içine aşağıdaki kodu yapıştırın:

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;
        }
    }
}
  • SC_NPCEnemy komut dosyasını canlı düşman örneğine ekleyin (NavMesh'te gezinmek için gerekli olan, NavMesh Agent adında başka bir bileşen eklediğini fark edeceksiniz)
  • Yakın zamanda oluşturulan ölü örnek prefab'ını Npc Dead Prefab değişkenine atayın
  • Ateş Noktası için yeni bir GameObject oluşturun, onu canlı düşman örneğinin içine taşıyın ve örneğin biraz önüne yerleştirin, ardından onu Ateş Noktası değişkenine atayın:

  • Son olarak canlı örneği Prefab'a kaydedin ve Scene'den silin.

Düşman Spawner'ı Kurmak

Şimdi SC_EnemySpawner'a geçelim. Bu komut dosyası, düşmanları dalgalar halinde ortaya çıkaracak ve ayrıca ekranda Oyuncu HP'si, mevcut Cephane, mevcut dalgada kaç Düşman kaldığı vb. gibi bazı kullanıcı arayüzü bilgilerini gösterecektir.

  • Yeni bir GameObject oluşturun ve adlandırın "_EnemySpawner"
  • SC_EnemySpawner betiğini buna ekleyin
  • Yeni oluşturulan düşman yapay zekasını Düşman Hazır Yapı değişkenine atayın
  • Aşağıdaki dokuyu Artı İşareti Dokusu değişkenine atayın

  • Birkaç yeni GameObject oluşturun ve bunları Sahnenin etrafına yerleştirin ve ardından Spawn Points dizisine atayın

Atanacak son bir değişkenin kaldığını fark edeceksiniz; o da Player değişkenidir.

  • SC_DamageReceiver komut dosyasını bir Player örneğine ekleyin
  • Player örneği etiketini şu şekilde değiştirin: "Player"
  • SC_DamageReceiver'da Oyuncu Denetleyicisi ve Silah Yöneticisi değişkenlerini atayın

  • Player örneğini SC_EnemySpawner'daki bir Player değişkenine atayın

Ve son olarak, düşman yapay zekasının yön bulabilmesi için sahnemizde NavMesh'i pişirmemiz gerekiyor.

Ayrıca, NavMesh'i pişirmeden önce Sahnedeki her statik Nesneyi Navigasyon Statik olarak işaretlemeyi unutmayın:

  • NavMesh penceresine gidin (Pencere -> Yapay Zeka -> Gezinme), Pişirme sekmesine tıklayın ve ardından Pişirme düğmesine tıklayın. NavMesh pişirildikten sonra şöyle görünmelidir:

Şimdi Oynat'a basıp test etme zamanı:

Sharp Coder Video oynatıcı

Her şey beklendiği gibi çalışıyor!

Kaynak
📁SimpleFPS.unitypackage4.61 MB
Önerilen Makaleler
Unity'de Bir Geyiğin Yapay Zekası Nasıl Yapılır?
Unity'de Düşmanın Yapay Zekasının Uygulanması
Unity 2D Platform Oyununa Düşman Ekleyin
Unity'de NavMeshAgent ile çalışma
Unity'de Oyuncuyu Takip Eden bir NPC Oluşturun
Unity Asset Store Paketinin İncelenmesi - Zombi Yapay Zeka Sistemi
Unity'de Hayatta Kalma Oyunu Nasıl Yapılır