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:

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:

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:

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:

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.unscaledDeltaTime;
yaziRenk.a -= animasyonHizi * Time.unscaledDeltaTime;
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.unscaledDeltaTime;
yaziRenk.a += animasyonHizi * Time.unscaledDeltaTime;
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:

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!
Yani kodu herhangi bir objeye mi atayım?
Instantiate Olması için kodun çalışması gerek kodun çalışması içinse bir objeye atıyorum hocam.
İstediğiniz herhangi bir script’ten LoadingEkrani.LevelYukle fonksiyonunu çağırabilirsiniz, evet.
Hocam kusuruma bakmayın biraz çok yazdım çok özür ama levelAdi mevzusunu anlamadım her şeyi anladım LoadingEkrani.LevelYukle falanı anladım ama levelAdi na acaba ne yazacağım veya levelAdi diye mi kalsın script te?
LoadingEkrani isimli script’i düzenliyorsanız düzenlemeyin çünkü LoadingEkrani.LevelYukle(“Scene’in İsmi”) fonksiyonunu o script’te değil kendi script’lerinizde çağıracaksınız.
NullReferenceException: Object reference not set to an instance of an object
LoadingEkrani.Update () (at Assets/Codes/LoadingEkrani.cs:60)
Hata var hocam. https://www.hizliresim.com/ncb9sxz sahne böyle.
Prefab’ı sahnenize koymayın, kod ile otomatik olarak Instantiate oluyor. Hatanın bundan kaynaklandığını düşünüyorum.
Örnek veriyorum 5 bölümlük oyunumuz var 5 ayrı scenede ve bölüm seçmek için ayrı bir scene var toplam 6 scene olsun. Bu canvas prefab ını hangi sahneye atmam gerek?
Ayrıca loading işlemi bitince sahnemizde zaman hemen başlar mı çünkü;
Daha denemedim ama oyunumda ara sahne var ara sahnenin bir kısmı gözükmeme gibi bir durum olur mu hocam?
Prefab ihtiyaç halinde otomatik olarak Instantiate oluyor o yüzden bir sahneye koymanıza gerek yok. Loading işlemi bitip diğer sahne yüklenince zaman hemen başlar. Bu sürenin ilk yaklaşık 1 saniyesinde loading ekranı ekrandan yumuşak bir şekilde kaybolacağı için, o esnada oyuncunun görmesi gereken kritik bir şey varsa onu oyuncu maalesef göremez. Bunu çözmek için “loadingIslemi.allowSceneActivation = true;” satırından önce Time.timeScale’i 0 yapıp “gameObject.SetActive( false );” satırından sonra da Time.timeScale’i 1 yapmayı deneyebilirsiniz.
Eyw çok teşekkürler hocam ALLAH RAZI OLSUN🙏
Hocam benim genel olarak şöyle bir sıkıntım var. Diğer izlediğim tüm videolarda da aynı sıkıntıyla karşılaştım. Görünüşe göre LoadSceneAsync methodu sahneyi arka planda yüklemeye tam olarak yardımcı olmuyor. Telefonda oyunumu test ettiğimde Unity logosunu geçtikten sonra oyunumun açılma süresi 5 saniye sürüyor. Telefondan telefona değişiyor. Hayal ettiğim şey şu aslında. 5 saniye boyunca yükleniyor ibaresinin ilerlemesi gerekiyor. Ama gel gelelim gerçekte bu olmuyor. Oyunun ilk saniyesinde yüzde değerini gösteriyor. Bir de 5. saniyede yüzde değerini gösteriyor. O aradaki 4 buçuk saniye boyunca hiç bir şey yapmıyor. Mevcut sahnedeyken, arka planda 2. sahneyi yükle dediğimiz anda hali hazırdaki sahnemizde donuyor.Tam o esnada Ne animasyon oynuyor. Ne başka bir şey.Yaprak kıpırdamıyor.
Bu yukarıda anlattığınız loading screen boyutu büyük oyunlar için mi geçerlidir. Yoksa benim unity sürümünde mi bir hata var. Biraz fazla uzattım kusura bakmayın. Eminim aynı sorunla karşılaşan başka arkadaşlar da vardır.
Progress niye sizde bir anda zıplıyor onu bilmiyorum. Takılma sorununun sebebi için şu cevap bana mantıklı geldi. Özetle, sahnedeki objelerin Awake ve Start fonksiyonları ana thread’de çalıştığı için, o fonksiyonlar oyunu kastırıyordur. Buna güzel bir çözüm benim aklıma gelmiyor ama belki bilmediğim bir çözüm vardır.
Scripti canvasa sürükledim. Canvası silmek yerine false yaptım. Ayrıca yine ilk sahneye bir tane daha canvas oluşturdum. İçine de bir tane buton ekledim. Butona tıklayınca LevelYukle (string) metodunu tetikledim. İşlem tamamdır.
Hocam iki sahnem var. Birinci sahnenin adı >> yükleniyor. İkinci sahnenin adı >> LevelAdi. Yukarıdaki anlatılanların aynısını yaptım. 60. satırda şu hatayı gösteriyor. NullReferenceException: Object reference not set to an instance of an object
LoadingEkrani.Update () (at Assets/LoadingEkrani.cs:60). Scripti tam olarak nereye sürükleyeceğiz. Sonrasında bir şeyler daha anlatmışsınız. Son bir kaç cümlenizi tam olarak anlayamadım hocam.
En son resimde, component’i nereye verdiğimi ve değişkenlerine ne değerler verdiğimi görebilirsiniz. Eğer LoadingEkrani canvas’ını sahnenizde bıraktıysanız, onu silin. Kod bu canvas’ın prefabını oyun esnasında Instantiate edecek.
Unity 4 desteği yok. Unity 4 SceneManagement desteklemediği için çalışmıyor. Unity 4 için de yardımcı olabilir misiniz ?
SceneManager.LoadSceneAsync’i Application.LoadLevelAsync ile değiştirebilirsiniz ama anladığım kadarıyla bu fonksiyon Unity Pro gerektiriyor.
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)
GetComponentInChildren<Text>
yerineGetComponentInChildren<TextMeshProUGUI>
yazabilirsiniz. Ama başausing TMPro;
da eklemeniz lazım.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?
Şuna benzer bir kod yazabilirsiniz:
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ı).
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.
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).