Birlikte öğreniyoruz...

Unity oyun motoruyla Tower Defence oyunu yapma serimizin 4. bölümünde kule atış sistemi ile kuleden merminin çıkma ve hedefi vurduğunda oluşan etkilerinin efektlerini eklemesini gerçekleştireceğiz.

Kuleye En Yakın Düşmanı Bulmak

Kulelerimizin düşman karakterlerine mermi atabilmesi için ilk önce namlularını düşmana doğru çevirmeleri gerekiyor. Bu yüzden ilk olarak kulelerin atış alanları belirleyerek, belirlediğimiz atış alanına girdiği zaman otomatik olarak hedefe doğru dönmelerini sağlayacağız.

Fakat şuan sahnemizde bir adet kırmızı ve mavi düşman oluşuyor. Bu sayıyı arttırarak düzenli olarak sahnede düşmanların oluşmasını ve bitiş noktasına ilerlemesini sağlayalım. Bunun için “DusmanDalgalariYoneticisi” script dosyamıza aşağıdaki bir satırı ekliyoruz.

void Start()
{
    baslangicNoktalari = GameObject.FindGameObjectsWithTag("BaslangicNoktalari");
    maviBitisNoktasi = GameObject.Find("MaviBitisNoktasi(Clone)");
    kirmiziBitisNoktasi = GameObject.Find("KirmiziBitisNoktasi(Clone)");

    dusmanyol = transform.GetComponents<NavMeshSurface>();

    foreach (var yol in dusmanyol)
    {
        yol.BuildNavMesh();
    }

    InvokeRepeating("HaritayiOlustur", 0f, 2f); 

}

Burada eklediğimiz “InvokeRepeating” sayesinde istediğimiz fonksiyonu oyun başladıktan belirli bir süre sonra ve belirli aralıklarla başlatabiliyoruz. Bizde “HaritayiOlustur” fonksiyonunu “0f” yani 0 saniye sonra başlat ve “2f” yani her 2 saniyede bir çalıştır demiş oluyoruz. Böylece sürekli olarak sahnede sürekli olarak düşmanlar oluşacak. Bunu sonradan değiştireceğiz ama testlerimizi yaparken işimize yarayacağı için geçici olarak ekledik.

Kule” isminde bir C# script dosyası oluşturalım ve kulelerimize atamasını gerçekleştirelim. Kulelerimizi oyun başladıktan sonra sahnemize eklediğimiz için prefab hallerine eklemeniz gerekiyor. Her kule şablonumuza bunu eklememiz gerekiyor.

Basit bir mantıkla düşünürsek Tower Defence oyunlarında her kulenin belirli bir mesafeye kadar ateş edebilme özelliği vardır. Bizde kulelerimizi bu mantığa göre ayarlayacağız yani ilk yapacağımız dışarıdan script dosyamıza atayabileceğimiz bir değişken oluşturmak. Ayrıca mesafeyi görebilmemiz içinde “OnDrawGizmos” fonksiyonunu kullanacağız.

using UnityEngine;

public class Kule : MonoBehaviour
{ 
    [SerializeField] private float atisMesafesi = 3f;

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, atisMesafesi);
    }
}
  • [SerializeField] private float atisMesafesi = 3f; yazarak “Float” türünde bir değişken oluşturduk. Bu değişkenimiz sayesinde kulelerin atış mesafesini belirleyebileceğiz.
  • OnDrawGizmos” fonksiyonu tıpkı “Update” ve “Start” fonksiyonları gibi oyunun başlamasıyla birlikte otomatik olarak başlayan bir fonksiyondur. Tabi “MonoBehaviour” sınıfını miras almış olması ve scriptin sahnede yer alan ve aktif durumda olan bir objede olması gerektiğini unutmayalım. Bu fonksiyon ile ekli olan objenin etrafına bir alan oluşturuyor olacağız. Gizmos.color = Color.red; yazarak bu alan çizgilerinin kırmızı renk olmasını belirtiyoruz. Gizmos.DrawWireSphere(transform.position, atisMesafesi); yazarak ise script ekli olduğu objenin etrafına çizilecek olan şeklin “Sphere” olmasını belirtiyoruz. İçerisine verdiğimiz parametreler ise kendi pozisyonundan “atisMesafesi” olarak belirlediğimiz mesafe kadar alan çizmesini belirtiyoruz.
OnDrawGizmos Görünümü
OnDrawGizmos Görünümü

Not: “OnDrawGizmos” ile belirlediğimiz sınır çizgileri sadece “Scene” ekranında gözükecektir.

Şimdi yapmamız gereken ise sahnede yer alan düşman karakterlerini bularak atış mesafesi alanımıza girmiş ve bize en yakın olanı tespit etmek olacak. Bunun için “Kule” isimli C# dosyamıza aşağıdaki fonksiyonu ekliyoruz.

void DusmaniBul()
{
    string[] dusmanEtiketleri = { "KirmiziDusman", "MaviDusman" };
    List<GameObject> dusmanlar = new List<GameObject>();


    for (int i = 0; i < dusmanEtiketleri.Length; i++)
    {
        GameObject[] dusmanlarArray = GameObject.FindGameObjectsWithTag(dusmanEtiketleri[i]);

        for (int ii = 0; ii < dusmanlarArray.Length; ii++)
        {
            dusmanlar.Add(dusmanlarArray[ii]);
        }
    }

    float enKisaMesafe = Mathf.Infinity;
    GameObject enYakinDusman = null;

    foreach (var dusman in dusmanlar)
    {
        float dusmanaUzaklik = Vector3.Distance(transform.position, dusman.transform.position);
        if (dusmanaUzaklik < enKisaMesafe)
        {
            enKisaMesafe = dusmanaUzaklik;
            enYakinDusman = dusman;
        }
    }
}

Sahnede yer alan düşmanları bulmak için etiketlerini kullanacağız. Tüm düşman etiketleri aynı olsa işimiz daha kolay olacaktı ama “KırmızıDusman” ve “MaviDusman” isimli etiketlere sahip olduğumuz için ve sonradan yeni düşman tipleri ekleyebileceğimiz için isimlerini tutan bir array yapısına ihtiyacımız var. Bunun için string[] dusmanEtiketleri = { "KirmiziDusman", "MaviDusman" }; yazarak “String” eleman alan bir array oluşturdum ve içerisine etiketlerimizi ekledim.

Sonrasında ise for (int i = 0; i < dusmanEtiketleri.Length; i++) yazarak bir for döngüsüne soktuk ve döngü içerisine GameObject[] dusmanlarArray = GameObject.FindGameObjectsWithTag(dusmanEtiketleri[i]); yazarak daha önceden belirlediğimiz etiketlere sahip ve sahnede yer alan düşmanları aratarak “dusmanlarArray” isimli array yapısını oluşturduk.

Farklı türlerdeki etiketler için diziler(array) oluşturduk ama bu dizileri birleştirmemiz lazım. Bu sebeple List<GameObject> dusmanlar = new List<GameObject>(); yazarak bir liste oluşturuyoruz ve array içindeki elemanları for (int ii = 0; ii < dusmanlarArray.Length; ii++) ile gezerek dusmanlar.Add(dusmanlarArray[ii]); ile de diziye ekliyoruz.

Aslında burada her farklı etiket için “FindGameObjectsWithTag” ile ile arama yaparak array oluşturduktan sonra bu array içindeki elemanları bir dizinin içerisine atmış oluyoruz. Böylece sahnede yer alan tüm düşman objelerini bir yere toplamış olduk.

Düşman objelerini bir yere toplama sebebimiz ise kulemize en yakın olanı tespit edebilmek. Yani kulemize en yakın durumda olan düşman karakterini ve kulemize olan mesafesini almamız gerekiyor.

İlk olarak float enKisaMesafe = Mathf.Infinity; yazarak en yakın mesafeye Mathf.Infinity” ile sonsuz bir değer giriyorum. En yakın düşmanı atacağım değişkeni de GameObject enYakinDusman = null; ile oluşturarak ilk başta null değer olmasını sağladım.

Şimdi “dusmanlar” list yapımızı bir “foreach” döngüsüne sokacağız ve tek tek düşmanlar ile kulemiz arasındaki mesafeyi ölçerek en yakın olanı tespit edeceğiz. Döngü içerisinde “Distance” fonksiyonunu kullanarak yani float dusmanaUzaklik = Vector3.Distance(transform.position, dusman.transform.position); yazarak kulenin pozisyonu ve düşman karakterinin pozisyonu arasındaki mesafeyi ölçüyoruz.

Döngü sırasında enKisaMesafe = dusmanaUzaklik; çıkan mesafe bilgisini ve düşman objesini enYakinDusman = dusman; kullanarak değişkenlerimize atıyoruz. if (dusmanaUzaklik < enKisaMesafe) yazarak döngünün her bir adımında elimizdeki sonucu bir önceki en yakın sonuç ile karşılaştırıyoruz ve daha iyi bir sonuç bulursak tekrar değişkenlere atamasını gerçekleştiriyoruz. İşlem sonunda elimizde en yakın mesafeye sahip düşman objesi ve mesafesi olacak.

Peki bu bilgileri elde etmemiz için “DusmaniBul()” fonksiyonunu sürekli olarak çalıştırmamız gerekiyor. Bunun sebebi belirli aralıklarla sahneye düşman karakterleri gönderiyoruz. Eğer DusmaniBul() fonksiyonunu bir kere çalıştırırsak sadece o sırada sahnede olan düşmanları tespit ederek işlem yapacaktır. Yani sahneye bir kule eklendiği zaman sürekli olarak “DusmaniBul()” fonksiyonunu çalıştırmamız lazım yani aşağıdaki gibi “Start()” fonksiyonu içerisine aşağıdaki InvokeRepeating fonksiyonunu ekleyerek 0.5 sn bir sürekli fonksiyonun çalışmasını sağlayacağız.

private void Start()
{
    InvokeRepeating("DusmaniBul", 0f, 0.5f);
}

Artık elimizde kulemize en yakın mesafede olan düşmanın bilgilerini tutan bir değişken var ve bu bilgi değiştikçe elimizdeki değişkendeki bilgide güncelleniyor.

Fakat son bir teyit alarak sistemimizi sağlamlaştıralım ve çıkan düşman bilgisini ileride farklı bir scripte göndereceğimiz değişkene atayalım. Bunun için aşağıda işaretli olarak belirtilen kısımları “DusmaniBul” script dosyasına ekleyelim.

void DusmaniBul()
    {
        string[] dusmanEtiketleri = { "KirmiziDusman", "MaviDusman" };
        List<GameObject> dusmanlar = new List<GameObject>();


        for (int i = 0; i < dusmanEtiketleri.Length; i++)
        {
           GameObject[] dusmanlarArray = GameObject.FindGameObjectsWithTag(dusmanEtiketleri[i]);

            for (int ii = 0; ii < dusmanlarArray.Length; ii++)
            {
                dusmanlar.Add(dusmanlarArray[ii]);
            }
        }

        float enKisaMesafe = Mathf.Infinity;
        GameObject enYakinDusman = null;

        foreach (var dusman in dusmanlar)
        {

            float dusmanaUzaklik = Vector3.Distance(transform.position, dusman.transform.position);
            if (dusmanaUzaklik < enKisaMesafe)
            {
                enKisaMesafe = dusmanaUzaklik;
                enYakinDusman = dusman;
            }
        }

        if (enYakinDusman != null && enKisaMesafe <= atisMesafesi)
        {
            hedef = enYakinDusman.transform;
        }
        else hedef = null;
    }

Burada if (enYakinDusman != null && enKisaMesafe <= atisMesafesi) yazarak sahnede bir obje olduğunu ve bu objenin kulemizin atış mesafesinin içerisinde olduğunu teyit etmiş oluyoruz. Eğer bu bilgiler doğruysa public Transform hedef; yazarak oluşturduğumuz değişkene bulduğumuz en yakın düşman karakterinin “Transform” bilgisini hedef = enYakinDusman.transform; ile aktarıyoruz. Eğer if..else bloğu içindeki kriterlere uymuyorsa da “null” bir değer atıyoruz.

Kulenin Atış Sistemini En Yakın Düşmana Doğru Çevirme

En yakın düşmanı artık bulabildiğimize göre kulemizin atış sistemini düşmana doğru çevirecek yapıyı yani nişan alma sistemini oluşturmamız gerekiyor.

Unity varsayılan fonksiyonu içerisine aşağıdaki kodları ekleyelim ve sonrasında ne işe yaradıklarını inceleyelim.

private void Update()
{
    if (hedef == null) return;

    Quaternion hedefeBak = Quaternion.LookRotation(hedef.position - transform.position);
    donecekKisim.rotation = Quaternion.Euler(0f, hedefeBak.eulerAngles.y, 0f);

}
  • if (hedef == null) return; yazarak hedef değişkenine gönderdiğimiz en yakın düşman bilgisinin boş olup olmadığını kontrol ediyoruz. Eğer hedef bilgisi boşsa “Return” ile geri döndürüyoruz.
  • Quaternion hedefeBak = Quaternion.LookRotation(hedef.position - transform.position); yazarak Quaternion sınıfındaki “LookRotation” fonksiyonu ile kulenin rotasyonunu düşmana göre çevirmesini sağlıyoruz.
  • donecekKisim.rotation = Quaternion.Euler(0f, hedefeBak.eulerAngles.y, 0f); kısmında yazdığımız “Euler” sayesinde ise kulenin atış sisteminin sadece “Y” ekseni doğrultusunda rotasyonunu ayarlamasını sağlamış olduk.

LookRotation ve Euler foksiyonlarını detaylı incelediğimiz Quaternion Sınıfı dersine göz atabilirsiniz.

Sürekli olarak kulenin atış sisteminin dönmesinden bahsettiğimi fark etmişsinizdir. Biz eğer direk kule objemize bu işlemi uygularsak komple kule dönecek ve gerçekçi olmayan bir görüntü oluşturacaktır. Bu durumu gidermek için kuleler için hazırladığımız prefab şablonlarını açarak dönecek kısmı tespit etmeniz gerekecek. Tabi kule modellemesinin bu yapıya uygun olması gerekiyor ama merak etmeyin indirdiğimiz kule modellemeleri buna uygun.

Aşağıdaki gibi tüm kule şablonlarımıza girerek hedefleme sistemlerinin isimlerini “DonecekKisim” olarak değiştiriyoruz.

Kule atış bölgelerinin belirlenmesi
Kule atış bölgelerinin belirlenmesi

Son olarak her şablonun ana parent objesinin içerisine “Kule” script dosyasını ekliyoruz ve “donecekKisim” alanına dışarıdan obje atamamızı gerçekleştiriyoruz.

Dönecek kısımları scripte atıyoruz.
Dönecek kısımları scripte atıyoruz.

Eğer işlemleri başarılı bir şekilde gerçekleştirdiyseniz aşağıdaki gibi bir sonuç elde etmiş olmanız lazım.

Kule dönüşlerinin tamamlanmış hali

1. Kule – Düşmana Mermi Gönderme Sisteminin Yapılışı

Şimdi kulelerimiz düşmana göre hedef aldığına göre atış yapmanın zamanı geldi. Bu işlem için aşağıdaki maddeleri tamamlamaya ihtiyacımız olacak.

  • Mermi modellemesi
  • Merminin çıkış noktasının belirlenmesi
  • Mermiyi hedefe gönderebilmek için kodlama
  • Mermi çıkış ve düşmanı vuruş efektlerine

Dana önceden indirdiğimiz kule şablonlarının içerisinde yer alan mermi şablonlarını kullanabiliriz. “Assets/FattyPolyTurretPart2Free/Prefabs/” klasöründe yer alan “Bullet00“, “Bullet02” ve “Bullet03” isimli prefab dosyalarını alarak “Assets/Prefabs” adresine kopyalayalım ve mermi şablonlarının isimlerini “BirinciKuleMermi“, “IkinciKuleMermi” ve “UcuncuKuleMermi” olarak değiştirelim.

Mermi şablonlarımızdan sonra merminin çıkış noktasını belirleyeceğiz. Her kulenin farklı atış mekanizmaları olacağı için sırayla işlem yapacağız. Örneğin ikinci kulede iki adet füze gönderme bölümü var ve farklı bir şekilde füzeler ilerleyecek. Bu yüzden İlk olarak birinci kule için işlemlerimizi yapacağız.

Birinci kule mermi çıkış alanının belirlenmesi
Birinci kule mermi çıkış alanının belirlenmesi

Birinci kulenin mermi çıkış noktasını belirledikten sonra “Kule” C# script dosyamıza aşağıdaki eklemeleri yapıyoruz.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Kule : MonoBehaviour
{
    [SerializeField] private float atisMesafesi = 3f;
    [SerializeField] public float atisHizi = 0.5f;
    private float atisYenilemeHizi = 0f;

    private Transform hedef;
    [SerializeField] private Transform donecekKisim;
    [SerializeField] private GameObject mermiSablonu;
    [SerializeField] private Transform atisNoktasi;

    private void Start()
    {
        InvokeRepeating("DusmaniBul", 0f, 0.5f);
    }

    private void Update()
    {
        if (hedef == null) return;

        Quaternion hedefeBak = Quaternion.LookRotation(hedef.position - transform.position);
        donecekKisim.rotation = Quaternion.Euler(0f, hedefeBak.eulerAngles.y, 0f);

        if (atisYenilemeHizi <= 0f)
        {
            AtisYap();
            atisYenilemeHizi = 1f / atisHizi;
        }
        atisYenilemeHizi -= Time.deltaTime;
    }

     void AtisYap()
    {   
        GameObject olusturulanMermi = Instantiate(mermiSablonu, atisNoktasi.position, atisNoktasi.rotation);
    }

    void DusmaniBul()
    {
        string[] dusmanEtiketleri = { "KirmiziDusman", "MaviDusman" };
        List<GameObject> dusmanlar = new List<GameObject>();


        for (int i = 0; i < dusmanEtiketleri.Length; i++)
        {
            GameObject[] dusmanlarArray = GameObject.FindGameObjectsWithTag(dusmanEtiketleri[i]);

            for (int ii = 0; ii < dusmanlarArray.Length; ii++)
            {
                dusmanlar.Add(dusmanlarArray[ii]);
            }
        }

        float enKisaMesafe = Mathf.Infinity;
        GameObject enYakinDusman = null;

        foreach (var dusman in dusmanlar)
        {

            float dusmanaUzaklik = Vector3.Distance(transform.position, dusman.transform.position);
            if (dusmanaUzaklik < enKisaMesafe)
            {
                enKisaMesafe = dusmanaUzaklik;
                enYakinDusman = dusman;
            }
        }

        if (enYakinDusman != null && enKisaMesafe <= atisMesafesi)
        {
            hedef = enYakinDusman.transform;
        }
        else hedef = null;
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, atisMesafesi);
    }
}
  • [SerializeField] public float atisHizi = 0.5f; ile kulelerimizin atış hızını belirleyebileceğimiz bir değişken ekledik. Aynı şekilde private float atisYenilemeHizi = 0f; ile de kulenin atışları arasındaki mermi yenilemesi için bir değişken oluşturduk.
  • Mermilerin çıkış noktasını belirlemek için [SerializeField] private Transform atisNoktasi; ile “Transform” türünde bir değişken ve mermi şablonunu ekleyebilmemiz için [SerializeField] private GameObject mermiSablonu; ile “GameObject” türünde bir değişken ekledik.
  • atesYenilemeHizi” değişkenine başlangıçta “0” değerini verdiğimizi için Update() fonksiyonu içerisine yazdığımız if bloğunda if (atesYenilemeHizi <= 0f) ilk düşman hedefini bulduğumuzda otomatik çalışacaktır. İçerisinde ise birazdan oluşturacağımız AtesEt(); fonksiyonunu çağırıyoruz. “AtesEt” fonksiyonuyla sahnemize mermi objesini ekleyeceğiz.
  • Burada kullandığımız atisYenilemeHizi -= Time.deltaTime; ve atisYenilemeHizi = 1f / atisHizi; kısmı aslında kulemizin gönderdiği iki mermi arasındaki zamanı hesaplamak için oluşturduğumuz bir sistem. Her if bloğuna girdiğinde “atesYenilemeHizi” değişkenine 2 atanır ve “Time.deltaTime” sayesinde “Update” fonksiyonu her çalıştığında 2 değerinden 0’a doğru geriler ve 0 olduğunda tekrar if bloğuna girer.
  • “AtesEt” fonksiyonu içerisinde GameObject olusturulanMermi = Instantiate(mermiSablonu, atisNoktasi.position, atisNoktasi.rotation); yazarak “Instantiate” metodu sayesinde sahnemizde bir mermi şablonu oluşturmuş oluyoruz. Mermi şablonu, kule için belirlediğimiz “MermiCikmaNoktasi” objesinin pozisyonunda oluşacaktır.

Oyunu bu şekilde çalıştırsanız mermiler, kulelerin çıkış noktalarında oluşur ve sabit olarak pozisyonda kalırlar. Mermilerin hedefe doğru gitmesi için “Mermi” isminde yeni bir C# script dosyası oluşturarak, “BirinciKuleMermi” şablonuna ekliyoruz ve içerisine aşağıdaki kodları ekliyoruz.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Mermi : MonoBehaviour
{

    private Transform hedef;
    [SerializeField] private float hiz = 7f;

    public void HedefiAta(Transform _hedef)
    {
        hedef = _hedef;
    }

    void Update()
    {

        if (hedef == null)
        {
            Destroy(gameObject);
            return;
        }

        Vector3 mesafe = hedef.position - transform.position;
        float mesafeKare = hiz * Time.deltaTime;

        if (mesafe.magnitude <= mesafeKare)
        {
            HedefVuruldu();
            return;
        }

        transform.Translate(mesafe.normalized * mesafeKare, Space.World);

    }

    void HedefVuruldu(){

        Destroy(gameObject);
    }
}
  • İlk olarak private Transform hedef; bir hedef değişkeni oluşturduk. Merminin atılacağı düşman belirlendikten sonra “Kule” scriptinden “Mermi” scripti içerisinde yer alan Public durumdaki “HedefiAta” fonksiyonuna erişerek hedef bilgisini göndereceğiz. Böylece “Mermi” script dosyası içinde yer alan hedef değişkeninde de düşman bilgisi olmuş olacak.
  • Şimdi elimizde hedef olduğu için mermiyi hedefe doğru göndereceğiz ama öncesinde bir doğrulama yapmamız gerekiyor. Bunun için eğer hedef bilgisi boş ise yani null durumdaysa Uptade() fonksiyonunun durması ve olası bir sahnede merminin kalması durumuna karşı merminin silinmesi için Destroy(gameObject); yazdık.
  • Merminin nasıl gideceğini belirlemek için ilk başta hedef ve merminin oluşma pozisyonu arasındaki mesafeyi Vector3 mesafe = hedef.position - transform.position; yazarak aldık. Sonrasında ise float kareMesafe = hiz * Time.deltaTime; yazarak normalde bir merminin ne kadar mesafe alacağını hesapladık. Time.deltaTime konusunu önceki derslerimizde incelemiştik ama temel mantığı oyundaki bir kareden diğer kareye geçerken ki süreyi bize bildirir.
  • mesafe.magnitude <= kareMesafe ile yaptığımız şey aslında bize gelen hedefin pozisyon bilgisi ile merminin sahnede oluşturulduğu pozisyon bilgisi arasındaki farklı alarak, gerçekte merminin ne kadar mesafe gideceği bilgisiyle karşılaştırmak. “mesafe.magnitude” elde ettiğimiz Vector3 türündeki fark bilgisini uzunluk bilgisine çevirir ve “kareMesafe” ile karşılaştırır. Eğer mesafe bilgisi daha küçükse mermi hedefe ulaşmış anlamına gelmektedir. Aslında tüm bunları yapma amacımız merminin hedefe gidip gitmediğini anlamak.
  • Eğer mermi hedefe ulaştıysa da “HedefVuruldu()” fonksiyonunu çağırarak, fonksiyon içerisinde yazan Destroy(gameObject); ile mermiyi silme işlemini gerçekleştiriyoruz.
  • Tabi mermiyi hedefe doğru gönderdiğimiz komut ise “transformTranslate” fonksiyonu yani transform.Translate(mesafe.normalized*kareMesafe, Space.World); yazdığımız kısım oluyor.

Şuanda birinci kulemizden hedefe doğru merminin gitmesi ve hedefe varınca merminin yok olmasını bekliyoruz. Eğer başarılı bir şekilde adımları gerçekleştirdiyseniz aşağıdaki gibi bir sonuç elde etmeniz lazım.

Merminin hedefe gitmesi ve sonrasında sahneden silinmesi

1. Kule – Kulenin Mermi Atış ve Hedefi Vurma Efektlerinin Eklenmesi

Birinci kulemiz için mermi gönderme sistemini yaptıktan sonra biraz daha gerçekçilik katabilmek için merminin kuleden ilk çıkış anına ve hedefi vurduğu zamana efekt ekleyeceğiz.

İlk olarak hedefi vurduğumuz kısma efekt ekleyelim. Eklemeyi yapabileceğimiz en iyi kısım “Mermi” C# script dosyası içinde yer alan “HedefVuruldu()” fonksiyonu olacaktır. Mermi silinmeden hemen önce bu alanda bir efekt gösterebiliriz.

 void HedefiVur()
{
    GameObject hedefOlusanEfekt =  Instantiate(hedefVurmaEfekti, transform.position, transform.rotation);
    Destroy(hedefOlusanEfekt, 1f);
    Destroy(gameObject);
}
  • GameObject hedefOlusanEfekt = Instantiate(hedefVurmaEfekti, transform.position, transform.rotation); yazarak merminin pozisyonu ve rotasyonunda bir objeyi sahneye eklemesini söyledik. Burada “Instantiate” metodu ile sahneye eklediğimiz “hedefVurmaEfekti” objesine script üzerinden efektimizi atayacağız. Bunun için “Mermi” script dosyasının içerisine aşağıdaki değişkeni eklemeyi unutmayın.
public class Mermi : MonoBehaviour
{
    private Transform hedef;
    private float hiz = 7f; 

    public GameObject hedefVurmaEfekti;
...
...
...
  • Efektimizi sahneye ekledikten sonra Destroy(hedefOlusanEfekt, 1f); komutunu kullanarak,, eklenme işleminden 1 saniye sonra sahneden silinmesini sağladık.

Diğer ekleyeceğimiz efekt ise mermi kuleden ilk çıktığında oluşacak efekt olacak. Bu işlem için en uygun yer “Kule” C# script dosyasının içerisinde yer alan “AtisYap” fonksiyonunu olacaktır. Böylece mermi oluştuktan sonra efektimizi göstereceğiz.

void AtisYap()
{
    GameObject olusturulanMermi = Instantiate(mermiSablonu, atisNoktasi.position, atisNoktasi.rotation);
    Mermi mermi = olusturulanMermi.GetComponent<Mermi>();

    if (mermi != null)
    {
        GameObject olusanKuleEfekti = Instantiate(birinciKuleEfekti, atisNoktasi.position, atisNoktasi.rotation);
        Destroy(olusanKuleEfekti, 1f);
        mermi.HedefiAta(hedef);
    }
}
  • Aynı şekilde GameObject olusanKuleEfekti = Instantiate(birinciKuleEfekti, atisNoktasi.position, atisNoktasi.rotation); yazarak kulenin atış noktası olarak belirlediğimiz kısımda “Instantiate” metodu ile efektimizi oluşturduk. Oluşan efektimizi işi bittikten sonra sahnede yer kaplayarak performansı düşürmemesi içinde efekt oluştuktan 1 saniye sonra silmek için Destroy(olusanKuleEfekti, 1f); yazdık.
  • Tabi efekti dışarıdan eklemek için kullanacağımız GameObject türündeki değişkeni scripte eklememiz gerekiyor.
public class Kule : MonoBehaviour
{
    [SerializeField] private float atisMesafesi = 3f;
    [SerializeField] private float atisHizi = 0.5f;
    private float atisYenilemeHizi = 0f;

    private Transform hedef;
    [SerializeField] private Transform donecekKisim;
    [SerializeField] private GameObject mermiSablonu;
    [SerializeField] private Transform atisNoktasi;
    [SerializeField] private GameObject birinciKuleEfekti;

    private void Start()
...

Şimdi efektler için hazır bir kaç şablonu kullanacağız ama isterseniz Unity Particle Effect Dersleri konumuza göz atarak efekt oluşturma hakkında bilgi alabilirsiniz. Hazır verdiğim efektleri kullanmak istiyorsanız aşağıdaki bağlantıdan indirebilir ve sonrasında “Assets/Prefabs” klasörünüze kopyalayabilirsiniz. Ayrıca “Prefabs” klasörünüzün içerisinde “Effects” isminde bir klasör açarak içine atarsanız proje büyüdüğü zaman aradığınızı daha kolay bulabilirsiniz.

Birinci Kule Efektleri

Şimdi kule ve mermi şablonlarımıza girerek efektlerimizi atama işlemi yapalım. Kule için kullanacağımız efekt “BirinciKuleAtisEfekti” isimli olan ve mermi için kullanacağımız efekt ise “HedefVurmaEfekti” isimli olanlar.

Kule ve mermiye efektleri ekleme
Kule ve mermiye efektleri ekleme

Oyunumuzu çalıştırdığımızda aşağıdaki gibi kulenin atışı sırasında ve düşmanı vurduğu zaman efektlerin başarılı şekilde oluştuğunu görebilirsiniz.

Kule ve mermi efektlerinin eklenmiş hali

Ufak bir aradan sonra diğer kulelerin atış sistemleri için farklı yöntemler kullanarak efektlerini ekleme işlemini gerçekleştireceğiz. Bu ders üzerinden takip etmeye devam edebilirsiniz.