Unity C# Delegate ve Event’ler

Yayınlandı: 29 Ekim 2019 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde

Yeniden merhabalar,

Bu derste C#‘taki delegate ve event türlerinden bahsedip, ilaveten event’lerin Unity‘e has bir başka sürümü olan UnityEvent‘i göstereceğim. Son olarak da, konuyla epey alakalı olduğu için lambda expression konseptinden bahsedeceğim.

Hazırsanız başlayalım!

Delegate ve event; daha önce hiç karşılaşmadıysanız isimleri bile korkutucu olabilir. Ancak kullanımları nispeten basit ve C# evreninde çok fazla yerde kullanılıyorlar. Örnek vereyim mi? AdMob kullanıyorsanız, oradaki OnAdClosed ve OnAdRewarded gibi değişkenler aslında birer event. Peki nedir bu event ve delegate’ler? Cevabı basit: içerisinde değer olarak fonksiyon tutan veri türleri. Yani nasıl int içerisinde bir tamsayı, bool içerisinde true ya da false tutuyorsa, delegate ve event’ler de içerisinde A fonksiyonunu, B fonksiyonunu vs. tutar. Ardından delegate/event vasıtasıyla bu fonksiyon(lar)ı istediğimiz zaman çağırabiliriz.

Delegate

Burada bilmemiz gereken iki şey var: delegate tanımlamak (declaration) ve delegate objesi oluşturmak (instantiation). Tanımlama esnasında, bu delegate’e değer olarak verebileceğimiz fonksiyonların almaları gereken parametreleri ve döndürmeleri gereken veri türünü (void, int vs.) belirliyoruz. O delegate’ten türeyen objelere sadece o parametrelere sahip fonksiyonları değer olarak verebiliriz. Basit bir örnek görelim:

using UnityEngine;

public class DelegateTest : MonoBehaviour 
{
	// Delegate tanımlaması
	public delegate void SayiDelegate( int sayi );

	// Delegate objesi oluşturmak
	public SayiDelegate delegateObjesi;
}

Delegate tanımlamaları 5 parçadan oluşur:

  1. Delegate’in erişilebilirliği: public, protected, private. Tıpkı class’larda olduğu gibi, bu değer delegate’e dışarıdan erişip erişemeyeceğimizi belirler. Verdiğim örnekte SayiDelegate‘in erişilebilirlik değeri public olduğu için herhangi bir class içerisinde bu delegate’in objelerini oluşturabiliriz ama diyelim private olsaydı DelegateTest sınıfı haricinde SayiDelegate’e erişemezdik.
  2. delegate kelimesi, tanımlama esnasında bunu koymak zorundasınız.
  3. Hedef fonksiyonların döndürdüğü veri türü: SayiDelegate objelerine değer olarak verebileceğimiz fonksiyonların ne döndürmesi gerektiğini belirler.
  4. Delegate’in ismi: Nasıl her class’ın eşsiz bir ismi olması gerekiyorsa, her delegate’in de eşsiz bir ismi olmak zorunda. Bizim örneğimizde delegate’in ismi SayiDelegate. Delegate isimlerine büyük harfle başlamak standarttır.
  5. Hedef fonksiyonların aldıkları parametreler: SayiDelegate objelerine değer olarak verebileceğimiz fonksiyonların hangi parametreleri almaları gerektiğini belirler. Bizim örneğimizde bu fonksiyonlar bir int parametre almak zorunda.

Delegate objesi oluşturmak kısmı ise oldukça basit; burada sanki tanımladığımız delegate’i bir class’mış gibi düşünebiliriz: nasıl normalde “public GameObject go;” diye değişken tanımlıyorduysak, aynı şekilde “public Delegateİsmi delegateObjesiAdı;” şeklinde delegate objesi oluşturuyoruz. Değer olarak fonksiyon tutan şeyler işte bu delegate objeleri (örnekteki delegateObjesi değişkeni).

Şu anda delegate objemize değer olarak bir fonksiyon vermedik, o yüzden şu anda bu obje bir işimize yaramıyor. Dilerseniz nasıl bu objeye değer olarak bir fonksiyon verebileceğimize bakalım:

using UnityEngine;

public class DelegateTest : MonoBehaviour
{
	// Delegate tanımlaması
	public delegate void SayiDelegate( int sayi );

	// Delegate objesi oluşturmak
	public SayiDelegate delegateObjesi;

	private void Start()
	{
		delegateObjesi = SayiTest1; // Çalışır
		delegateObjesi = SayiTest2; // Çalışır
		//delegateObjesi = SayiTest3; // Hata: döndürdüğü veri türü yanlış
		//delegateObjesi = SayiTest4; // Hata: aldığı parametre türü yanlış
		//delegateObjesi = SayiTest5; // Hata: aldığı parametreler yanlış
	}

	private void SayiTest1( int deger ) // Parametrenin ismi delegate ile aynı olmak zorunda değil!
	{
		Debug.Log( deger );
	}

	public static void SayiTest2( int i ) // Fonksiyon static olabilir
	{
		i = i * 2;
		Debug.Log( i );
	}

	public bool SayiTest3( int sayi )
	{
		return sayi == 0;
	}

	public void SayiTest4( float f )
	{
		Debug.Log( f );
	}

	public void SayiTest5( int sayi, bool debugLog )
	{
		if( debugLog )
			Debug.Log( sayi );
	}
}

Gördüğünüz üzere, tıpkı değişkenlere “=” ile değer verdiğimiz gibi, delegate objelerine de “delegateObjesi = Fonksiyonİsmi;” şeklinde değer veriyoruz. İlk 2 fonksiyon delegateObjesi’ne sorunsuz bir şekilde değer olarak verilebilirken, diğer 3 fonksiyon hata vermekte. Bunlara sırayla bakacak olursak:

  • SayiTest1: Fonksiyon void döndürüyor ve bir int parametre alıyor, o yüzden delegate objesi için geçerli bir fonksiyon. Burada şunları öğreniyoruz: fonksiyon public olmak zorunda değil ve parametrenin ismi, delegate tanımlamasında verdiğimiz isimle aynı olmak zorunda değil. Delegate tanımlarken SayiDelegate’i public yaptık ama fonksiyon private diyebilirsiniz; SayiDelegate’in public olup olmaması sadece hangi class’larda SayiDelegate objesi oluşturabileceğimizi etkiler, ona değer olarak verdiğimiz fonksiyonların public olup olmamasını etkilemez/kısıtlamaz.
  • SayiTest2: Delegate’lere static fonksiyonları da değer olarak verebiliriz, o yüzden bu da geçerli bir fonksiyon.
  • SayiTest3: void değil bool döndürdüğü için geçersiz bir fonksiyon.
  • SayiTest4: int değil float parametre aldığı için geçersiz bir fonksiyon.
  • SayiTest5: 1 değil 2 parametre aldığı için geçersiz bir fonksiyon.

Delegate objemize değer olarak fonksiyon verdik, peki bunu nasıl çağıracağız? Çok basit:

private void Start()
{
	delegateObjesi = SayiTest1;
	delegateObjesi = SayiTest2;

	delegateObjesi( 5 );
}

Tıpkı bir fonksiyon çağırır gibi! Burada parametre olarak girdiğimiz 5 değeri, delegate objesine değer olarak verdiğimiz fonksiyonun parametresine aktarılır. Yani şu anda SayiTest2(5); ile aynı şeyi yapmış oluyoruz.

Kodu test edecek olursanız konsola “10” yazdırıldığını göreceksiniz. Peki SayiTest1’in Debug.Log yapması gereken “5” niye yazdırılmadı? Çünkü “delegateObjesi = birFonksiyon;” ibaresi kullanınca, delegate objesinin içerisinde başka bir fonksiyon varsa o fonksiyon değer olmaktan çıkar ve yeni değer birFonksiyon olur. Ama endişelenmeyin çünkü delegate objelerine birden çok fonksiyonu değer olarak verebiliriz:

private void Start()
{
	delegateObjesi += SayiTest1;
	delegateObjesi += SayiTest2;

	delegateObjesi( 5 );
}

Tek yaptığımız “=” ibaresini “+=” ile değiştirmek oldu! Artık konsola önce “5”, sonra “10” yazdırılıyor. Benzer şekilde, “-=” ile de bir fonksiyonu delegate objesinden çıkarabilirsiniz. Eğer çıkarmak istediğiniz fonksiyon halihazırda delegate objesine eklenmemişse hiçbir şey olmaz, bir hata oluşmaz.

Aynı fonksiyonu bir delegate objesine birden çok kez ekleyebilirsiniz. Bu durumda o fonksiyonu delegate’e kaç kere eklediyseniz, fonksiyon o kadar kez çağrılır. Bu yüzden bir fonksiyonun bir delegate objesine sadece bir kere eklendiğinden %100 emin olmak istiyorsanız, önce -= ve ardından += yapabilirsiniz:

delegateObjesi -= SayiTest1;
delegateObjesi += SayiTest1;

DelegateTest örneğinde sadece Start’ta delegate objesine değer veriyoruz, o yüzden burada buna gerek yok ama bazen çok daha karmaşık senaryolarla uğraşıyor ve bir fonksiyonu bir delegate’e birden çok yerde değer olarak veriyor olabilirsiniz, işte bu durumlarda bu -= ve += olayı önem kazanabilir.

NOT: İçerisinde hiç bir fonksiyon tutmayan bir delegate objesini delegateObjesi(5); şeklinde çağırmaya kalkarsanız NullReferenceException alırsınız, bunun için bir delegate’i çağırmadan önce daima delegate objesinin null olup olmadığına bakın:

if( delegateObjesi != null )
	delegateObjesi( 5 );

Delegate tanımlarken void ile kısıtlı olmadığımızdan bahsetmiştim. Burada akla şu soru gelebilir: int döndüren bir delegate’im varsa ve bu delegate objesine iki fonksiyon eklediysem, “int sonuc = delegateObjesi(5);” yaptığımda hangi fonksiyonun döndürdüğü değere erişeceğim? İşte bu güzel bir soru. Cevap: belirsiz. Eğer delegate objesindeki her iki fonksiyonun da döndürdüğü değerlere ayrı ayrı erişmek istiyorsanız kodunuzda biraz değişiklik yapmanız gerekecek:

using UnityEngine;

public class DelegateTest : MonoBehaviour
{
	// Delegate tanımlaması
	public delegate int SayiDelegate2( string yazi );

	// Delegate objesi oluşturmak
	public SayiDelegate2 delegateObjesi;

	private void Start()
	{
		delegateObjesi += SayiTest1;
		delegateObjesi += SayiTest2;

		if( delegateObjesi != null ) // Aslında delegateObjesi'nin boş olmadığını bildiğimiz için burada gerek yok
		{
			System.Delegate[] fonksiyonlar = delegateObjesi.GetInvocationList();
			for( int i = 0; i < fonksiyonlar.Length; i++ )
			{
				int sonuc = ( (SayiDelegate2) fonksiyonlar[i] ).Invoke( "Test" );
				Debug.Log( sonuc );
			}
		}
	}

	private int SayiTest1( string yazi )
	{
		return yazi.Length;
	}

	public static int SayiTest2( string y )
	{
		return -1;
	}
}

Öncelikle delegateObjesi.GetInvocationList(); ile, delegate objesindeki tüm fonksiyonları bir array’de tutuyoruz. Bu array’in türü System.Delegate; aslında tüm delegate objeleri arkaplanda birer System.Delegate sınıfı objeleridir. Fonksiyonlara eriştikten sonra, bir for döngüsü içerisinde bu fonksiyonları Invoke vasıtasıyla tek tek çağırıyoruz. Invoke kullanabilmek için öncelikle System.Delegate objelerimizi SayiDelegate2‘ye typecast yapmak zorundayız.

En neticede yukarıdaki kodu çalıştırırsanız, konsola “4” ve “-1” yazdırıldığını göreceksiniz.

Bu şekilde delegate’leri kabaca noktalamış olduk. Ama dipnot olarak şunları da ekleyelim:

  • Delegate objeleri tıpkı birer değişken gibi oldukları için, onları fonksiyonlarınıza parametre olarak verebilirsiniz.
  • Delegate tanımlamalarını bir class içerisinde yapmak zorunda değilsiniz. Örneğin yukarıdaki SayiDelegate2’yi DelegateTest sınıfı içerisinde tanımladığımız için ona diğer sınıflardan DelegateTest.SayiDelegate2 şeklinde erişmek zorundayız. Ama tanımlama işlemini class’ın dışında yaparsak, her yerden direkt SayiDelegate2 diye erişebiliriz.
  • Bir objenin fonksiyon(lar)ını bir delegate objesine eklerseniz ve daha sonra bu obje yok olursa (mesela Destroy(gameObject) ile), objenin fonksiyonları delegate objesinden otomatik olarak silinmez. Buna dikkat etmezseniz çok başınız ağrıyabilir. Bu yüzden obje yok olmadan önce elle fonksiyonları delegate objesinden çıkarmalısınız. Script’lerinizde bu işlemi OnDestroy() içerisinde yapabilirsiniz.
  • Bir kod incelerken zaman zaman System.Action veya System.Func türünde değişkenlere denk gelebilirsiniz. Bunlar da aslında birer delegate tanımlamalarıdır. Dilerseniz isimli bir delegate tanımlamak yerine (SayiDelegate), direkt bu delegate tanımlamalarını da kullanabilirsiniz. Şöyle ki:
public delegate void SayiDelegate( int sayi );
public delegate int SayiDelegate2( string yazi );
	
//public SayiDelegate delegateObjesi; // SayiDelegate ile
public System.Action<int> delegateObjesi; // System.Action ile

//public SayiDelegate2 delegateObjesi2; // SayiDelegate2 ile
public System.Func<string, int> delegateObjesi2; // System.Func ile
  • System.Action‘da “<” ve “>” işaretleri (generic) arasına, delegate’in aldığı parametreleri yazabilirsiniz. System.Action delegate’ler void döndürürler. System.Func delegate’ler ise void harici bir şey döndürürler ve bu döndürdükleri türü, “<” ve “>” işaretlerinin son parametresi olarak alırlar; ondan önceki parametreler ise, fonksiyonun aldığı parametreleri belirtir.

Event

Event’ler hemen hemen delegate objeleri ile aynı şeylerdir. İkisi de bir delegate tanımlamasına ihtiyaç duyarlar, ikisi de içerisinde değer olarak fonksiyonlar tutarlar. Ama aralarında başlıca 3 fark vardır:

  1. Event objesi tanımlarken event kelimesi kullanılmalıdır:
// Delegate tanımlaması
public delegate void SayiDelegate( int sayi );

// Delegate objesi oluşturmak
public SayiDelegate delegateObjesi;

// Event objesi oluşturmak
public event SayiDelegate eventObjesi;
  1. Event objelerine fonksiyonları sadece “+=” ile ekleyebilirsiniz, “=” yapmaya kalkarsanız hata alırsınız. Delegate’lerdeki “-=” olayı event’lerde de bulunmaktadır.
  2. Event objesindeki fonksiyonları eventObjesi() şeklinde çağırma işlemini sadece event objesinin tanımlandığı sınıfta yapabilirsiniz. Örneğin eventObjesi’ni A sınıfında tanımladıysanız, A sınıfı harici bir sınıf eventObjesi(); kodunu çalıştıramaz.

UnityEvent

Hiç UI Button objesinin On Click event’ine ve bu event’e nasıl Inspector’dan fonksiyonlar ekleyebildiğinize dikkat ettiniz mi? On Click aslında bir UnityEvent’tir. İşte UnityEvent’in event’e göre en büyük artısı budur: kendisine illa koddan değil, Inspector’dan da fonksiyon ekleyebilirsiniz. Ama bunun yanında önemli bir eksisi de vardır: System.Func gibi delegate’lerin aksine, UnityEvent’ler void harici bir şey döndürmeyi desteklemezler.

İsterseniz daha önceki SayiDelegate örneğimizi UnityEvent’e çevirmeyi görelim:

using UnityEngine;
using UnityEngine.Events; // <-- Buraya dikkat!

public class DelegateTest : MonoBehaviour
{
	// Delegate tanımlaması
	public delegate void SayiDelegate( int sayi );

	// UnityEvent tanımlaması
	[System.Serializable] public class SayiUnityEvent : UnityEvent<int> { }

	// Delegate objesi
	public SayiDelegate delegateObjesi;

	// UnityEvent objesi
	public SayiUnityEvent unityEventObjesi;

	private void Start()
	{
		// Fonksiyon eklemek
		delegateObjesi += SayiTest1;
		unityEventObjesi.AddListener( SayiTest1 );

		// Fonksiyonları çağırmak
		if( delegateObjesi != null )
			delegateObjesi( 5 );

		if( unityEventObjesi != null )
			unityEventObjesi.Invoke( 5 );

		// Fonksiyon çıkarmak
		delegateObjesi -= SayiTest1;
		unityEventObjesi.RemoveListener( SayiTest1 );

		// Tüm fonksiyonları çıkarmak
		delegateObjesi = null;
		unityEventObjesi.RemoveAllListeners();
	}

	private void SayiTest1( int deger )
	{
		Debug.Log( deger );
	}
}
  • UnityEvent’lerle çalışırken script’inizin başına “using UnityEngine.Events;” eklemelisiniz.
  • UnityEvent tanımlaması, UnityEvent class’ından türeyen ve System.Serializable attribute‘üne sahip yeni bir sınıf oluşturarak yapılır.
  • +=“in karşılığı AddListener, “-=“in karşılığı RemoveListener‘dır.
  • UnityEvent’e atalı fonksiyonlar Invoke ile çağrılır.

Bu noktalara dikkat ederseniz, Inspector’dan da üzerine fonksiyon eklemesi yapabildiğiniz event’lere sahip olacaksınız:

Lambda Expression

Kulağa delegate ve event’ten daha korkutucu gelen bir şey varsa herhalde o da lambda expression’dır. Ancak aslında kullanımı çok kolaydır. Kabaca bahsedecek olursak, lambda expression dediğimiz şey, kod içerisinde dinamik olarak isimsiz fonksiyonlar oluşturmaya yarar. C# dünyasında lambda expression’lar, delegate’ler ile birlikte kullanılırlar. O halde bodoslama dalış yapalım:

using UnityEngine;

public class DelegateTest : MonoBehaviour
{
	public delegate void SayiDelegate( int sayi );

	public SayiDelegate delegateObjesi;

	private void Start()
	{
		delegateObjesi += ( int deger ) =>
		{
			Debug.Log( deger );
		};

		delegateObjesi( 5 );
	}
}

Burada, SayiTest1 fonksiyonumuzu şu şekilde bir lambda expression’a çevirdik:

( int deger ) =>
{
	Debug.Log( deger );
};

Lambda expression’lar şu şekilde tanımlanır:

  1. Normal bir fonksiyon tanımlar gibi, parantezler açılır ve içerisine parametreler girilir.
  2. Lambda expression’lara has “=>” (lambda işleci) konulur.
  3. Süslü parantezler içerisine fonksiyonun kodu yazılır.

İstersek bu lambda expression’ı şu şekilde de yazabiliriz: “( deger ) => Debug.Log( deger );“. Çünkü tek satırlık gövdeye sahip lambda expression’larında süslü parantezler zorunlu değildir. İlaveten, lambda expression’ı değer olarak verdiğimiz delegateObjesi‘nin int parametre aldığı zaten bilindiği için, parametrenin türünü lambda expression’da tekrardan yazmak zorunda değiliz.

Lambda expression’ları çok güçlü yapan özelliklerden birisi, kendisini çevreleyen fonksiyonun parametrelerine ve değişkenlerine erişim imkanı olmasıdır:

using UnityEngine;

public class DelegateTest : MonoBehaviour
{
	public delegate void SayiDelegate( int sayi );

	public SayiDelegate delegateObjesi;

	private void Start()
	{
		int sayi = 10;
		delegateObjesi += ( deger ) => Debug.Log( deger * sayi );

		delegateObjesi( 5 );
	}
}

Bu kodu çalıştırınca konsola “50” bastırılır. Burada dikkat etmeniz gereken bir nokta, for loop’larında sıklıkla kullanılan i değişkenini lambda expression’a dahil etmek istediğinizde karşınıza çıkar:

private void Start()
{
	for( int i = 1; i <= 5; i++ )
	{
		delegateObjesi += ( deger ) => Debug.Log( deger * i );
	}

	delegateObjesi( 5 );
}

Bu kodu çalıştırırsanız, konsola “5”, “10”, “15”, “20”, “25” bastırılmasını beklerken onun yerine beş defa “25” bastırılır (C#’ın yeni sürümlerinde bu sorun giderilmiş olabilir). Bu sorunu şu şekilde çözmek zorundasınız:

for( int i = 1; i <= 5; i++ )
{
	int iKopya = i;
	delegateObjesi += ( deger ) => Debug.Log( deger * iKopya );
}

Yani for‘un içinde i değişkeninin bir kopyasını oluşturup, lambda expression’da bu kopya değişkeni kullanmalısınız.

Lambda expression’larla ilgili çok dikkat edilmesi gereken bir nokta, “-=” kullanımıdır. Lambda expression’lar bunu doğrudan desteklemez. Örneğin şu koddaki -= satırı bir işe yaramaz:

delegateObjesi += ( deger ) => Debug.Log( deger * iKopya );
delegateObjesi -= ( deger ) => Debug.Log( deger * iKopya );

Çünkü her yeni lambda expression, aslında arkaplanda ayrı bir fonksiyondur. Lambda expression’larda “-=“i ancak şu şekilde destekleyebilirsiniz:

private void Start()
{
	SayiDelegate lambdaObjesi = ( deger ) => Debug.Log( deger );
	delegateObjesi += lambdaObjesi;

	delegateObjesi( 5 );

	delegateObjesi -= lambdaObjesi;
}

Yani önce lambda expression’ı bir delegate objesine (lambdaObjesi) değer olarak vermeli, ardından artık bu delegate objesini “+=” ve “-=” ile kullanmalısınız.

Bonus: Örnek Kod

Yazıya son vermeden önce, dilerseniz lambda expression’ları ciddi bir örnek üzerinde görelim. Bu örnekte GecikmeliCalistir.Geciktir isminde bir fonksiyon yer almakta; bu fonksiyon, kendisine parametre olarak girilen delegate objesini birkaç saniye gecikmeli olarak çağırmaya yarıyor. Örnek kodda bu fonksiyonu çağırırken, delegate objesine değer olarak bir lambda expression giriyoruz. Bu şekilde o lambda expression’ın gecikmeli olarak çağrılmasını sağlıyoruz:

using System.Collections;
using UnityEngine;

public class GecikmeliCalistir : MonoBehaviour
{
	// Bu class singleton prensibi üzerine kurulu, yani bu class'tan oyun esnasında sadece bir tane olacak
	private static GecikmeliCalistir instance;

	// "fonksiyon" delegate objesini saniye kadar geciktirerek çalıştırır
	// Delegate tanımlamamız System.Action, yani parametre almayan ve void döndüren fonksiyonlar
	public static void Geciktir( float saniye, System.Action fonksiyon )
	{
		// Eğer singleton objemiz henüz yoksa, yeni bir obje oluşturup onu singleton olarak ayarla
		if( instance == null )
		{
			instance = new GameObject().AddComponent<GecikmeliCalistir>();

			// Singleton objenin sahneler arası geçişlerde yok olmasını önle
			DontDestroyOnLoad( instance.gameObject );
		}

		// Delegate objesi null değilse bir coroutine başlat
		if( fonksiyon != null )
			instance.StartCoroutine( SaniyeliGeciktirCoroutine( saniye, fonksiyon ) );
	}

	// "saniye" kadar bekledikten sonra "fonksiyon" delegate objesini çağıran coroutine
	// Coroutine'ler hakkında daha fazla bilgi için: https://yasirkula.com/2018/11/20/unity-3d-coroutineler/
	private static IEnumerator SaniyeliGeciktirCoroutine( float saniye, System.Action fonksiyon )
	{
		// "saniye" kadar bekle
		yield return new WaitForSeconds( saniye );

		// "fonksiyon" delegate objesindeki fonksiyonları çağır
		fonksiyon();
	}
}
void Start()
{
	// Delegate objesi (bu örnekte aşağıdaki lambda expression) çağrılmadan önceki süreyi (Time.time) depola
	float geciktiktenOnceZaman = Time.time;

	// Lambda expression'ı 2.5 saniye gecikmeli çalıştır
	GecikmeliCalistir.Geciktir( 2.5f, () =>
	{
		// Bu kod her ne kadar Start fonksiyonu içerisinde yer alsa da, aslında Start fonksiyonundan
		// 2.5 saniye sonra çağrılıyor. Bunu göstermek için tekrar Time.time'ı bir değişkene at
		// ve iki Time.time değerini konsola yazdır
		float geciktiktenSonraZaman = Time.time;
		Debug.Log( "Önce: " + geciktiktenOnceZaman + ", Sonra: " + geciktiktenSonraZaman );
	} );
}

Ve bu şekilde bu yazımı noktalıyorum. Biraz uzun oldu ama umarım faydalı olmuştur. Sonraki derslerde görüşmek ümidiyle!

yorum
  1. Kaan dedi ki:

    Hocam merhaba,
    Delegate’lerdeki “-=” olayı event’lerde de bulunmaktadır. Demişsiniz, delegelerde çakışma yaşanmaması durumlarda fonksiyonun çıkarılmadı gerekiyor. Peki eventlerde bu olay zorunlu mu? Örneğin karatker silindi eventi çıkarmak zorunda mıyız?

  2. Emre dedi ki:

    Merhaba hocam. event Action ile Action arasında ne fark var? deneme yaptığımda bir işlemi ikisi ile de yapabiliyorum ama aradaki farklarını anlayamadım.

    • yasirkula dedi ki:

      Bu farkları dersteki Event başlığı altında listeledim. Burada kafanıza takılan noktalar varsa hangileri olduğunu belirtirseniz ona göre yardımcı olmaya çalışırım.

      • Emre dedi ki:

        Sağolun hocam. Anladığım kadarıyla, Player engele çarptığında event action ile de action ile aynı durumu çağırabiliriz. Farkları classlar arası çağırabilirliği. Bunun oyuncuya etkisi olmuyor.

      • yasirkula dedi ki:

        Evet oyuncu için event olup olmaması bir fark oluşturmuyor.

  3. Kadir dedi ki:

    süper yazı olmuş. benim gibi yeni başlayanlar için çok açıklayıcı.

  4. Emre dedi ki:

    Hocam merhaba bu güzel yazı için teşekkür ederim. Bir sorum var. Oyun ekranımda farklı işlevleri olan buttonlarım var fakat ben bunları tıkladığım anda değil , bu buttonların hepsini kapsayan farklı bir butona bastığımda, bu buttonların işlevlerinin tıkladığım sıraya göre otomatik olmasını istiyorum. Bunu yapmak için hangi methodları kulanmam gerekir,Event,Delegate kullanmam lazım mı, Invoke Repeating methodu mu yoksa Coroutune mi kullanmalıyım kafam karıştı. Sizin tavsiyenize ihtiyacım var.

  5. Ahmet dedi ki:

    Çok iyisiniz çok TEŞEKKÜRLER

  6. Salih dedi ki:

    Hocam selamlar. Konuyla alakalı mı değil mi bilmiyorum. Oyunu ilk açışımıza özel bir sahneyi (bir cutscene ve ardından oynanabilir bir bölüm. Tutorial kısmı gibi yani) nasıl yapabiliriz?
    2. sorum ise açık dünya bir yarış oyunu yapıyorum. Bir yarışa girdikten hemen sonra çok kısa bir cutscene ( sadece kameralar ile) gösterilecek, ardından da oyuna geçilecek. Biraz uğraştım ancak cutscene kısmından sonra kamera, olması gereken pivot’a dönmedi. Bunu nasıl yapabilirim? Daha doğru bir yolu var mı? Teşekkürler

    • yasirkula dedi ki:

      Kamera hareketleri için Cinemachine kullanabilirsiniz. Cutscene’lerinizde hazır bir mp4 video oynatmayacaksanız, Timeline ile değişik animasyonları harmanlayarak Unity içerisinden cutscene oluşturabilirsiniz. Bu asset’lerle alakalı internette çok sayıda ders bulabilirsiniz.

  7. Süleyman dedi ki:

    Güzel anlatımınız için teşekkürler.

  8. kareka17 dedi ki:

    Kolay gelsin,
    Konuyla alakalı değil ama çözümünü bir türlü bulamadığım için size danışmak istedim. 2 soru sormak istiyorum. Birincisi:
    Mesela bir alarm uygulaması tarzı bir şey yaptım diyelim ve alarmın çalmasına 10 dakika kalmış olsun. Uygulama açıkken hiçbir problem yok ama uygulamayı kapattıktan sonra tekrar girdiğimde yine 10 dakika kalmış oluyor. Yani uygulamayı arka plan da çalıştırmaya nasıl devam edebilirim ? (Bunu farm oyunları içinde kullanmak istiyorum mesela adam 1 saatlik domates ekti ama oyunu kapattı, bu 1 saat devam etmiyor bunu devam ettirmek istiyorum)
    İkinci olarak bu uygulama süre dolduğu zaman kullanıcıya bildirim gönderebilir mi ?
    Cevabın için şimdiden çok teşekkür ederim..

    • yasirkula dedi ki:

      Oyundan çıkmadan önce DateTime.UtcNow’ın değerini bir yere kaydedebilir (mesela PlayerPrefs), oyunu tekrar açınca bu değer ile yeni DateTime.UtcNow arasındaki saniye farkını ölçebilirsiniz. PlayerPrefs kullanacaksanız, DateTime’ı ToString ile string’e çevirip kaydetmeli, yeniden yüklerken de string’i DateTime.Parse fonksiyonu ile DateTime’a çevirmelisiniz. Eğer oyun ilk kez açıldıysa, PlayerPrefs.HasKey fonksiyonu false döndürecektir, bu durumda DateTime.Parse yapmaya çalışmamalısınız.

      İkinci dediğiniz local notification ve/veya push notification’a giriyor ama benim bu konularda bir tecrübem yok maalesef.

Cevap Yazın

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.