Hepinize merhabalar,
Bu derste, Unity‘nin popüler ve bir o kadar da güçlü bir özelliği olan coroutine‘lerden bahsedeceğiz. Coroutine’ler vasıtasıyla örneğin bir kodun çalışmasını birkaç saniye geciktirebilir, belli bir işlemin bitmesini bekleyebilir veya işlemi birkaç saniyeye yayabilirsiniz (bir objeyi bir yerden başka bir yere 2 saniyede hareket ettirmek gibi).
Hazırsanız derse başlayalım!
Coroutine’ler de aslında birer fonksiyondurlar ve birkaç ufak fark haricinde, aşina olduğunuz normal fonksiyonlardan bir farkları yoktur:
- coroutine’ler IEnumerator döndürmelidir. Bu arayüze erişebilmek için kodunuzun başına
using System.Collections;satırını eklemeniz gerekir. Ama belki fark ettiğiniz üzere, yeni oluşturulan script’lere Unity otomatik olarak bu satırı ekler çünkü coroutine’ler Unity’de sıklıkla kullanılmaktadır - coroutine’ler StartCoroutine fonksiyonu ile çağrılmalıdır (aksi taktirde coroutine çalışmayacaktır)
- coroutine’in içinde en az bir yield return ifadesi kullanılmalıdır
Örneğin alttaki koda bir göz atalım:
void Start()
{
Debug.Log( "Start başlangıç" );
StartCoroutine( CoroutineTest() ); // CoroutineTest coroutine'ini başlat
Debug.Log( "Start bitiş" );
}
IEnumerator CoroutineTest()
{
Debug.Log( "İlk frame" );
yield return null; // 1 frame bekle
Debug.Log( "İkinci frame" );
}
Tahmin edeceğiniz üzere, bu koddaki coroutine’imiz CoroutineTest fonksiyonu. Bu coroutine içerisine yazdığımız yield return null; satırı, coroutine’in orada 1 frame beklemesini sağlar. Coroutine’ler, onları çağıran fonksiyonlardan bağımsız olarak hareket eder. Yani bizim CoroutineTest fonksiyonunu 1 frame bekletmemiz, Start fonksiyonunu da 1 frame bekletiyor olmamız anlamına gelmez. CoroutineTest 1 frame beklerken, Start normal seyrinde çalışmaya devam eder. Bu yüzden de bu programın çıktısı şöyle olur:
Start başlangıç
İlk frame
Start bitiş
İkinci frame
Şimdi bir de şu coroutine’e göz atalım:
IEnumerator CoroutineTest()
{
Debug.Log( "Zaman: " + Time.time );
yield return new WaitForSeconds( 1.5f ); // 1.5 saniye bekle
Debug.Log( "Zaman: " + Time.time );
}
Gördüğünüz üzere, WaitForSeconds fonksiyonu vasıtasıyla bir coroutine’i belli bir süre boyunca bekletebilirsiniz. Burada dikkat etmeniz gereken nokta, bu sürenin Time.timeScale’e bağlı olmasıdır. Yani eğer timeScale’i 0.5 yaparak oyunu yavaş çekime aldıysanız, bu kod 1.5 saniye değil de 3 saniye bekler. Eğer timeScale’den bağımsız bir şekilde birkaç saniye beklemek isterseniz, WaitForSecondsRealtime fonksiyonunu kullanabilirsiniz.
Aşağıdaki örnekte ise, WWW sınıfını kullanarak internetten nasıl resim indirip bu resmi, script’in atandığı objenin materyaline atayabileceğinizi görüyorsunuz. Ayrıca bir coroutine’in parametre de alabileceğini görmüş oluyorsunuz. Bir WWW objesini yield return yaparsanız, kodunuz o WWW işleminin bitmesini bekler (bu örnekte, resmin internetten inmesini). Koddaki using ibaresinin coroutine’ler ile bir alakası olmadığı için ona çok takılmayın.
void Start()
{
StartCoroutine( ResimIndir( "http://flags.fmcdn.net/data/flags/w580/tr.png" ) ); // ResimIndir coroutine'ini, girilen parametre ile başlat
}
IEnumerator ResimIndir( string url )
{
using( WWW www = new WWW( url ) ) // WWW bir IDisposable obje olduğu için using kullanıyoruz, yani bunun coroutine'lerle bir alakası yok
{
yield return www; // Resmin inmesini bekle
if( !string.IsNullOrEmpty( www.error ) ) // Resmi indirirken bir hata alıp almadığımıza bak
Debug.LogError( "Bir hata oluştu: " + www.error );
else
{
// Bir hata yok, resmi bu objenin materyaline texture olarak ata
Texture2D resimTexture = www.texture;
GetComponent<Renderer>().material.mainTexture = resimTexture;
}
}
}
NOT: eğer WWW yerine UnityWebRequest kullanmak isterseniz, işlemin bitmesini beklemek için UnityWebRequest objesinin Send/SendWebRequest fonksiyonunu çağırınca döndürülen objeyi yield return yapabilirsiniz.
Aşağıdaki örnek kod, kodun atandığı objeyi 3 saniyede mevcut konumundan 10,10,10 konumuna taşımaya yarar:
IEnumerator ObjeyiKimildat()
{
float kimildamaSuresi = 3f; // Objeyi 3 saniyede hareket ettir
float gecenSure = 0f;
Vector3 mevcutKonum = transform.position;
Vector3 hedefKonum = new Vector3( 10, 10, 10 );
while( gecenSure < kimildamaSuresi ) // Henüz kimildamaSuresi kadar süre geçmediği müddetçe bu kodu çalıştır
{
gecenSure += Time.deltaTime; // gecenSure her saniye 1 artar
transform.position = Vector3.Lerp( mevcutKonum, hedefKonum, gecenSure / kimildamaSuresi ); // Objenin konumunu mevcutKonum ile hedefKonum arasında oynat
yield return null; // 1 frame bekle (yumuşak hareket için objenin konumunu bir anda değil, frame frame değiştirmeliyiz)
}
// kimildamaSuresi kadar süre geçti, objenin hedefKonum'a tam oturduğundan emin ol
transform.position = hedefKonum;
}
Dilerseniz, yine StartCoroutine ile bir coroutine’in içerisinden başka bir coroutine’i başlatabilir ve bu coroutine’in bitmesini beklemek için, onu yield return yapabilirsiniz (eğer beklemek istemezseniz yield return yapmak zorunda değilsiniz):
IEnumerator CoroutineTest()
{
Debug.Log( "Test2'yi beklemeden önce" );
yield return StartCoroutine( CoroutineTest2() ); // CoroutineTest2 coroutine'ini başlat ve bitmesini bekle
Debug.Log( "Test2'yi bekledikten sonra" );
Debug.Log( "Test2'yi başlatmadan önce" );
StartCoroutine( CoroutineTest2() ); // CoroutineTest2 coroutine'ini başlat ama bitmesini bekleme
Debug.Log( "Test2'yi başlattıktan sonra" );
}
IEnumerator CoroutineTest2()
{
Debug.Log( "Test2 başlangıç" );
yield return new WaitForSecondsRealtime( 1 );
Debug.Log( "Test2 bitiş" );
}
Peki diyelim ki çalışmakta olan bir coroutine’i durdurmak istiyorsanız napacaksınız? Burada ise StopCoroutine ve StopAllCoroutines fonksiyonları devreye giriyor. StopAllCoroutines fonksiyonu, bu objede StartCoroutine ile başlatılan tüm coroutine’leri durdurmaya yararken, StopCoroutine fonksiyonu ise, parametre olarak girilen belli bir coroutine’i durdurmaya yarar. Bunun için, durdurmak istediğiniz coroutine’i bir Coroutine değişkeninde tutmanız lazım. Örneğin:
private Coroutine donmeCoroutine;
void Update()
{
if( Input.GetKeyDown( KeyCode.E ) ) // E tuşuna basınca
{
if( donmeCoroutine == null ) // Çalışmakta olan bir DondurCoroutine yok, bu coroutine'i çalıştır ve değişkene at
donmeCoroutine = StartCoroutine( DondurCoroutine() );
else // DondurCoroutine çalışıyor, onu durdur
{
StopCoroutine( donmeCoroutine );
donmeCoroutine = null;
}
}
}
IEnumerator DondurCoroutine()
{
while( true ) // Bu coroutine, durdurulmadığı sürece sürekli çalışmaya devam eder
{
transform.Rotate( 0, 90 * Time.deltaTime, 0 ); // Objeyi Y ekseninde döndür
yield return null;
}
}
Bu şekilde bu dersin sonuna geldik. Daha sonraki derslerde görüşmek üzere!