Unity 3D Coroutine’ler

Yayınlandı: 20 Kasım 2018 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde
Etiketler:, , , , ,

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:

  1. 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
  2. coroutine’ler StartCoroutine fonksiyonu ile çağrılmalıdır (aksi taktirde coroutine çalışmayacaktır)
  3. 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!

yorum
  1. Batuhan Mafratoglu dedi ki:

    Abi ben playerprefs A sahnesinde altin degerini alip B sahnesindeki magza yerini yazdirmak istiyorum bunu nasil yapabilirim bi turlu yapamadim

    • Batuhan Mafratoğlu dedi ki:

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      using UnityEngine.SceneManagement;
      using UnityEngine.UI;
      using System;

      public class Menu : MonoBehaviour {

      public GameObject menucanvas;
      public GameObject settingcanvas;
      public GameObject shopcanvas;
      public GameObject infocanvas;
      public GameObject backbtn;
      public Toggle mutetog;
      public Toggle shoptog;
      public Text infotext;
      int newaltin;
      public static int totalaltin;
      public Text toplamaltintext;
      public Text bincoinstext;

      void Start ()
      {
      newaltin = PlayerPrefs.GetInt(“Altin”);
      totalaltin = Rabbit.altindeger;
      newaltin = Convert.ToInt32(toplamaltintext);

      }

      void Update ()
      {
      totalaltin += newaltin;
      PlayerPrefs.SetInt(“Altin”, totalaltin);
      toplamaltintext.text = totalaltin.ToString();

      }

      public void Playbutton()
      {
      menucanvas.SetActive(false);
      settingcanvas.SetActive(false);
      SceneManager.LoadScene(1);
      }

      public void Settingsbutton()
      {
      menucanvas.SetActive(false);
      settingcanvas.SetActive(true);
      }

      public void Shopbutton()
      {
      menucanvas.SetActive(false);
      shopcanvas.SetActive(true);

      }

      public void Backbutton()
      {
      if (mutetog.isActiveAndEnabled==true)
      {
      menucanvas.SetActive(true);
      settingcanvas.SetActive(false);
      }
      else if (shoptog.isActiveAndEnabled == true)
      {
      menucanvas.SetActive(true);
      shopcanvas.SetActive(false);
      }
      else if (infotext.isActiveAndEnabled == true)
      {
      infotext.gameObject.SetActive(false);
      settingcanvas.SetActive(true);
      backbtn.SetActive(false);
      }

      }

      public void Infobutton()
      {
      settingcanvas.SetActive(false);
      infocanvas.SetActive(true);
      infotext.gameObject.SetActive(true);
      backbtn.SetActive(true);
      }

      public void Exitbutton()
      {
      Application.Quit();
      }

      public void Bincoins()
      {
      if(Rabbit.altinvalue == 2000)
      {
      Debug.Log(“Satın alındı”);
      Rabbit.altinvalue -= 2000;
      }
      else
      {
      Debug.Log(“Para Yeterli Değil”);
      }

      }

      private void OnApplicationQuit()
      {
      PlayerPrefs.SetInt(“Altin”, totalaltin);
      }
      }

      • yasirkula dedi ki:

        A sahnesinde altının değerini kaydetmek için PlayerPrefs.SetInt(“Altin”, altın miktarı) fonksiyonunu kullanıp B sahnesinde bu değere PlayerPrefs.GetInt(“Altin”) şeklinde erişebilirsiniz. Attığınız kod static değişkenlerle vs. karmaşık geldi ben onu tam çözemedim.

  2. Dilaver dedi ki:

    Harika bir ders herkesin işine yaricaktir teşekkürler hocam

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.