Unity’de Bir Objenin Sahneler Arası Geçişlerde Yok Olmasını Önlemek + Singleton Prensibi

Yayınlandı: 03 Ağustos 2014 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde

GÜNCELLEME (01.09.2018): Kodlar C#’a çevrildi ve OnLevelWasLoaded fonksiyonu, SceneManager fonksiyonları ile değiştirildi.

Hepinize merhaba,

Bu dersimizde, Unity 3D‘de bir objenin sahneler (scene) arası geçiş yapmasını ve ne olursa olsun o objeden aynı anda sadece bir tane olmasını nasıl sağlayacağınızı göreceğiz.

Örneğin oyununuz çok zor bir runner oyunu ve oyuncu ortalama 5-10 saniyede bir ölüp duruyor. Oyuncu ölünce Application.LoadLevel ile bölüme restart atıyorsunuz diyelim. Eğer oyununuzun bir arkaplan müziği varsa her restart atışınızda bu müzik başa saracaktır ve kısa bir süre sonra bu oyuncuyu gıcık edecektir. Onun yerine oyuncu ölüp bölüme restart atılsa bile müziğin kaldığı yerden devam etmesini istiyorsunuz diyelim. İşte burada iki prensip bir arada kullanılmakta:

1- Müzik objesi scene’ler arası geçişte yok olmuyor, böylece müzik kaldığı yerden çalmaya devam ediyor

2- Bölüme restart atınca müzik objesinden elinizde iki tane oluyor: birisi önceki scene’den gelen ve çalmaya devam eden müzik objesi, öteki ise scene’e restart atınca sıfırdan oluşan müzik objesi. Bu sıfırdan oluşan müzik objesinin çalmasını istemiyoruz (singleton prensibi)

Anlayacağınız üzere, singleton prensibi bir objeden aynı anda sadece bir tane olmasını (ve bu objenin de scene’ler arası geçiş yaparken bizimle gelen obje olmasını) sağlar. Saydığım bu iki özelliği de oyununuzda uygulamak çok basit.

SingletonMuzik adında yeni bir C# script oluşturup bunu müzik objenize verin ve ardından scripti şöyle değiştirin:

using UnityEngine;

public class SingletonMuzik : MonoBehaviour
{
	private static SingletonMuzik obje = null;

	void Awake()
	{
		if( obje == null )
		{
			obje = this;
			DontDestroyOnLoad( this );
		}
		else if( this != obje )
		{
			Destroy( gameObject );
		}
	}
}

Artık levela restart attığınızda müzik kaldığı yerden çalmaya devam edecek. Harika!

Peki bu birkaç satırlık script nasıl oluyor da bu işi başarıyor? Şöyle ki, elimizde SingletonMuzik türünde static bir değişken var: “obje“. Bu static değişkenin ilk değeri “null“. “obje” değişkenimizin görevi, scene’ler arası geçiş yapan müzik objesindeki SingletonMuzik scriptini depolamak. Static değişkenlerde şöyle bir şey var ki bir static değişkenin ilk değerini alması sadece bir kere gerçekleşir. Yani “obje” değişkeni, script ilk kez çalıştırıldığında null değeri alır ama sonradan levela restart atıp scriptin yeniden çalıştırılmasını sağlasak da “obje”nin değeri tekrar otomatik olarak null’a ayarlanmaz!

Awake fonksiyonu scene açıldığında tek seferlik çalıştırılır, tıpkı Start fonksiyonu gibi. Tek fark, Awake fonksiyonu Start fonksiyonundan daha önce çalıştırılır. Bu fonksiyonda “obje“nin değerinin null olup olmadığına bakıyoruz. Eğer null ise, bunun anlamı henüz bizimle scene’ler arası geçiş yapan bir müzik objesi yok demektir (bir başka deyişle oyunu yeni başlatmışız demektir). Bu durumda obje’ye değer olarak scriptin kendisini (this) veriyoruz ve bu scriptin atandığı müzik objesinin scene’ler arası geçişlerde yok olmamasını sağlıyoruz: “DontDestroyOnLoad(this);“.

Oyuna restart atınca elimizde iki müzik objesi oluyor: bizimle scene’ler arası geçiş yapan obje ve scene açıldığında sıfırdan oluşturulan müzik objesi. Biz sıfırdan oluşturulan müzik objesinin çalmasını istemiyoruz. Neyse ki scriptin Awake fonksiyonundaki “else if” koşulu bunu bizim için yapıyor: müzik objesindeki SingletonMuzik scriptinin (this), “obje” değişkeninde depolanan script ile aynı olup olmadığına bakıyoruz. Hayır, aynı değil çünkü “obje” değişkeninde depolanan SingletonMuzik scripti, bizimle scene’ler arası geçiş yapan müzik objesindeki SingletonMuzik scripti. Bu durumda scene açıldığında yeniden oluşturulan müzik objesini “Destroy( gameObject );” fonksiyonu yardımıyla siliyoruz ve elimizde sadece scene’ler arası geçiş yapan müzik objesi kalmış oluyor.

Tek bir şeye dikkat etmeniz lazım: müzik objesi scene’ler arası geçiş yaptığı için eğer ki menüye geçiş yaparsanız müzik de sizinle beraber menüye gelecek ve burada da çalmaya devam edecektir. Bunu önlemek ve menüye dönünce müzik objesinin yok olmasını sağlamak için “SingletonMuzik“i şuna benzer bir şekilde değiştirebilirsiniz:

using UnityEngine;
using UnityEngine.SceneManagement;

public class SingletonMuzik : MonoBehaviour
{
	private static SingletonMuzik obje = null;

	void Awake()
	{
		if( obje == null )
		{
			obje = this;
			DontDestroyOnLoad( this );
			
			SceneManager.sceneLoaded += SahneYuklendi;
		}
		else if( this != obje )
		{
			Destroy( gameObject );
		}
	}

	// Yeni bir sahne yüklenince çağrılır
	void SahneYuklendi( Scene scene, LoadSceneMode mode )
	{
		if( scene.name == "AnaMenu" )
		{
			obje = null;
			Destroy( gameObject );
		}
	}
}

SceneManager.sceneLoaded += SahneYuklendi;” kodu vasıtasıyla, yeni bir scene yüklenince “SahneYuklendi” fonksiyonunu çağırıyoruz. Bu fonksiyonda, geçiş yapılan scene’in adının “AnaMenu” olup olmadığına bakıyoruz ve eğer öyleyse müzik objesini yok ediyoruz.

Umarım ders faydalı olur; başka derslerde görüşmek üzere!

yorum
  1. GençDevelop dedi ki:

    Merhaba hocam ben sahnedeki müziği kapatma butonu yapmak istiyorum player prefs olarak nasıl yapabilirim?

  2. Arda dedi ki:

    Merhaba ben yok edilen bir game objecti geri çağrabiliyormuyum yani Destroy(Elmas); dedim diyelim sonra bunu geri çağırmak istiyorum nası yaparım

    • yasirkula dedi ki:

      Maalesef mümkün değil. En basit alternatif, objeyi Destroy etmek yerine SetActive(false) ile inaktif hale getirip, geri çağırmak istediğinizde SetActive(true) ile tekrar aktif yapmak.

  3. Mustafa Efdal dedi ki:

    Hocam selamlar,
    Yakın zamanda unity’de yapımına başladığım oyunda bir hata alıyorum. İkinci level’ı direkt olarak çalıştırdığım zaman herhangi bir problem olmadan oynayabiliyorum, karakter hareket ediyor ve yaptığım tüm kodlamalar çalışıyor. Ancak oyunu en baştan başladığım zaman, main screen’den başlıyor level1’e geçiş yapıyorum oynuyorum bölüm bittikten sonra beni bir sahneye geçiş yapıyorum ve ordan tekrar next level dediğimde ikinci seviyeye geçiyor. sahne açılıyor kodladığım tuşlar çalışıyorum. mouse ile kamerayı kontrol edebiliyorum ancak karakter oynamıyor, hareket etmiyor. Bunun çözümü ne olabilir?

    Kullandığım karakter Unity’nin fpscontroller’ı.

    • yasirkula dedi ki:

      Oyunu ilk bölümden başlatınca konsolda herhangi bir hata alıyor musunuz? Eğer almıyorsanız sıkıntıyı tahmin edebileceğimi sanmıyorum, karakteri hareket ettiren kodu bulup bu kodun hangi objede olduğunu tespit etmeli ve bu objeye ne oluyor gözlemlemelisiniz.

      • Umut dedi ki:

        hocam ben alttaki fonksiyonu kullanarak müzikleri aç kapa yapıyorum oyun açıldıktan sonra sıkıntı yok oyna restart atınca bu sefer sesi aç kapa yapamıyorum sebebi nedir?

        public List ses_muzikler;

        public void ses_muzikler_duzenle(bool deger){
        foreach (AudioSource ses in ses_muzikler) {
        ses.enabled=deger;
        PlayerPrefs.SetInt(“muzik”, (deger ? 1 : 0));

        }

        }

      • Umut dedi ki:

        Hocam yukardaki scr.pti kullanıyom müzik tekrar etmesi için onu yazmayı unutmuşum bu scriptti kaldırınca herzaman oyunu aç kapa yapıyorum lakin bu scriptti ekleyince ilk seferde yapıyorum restart yapınca olmuyor?

      • yasirkula dedi ki:

        SingletonMuzik’e şu kodu ekleyin:

        public static AudioSource[] ses_muzikler = new AudioSource[0];

        void Start()
        {
        ses_muzikler = GetComponentsInChildren();
        }

        Artık ses_muzikler_duzenle fonksiyonunun içerisinde ses_muzikler değil SingletonMuzik.ses_muzikler’i kullanın.

  4. radyodunyasi5656 dedi ki:

    Abi magazadan bi obje alıp yerlestirip oyunu yeniden açınca o objenin orda olmasını anlat sana zahmet ya

    • yasirkula dedi ki:

      En basitinden PlayerPrefs kullanabileceğiniz gibi, bir xml save dosyası oluşturup bunu Application.persistentDataPath’te de tutabilirsiniz. Tabi ikinci yöntem, ilkinden daha karmaşık ama daha güçlü.

Arda için bir cevap yazın Cevabı iptal et

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.