Syntax

Üstün
Katılım
11 Şubat 2024
Mesajlar
1.835
Çözümler
10
Beğeniler
1.843
Merhaba, bu rehberde NavMesh kullanarak basit bir düşman AI sistemi oluşturacağız. Düşman oyuncuyu takip edecek, belirli mesafede duracak ve ona bakacak.

  • Düşmana mapi tanımlayalım
Hirerachyde boş bir obje oluşturalım. Adını 'NavMesh' koydum ben, siz istediğinizi koyabilirsiniz.​
1766003216269.webp
Bu boş objeye navmesh surface componenti ekliyoruz. Bu component AI karakterlerin haritada yürüyebileceği alanları belirlemesini sağlıyor.
Sonrasında haritamızın layerlarını seçmemiz lazım bu kısımda.

1766003192223.webp


Layerları ayarladıktan sonra 'Bake' etmelisiniz.

  • Düşmana hareket etmesini sağlayan componenti ekleyelim.

Base offset: Düşmanın pivot noktasını ayarlamamız için bunu varsayılan olarak bırakacağız.​
Speed: Düşmanın hızı.​
Angular Speed: Düşmanın maksimum dönme hızı. (saniye başına derece cinsinden.)​
Acceleration: düşmanın hızlanması.​
Stopping Distance: düşmanın ile karakterimiz arasındaki durma mesafesi. Değeri arttıkça daha uzakta duracak.​
Auto Braking: düşman hedefe yakınken yavaşlamaya başlar.​
Radius:düşmanın genişliği/çapı​
Height: Düşmanın boyu.​
Quality: Engelden kaçınma.​
Priority: Birden fazla düşman olacak oyunda. Bu düşmanların yolu kesişirse çarpışır, çarpışacaksa hangisi yol verir gibi bir ayar.​
Auto Traverse OffMesh Link: navmesh Link'leri arasında otomatik geçiş yapsın mı? Navmesh link yüzeyler arasındaki geçiş oluyor. Mesela merdivenden zipline platformuna geçiş.​
Auto Repath: yeniden yol oluşturur.​
Area Mask: Düşman hangi alanlarda yürüyebilir?​
1766004937675.webp

  • Scriptimizi yazalım

İlk olarak düşmanın temel özelliklerini ve referanslarını tutan scripti oluşturacağız.​
C#:
using UnityEngine;
using UnityEngine.AI;
public class EnemyRef : MonoBehaviour
{
    [HideInInspector] public NavMeshAgent agent;
    [HideInInspector] public Animator animator;
    public float health = 100f;

    [Header("Stats")]
    public float pathUpdateDelay = 0.2f;


    private void Awake()
    {
        agent = GetComponent<NavMeshAgent>();
        animator = GetComponent<Animator>();


    }



    public void TakeDamage(float damage)
    {
        health -= damage;
        Debug.Log($"Düşman hasar aldı! Kalan can: {health}");

        if (health <= 0f)
        {
            Debug.Log("Düşman öldü!");
            Destroy(gameObject);
        }
    }



}
[HideInInspector]: Değişkenlerin Inspector'da gözükmesini engeller.​
Pathupdatedelay değişkeni, yolu her frame yerine 0.2 saniyede bir güncellememiz için var.​
TakeDamage fonksiyonu, hasar sistemini içeriyor.​
Sıra enemy scriptine geldi. Bu script ile düşmanın menzilin içine girmesi ve karakterimize bakmasını sağlayacağız.​
C#:
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.AI;
public class Enemy : MonoBehaviour
{
    public Transform target;
    EnemyRef enemyRef;
    float shootingDistance;
    private float pathUpdateDeadLine;


    private void Awake()
    {
        enemyRef = GetComponent<EnemyRef>();
    }
    private void Start()
    {
        shootingDistance = enemyRef.agent.stoppingDistance;
    }

    private void Update()
    {

        if (target != null)
        {
            bool inRange = Vector3.Distance(transform.position, target.position) <= shootingDistance;

            if (inRange)
            {
                LookAtTarget();
            }
            else
            {
                UpdatePath();
            }
            enemyRef.animator.SetBool("shooting", inRange);
        }
        //enemyRef.animator.SetFloat("speed", enemyRef.agent.desiredVelocity.sqrMagnitude);
    }

    private void LookAtTarget()
    {
        Vector3 lookPos = target.position - transform.position;
        lookPos.y = 0;
        Quaternion rotation = Quaternion.LookRotation(lookPos);
        transform.rotation = Quaternion.Slerp(transform.rotation, rotation, 0.2f);
    }

    private void UpdatePath()
    {
        if (Time.time >= pathUpdateDeadLine)
        {
            pathUpdateDeadLine = Time.time + enemyRef.pathUpdateDelay;
            enemyRef.agent.SetDestination(target.position);
        }
    }



}
inRange boolu, iki pozisyon arasındaki mesafeyi hesaplar ve stopping distance ile karşılaştırır. Düşmanın durmasını kontrol etmek için var.
LookAtTarget() Fonksiyonu, düşmanın karakterimize bakmasını sağlıyor. Burada y eksenini sıfırlama sebebimiz düşmanın yukarı/aşağı eğilmesini engellemek için.
Slerp, yumuşak dönüş sağlaması için kullanıyoruz.
UpdatePath() Fonksiyonu, düşmanın yolunu güncelliyor.

  • Düşman animasyonları
enemyRef.animator.SetBool("shooting", inRange);
enemyRef.animator.SetFloat("speed", enemyRef.agent.desiredVelocity.sqrMagnitude);
Bu kodlardan ilki ateş etme animasyonunu aktifleştiriyor. Eğer stopping distance olması gereken değerdeyse ateş ediyor.​
İkincisi ise karakterimizin yürüme animasyonunu çalıştırmak için var.​
  • Scriptleri düşmana ekleyelim.

1766005790581.webp
Target burada oyuncu olacak. Health değişkeni ise düşmanın canı. Path Update delay ise düşmanın yolunun hesaplanma gecikmesi.​



Yapmamız gerekenler bunlar. Bunun sonunda düşmanımız karakterimizi takip edecek.
Animasyonları kendi düşman karakterinize göre ayarlamanız gerekmektedir.
Eğer yapmazsanız, oyun çalışır fakat animasyonlar çalışmaz. İyi sosyaller.​
 

Dosya Ekleri

  • 1766004961344.webp
    1766004961344.webp
    10,3 KB · Görüntüleme: 21
İnceledim, çok güzel bir basit başlangıç rehberi olmuş, eline sağlık. :thumbsup:

Yapay zekalar geldiği gibi hem blueprint geliştirmede, hem de düşman sistemi kodlamada derin bir nefes aldık. Kullanmasını bilene adım adım geliştirmek hem çok zevkli, hem de en mantıklı yöntem oluyor.
 
Yeni Unity sürümlerinde NavMeshSurface component'i varsayılan olarak gelmeyebiliyor. Package Manager üzerinden "AI Navigation" paketini indirmeleri gerekebilir, o detayı arayanlar çıkacaktır. Güzel rehber. Elinize sağlık.