Unity 3D Basit Bir Loading Ekranı Yapımı

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

SON GÜNCELLEME: 08.08.2019 (Kod C#’a çevrildi)

Hepinize yeniden merhabalar,

Bu dersimizde Unity için basit bir loading ekranı yapacağız. Bu loading ekranı ile scene’ler arası yumuşak bir şekilde geçiş (transition) yapabilecek ve sonraki sahnenin % kaçının yüklendiğini oyuncuya gösterebileceğiz.

O halde son gaz derse başlayalım…

İlk iş yeni bir sahne açalım ve GameObject-UI-Panel yolunu izleyerek sahnede bir panel oluşturalım. Panele “Arkaplan” ismini verelim. Bu paneli loading ekranının arkaplanı için kullanacağız. Bu derste arkaplanımız simsiyah olacak. O halde Panel’in Image component‘inin Color (renk) değerini siyah yapın ve A (alpha) değerini 255 yapın:

resim1

Son olarak da “Source Image” değişkeninin değerini None yapın çünkü arkaplan için referans bir resme ihtiyacımız yok, arkaplan sadece siyah renkten oluşacak.

Şimdi GameObject-UI-Text ile sahnede bir yazı oluşturun ve yazıya “Durum” ismini verin. Yazının rengini istediğiniz gibi değiştirin, ben beyaz yaptım. Ayrıca dilerseniz Add Component-UI-Effects-Outline yolunu izleyerek yazıya bir dış hat da ekleyebilirsiniz. Ben mavi bir dış hat verdim:

resim2

Text component‘inin “Text” değişenine değer olarak “Yükleniyor %0” verin. Ardından anchor’lar ve font size ile oynayarak yazının ekranda kapladığı alanı genişletin zira şu anda yazı çok ufak. Benim yaptığım ayarlamalar şöyle:

resim3

Arayüzümüz neredeyse hazır. Hierarchy‘den Canvas objesini seçin ve Canvas component‘indeki “Sort Order“ı 100 yapın. Bir sahnede birden çok canvas varsa en üste sort order’ı büyük olan canvas çizilir. Biz loading ekranının daima en üstte olmasını istiyoruz.

Bence arayüzümüz tamamlandı. Şimdi bu arayüzü bir prefab‘a çevirelim. Neden diye sorabilirsiniz. Çünkü bu loading ekranından her sahneye bir tane koymakla uğraşmak istemiyorum. Ne zaman ki loading ekranını kullanacağız, o vakit bu prefab’ı kod vasıtasıyla Instantiate edeceğiz. Instantiate edebilmek için ise objeyi prefab yapmalıyız. O halde Project panelinde “Resources” adında yeni bir klasör oluşturun (sebebini kodu yazarken açıklayacağım) ve Canvas objesini Resources klasörünün içine sürükle-bırak yaparak bir prefab’a çevirin. Prefab’ın ismini “LoadingEkrani” yapın:

resim4

Kod yazmaya başlayabiliriz. “LoadingEkrani” adında yeni bir C# scripti oluşturun ve içini şöyle değiştirin:

using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class LoadingEkrani : MonoBehaviour
{
	// LoadingEkrani scriptine kolayca erişebilmek için static değişken
	private static LoadingEkrani instance = null;

	public Image panel;
	public Text yazi;

	private Color panelRenk;
	private Color yaziRenk;

	// Değeri arttıkça yumuşak geçiş animasyonu hızlanır
	public float animasyonHizi = 1.25f;

	// Level yükleme işlemi
	private AsyncOperation loadingIslemi;

	// Yeni bir level yükle
	public static void LevelYukle( string levelAdi )
	{
		// Eğer sahnede bir LoadingEkrani yoksa yeni bir tane oluştur
		if( instance == null )
		{
			instance = Instantiate( Resources.Load<LoadingEkrani>( "LoadingEkrani" ) ); // Resources klasöründen LoadingEkrani prefab'ını yükle
			DontDestroyOnLoad( instance.gameObject ); // Loading ekranı sahneler arası geçişte kaybolmasın
		}

		// Loading ekranını aktifleştir
		instance.gameObject.SetActive( true );

		// Yeni leveli yüklemeye başla
		instance.loadingIslemi = SceneManager.LoadSceneAsync( levelAdi );

		// Yeni levelin yüklenmesi tammalansa bile hemen yeni levela geçiş yapma
		instance.loadingIslemi.allowSceneActivation = false;
	}

	void Awake()
	{
		// Panel ve yazının renklerini değişkenlerde depola
		panelRenk = panel.color;
		yaziRenk = yazi.color;

		// En başta loading ekranını görünmez yap
		panelRenk.a = 0;
		yaziRenk.a = 0;

		// Görünmez yaptığımız renkleri panel ve yazıya değer olarak ver
		panel.color = panelRenk;
		yazi.color = yaziRenk;
	}

	void Update()
	{
		// Yükleme yüzdesini güncelle
		yazi.text = "Yükleniyor %" + (int) ( loadingIslemi.progress * 100 );

		// Yükleme tamamlandıysa
		if( loadingIslemi.isDone )
		{
			// Loading ekranını yumuşak bir şekilde kaybet (fade out)
			panelRenk.a -= animasyonHizi * Time.deltaTime;
			yaziRenk.a -= animasyonHizi * Time.deltaTime;

			panel.color = panelRenk;
			yazi.color = yaziRenk;

			// Loading ekranı tamamen kaybolduysa objeyi deaktif et
			if( panelRenk.a <= 0 ) // >
			{
				gameObject.SetActive( false );
			}
		}
		else // Yükleme işlemi bitmediyse
		{
			// Loading ekranını yumuşak bir şekilde görünür yap (fade in) 
			panelRenk.a += animasyonHizi * Time.deltaTime;
			yaziRenk.a += animasyonHizi * Time.deltaTime;

			panel.color = panelRenk;
			yazi.color = yaziRenk;

			// Loading ekranı tamamen görünür hale geldiyse 
			if( panelRenk.a >= 1 )
			{
				// Artık yükleme işlemi bitince yeni levela geçebilelim
				loadingIslemi.allowSceneActivation = true;

				// Renklerin görünürlüğü 1'in üzerine çıkmamalı
				panelRenk.a = 1;
				yaziRenk.a = 1;
			}
		}
	}
}

Bu kod biraz uzun durabilir ama aslında anlaması zor olmayan bir kod. Kısaca kodu açıklayacak olursam:

static bir değişken (instance) vasıtasıyla singleton pattern‘den faydalanıyoruz. Loading ekranımızdan oyunda aynı anda sadece bir tane olmalı ve static değişken sayesinde bunu sağlayabiliyoruz.

animasyonHizi adında bir değişkenimiz var. Bu değişkenin değeri 1 olursa loading ekranının belirme süresi 1 saniye olurken değişkenin değeri 2 olursa loading ekranının belirme süresi 0.5 saniye olur. Yani bu değişkenin değeri arttıkça loading ekranının belirme süresi kısalıyor.

loadingIslemi dediğimiz değişken ise, oyun sırasında arkaplanda başka bir level yüklememize olanak sağlıyor. Böylece oyuncu mevcut leveli görürken aslında arkaplanda yeni bir level yüklenmekte oluyor.

Scriptimizde LevelYukle adında static bir fonksiyon bulunmakta. Yeni bir level yüklerken kullanmanız gereken fonksiyon bu. Yani artık SceneManager.LoadScene(“level1”) yazmak yerine LoadingEkrani.LevelYukle(“level1”) yazmalısınız. Bu fonksiyonun içerisinde öncelikle singleton’u sağlıyoruz. Yani eğer sahnemizde bir loading ekranı objesi yoksa yeni bir tane oluşturuyoruz (Instantiate) ve DontDestroyOnLoad komutu ile sahneler arası geçişlerde bu objenin yok olmasının önüne geçiyoruz. Instantiate fonksiyonu içerisinde referans objeyi çekerken Resources.Load fonksiyonunu kullanıyoruz. İşte Resources klasörü burada devreye giriyor. Scriptlerinizden Project klasöründeki bir asset’e erişmek istiyorsanız, yapmanız gereken şey o asset’i Resources klasörü içine atmak. Artık Resources.Load fonksiyonu ile o asset’e erişebilirsiniz. Fonksiyondaki “<LoadingEkrani>” kısmını yadırgayabilirsiniz, generic fonksiyonlar bu şekilde çağrılır. Bu kısmın yaptığı şey, yüklediğimiz asset’in türünü belirlemektir.

Singleton’u hallettikten sonra SceneManager.LoadSceneAsync fonksiyonu vasıtasıyla, yeni levelin arkaplanda yüklenmesi işlemini resmî olarak başlatıyoruz. loadingIslemi değişkeninin allowSceneActivation değişkeniniyse false yapıyoruz. Eğer bu değişken false olursa, arkaplandaki levelin yüklenmesi işlemi bitse dahi o levele anında geçiş yapılmaz. Bunu yapıyoruz çünkü diğer levele geçmeden önce loading ekranımızın ekranda tamamen belirmesini istiyoruz. Böylece yeni levela geçince sahne gözümüz önünde küt diye değişmeyecek çünkü loading ekranımız sahneyi kaplayarak bizim bu geçişi görmemizi engelleyecek.

Awake fonksiyonu, obje oluşturulduğu anda tek seferlik çalıştırılır. Bu fonksiyonda tek yaptığımız şey loading ekranını görünmez kılmak. Zira en başta loading ekranı yumuşak bir şekilde yoktan belirecek.

Update fonksiyonu ise yumuşak belirme/kaybolma animasyonlarını kodladığımız yer. Öncelikle sahnenin % kaçının yüklendiğini Durum yazısına iletiyoruz. Sahnenin % kaçının yüklendiğini loadingIslemi‘nin progress değişkeni depolamakta. Bu değişken 0 ile 1 arasında bir değer alır (örneğin 0.76). Bu değeri 100 ile çarparak %’lik hale dönüştürüyoruz (%76 gibi).

loadingIslemi‘nin isDone adında bir değişkeni bulunmakta. Diğer levelin yüklenmesi tamamlandıysa ve loadingIslemi’nin allowSceneActivation’ı true ise bu değişken true değeri alır, yoksa false değeri alır. Eğer diğer levela geçiş yaptıysak (isDone true ise), loading ekranının ekrandan yumuşak bir şekilde kaybolmasını ayarlıyoruz. Bunun için Panel’in ve Text’in color değişkenlerinin alpha‘sını (görünürlük) azaltıyoruz. Eğer loading ekranı tamamen kaybolduysa, objeyi deaktif ederek artık Update’in çalışmasını engelliyoruz.

Eğer isDone false ise, yani diğer level hâlâ yükleniyor ise, loading ekranını yumuşak bir şekilde görünür yapıyoruz. Loading ekranı tamamen görünür hale geldiğinde ise allowSceneActivation‘ı true yapıyoruz, böylece yeni levelin yüklenmesi tamamlandığında artık yeni levela güvenli bir şekilde geçiş yapabiliyoruz.

Basit anlamda scriptimiz böyle. Belki çok fazla yeni fonksiyon ve değişken görmüş ve biraz afallamış olabilirsiniz. Ama eminim ki takıldığınız noktaları Google amcaya sorarak çözebilirsiniz. Şimdi isterseniz scripti loading ekranına verelim. Bunun için scripti sürükleyerek sahnemizdeki LoadingEkrani objesine (Canvas) verin. Ardından Panel ve Yazi public değişkenlerine Inspector‘dan değerlerini verin:

resim5

Son olarak da bu değişiklikleri prefab’a uygulamak için Inspector‘un tepesindeki Apply butonuna tıklayın (Unity’nin son sürümlerinde Apply butonu yerine Overrides-Apply All butonu vardır). İşte bu kadar! Artık sahnedeki canvas objesini rahatça silebilirsiniz. Geriye sadece sistemi test etmek kalıyor. Bunun içinse tek yapmanız gereken, kendi kodlarınızdaki SceneManager.LoadScene fonksiyonlarını LoadingEkrani.LevelYukle fonksiyonu ile değiştirmek.

Umarım faydalı olur, sonraki derslerde görüşmek dileğiyle!

yorum
  1. Bayram dedi ki:

    hocam buttonların child olarak text mesh proları var.Level butonlarınada 1 levelden 10.levele kadar olan butonları attım ama bu hataları alıyorum

    NullReferenceException: Object reference not set to an instance of an object
    level_menu_button.LevelButonlariniGuncelle () (at Assets/kodlar/level_menu_button.cs:47)
    level_menu_button.SagaGit () (at Assets/kodlar/level_menu_button.cs:40)

  2. Bayram dedi ki:

    Hocam diyelim oyun 50 levelli olsun ve her sayfada 10 level görünsün ve sağa,sola butonlar yerleştirip bu butonlara basınca leveller menüsünde gezebilsin şuradaki butonlar gibi
    https://i.hizliresim.com/JVaYWE.jpg
    Bunu nasıl yapabilirim?

    • yasirkula dedi ki:

      Şuna benzer bir kod yazabilirsiniz:

      public Button[] levelButonlari;
      private int ilkLevel = 1, sonLevel = 10;
      
      void Start()
      {
      	LevelButonlariniGuncelle();
      	
      	for( int i = 0; i < 9; i++ )
      	{
      		int j = i;
      		levelButonlari[i].onClick.AddListener( () => LevelButonunaBasildi( j ) );
      	}
      }
      
      public void SolaGit()
      {
      	if( ilkLevel > 10 )
      	{
      		ilkLevel -= 10;
      		sonLevel -= 10;
      		
      		LevelButonlariniGuncelle();
      	}
      }
      
      public void SagaGit()
      {
      	if( sonLevel <= 40 )
      	{
      		ilkLevel += 10;
      		sonLevel += 10;
      		
      		LevelButonlariniGuncelle();
      	}
      }
      
      private void LevelButonlariniGuncelle()
      {
      	for( int i = 0; i < 9; i++ )
      		levelButonlari[i].GetComponentInChildren<Text>().text = ( ilkLevel + i ).ToString();
      }
      
      private void LevelButonunaBasildi( int index )
      {
      	int levelIndex = ilkLevel + index;
      	
      	// levelIndex 1'den 50'ye kadar bir değere sahip, burada o index'teki level'i başlat
      }
      

      Ardından sol ok butonunun On Click event’ine SolaGit fonksiyonunu, sağ ok butonunun On Click event’ine de SagaGit fonksiyonunu değer olarak vermeli ve script’in “Level Butonlari” değişkenine de 10 tane level butonunu değer olarak eklemelisiniz (bu butonların sıralaması baştan sona doğru olmalı).

      • Bayram dedi ki:

        NullReferenceException: Object reference not set to an instance of an object
        level_menu_button.LevelButonlariniGuncelle () (at Assets/kodlar/level_menu_button.cs:47)
        level_menu_button.SagaGit () (at Assets/kodlar/level_menu_button.cs:40)

        Hocam bu hataları alıyorum.

      • yasirkula dedi ki:

        Ya levelButonlari’na Inspector’dan değer vermemişsinizdir, ya da butonlarınızın içinde bir Text objesi yoktur (Text, butonun child objesi ise de düzgün çalışır).

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.