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.
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.
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.
Eğer işlemleri başarılı bir şekilde gerçekleştirdiyseniz aşağıdaki gibi bir sonuç elde etmiş olmanız lazım.
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 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ı şekildeprivate 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 mermiSablon
u; 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ızAtesEt();
fonksiyonunu çağırıyoruz. “AtesEt” fonksiyonuyla sahnemize mermi objesini ekleyeceğiz. - Burada kullandığımız
atisYenilemeHizi -= Time.deltaTime;
veatisYenilemeHizi = 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çinDestroy(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 isefloat 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.
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çinDestroy(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.
Ş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.
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.
Evet arkadaşlar biraz uzun bir ara verdik ama kaldığımız yerden hızlı bir şekilde devam edeceğiz. Fakat fark ettim ki biraz detaylı yapmaya çalıştığımız için yeni öğreneceğimiz konularda biraz geride kalıyoruz. Ama üstesinden geleceğimize inanıyorum.
2. Kule ve 3. Kulenin Atış Sistemlerinin Yapılışı
Birinci kule için gerekli işlemleri yaptıktan sonra ikinci ve üçüncü kuleler için gerekli olan sistemi de ekledim. Fakat biraz düzene sokmak için birinci kule için yazdığımız kodların yapılarında da ufak değişiklikler yapmak durumunda kaldım. Aşağıda “Mermi” C# script dosyamızın tüm kodlarını gönderiyorum. Sonrasında detaylarına gireceğiz.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Mermi : MonoBehaviour { [SerializeField] private GameObject hedefVurmaEfekti; [SerializeField] private Rigidbody fizik; [SerializeField] private float guc; private bool balistikYukselis; private bool birKez; private Transform hedef; private float hiz = 7f; public void HedefiAta(Transform _hedef) { hedef = _hedef; } private void FixedUpdate() { if (gameObject.CompareTag("SavunmaFuze")) { Vector3 fark = hedef.position + new Vector3(0,.4f,0) - transform.position; float kareMesafe = hiz * Time.deltaTime; transform.Translate(fark.normalized * kareMesafe, Space.World); } else if (gameObject.CompareTag("BalistikFuze")) { if (transform.position.y < 6 && balistikYukselis == false) { fizik.AddForce(Vector3.up * guc * Time.deltaTime * hiz); } else { fizik.velocity = transform.forward * Time.deltaTime * guc; Quaternion balistik = Quaternion.LookRotation(hedef.position + new Vector3(0.2f, .5f, 0.2f) - transform.position); fizik.MoveRotation(Quaternion.RotateTowards(transform.rotation, balistik, 10f)); balistikYukselis = true; } } else if (gameObject.CompareTag("MancinikBomba")) { Debug.DrawLine(hedef.position, transform.position, Color.red); if (!birKez) { fizik.velocity = VelocityHesapla(hedef.position, transform.position, 1f); birKez = true; } } } void Update() { if (hedef == null) { Destroy(gameObject); return; } } Vector3 VelocityHesapla(Vector3 hedef, Vector3 baslangic, float sure) { Vector3 fark = hedef - baslangic; Vector3 fark_x_z = fark; fark_x_z.Normalize(); fark_x_z.y = 0f; float Sy = fark.y; float Sxz = fark.magnitude; float Vxz = Sxz / sure; float Vy = Sy / sure+ 0.5f * Mathf.Abs(Physics.gravity.y) * sure; var sonuc = fark_x_z * Vxz; sonuc.y = Vy; return sonuc; } private void OnTriggerEnter(Collider other) { if (other.gameObject.layer == 10 || other.gameObject.layer == 9) { GameObject hedefOlusanEfekt = Instantiate(hedefVurmaEfekti, transform.position + new Vector3(0,.5f,0), transform.rotation); Destroy(hedefOlusanEfekt, 1f); Destroy(gameObject); } } }
Şimdi kulelerimizin atış sistemlerini yaparken farklı yollardan ilerledik. Bu şekilde alternatif yöntemleri öğrenmiş olacağız.
Birinci kulemiz için “Translate” metodunu kullanarak mermimizin hedefe ilerlemesini sağladık ama diğer kulelerimiz için Unity oyun motorunun fizik yapısını biraz devreye sokacağız. Eğer eski koddaki değişiklikleri fark ettiyseniz, “Update” metodundan “FixedUpdate” metoduna kodlarımızı taşıdığımı da görmüşsünüzdür. Unity fizik işlemlerinde “FixedUpdate” metodunun kullanılmasını tavsiye ediyor.
İlk önce Unity Fizik konusu hakkında bilgi almak için Unity Fizik İşlemleri konumuza göz atmanızı öneririm. Eğer konuya biraz ikinci kulemiz için basit bir fizik işlemi uygulayacağız.
2. Kulenin Fizik Yapısının Kurulması
İsterseniz fizik işlemlerinden sonra 2. kulemizin nasıl bir atış sistemine sahip olduğuna bakalım.
Fark ettiyseniz mermilerimiz ilk başta yukarıya doğru hareket edip sonra hedefe doğru ilerliyorlar. Bu işlem için aşağıdaki kodumuzu uyguladık.
else if (gameObject.CompareTag("BalistikFuze")) { if (transform.position.y < 6 && balistikYukselis == false) { fizik.AddForce(Vector3.up * guc * Time.deltaTime * hiz); } else { Quaternion balistik = Quaternion.LookRotation(hedef.position - transform.position); fizik.MoveRotation(Quaternion.RotateTowards(transform.rotation, balistik, 10f)); fizik.velocity = transform.forward * Time.deltaTime * guc; balistikYukselis = true; } }
- İkinci kule mermisine “BalistikFuze” isminde bir etiket ekledikten sonra
else if (gameObject.CompareTag("BalistikFuze"))
yazarak etiketinden yakalayarak bu bloğun içerisindeki kodların çalışmasını sağladık. if (transform.position.y < 6 && balistikYukselis == false)
içerisinde yazan “transform.position.y < 6 ” ile mermimiz sahnede oluştuktan sonra Y ekseninde yani yukarı doğru 6 birimlik ilerlemesini sağlıyoruz. if bloğunda yer alan diğer kontrolümüz ise “balistikYukselis” değişkeninin değerini kontrol etmek. “balistikYukselis” değişkenine ilk başta değer atamadığımız için “false” olarak gelecek ve if bloğuna otomatik olarak girecek. Ama yükseklik 6 birimden yüksek olduğundabalistikYukselis = true;
durumuna geçmesini sağlayacağız. Böylelikle yükseldikten sonra aşağıya doğru inerken yeniden yükselmeye çalışmasını engellemiş olduk.fizik.AddForce(Vector3.up * guc * Time.deltaTime * hiz);
ile mermimize yukarı yönde(Vector3.up) bir güç uygulaması için mermimize “Rigidbody” bileşeni ekledik ve “AddForce” ile bir güç uyguladık.- Mermi objemiz yukarı doğru 6 birim çıktıktan sonra artık if bloğuna girmeyerek else bloğuna girecektir. Yani artık dönerek hedefe doğru gitmesi gerekiyor. Bunun için “Quaternion.LookRotation” sayesinde mermi ile hedef arasındaki pozisyonlarının farkını alarak merminin hedefe doğru dönmesini sağlıyoruz.
fizik.MoveRotation
ile mermimizin fizik bileşenine yani “Rigidbody” bileşenine müdahale ederek yumuşak bir şekilde dönmesini sağlıyoruz. “MoveRotation” metodu parametre olarak Quaternion türünde bir değer istediği için “Quaternion.RotateTowards” metodunu ekledik. Burada kullanabileceğimiz lerp, euler vb farklı alternatiflerimizde vardı fakat biz “RotateTowards” metodunu kullandık. “RotateTowards” metodu içerisine başlangıç ve bitiş pozisyonlarını verdikten sonra yazdığımız “10” değeri ise her bir karede maksimum kaç derece döneceğini belirtmiş olduk.- Son olarak ise
fizik.velocity = transform.forward * Time.deltaTime * guc;
yazarak mermimize fizik bileşeni sayesinde hız uygulayarak hedefe yönelmesini sağladık.
Videoda fark ettiğiniz gibi mermimizi takip eden bir iz var. Bu izi ise “Trail Renderer” bileşenini ile sağladık. “Trail Renderer” bileşenini de incelediğimiz Unity Görsek Efekt Bileşenleri dersine göz atabilirsiniz.
Hem mermi, hem kule için eklediğimiz bileşenleri ve değişiklikleri için aşağıdaki görselleri inceleyebilir ve kendi tarafınızda da değişiklikleri yapabilirsiniz.
2. Kulenin Animasyonlarının Eklenmesi
İkinci kulemizde birinci kulede olduğu gibi hem mermilerin çıkma noktasına hem de hedefi vurma anlarına animasyon ekleyerek biraz daha gerçekçi yapmaya çalışacağız.Aşağıda verdiğim bağlantı üzerinden kuleler için kullandığım animasyonları indirerek “Assets/Prefabs/” klasörünüzün altında efektler isminde bir klasör oluşturduktan sonra içine atabilirsiniz.
Aslında tüm bu klasör isimlerinin, değişkenlerin, metotların, sınıfların isimleri vs İngilizce olması sizin için çok iyi olacaktır. İngilizcesi olmayan arkadaşların ilk projede sıkılmamaları için bu şekilde yaptık fakat sonraki projelerde el alışkanlığınızın olması ve standartlara uymak için Türkçe kullanmayacağız.
Kule efektleri için birinci kulede kullandığımız “HedefVuruldu” metodunu değiştirerek Unity varsayılan fonksiyonlarından olan “OnTriggerEnter” metodunu kullanmaya karar verdik. Böylece objelerin collider ları çarpıştığı zaman “OnTriggerEnter” metodu ile otomatik olarak tespit edip animasyonumuzu gösterteceğiz.
private void OnTriggerEnter(Collider other) { if (other.gameObject.layer == 10 || other.gameObject.layer == 9) { GameObject hedefOlusanEfekt = Instantiate(hedefVurmaEfekti, transform.position + new Vector3(0, .5f, 0), transform.rotation); Destroy(hedefOlusanEfekt, 1f); Destroy(gameObject); } }
- Burada if içinde belirttiğimiz
other.gameObject.layer == 10
veother.gameObject.layer == 9
sayesinde hangi objelerle çarpıştığında merminin yok olmasını ve efekt oluşmasını istediğimiz belirtmiş oluyoruz. Tüm düşman karakterlerini “Dusman” ve tüm zemin objelerine ise “Zemin” isimde katman(Layer) oluşturarak ekledik. Daha önceden oluşturduğumuz metottan tek farkımız bu kısım.

- Sonrasında ise yine objemizi ve oluşturduğumuz objeyi yok etmek için “Destroy” metodunu kullanıyoruz. Burada
new Vector3(0,.5f,0)
eklememizin sebebi ise efektlerin biraz daha düşman karakterinin üst kısmına doğru göstermeye çalışmak.
3. Kulenin Fizik Yapısının Kurulması
Üçüncü kulemiz bir mancınık olduğu için ve yeni bir yöntem daha görebilmek için biraz daha kapsamlı bir fizik işlemi uygulayacağız. Ama şunu söylemem gerek arkadaşlar eğer kapsamlı ve gerçekçi bir oyun yapmak istiyorsanız bu işlemler bile yeterli olmayacaktır. İşte merminim havada kalma süresi, hangi açı ile göndereceği, havadaki sürtünme kuvveti vs tüm detaylara girmeniz lazım.
Fakat bu kadar detaylı bir oyun yapmıyoruz. Sanırım zamanında lisede gördüğüm ama orada öğrenemediğim eğik atış hareketi işlemini uygulayacağız. Eğik atış sistemini anlamak için aşağıdaki görsele bir göz atabilirsiniz.

Evet biraz beyin yakıyor olabilir. Biz burada objemize bir rigidbody yani fizik bileşeni ekledikten sonra uygulayacağımız velocity için Vector3 türündeki değeri hesaplamaya çalışıyoruz. Aşağıda belirttiğim “VelocityHesapla” metodu sayesinde mermiyi nasıl göndereceğimizi hesaplamış oluyoruz. Fakat buradaki fizik mantığını anlatma işini başka bir konuya saklıyorum.
Fakat detaylı bilgilerin yer aldığı aşağıdaki kaynaklara göz atarak fikir alabilirsiniz.
- https://vilbeyli.github.io/Projectile-Motion-Tutorial-for-Arrows-and-Missiles-in-Unity3D/
- https://forum.unity.com/threads/throw-an-object-along-a-parabola.158855/#post-1087673
Vector3 VelocityHesapla(Vector3 hedef, Vector3 baslangic, float sure) { Vector3 fark = hedef - baslangic; Vector3 fark_x_z = fark; fark_x_z.Normalize(); fark_x_z.y = 0f; float Sy = fark.y; float Sxz = fark.magnitude; float Vxz = Sxz / sure; float Vy = Sy / sure + 0.5f * Mathf.Abs(Physics.gravity.y) * sure; var sonuc = fark_x_z * Vxz; sonuc.y = Vy; return sonuc; }
Kodları ekledikten sonra aşağıdaki gibi bir görüntü elde edeceksiniz. Animasyon ve efektler hariç olarak düşünebilirsiniz. Birazdan bu konulara da değineceğim.
Ayrıca üçüncü kulemizin ve mermisin inspector ayarları için aşağıdaki görsele göz atabilirsiniz.
3. Kulenin Animasyon ve Efekt Sistemi
3. Kulemiz için sadece hedefi vurma anı için efekt ekleyeceğiz. Merminin çıkma anı animasyon eklemesi yapacağız. Dersimizde birde animasyon eklemesi olması için bu şekilde bir değişiklik yaptım.
Üçüncü kulemizin animasyonu da “OnTriggerEnter” metodu üzerinden zemin ya da düşman karakteriyle etkileşime girdiği zaman çalışıyor. Hem animasyon 1(1f) saniye boyunca gözüktükten sonra hem de mermimiz “Destroy” metodu ile siliniyor.
private void OnTriggerEnter(Collider other) { if (other.gameObject.layer == 10 || other.gameObject.layer == 9) { GameObject hedefOlusanEfekt = Instantiate(hedefVurmaEfekti, transform.position + new Vector3(0, .5f, 0), transform.rotation); Destroy(hedefOlusanEfekt, 1f); Destroy(gameObject); } }
3. kule için olan efekti bu bağlantıdan indirebilirsiniz. Peki animasyon için ne yaptık biraz da ona bakalım. Eğer animasyonu fark etmediyseniz mancınık mermi attıktan sonra yeniden dolma işlemi sırasındaki bölümüne bakabilirsiniz.
İlk olarak animasyonumuzu yönetmek için “Create/ Animator Controller” üzerinden bir “Animator Controller” dosyası oluşturuyoruz ve ismini “MancinikAnimasyonKontrol” olarak değiştiriyoruz.
Animator yapımızın içerisine iki adet “State” ekleyeceğiz. Bunlardan biri mancınık üzerinde bombanın durduğu ilk pozisyon olan “Ilk Pozisyon” state diğeri ise bombanın mermiyi gönderdikten sonra yeniden dolma kısmı yani “Bomba Yenileme” isimli state.
Animasyonu kontrol edebilmek içim ise “BombaYenileme” isminde boolean türünde bir parametre oluşturduk. “BombaYenileme” değişkeni true olduğu zaman mermi yenileme animasyonu çalışacak false olduğunda ise ilk pozisyonuna geçecek.
“Ilk Pozisyon” state için bir hareket kullanmıyoruz aslında kulenin şablon dosyasının varsayılan hali. Ama “Bomba Yenileme” için aşağıdaki gibi bir basit bir animasyon ekledim.
Animasyonları yazıya dökerek anlatmak gerçekten çok zor oluyor. Atlamış olabileceğim bir yer olabilir diye dosyaları bu adresten indirebilirsiniz. Tabi en önemlisi Unity animasyon dersleri konusunu incelediğinizi varsayıyorum.
Şimdi burada anlatacağımız animasyon işlemini hareket ettirebilmek için “Mermi” scripti yerine “Kule” script dosyası üzerinden gerçekleştirdik. Ayrıca bazı ek değişiklikler yaptığımız için “Kule” C# script dosyasının tamamını aşağıda gönderiyorum.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Kule : MonoBehaviour { [SerializeField] private float atisMesafesi = 3f; [SerializeField] private float atisHizi = 0.5f; [SerializeField] private Transform donecekKisim; [SerializeField] private GameObject mermiSablonu; [SerializeField] private Transform[] atisNoktasi; [SerializeField] private GameObject kuleEfekti; [SerializeField] private Transform mancinikHareketi; [SerializeField] private Animator animator; private float atisYenilemeHizi = 0f; private Transform hedef; private GameObject olusturulanMermi; private void Start() { InvokeRepeating("DusmaniBul", 0f, 0.5f); } private void Update() { if (hedef == null) return; if (transform.name == "KureBirinci(Clone)") { Quaternion hedefeBak = Quaternion.LookRotation(hedef.position - transform.position); donecekKisim.rotation = Quaternion.Euler(0f, hedefeBak.eulerAngles.y, 0f); } else if (transform.name == "KuleIkinci(Clone)") { Quaternion hedefeBak = Quaternion.LookRotation(hedef.position - transform.position); donecekKisim.rotation = Quaternion.Euler(-70f, hedefeBak.eulerAngles.y, 0f); } else if (transform.name == "KuleUcuncu(Clone)") { Quaternion hedefeBak = Quaternion.LookRotation(hedef.position - transform.position); donecekKisim.rotation = Quaternion.Lerp(donecekKisim.rotation, Quaternion.Euler(0f, hedefeBak.eulerAngles.y, 0f), 300f * Time.deltaTime); } if (atisYenilemeHizi <= 0f) { if (gameObject.CompareTag("Mancinik")) { animator.SetBool("BombaYenileme", false); while (mancinikHareketi.eulerAngles.x <= 44 && !animator.GetBool("BombaYenileme")) { mancinikHareketi.rotation = Quaternion.Lerp(mancinikHareketi.rotation, Quaternion.Euler(45f, donecekKisim.eulerAngles.y, donecekKisim.eulerAngles.z), 0.1f); } } AtisYap(); atisYenilemeHizi = 1f / atisHizi; } atisYenilemeHizi -= Time.deltaTime; } public void AtisYap() { for (int i = 0; i < atisNoktasi.Length; i++) { if (mermiSablonu.CompareTag("MancinikBomba")) { olusturulanMermi = Instantiate(mermiSablonu, atisNoktasi[i].position, atisNoktasi[i].rotation); StartCoroutine(MancinikHareketiYap()); } else { olusturulanMermi = Instantiate(mermiSablonu, atisNoktasi[i].position, atisNoktasi[i].rotation); } if (olusturulanMermi != null) { Mermi mermi = olusturulanMermi.GetComponent<Mermi>(); if (mermi != null) { mermi.HedefiAta(hedef); } } if (kuleEfekti != null) { GameObject olusanKuleEfekti = Instantiate(kuleEfekti, atisNoktasi[i].position, atisNoktasi[i].rotation); } } } public IEnumerator MancinikHareketiYap() { while (mancinikHareketi.eulerAngles.x > 0.2) { mancinikHareketi.rotation = Quaternion.Lerp(mancinikHareketi.rotation, Quaternion.Euler(0f, donecekKisim.eulerAngles.y, donecekKisim.eulerAngles.z), 0.1f); yield return null; } animator.SetBool("BombaYenileme", true); } 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); } }
“Kule” scriptine eklediğimiz aşağıdaki kısımda mancınığın mermi fırlatmasını yapacak kısmının x koordinatındaki açısını mancinikHareketi.eulerAngles.x <= 44
kontrol ediyoruz. Eğer 45 dereceden küçükse “While” döngüsü içinde kalıyor. While döngüsü içinde ise objemize “Lerp” ile yumuşak bir geçiş yaparak 45 dereceye ulaşmasını yani dolayısıyla while döngüsünden çıkmasını sağlıyoruz.
if (gameObject.CompareTag("Mancinik")) { animator.SetBool("BombaYenileme", false); while (mancinikHareketi.eulerAngles.x <= 44 && !animator.GetBool("BombaYenileme")) { mancinikHareketi.rotation = Quaternion.Lerp(mancinikHareketi.rotation, Quaternion.Euler(45f, donecekKisim.eulerAngles.y, donecekKisim.eulerAngles.z), 0.1f); } }
“AtisYap()” metodu içinde if (mermiSablonu.CompareTag("MancinikBomba"))
yazarak kulenin mancınık olduğunu doğruladığım bir if bloğu ekledim. Bu if bloğu içerisinde mermiyi “Instantiate” ile oluşturduktan sonra StartCoroutine(MancinikHareketiYap());
ile oluşturduğum “MancinikHareketiYap” isimli Coroutine metodunu çağırdım.
Corutine içinde ise nasıl mancınığı atış yapıyor gibi hareket ettirdiysek bu sefer tam tersi pozisyona getiriyoruz. Objenin x pozisyonundaki açısı 0.2’den büyük olduğunda ise merminin yenilenme animasyonu için animator.SetBool(“BombaYenileme”, true); yazdık. Bu komutla oluşturduğumuz “BombaYenileme” parametresine true değeri göndererek hazırladığımız animasyonu oynatmış oluyoruz.
public IEnumerator MancinikHareketiYap() { while (mancinikHareketi.eulerAngles.x > 0.2) { mancinikHareketi.rotation = Quaternion.Lerp(mancinikHareketi.rotation, Quaternion.Euler(0f, donecekKisim.eulerAngles.y, donecekKisim.eulerAngles.z), 0.1f); yield return null; } animator.SetBool("BombaYenileme", true); }
Devamı ne zaman gelir
Soran Adam, Yeni bir kaç projeye dahil olmak zorunda kaldım ama en kısa zamanda devamını eklemeye çalışacağım.
Selamlar Dreamon. Anlatma şeklini ve tarzını çok beğeniyorum. Oyunlardaki görev ve ödül mantığını anlatabilir misin? Mesela haritanın her hangi bir yerine 5 tane obje yerleştirdiğimizde ödül alsak veyahutta görev tamamlasak.
Teşekkürler. Aslında güzel fikir değerlendirmeye alacağım.