Categories
Oyun Geliştirme Rehber

Unity Kod Optimizasyonu ile RAM’e Hükmedin

Selamlar, bu yazımızda Unity kod optimizasyonu hakkında detaylı bilgi vereceğiz. Çeşitli durumlar için geçerli pek çok optimizasyon çeşidi bulunuyor.

Object Pooling

Nesne Birleştirme ve Nesne Oluşturma olarakta geçiyor. Burada amaç en başta, kullanılacak nesneleri oluşturmak ve yok etmeden tekrar kullanmak. Çoğu zaman yapılan en büyük yanlışlardan biri bu oluyor. Prefab kullanılacağı zaman ‘Instantiate’ metodu ile bir kopyası oluşturuluyor. Objenin işi bittikten sonra ise tamamen yok ediliyor. Bu işlemler tek seferlik yapıldığı zaman pek sorun gibi durmasada tekrar edildiğinde çok fazla bellek kullanımına yol açıyor. 

Bu aşamada ‘Object Pooling’ devreye giriyor. Amacını yukarıda özetlemiştik. Şimdi ise biraz detaylandıralım ve örneklemelere geçelim.

Eskiden bir ‘Shooter’ oyunu yaptıysanız silahların mermilerini her seferinde yeniden klonlar ve işleri bittiğinde ise onları yok ederdiniz. Object Pooling ile kullanılacak mermileri sahne başlangıcında oluşturuyoruz ve aktifliklerini kapatıyoruz. Artık bir mermi havuzumuz var tıpkı şarjör gibi. Silah her ateşlendiğinde mermi klonlamak yerine havuzda bulunan bir mermi çekilir, pozisyonu değiştirilir ve aktif hale getirilir. İşi bittiğinde ise pasif hale getirilir. Bu sayede objeler sadece başlangıçta oluşturulur ve asla yok edilmez.

Örnek göstermeye başlayalım. Öncelikle objelerin zaten sahnede olduğu durumu canlandırıyoruz. Kullanılacak objelerimizin hepsi başta sahnemizde yer alıyor.

unity 3d agent spawn point

Bu nesnelerimizin sadece aktiflikleri kapalı.

unity 3d agent inspector

Tüm objeleri yine manuel olarak scriptimizde bulunan listeye atayalım.

spawn script component unity kod optimizasyonu

Spawn işlemi için bir coroutine oluşturuyor ve başlatıyoruz.

unity kod optimizasyonu coroutine kod blogu

Objemizi LinQ ile aktiflik durumuna göre çekiyoruz (Performans bakımından LinQ kullanmanız önerilmez). Null kontrol yapıyor, aktifliğini açıyoruz ve sonrasında kullanımdan kalan velocitysini sıfırlıyoruz.

unity kod optimizasyonu enumerator kod blogu

Object Pooling genel olarak bu şekilde kullanılıyor. Objelerin tekrar kullanılmak üzere  sahnede saklanması sizlere çok fazla performans artışı sağlayacaktır.

Update vs Coroutine

Unity’de sürekli ya da belirli bir süre sonra yapılacak işlemler için Coroutineleri kullanabiliyoruz. Bazı durumlarda ise Update içerisine yazdığımız kodları Coroutine içerisine yazmak performans bakımından çok daha avantajlı bir hale gelebiliyor. Arada çok üst düzey bir fark var mı derseniz tepkimelere ve bellek kullanımlara bakıldığında üst düzey bir ayrım göremezsiniz. Ancak çoğu durumda Update kullanmanın gereksizliğini ortaya koyacak derecede esneklik sağlayan Coroutinelerin özellikleri güzel bir geliştirme avantajı da sunuyor. Gelin beraber bu ufak farklara bakalım.

Object Pooling de zaten bir Coroutine başlatmıştık. StartCoroutine fonksiyonu ile bir routine başlatabiliyoruz. Coroutinelerde iş akışını durdurmak istiyorsak aşağıdaki gibi bir kullanım yapabiliyoruz.

unity 3d wait kod bloğu

Bu kod satırı sayesinde, 5 saniyelik bir bekleme sonrasında kodlarımız çalışmaya devam ediyor.

İstersek başladığı karenin bitmesini beklemesini de söyleyebiliriz.

unity 3d wait for end of frame

While ile kullanarak Update gibi bir çağrı elde edebiliriz. 

unity 3d while wait for end of frame

İşte bu durumlarda Update yerine kullanmak avantajlı duruma getiriyor bizleri. Update içerisinde çeşitli conditionlar yazmak ya da farklı farklı fonksiyonları kullanılmasalar çağırarak her seferinde ek yüklerin ortaya çıkmasına neden oluyoruz. Birden fazla Coroutine oluşturarak her akışın kendine ait bekleme noktalarına sahip olmasını sağlayabiliriz. 

Her akışı farklı düzende kontrol edebiliriz. Tek bir akış oluşturup tekrar limitlerini belirleyebiliriz. Bunların çoğunu Update fonksiyonunda yapamıyoruz çünkü akışı rahat kontrol edemiyor, koşul mekanizmaları oluşturuyoruz. Mekanizmalar oluşturmak için çeşitli koşullar geliştirmemiz gerekiyor ve de kodların çalışmasını engellemek için bir yerden kesmek. Bu durumda ise Update çığrından çıkabiliyor.

String.Empty.

String oluştururken herhangi bir değer girilmediğinde “” tırnak kullanarak boş olarak tanımlarız. Ancak bu atamayı yaptığımızda string bellekte yerini alır ve nesne artık oluşturulmuştur. 

String.Empty kullanıldığında ise nesne empty alana referans verilir. Yani bellekte zaten olan bir alanı referans olarak gösterir. Bu sayede ekstra yer kaplamamış olur. 

String.Emptynin kullanımı String.IsNullOrEmpty fonksiyonunu da beraberinde getiriyor. Null veya uzunluğu 0 olan objeleri rahatlıklı kontrol etmek amacıyla kullanılan bir fonksiyondur. Her iki durumu da destekler.

String.Empty ile ilgili detaylı bilgiye .Net dokümanlarından erişebilirsiniz.

StringBuilder

String birleştirme işlemleri Unity ya da herhangi bir C# projenizde fazla yer alıyor, 2 – 3 den fazla string i birleştirmeye çalışıyorsanız, bu işlemleri artık ‘+’ operatörü ile yapmak yerine StringBuilder’ı kullanmaya başlayabilirsiniz. Performans bakımından gayet güzel bir etkiye sahip.

.Net dokümanına buraya tıklayarak erişebilirsiniz.

Caching Components

GetComponent fonksiyonu ile aldığımız Component’lar bir değişkene atarak tekrar kullanılabilir. GetComponent objeye erişmek için çok fazla güç harcıyor. Objenin bir referansını oluşturup sonrası için de kullanmak çok daha mantıklı.

unity kod optimizasyonu rigid body component

Finding Objects

Obje aramalarında en çok yapılan kullanılanlar GameObject.Find ve GameObject.FindWithTag fonksiyonları. Bu fonksiyonların kullanımı çok maliyetlidir ancak Find fonksiyonunun maliyeti tüm objeler için yaptığı aramadan dolayı daha fazladır. Tage göre arama çok daha az maliyetlidir. 

unity 3d finding objects

Bir obje aramak yerine direkt slota assign etmek yukarıda bulunan iki yöntemden çok daha kullanışlıdır.

unity kod optimizasyonu finding objects

Camera.Main

Bu fonksiyon yüksek maliyetli, aktif kamerayı getiren bir Unity API çağrısıdır. Finding Objects kısmında belirttiğimiz fonksiyonlardan pek bir farkı bulunmamaktadır. Hepsi bellekte bulunan tüm objeleri gezer. Bunları önlemek amacıyla bu fonksiyonların döndürdüklerini cachelemek gerekiyor ve bir daha kullanmamak gerekiyor.

Local Declare Garbage

Çalıştırılacak fonksiyonlar içerisinde kullanılan değişkenlerin tanımlamaları fonksiyon içerisinde yapılıyorsa değişken her seferinde belleğe yazılır eski adresine tanımlanmaz. Bu sebeple büyük bir şişme gerçekleşir. Performansı etkiler.

unity 3d update get component

Bu durumda her zaman farklı bir array oluşur. Bunun yerine aşağıdaki şekilde bir kullanım yapmamız çok daha iyi olacaktır.

unity 3d start get component

Unity’de hangi platform için çalışırsanız çalışın, Unity kod optimizasyonu uygulamanız sizlerin yararına olacaktır. Daha birçok optimizasyon çeşidi ve durumu bulunmaktadır. Bunları projelerinizde ihtiyaç doğrultusunda kullanabilir ve öğrenebilirsiniz.

Unity kod optimizasyonu yapmak için yeterli tecrübeniz ve bilgi birikiminiz yoksa, FastnTech olarak size yardımcı olabiliriz.