Unıty Prefab Sahne Yöntemi

Sosyal medyada paylaşın

Bu yazıda prefab sahne kullanımından bahsedeceğim.

Prefab Sahne Nedir ?

Günümüzde birçok oyun şirketi tarafından kullanılan ve kullanılmaya başlayan bir sahne sistemidir. Genellikle yüksek sayıda level barındıran oyunlarda kullanılır. Leveli normal Scene olarak depolamaktan farkı sahnenin içindeki şeyleri Scene yerine bir GameObject’de prefab olarak tutmamız ve tüm levelleri tek bir sahne üzerinde oynatabilmemizdir. Prefab sahne yönteminin avantajları ve dezavantajları mevcut.

Avantajları;

  • Her leveli ayrı bir sahne olarak kullanmaya göre çok aşırı olmasa da hatırı sayılır derecede boyut tasarrufu sağlıyor.
  • Örneğin 100 leveliniz olsun. Normal Scene yöntemini kullanırsanız her sahne açılırken sahnedeki ışık, skybox, varsa fog vb sahne elementleri yüklenecek ve sahne silinirken de silinecek. Bu yöntemde her levelde sabit olan ne varsa onları bir kez yükletip bir kez sildiriyoruz. Bu sebeple GPU’dan bayağı tasarruf elde ediyoruz.
  • Yönetimi kolaylaştırır.

Dezavantajları:

  • Eğer leveliniz çok ağır ise yükleme ve silme işlerimlerinde kasma yaşanabilir. Bu durumda Async Operation kullanmanızı tavsiye ederim.

Bildiğim kadarıyla teorik kısmını anlatmaya çalıştım gelelim pratiğe.


Sabit Sahnemizi Oluşturalım

Öncelikle tüm levellerimizin üstünde olacağı boş bir sahne oluşturup tüm levellerde kullanacağımız ne varsa bu sahneye atıyoruz. Örneğin ışık, fog, canvas gibi. Bu sayede GPU ve belli donanım parçalarından tasarruf sağlayacağız.

Boş bir obje açıp ismini “level-1” yapıyoruz. Bu “level-1” adlı objemiz bizim o levelde kullanacağımız ve diğer tüm levellerden farklı şeyleri barındıracak. Örneğin player, engeller, toplanabilen objeler (altın, elmas vb)

“level-1” adlı objemizi Assest klasörü altında “Resources” adlı klasör oluşturup içine sürükleyip prefab haline getiriyoruz. Level sayımıza göre bu adımları tekrarlıyoruz.

Kodlamaya Geçiyoruz

Öncelikle neden static class kullandığımı açıklamak istiyorum. Örneğin karakteriniz finish çizgisine değdiği zaman bir sonraki leveli yükleteceksiniz. Bu durumda çarpışmayı algıladığınız scripte LevelManageri tanımlayıp ondan sonra o scripte geçip sonraki leveli yükleten fonksiyonu çalıştırmanız gerekecekti. Ama LevelManager’i static class ve singleton olarak tanımladığım için istediğim yerden tanımlama yapmadan LevelManager.instance diyip erişebileceğim. Static class’larında getirdiği dezavantajlar var ama bu başka bir yazıda konuşalım.

Awake methodu içinde “levelnumber” adında kaydedilmiş PlayerPrefs sınıfında bir veri var mı diye kontrol ediyoruz. Eğer varsa oyun daha önceden oynanmış anlamına geliyor. Daha sonra level spawn’ında kullanabilmek için bir int değişkene atıyoruz. Resources.Load dediğimiz satırda sadece istediğimiz dosyanın ismini string olarak veriyoruz çünkü bu kod deyimi dosyayı sadece “Resources” adlı klasörde arıyor. Öncesinde tekrar “/Resources/level-” yazmamız gerekmiyor.

Daha sonra açık leveli kapatabilmek için OpenLevel adlı Gameobject değişkenine açtığımız leveli atıyorum.

Eğer daha önce “levelnumber” adında bir veri yedeklenmediyse bu oyuna yeni başladığımız anlamına gelir ve bu durumda batka levelNumber=1 olarak tanımladığımız değişken yardımıyla level-1 adlı leveli yükletiyoruz. levelNumber adlı değişkeninin değerini başlangıçta 1 vermemin nedeni prefab level isimlerimin level-1’den başlıyor olması.

Alt kısmında Singleton kullanıyoruz. Eğer bu konu hakkında bilgiye sahip değilseniz şuradan ulaşabilirsiniz.

    private void Awake()
    {

        if (PlayerPrefs.HasKey("levelnumber"))
        {
            levelNumber = PlayerPrefs.GetInt("levelnumber");
            var level = Instantiate(Resources.Load<GameObject>("level-" + levelNumber));
            openLevel= level;
        }

        else
            openLevel= Instantiate(Resources.Load<GameObject>("level-" + levelNumber));

        if (instance == null)
            instance = this;

        DontDestroyOnLoad(this.gameObject);

    }

Bu fonksiyonda bir sonraki leveli yükletme ve şuanki açık leveli kapatma işlemlerini gerçekleştiriyoruz. Yine yukarıdaki gibi aynı mantıkla sıradaki leveli yükletip hangi levelde kaldığımızı yedeklediğimiz “levelnumber” adlı veriyi şuanki açık level numarasına eşitliyoruz.

 [ContextMenu("LOAD LEVEL")]
    public void LoadNextLevel()
    {
        DeleteLevel();
        levelNumber++;
        var scene = Instantiate(Resources.Load<GameObject>("/Resources/level-" + levelNumber));
        openLevel= scene;

        PlayerPrefs.SetInt("levelnumber", levelNumber);
        PlayerPrefs.Save();

    }

Bu fonksiyona bir sonraki leveli yükletmek istediğiniz yerden aşağıdaki kodu kullanarak erişebilirsiniz.

LevelManager.instance.LoadNextLevel();

Burada OpenLevel adlı objemiz boş değil yani herhangi bir level açıksa onu siliyoruz. Açıkcası objenin boş olması pek muhtemel değil fakat yinede ufak çapta önlem alıyoruz.

    public void DeleteLevel()
    {
        if (openLevel!= null)
        {
            Destroy(openLevel);

        }
    }

Son olarak eğer leveller bitince tekrar baştan başlasın istiyorsanız LoadNextLevel methodunda levelNumber’i kontrole tutup level sayınızdan büyükse sıfıra eşitleyip tekrar PlayerPrefs ile kaydedebilirsiniz.


Projenin tamamına buradan ulaşabilirsiniz.