Unity Optimizasyon Önerileri

Yayınlandı: 19 Haziran 2016 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde

Yine ve yeniden merhabalar,

Bu yazıda, kimilerini kendim tecrübe ettiğim, kimilerini de ordan burdan derlediğim optimizasyon önerilerinde bulunacağım (Unity 3D için). Yeni şeyler öğrendikçe bu yazıyı sürekli güncellemeye çalışacağım.

Optimizasyon çok ucu açık bir şey olduğu için kimsenin “optimizasyon konusuna hakimim” gibi bir söylemde bulunabileceğini sanmıyorum. Tam olarak da bu yüzdendir ki, kendi bildiğiniz optimizasyon tekniklerini de bu yazı altında yorum olarak paylaşırsanız burada Türk oyun geliştiricileri için faydalı bir kaynak oluşturabiliriz (diye ümit ediyorum).

Script Optimizasyonu

Genel Kural-1: Oyununuzda sistemi en çok hangi bileşen(ler)in yorduğunu görmek için her daim Profiler kullanın.

  • Scriptlerinizde içi boş Unity fonksiyonları bırakmayın (özellike Update, LateUpdate ve FixedUpdate). Bu fonksiyonlar Unity Script Reference‘de “MonoBehaviour” altında “Messages” olarak listelenir ve scriptlerinizde bu fonksiyonlardan herhangi birisi varsa, o fonksiyon içi boş olsa bile çağrılır. Update, LateUpdate ve FixedUpdate fonksiyonları oyun sırasında defalarca kez çağrıldığı için de özellikle bu fonksiyonlar konusunda özen gösterin: fonksiyonu kullanmıyor musunuz? O zaman kodunuzdan silin.
  • Bir scriptten bir component’e birden çok kez erişiyorsanız, o component’e her seferinde GetComponent ile erişmek yerine onu bir değişkenin içinde tutun ve sonraki seferlerde bu değişkeni kullanın. Bunun en yaygın örneği Transform component’i. Her ne kadar Unity 5 ile Transform’un, “transform” değişkeni vasıtasıyla otomatik olarak cache’lendiğini söyleyenler olsa da, forumlarda bunun aksini iddia edenler de var. Ben işi garantiye almak için sık kullandığım her component’i cache’liyorum (değişkene atıyorum).

Optimizasyon öncesi:

void Update()
{
    // İleri yönde hareket et
    transform.Translate( Vector3.forward * Time.deltaTime );
}
 
void FixedUpdate()
{
    // Yukarı yönde güç uygula
    GetComponent<Rigidbody>().AddForce( Vector3.up );
}
 
public void MuzikCal()
{
    GetComponent<AudioSource>().Play();
}

Optimizasyon sonrası:

private Transform cachedTransform;
private Rigidbody cachedRigidbody;
private AudioSource cachedAudioSource;
 
void Awake()
{
    cachedTransform = transform;
    cachedRigidbody = GetComponent<Rigidbody>();
    cachedAudioSource = GetComponent<AudioSource>();
}
 
void Update()
{
    // İleri yönde hareket et
    cachedTransform.Translate( Vector3.forward * Time.deltaTime );
}
 
void FixedUpdate()
{
    // Yukarı yönde güç uygula
    cachedRigidbody.AddForce( Vector3.up );
}
 
public void MuzikCal()
{
    cachedAudioSource.Play();
}
  • GameObject.Find ile sahnedeki bir objeye erişmek yavaş bir işlemdir. Bunun daha optimize versiyonu GameObject.FindWithTag‘dır. Ancak daha daha iyi bir optimizasyon, ilgili objeyi bir değişkende tutmaktır. Özellikle Object.FindObjectOfType kullanıyorsanız bu optimizasyon daha çok önem arz etmektedir.

Optimizasyon öncesi:

void Update()
{
    // Bu obje'yi, Obje2'nin olduğu yere ışınla
    transform.position = GameObject.Find( "Obje2" ).transform.position;
}
 
void Obje3uDeaktifEt()
{
    GameObject.FindWithTag( "Obje3unTagi" ).SetActive( false );
}

Optimizasyon sonrası:

private Transform cachedTransform;
private Transform obje2ninTransformu;
private GameObject obje3;
 
void Awake()
{
    cachedTransform = transform;
    obje2ninTransformu = GameObject.Find( "Obje2" ).transform;
    obje3 = GameObject.FindWithTag( "Obje3unTagi" );
}
 
void Update()
{
    // Bu obje'yi, Obje2'nin olduğu yere ışınla
    cachedTransform.position = obje2ninTransformu.position;
}
 
void Obje3uDeaktifEt()
{
    obje3.SetActive( false );
}

NOT: Camera.main değişkeni, sahnedeki kameraya kolayca erişebilmenize olanak sağlar ama arkaplanda GameObject.FindWithTag(“Main Camera”) fonksiyonunu çağırır. Yani Camera.main’e sıklıkla erişiyorsanız, sonucu bir değişkene atıp o değişkeni kullanmanız önerilir.

  • Bazen oyun boyunca bir objeyi sürekli Instantiate ve Destroy etmeniz gerekebilir (örneğin bir FPS oyunundaki mermi prefabı veya bir patlama partikül efekti). Ancak bu Instantiate ve Destroy komutlarının çok fazla kullanımı performans açısından iyi bir şey değil. Tam olarak da böyle durumlar için pooling pattern dediğimiz bir teknik geliştirilmiş. Bunun çalışma prensibi basit: sürekli kullandığınız bir prefab’ın klonunu Destroy etmek yerine deactivate edip pool’a (havuz) atıyorsunuz. Daha sonradan bu prefab’ın başka bir klonuna ihtiyaç duyduğunuzda önce havuzda hazırda bir klon var mı diye bakıyorsunuz. Varsa havuzdaki klonu kullanıyor, yoksa ancak o zaman yeni bir klon Instantiate ediyorsunuz. Bu basit teknik, FPS oyunlarından tutun infinite runner oyunlarına kadar pek çok oyun türünde kullanılmaya müsaittir. Daha fazla detay için şu derse göz atabilirsiniz: https://yasirkula.com/2019/10/30/unity-pooling-obje-havuzu-patterni/
  • Vector struct’larında (mesela Vector3) Distance adında bir fonksiyon ve magnitude adında bir değişken bulunmakta. Bu iki komut da esasında bir vektörün uzunluğunu ölçmeye yarıyor. Ancak hesaplama sırasında karekök kullanmak gerektiğinden aslında bu işlem sanıldığından daha zahmetli bir şey. Eğer ki bu uzunluk değerini direkt kullanıcıya göstermeyecekseniz o zaman daha hızlı çalışan sqrMagnitude değişkenini kullanmayı düşünebilirsiniz. Bu fonksiyon, bir vektörün uzunluğunun karesini döndürür ve bu değeri hesaplamak magnitude’a (ve Distance’a) göre daha hızlıdır.

Optimizasyon öncesi:

public float maksimumMesafe = 5f;
Vector3 vektor1, vektor2, vektor3;
  
void BirFonksiyon()
{
    // vektor1 ve vektor2 arasındaki mesafe 
    // maksimumMesafe'den küçükse
    if( Vector3.Distance( vektor1, vektor2 ) <= maksimumMesafe )
    {
        // bla bla 
    }
} 
 
void BaskaBirFonksiyon() 
{ 
    // vektor3'ün uzunluğu 8.0'dan büyükse 
    if( vektor3.magnitude > 8f )
    {
        // bla bla
    }
}

Optimizasyon sonrası:

public float maksimumMesafe = 5f;
private float maksimumMesafeKaresi;
Vector3 vektor1, vektor2, vektor3;
 
void Start()
{
    maksimumMesafeKaresi = maksimumMesafe * maksimumMesafe;
}
 
void BirFonksiyon()
{
    // vektor1 ve vektor2 arasındaki mesafe 
    // maksimumMesafe'den küçükse
    if( ( vektor1 - vektor2 ).sqrMagnitude <= maksimumMesafeKaresi )
    { 
        // bla bla
    } 
} 
 
void BaskaBirFonksiyon() 
{ 
    // vektor3'ün uzunluğu 8.0'dan büyükse
    if( vektor3.sqrMagnitude > 64f )
    {
        // bla bla
    }
}
  • Eğer kodlarınızda bir sayıyı veya değişkeni sürekli belli bir sayıya bölüyorsanız; o zaman bu bölme işlemini bir çarpma işlemi ile değiştirin çünkü çarpma işlemi bölme işlemine göre çok daha hızlı hesaplanır.

Optimizasyon öncesi:

void Update()
{
    // Objeyi saniyede 0.5 birim ilerlet
    transform.Translate( Vector3.forward * Time.deltaTime / 2 );
}

Optimizasyon sonrası:

private Transform cachedTransform;
 
void Awake()
{
    cachedTransform = transform;,
}
 
void Update()
{
    // Objeyi saniyede 0.5 birim ilerlet
    cachedTransform.Translate( Vector3.forward * Time.deltaTime * 0.5f );
}
  • SendMessage fonksiyonu da performans için hiç iyi değildir ve mecbur kalmadıkça kullanılmamalıdır. Onun yerine, çağırmak istediğiniz fonksiyona sahip olan component’i bir değişkende tutup bu component üzerinden direkt o fonksiyonu çağırmak “çok” daha hızlıdır.
  • foreach fonksiyonu, bir dizi objenin üzerinden hızlıca geçmek için ideal durabilir (başta ben de çok kullanıyordum). Ancak işin aslı, her foreach kullanımında arkaplanda bir IEnumerator objesi oluşturulur ve bu da Garbage Collector‘a fazladan yük anlamına gelir. Bu yüzden foreach kullanmak yerine birkaç satır fazla kod yazın ama normal for kullanın.

Optimizasyon öncesi:

public Transform[] hareketEttirilecekObjeler;
 
void Update()
{
    foreach( Transform obje in hareketEttirilecekObjeler )
    {
        obje.Translate( Vector3.forward * Time.deltaTime );
    }
}

Optimizasyon sonrası:

public Transform[] hareketEttirilecekObjeler;
 
void Update()
{
    for( int i = 0; i < hareketEttirilecekObjeler.Length; i++ )
    {
        Transform obje = hareketEttirilecekObjeler[i];
        obje.Translate( Vector3.forward * Time.deltaTime );
    }
}

Fizik Optimizasyonu

  • Bazı oyunlarda fizik çok önemli bir yere sahipken bazı oyunlarda ise fiziksel etkileşimler minimum düzeydedir (hatta belki de hiç fizik kullanılmamaktadır). Fizik olaylarının hesaplaması FixedUpdate‘te yapılır ve FixedUpdate fonksiyonu da belli bir frekansta çalıştırılır. Eğer Edit-Project Settings-Time‘a girecek olursanız, fizik hesaplamalarının varsayılan olarak 0.02 saniyelik bir periyotla gerçekleştiğini görebilirsiniz (Fixed Timestep). Eğer ki oyununuzda fizik olayları çok büyük bir yere sahip değilse, bu değeri yavaş yavaş artırıp bunun oyununuzdaki fiziksel etkileşimlere olan etkisini kontrol edin. Eğer bu değeri çok artırırsanız fiziksel etkileşimler istendiği gibi çalışmamaya başlar, o yüzden deneye yanıla ince ayar yapmanız en iyisi.
  • Bir objenizde collider var ve bu obje oyun sırasında hareket mi ediyor? O zaman ya bu objede ya da bu objenin bir parent’ında bir Rigidbody de olduğundan emin olun (objenin fiziksel güçlerden [yerçekimidir, AddForce’dur vb.] etkilenmesini istemiyorsanız da Rigidbody’deki “Is Kinematic“i işaretleyin). Aksi taktirde Rigidbody’siz ama collider’lı bu obje her hareket ettiğinde fizik motoru bazı ağır hesaplamalar yapmak zorunda kalır.
  • Fizik objelerinizde mümkün olduğunca az Mesh Collider kullanın. Mesh Collider kullanan çoğu objenin şeklini aslında birkaç primitive collider (Box Collider, Sphere Collider ve Capsule Collider) kullanarak da yaklaşık olarak taklit edebilirsiniz (ki bu “yaklaşık” olarak tahmin etme durumu, çoğu fiziksel etkileşim için yeterli olur). O halde napıyoruz? Mesh Collider kullanan objeyi Unity’nin sağladığı primitive collider’lar ile taklit etmeye çalışıyoruz. Her bir primitive collider için ayrı bir child GameObject oluşturabileceğiniz gibi tüm primitive collider’ları aynı objeye de uygulayabilirsiniz (size kalmış).
  • Raycast fonksiyonlarınızda raycast ışını için bir maksimum uzunluk belirleyin. Çoğu durumda sonsuz uzunlukta bir raycast ışını yollamak yerine örneğin 100 birim uzunluğunda bir raycast ışını yollamak yeterlidir. Bir başka önemli husus ise layer mask kullanımı. Sahnedeki tüm objeler üzerinden raycast testi yapmak yerine sadece belli bir layer’daki objelere karşı raycast yaparsanız daha hızlı sonuç alırsınız (tahmin edebileceğiniz üzere). Örneğin bir karakterin havada mı yoksa yerde mi olduğunu kontrol etmek istiyorsanız gökyüzündeki bulutları raycast’e karıştırmanıza gerek yok, sadece zemin objelerine karşı raycast yapmanız yeterli. Bunun için zemin objelerine “Zemin” isminde bir layer verip bu layer’ı Raycast fonksiyonunuzda kullanabilirsiniz.

Optimizasyon öncesi:

// Karakter yerde mi kontrol ediyoruz
public bool IsGrounded()
{
    // Karakterin pozisyonundan aşağı yönde 1 birim uzunluğunda
    // raycast ışını yollayıp herhangi bir objeye çarpıp çarpmadığına bak
    return Physics.Raycast( transform.position, Vector3.down, 1f );
}

Optimizasyon sonrası:

// Bu değişkene Inspector'dan değer olarak "Zemin" layer'ı verilmeli
public LayerMask zeminLayer;
 
// Karakter yerde mi kontrol ediyoruz
public bool IsGrounded()
{
    // Karakterin pozisyonundan aşağı yönde 1 birim uzunluğunda
    // raycast ışını yollayıp herhangi bir zemin objesine 
    // çarpıp çarpmadığına bak
    return Physics.Raycast( transform.position, Vector3.down, 1f, zeminLayer );
}

Grafik Optimizasyonu

Genel Kural-1: Minimum sayıda materyal kullanın ve shader’larınızı olabildiğince basit tutun/seçin.

Genel Kural-2: Dinamik (gerçek zamanlı) gölgelendirmeden mümkün olduğunca az faydalanın. Eğer sahnenizdeki sabit duran (static) objeler bile dinamik gölge düşürüyorsa burada performans açısından büyük bir sıkıntı söz konusu. Sahnenizdeki sabit objeleri (ev, duvar, masa, çanak, çömlek vb. yeri kesinlikle hiç değişmeyecek ve oyun sırasında kesinlikle yok olmayacak objeler) Inspector‘dan “Static” yapın ve onlara lightmapping uygulayın. Hem çok daha kaliteli gölgeler elde edersiniz hem de oyunu çalıştıran cihaz derin bir “oh” çeker.

Genel Kural-3: Game panelinin sağ üstündeki Stats kapalıysa açın ve oyun boyunca bir gözünüz “Batches” ve “SetPass calls“ta olsun. Bu değerler ne kadar az olursa o kadar iyi. Bunu başarmak için de oyununuzda olabildiğince çok obje aynı materyali kullanmalı. Her objenin kendi texture’u var, o zaman napacağım derseniz cevabım “texture atlasing“. İlaveten, çoğu 3D modelleme uygulaması, modeli “vertex color” ile boyamanıza imkan verir. Eğer ki modeliniz düz renklerden oluşuyorsa ona texture vermek yerine vertex color uygulayabilir ve Unity içerisinde de vertex color’ları gösteren bir shader kullanabilirsiniz.

Doğru Bilinen Yanlışlar-1: Oyununuzdaki texture’ların boyutu düşük olsun diye onları Unity’e .jpeg formatında atmayın. Ya da benzer şekilde, aslı 512×512 olan kaliteli bir texture’u, boyutu düşük olsun diye resim editörünüzden (Photoshop vb.) 256×256’ya ufaltıp öyle Unity’e yollamayın. Unity’e elinizdeki en kaliteli kaynak dosyayı yollayın (örneğin .psd veya .png ve 512×512). Siz bir resim dosyasını Unity’e hangi formatta atarsanız atın bu resim dosyası Unity içerisinde otomatik olarak bir texture‘a dönüştürülür. Bu esnada bu resim dosyanın kaç MB olduğunun texture’un boyutuna bir etkisi olmazken, resim dosyasının kalitesi, Unity tarafından oluşturulan texture’un kalitesine direkt olarak etki eder. Siz Unity’e 512×512’lik bir resim dosyası atsanız bile, bunu Inspector‘da en altta yer alan “Max Size“dan otomatik olarak 256×256’lık bir texture’a da dönüştürebilirsiniz.

  • Unity 5 ile gelen Standard Shader‘ı minimum düzeyde kullanın. Bu shader, henüz yeni olduğu için yeterince optimize edilmemiş durumda (özellikle mobil için). Materyaliniz için bir shader seçerken öncelikle Mobile altında listelenmiş shader’lara bakın, burada ihtiyacınızı karşılayan bir shader yoksa o zaman Legacy Shaders‘a yönelin.
  • Özellikle mobilde Image Effect kullanımını olabildiğince minimum düzeyde tutun. Bu efektler sandığınızdan daha fazla performans harcayabilir.
  • Fog (sis) kullanmak da performans için iyi değilmiş. Eğer fog, oyununuzda çok ciddi bir yere sahip değilse kullanmaktan kaçının.
  • Bump mapping (normal mapping), bir objeye düşen ışığın çok daha gerçekçi durmasına yardımcı olabiliyor ancak tahmin edebileceğiniz üzere bunu yaparken de ekstra performans yiyor. Eğer kullanacaksanız sadece çok kritik objeler için normal mapping kullanın (mesela player’ın elinde tuttuğu silah objesi için). Bu objelerin normal map texture’larının Max Size‘ını, objenin görünümüne büyük bir etki etmeyecek şekilde olabildiğince düşürmeye çalışın.
  • Unity’nin son sürümleri ile gelen dinamik skybox yerine eski usül, cubemap kullanılarak hazırlanmış skybox’lar kullanın. Yeni skybox dinamik olarak oluşturulduğu için performans açısından daha kötü.
  • Eğer ki oyununuzda skybox (gökyüzü) kullanmıyorsanız şu ayarları yapın:

Main Camera-“Clear Flags“: “Solid Color”

Window-Lighting-Skybox: “None”

Window-Lighting-Ambient Source: “Color”

Window-Lighting-Reflection Source: “Custom” (reflective shader kullanmıyorsanız Cubemap‘i “None” olarak bırakın)

  • Yine Window-Lighting‘de “Precomputed Realtime GI” ve “Baked GI” diye 2 seçenek var. Bu seçenekler lightmapping ile alakalılar. Aralarındaki fark ise şöyle ki: “Precomputed Realtime GI”da ortamdaki ışık sayısı, ışıkların rengi veya şiddeti dinamik olarak değişse bile sahnedeki ışıklandırma ona göre otomatik olarak güncellenirken “Baked GI”da ortamdaki ışıkların durumu değişse de lightmap sabit kalır.

Çoğu oyunda sahnedeki ışıklar sabit olduğu için “Baked GI” doğru tercihtir. İlaveten, “Baked GI” ile oluşturulan lightmap görsel olarak daha kaliteli olur. Yani dikkat etmeniz gereken noktalar şunlar:

– oyunda lightmapping kullanmayacak mısınız (çoğu basit oyunda kullanmazsınız)? O halde hem “Precomputed Realtime GI”ı hem de “Baked GI”ı kapatın

– lightmapping kullanacaksınız ve sahnedeki ışıklandırma dinamik olarak değişecek mi? O zaman “Precomputed Realtime GI”ı açık bırakırken “Baked GI”ı kapatın

– lightmapping kullanacaksınız ve sahnedeki ışıklandırma sabit mi kalacak? O zaman “Baked GI”ı açık bırakırken “Precomputed Realtime GI”ı kapatın

– lightmapping kullanacaksınız ve oyunu mobil platformlara mı tasarlıyorsunuz? O zaman çok zorunda kalmadığınız sürece “Precomputed Realtime GI” kullanmayın

  • (Android) Player Settings-Resolution and Presentation‘daki “Use 32-bit Display Buffer” seçeneğini kapatarak oyununuzu mobil cihazınızda test edin. Eğer grafiklerde göze çok batan bir değişiklik olmadıysa bu seçeneği kapalı bırakın.
  • (Özellikle Android) Eğer ki özellikle OpenGLES3 gerektiren bir shader kullanmadığınızı düşünüyorsanız Player Settings-Other Settings‘teki “Auto Graphics API” seçeneğini kapatın ve “Graphics APIs” listesinden OpenGLES3’ü kaldırıp listede sadece OpenGLES2‘yi bırakın ve oyunu test edin. Eğer göze batan bir değişiklik olmadıysa bu ayarı böyle bırakın. Forumlarda, bu değişikliğin performansa çok etki ettiğini söyleyenler var.
  • Bazı insanlar Unity’de Vsync‘i açınca oyunlarının daha hızlı hale geldiğini iddia ediyorlar. Bunu kesin olarak bilmiyorum ancak denemek isterseniz Edit-Project Settings-Quality yolunu izleyip grafik kalitesi olarak build aldığınız platformun varsayılan ayarına geçiş yapın (varsayılan ayar yeşil bir tik ile gösterilir). Ardından “V Sync Count“u “Every V Blank” yapın ve oyunu test edin.
  • Bazen oyununuzdaki bazı dinamik objelerin de gölge düşürmesini isteyebilirsiniz. Bu objeler static olmadığı için bunların gölgelendirmeleri mecburen gerçek zamanlı olarak hesaplanmalı. Gerçek zamanlı (dinamik) gölgeler çok fazla performans harcar ve bunları olabildiğince optimize etmek veya sayılarını azaltmak büyük önem arz eder. Unity’de “Hard Shadows” ve “Soft Shadows” olmak üzere iki farklı gölgelendirme yöntemi mevcut. “Hard Shadows” daha kaba durur ancak daha az performans harcar. Bu yüzden, illa dinamik gölge kullanacaksanız, görsel olarak çok çok büyük bir fark olmadığı sürece ışıklandırmalarınızda “Hard Shadows” kullanın.
  • Eğer sahnenizde Light Probe veya Reflection Probe kullanmıyorsanız, objelerinizin Mesh Renderer‘larının Light Probes ve Reflection Probes değerlerini Off yapın. İlaveten “Motion Vectors” değerini de “Force No Motion” yapın. Mümkün olduğunca Cast Shadows (objenin gölge bırakmasını sağlar) ve Receive Shadows (diğer objelerin gölgelerinin bu objenin üzerine düşmesini sağlar) değerlerini kapatın. Örneğin çok ufak objelerin veya gölgesi belli bile olmayan objelerin gölge hesaplamaları yüzünden boş yere performansınızı düşürmelerine izin vermeyin.
  • Belki çok basit kaçacak ama, Edit-Project Settings-Quality‘den oyunun grafik ayarını düşürmek (build alınan platformun Default grafik ayarını değiştirmek) belki de oyununuz için fazlasıyla yeterli olabilir.
  • Texture‘larda size-of-2 diye bir kural var. Bilgisayarlar en temelinde binary (0 ve 1) üzerine kurulu olduğundan dolayı her türlü işlem aslında 2 tabanında gerçekleşir. Belki de bu yüzdendir ki oyun motorları, texture ebatları olarak hep 2’nin katlarını tercih eder (16, 32, 64, 128, 256…). Eğer ki Unity’e 2’nin katlarından oluşmayan ebatlarda bir texture verirseniz, Unity bu texture’u kendisi 2’nin bir katına çevirir. Yani isteseniz de istemeseniz de aslında size-of-2 texture’lar kullanıyorsunuz (sprite’lar için geçerli olmayabilir, emin değilim). Bu yüzden texture oluştururken kendiniz size-of-2 kuralına uymaya çalışın. Bu şekilde texture’u Unity’de efektif bir şekilde compress edebilme şansına da sahip olacaksınız. Yok illa ki 75×75 texture kullanacağım diyorsanız da 128-75=53 pixellik boş alanı başka bir texture için kullanmayı düşünebilirsiniz (texture atlasing).

Oyun Boyutu (Build) Optimizasyonu

NOT: Unity 2018.2.0 ile birlikte, şu asset’i kullanarak oyununuzda en çok hangi asset’lerin yer kapladığını görebilirsiniz: https://www.assetstore.unity3d.com/en/#!/content/119923

Genel Kural-1: Oyunlarda en çok boyutu genellikle texture‘lar ve müzik dosyaları kaplar. Daha ufak bir build için bu dosyaların compression ayarlarında olabildiğince ince ayar yapmak isteyebilirsiniz.

  • Resources veya StreamingAssets klasörlerine koyduğunuz asset’ler, hiç kullanılmasalar bile oyuna dahil olurlar ve oyunun boyutuna olumsuz etki ederler. Bu yüzden bu klasörlerde kullanmadığınız asset’ler bırakmayın. Eğer projenize import ettiğiniz bir plugin’in içerisinde Resources klasörü varsa, klasörün içindeki asset’lerin plugin’in çalışması için gerekli olup olmadığını kontrol edin. Bazen plugin’in demo sahnesinde kullanılan asset’ler Resources klasöründe yer alır ve siz farkında olmadan oyununuza dahil olurlar.
  • Saydam olmayan veya saydamlığını (alpha kanalı) kullanmadığınız texture’ların Inspector’larındaki “Alpha Source“u None yapın.
  • (Android) Unity’nin son sürümleriyle birlikte x86 işlemcili Android cihazlara da oyunumuzu build alabiliyoruz. Bu her ne kadar kulağa güzel gelse de oyunun x86 işlemci versiyonunu APK’ya eklemek dosya boyutunu ciddi miktarda artırabiliyor. Şu ana kadar gördüğüm her telefon ARM işlemci kullandığı için ben oyunlarımı henüz x86 işlemciye çıkarmıyorum. Bunun için de Player Settings-Other Settings‘teki “Device Filter“ın değerini “ARMv7” yapıyorum. Bu sayede apk’yı fazlalık mb’lardan kurtarıyorum.
  • (Android) Oyunumuzu build alırken, kodumuzda kullandığımız class’ları içeren dll‘ler de apk dosyasına eklenir ancak bu dll’ler ile gelen kullanmadığımız diğer class’lar boş yere fazlalık oluşturur. Bu class’ları Unity’nin build alırken otomatik olarak yok saymasını sağlayan bir özellik var: “Stripping Level” (Other Settings). Bu seçeneği “Strip Assemblies” yaparsanız dll dosyalarındaki fazlalıklar atılır (dokümantasyonda yazdığına göre Reflection kullanıyorsanız bu bir sıkıntı oluşturabilirmiş). Eğer bu seçeneği “Use micro mscorlib” yaparsanız “Strip Assemblies”e ilaveten bir de .NET kütüphanesinde de bir optimizasyon söz konusu olur (benim kullandığım seçenek bu). “Micro mscorlib” ile desteklenen fonksiyon ve class’ların listesini şurada bulabilirsiniz: http://docs.unity3d.com/352/Documentation/ScriptReference/MonoCompatibility.html
yorum
  1. burak dedi ki:

    hocam merhaba unityde yeni bir material oluşturdum shader değiştirmek istiyorm ama hiç bir şekilde tıklanmıyor shader kısmı kapalı durumda problem nedir nasıl çözebilirm ? 5.6.7f1 versiyonu kullanıyorum bilgisayarım 32bit olduğu için.

    • yasirkula dedi ki:

      Eğer sahnedeki bir obje seçili ise ve objenin materyali varsayılan materyal ise, o materyalin herhangi bir değerini değiştiremezsiniz. Project panelinden materyal asset’inizi seçtiğinize emin misiniz?

  2. Bulent Levent dedi ki:

    Yasir bey yaptığım ( Android ) oyunda setpass Calls 1500 e kadar çıkıyor . Oyundaki karakter hariç tum objelerin gölgesini kapattım ( Shodow casters 8 ) şu an oyun taslak .(Telefonuma oyunu attım çok kasıyor karakter nerede ise hareket etmiyor .) Sadece haritayı yaptım ve objeleri yaptım.( Yol kopru bina vs. ) Harita buyuk , objelerin yarısını bile eklemedim .Hiç bir kod yazmadım (Sadece karakter hareket kodu var.)
    Nasıl optimize edebilirim . Bu şekilde unity ile çok kuçuk oyunlar yapılabilir . Acaba boşuna mı uğraşıyoruz .
    Bu işte çok acemiyim kesin bilmediğim birşeyler vardır .(Oyunun yavaşlamaması için gölgelerin kapatılması grafiğin duşurulmesi çok saçma Sonuçta gta 5 gibi oyun yapmıyoruz )
    Bilgi ve tavsiye ricası ile iyi geceler .

    • yasirkula dedi ki:

      Kameranın Far Clip Plane’ini olabildiğince düşürün. Sahnenizde birden çok ışık kullanmamaya çalışın. Point Light’larda kesinlikle gölge kullanmayın, çok zorlar. Gölge olmazsa olmaz diyorsanız sadece ana Directional Light’ınızda Hard Shadow kullanıp Edit-Project Settings-Quality’den Shadow Distance’ı olabildiğince beriye çekin (bu ayarı, Android için hangi grafik Level’inde yeşil tik yanıyorsa onda değiştirin). Mobilde terrain performanslı değil diye okumuştum, bu yüzden terrain kullanıyorsanız sıkıntı yaşayabilirsiniz. Çözüm yolu: bilmiyorum. Post Processing efektler kullanıyorsanız onları kapatın, mobilde post processing açabilmek için ya oyun çok optimize olmalı ya da çok basit bir şey olmalı. Sahnenizde büyük büyük binalar varsa ve oyun boyunca genel olarak bu binaların yakınındaysak, Occlusion Culling ile binaların arkasındaki objelerin boş yere çizilmesini önleyebilirsiniz. Ben henüz denemediğim için nasıl yapıldığını bilmiyorum. Kullandığınız materyal sayısını minimuma çekin. Bunun için texture atlasing denen yöntemden olabildiğince faydalanın. Çok poligonlu modeller kullanmayın, poligon yerine normal mapping ile detay vermeye çalışın. Kompleks modeller için LOD kullanın, böylece model uzaktayken daha az performans harcar. Texture’larınızın boyutunu Inspector’da Max Size’dan düşürebildiğiniz kadar düşürün, bazen 256×256 iken bile çok güzel gözüken bir texture boş yere 1024×1024 yer kaplayabiliyor. Materyallerinizde Standard shader kullanmayın, onun yerine Legacy Shaders-Diffuse veya Mobile-Diffuse gibi shader’lar kullanın. Standard shader mobilde kötü performansa sahip.

  3. bünyamin dedi ki:

    hocam merhabalar,
    ben mimari mobil uygulama üzerinde çalışma yapıyorum, bazı modellerimi buluttan almam gerekiyor,
    bunun için asset Bundle kullanmaya çalışıyorum ancak bir türlü beceremedim bu konuda bana yarduımcı olabilir misiniz?
    asset Bundle hakkında bilgi ve nasıl kullanıldığını anlatabilir misiniz kısa ve öz şekilde,
    çok teşekkür ederim şimdiden.

    • yasirkula dedi ki:

      Daha önceden sadece bir kere asset bundle kullandım onda da Unity’nin Asset Bundle Manager’ından faydalanmıştım: https://docs.unity3d.com/Manual/AssetBundles-Manager.html

      Asset Bundle’lar seçtiğiniz asset’leri bir arşiv formatına sıkıştırıyor ve daha sonradan bu asset bundle’ları istediğiniz konumdan indirip Unity’nin belleğine yükledikten sonra bu asset bundle’daki asset’lere erişebiliyorsunuz.

      Bu konuda güncel video dersler izlemenizi öneririm çünkü benim tecrübem güncel değil ve tam detayları hatırlamıyorum.

  4. umutercan dedi ki:

    2d android bir oyunum var hocam optimizasyonlar için hangilerini önerirsiniz?

  5. niyazi mustafa dedi ki:

    Hocam unity de Croosinput kullandığımda yeni sahne geçişinde veya restart olduğunda oyun tuşa basıyorsam ve tam o sırada elimi tuştan çektiysem bir sonra ki sahnede tuş basılı kalıyor karakter kendi kendine hareket ediyor.

    • yasirkula dedi ki:

      Yeni sahnenin başında, CrossPlatformInputManager.SetButtonUp veya CrossPlatformInputManager.SetAxisZero gibi fonksiyonlarla o takılı kalan buton veya axis’i sıfırlamayı deneyebilirsiniz.

  6. Batuhan dedi ki:

    Merhaba hocam ben bi nesneyi iki sayı arasında random olarak spawnlıyorum.Ama bu sayıların içindeki belli bir aralıkta çıkmasını istemiyorun.Şöyle yani -0.9 la 0.9 arasında -0.4 le 0.5 arasında spawnlanmasını istemiyorum.

    • yasirkula dedi ki:

      0’dan 0.9’a kadar random bir sayı oluşturup, sayı 0.5’ten küçükse sayıdan 0.9 çıkarabilir, yoksa sayıyı olduğu gibi kullanabilirsiniz.

  7. niyazi mustafa dedi ki:

    Merhaba enemy adında düşmanlarım var healt bar ve melee attack kodlarım sorunsuz çalışyor yalnız prefab veya duplicate yapmak istediğimde yani birden fazla düşman yaratmak istediğimde haliyle bir düşmanı öldürsem bile hepsi ölüyor bunu nasıl çözebilirim.

    • yasirkula dedi ki:

      Düşmanın canını nasıl azaltıyorsunuz? Düşmanlar için static bir sağlık değişkeni kullanıyorsanız öyle yapmayın, her düşmanın kendi canı olmalı.

      • niyazi mustafa dedi ki:

        Enemy üzerinde ki script bu şekilde

        public float enemyHealt, wolfSkillDamage,oneAttacakDamage;
        public Slider healtSlider;
        public CircleCollider2D enemy;

        void Update()
        {

        if (PlayerController.instance.dusmaniGoruyorum && CrossPlatformInputManager.GetButtonDown(“Fire1”))
        {
        enemyHealt = enemyHealt – oneAttacakDamage;
        healtSlider.value = enemyHealt;
        if (enemyHealt <= 0)
        {
        Destroy(gameObject);
        }
        }
        }

        statik bir değer yok.

        raycast ile düşmana yaklaşıtığımda kılıç boyu kadar yaptım ve fire tuşuna bastığımda canını azaltıyorum
        canvas yardımıyla healt bar yaptım ama bütün düşmanlar ölüyor.

      • yasirkula dedi ki:

        Tek bir PlayerController objeniz olduğu için, haliyle tek bir PlayerController.instance.dusmaniGoruyorum değişkeniniz bulunmakta. Bu değişkenin değeri true ise de, Fire1 tuşuna basınca tüm düşmanların canı azalıyor. PlayerController’a “public bool DusmaniGoruyorMuyum(Enemy dusman)” fonksiyonu ekleyip Enemy script’inizde de dusmaniGoruyorum değişkeni yerine bu fonksiyonu çağırmanızı öneririm: PlayerController.instance.DusmaniGoruyorMuyum(this)

      • niyazi mustafa dedi ki:

        player controlde boyle bir metodum var dediğniz gibi yaptım

        public bool DusmanYakinda(EnemyHealtBar enemy)
        {
        if (leftHit() || rightHit())
        {
        Debug.Log(“düşman yakında ve önümde”);
        return true;

        }
        Debug.Log(“düşman gözükmüyor yada arkamda”);
        return false;
        }

        enemy healt kısmınıda

        if (PlayerController.instance.DusmanYakinda(this) && CrossPlatformInputManager.GetButtonDown(“Fire1”))
        {
        enemyHealt = enemyHealt – oneAttacakDamage;
        healtSlider.value = enemyHealt;
        if (enemyHealt <= 0)
        {
        Destroy(this);
        }
        }
        yaptım ama olumsuz olmadı yine vurduğum zaman hepsine işliyor

      • yasirkula dedi ki:

        leftHit() ve rightHit() fonksiyonlarınızda Raycast atıp bu raycast Dusman tag’ine sahip bir objeye çarpıyor mu diye kontrol ediyorsunuz tahminimce. Bu iki fonksiyona da “EnemyHealtBar enemy”i parametre olarak verip, sadece raycast’in çarptığı obje enemy.gameObject’e eşitse true döndürün.

      • niyazi mustafa dedi ki:

        Evet Hocam şu şekilde o metodun kod satırları
        public bool leftHit()
        {
        RaycastHit2D playerRay = Physics2D.Raycast(transform.position, yon = new Vector2(transform.localScale.x, 0), 2f, isEnemy);
        if (playerRay.collider == null)
        {
        return false;

        }
        return true;
        }

        tag olarak degılde layer olarak düşman layerında çarpan yani düşman oluyor sadece düşmana çarptığında true false döndürüyor.

      • yasirkula dedi ki:

        Onu şöyle değiştirebilirsiniz:

        public bool leftHit(EnemyHealtBar enemy)
        {
        RaycastHit2D playerRay = Physics2D.Raycast(transform.position, yon = new Vector2(transform.localScale.x, 0), 2f, isEnemy);
        return playerRay.collider != null && playerRay.collider.gameObject == enemy.gameObject;
        }

        Ama bence daha güzeli, “Fire1” input’u true olduğu vakit, yine böyle raycast’lerle yanlardaki düşmanları (varsa) bulun ve bu düşmanların canını azaltın. Yani can azaltma kodunu düşmana değil player’a verin. Örneğin şöyle bir kod:

        Collider col = playerRay.collider;
        if( col != null )
        {
        EnemyHealtBar dusman = col.GetComponent<EnemyHealtBar>();
        if( dusman != null )
        {
        // Burada dusman objesinin canını azaltın
        }
        }

      • niyazi mustafa dedi ki:

        Hocam problemi buldum
        healtbar kullanmayip public olarak yada float olarak sadece deger verip inspectorde vurdufum zaman o enemynin cani gidiyor ama healtbari aktif etrigimde butun healtbarlar vurdugum deger kadar eksiliyor ve haliyle hepsi ölüyor

      • niyazi mustafa dedi ki:

        Hocam aksam eve gittiğimde değişiklikleri yapıp size döneceğim

      • niyazi mustafa dedi ki:

        hocam şu kısmı nerede ve nasıl kullanacağım anlayamadım
        Collider col = playerRay.collider;
        if( col != null )
        {
        EnemyHealtBar dusman = col.GetComponent();
        if( dusman != null )
        {
        // Burada dusman objesinin canını azaltın
        }
        }

      • yasirkula dedi ki:

        leftHit ve rightHit’in içine yapıştırıp, Player’ın Update fonksiyonunda CrossPlatformInputManager.GetButtonDown(“Fire1”) true ise bu iki fonksiyonu da çağırabilirsiniz. Sadece orada belirttiğim yere EnemyHealtBar’ın canını azaltan bir kod yazmanız lazım. Ve EnemyHealtBar’da artık şu kodu çalıştırmamanız:

        if (PlayerController.instance.DusmanYakinda(this) && CrossPlatformInputManager.GetButtonDown(“Fire1”))
        {
        // blabla
        }

  8. niyazi mustafa dedi ki:

    Merhaba oyun içi camera optimizasyonunu nasıl sağlarız. Her ekranda aynı görüntüyü nasıl sağlarız UI elemanları için söylemiyorum oyun içi görüntüden bahsediyorum yardımcı olabilecek birileri varmıdır.

    • yasirkula dedi ki:

      Aynı görüntüden kastınız, grafik kalitesi olarak mı aynı (gölge kalitesi vs.) yoksa ekranın sağında ve solunda kalan objelerin gözüküp gözükmemesi olarak mı aynı?

      • niyazi mustafa dedi ki:

        her ekranda ayni cozunurlugu nasil sunarim bunun cozumunu bulamadim bir cok yontem denedim ama maalesef bir sonuca varamadim

      • yasirkula dedi ki:

        Diyelim 100×200 çözünürlüklü bir ekran ile 100×300 çözünürlüklü bir ekran olsun. Bu iki ekran arasında 100 piksellik yükseklik farkı var. Bu iki ekranda aynı çözünürlüğü nasıl vermek istiyorsunuz? Görüntü 100×300 çözünürlüklü ekranda uzatılmış/gerilmiş olarak mı gözüksün istiyorsunuz? İsterseniz oyunun başında Screen.SetResolution fonksiyonu ile ekranı belli bir çözünürlüğe ayarlayabilirsiniz.

      • niyazi mustafa dedi ki:

        haklısınız evet biliyorum ama nasıl optimize edeceğiz bu sorunu nasıl aşarız

      • yasirkula dedi ki:

        Görüntü uzatılmış/gerilmiş dursun fark etmez diyorsanız Screen.SetResolution fonksiyonunu ile ekran çözünürlüğünü istediğiniz çözünürlük yapmayı deneyebilirsiniz: https://docs.unity3d.com/ScriptReference/Screen.SetResolution.html

  9. Uğur dedi ki:

    Hocam halen oyunun açılış ekranından ana menüye girişi 5-10 saniye sürüyor ana menüden oyuna geçmesi bazen daha da uzun olabiliyor ve oyun oynarken sürekli kasıyor bilgisayarda unity deyken kasmıyor ama telefonda kasmaya başlıyor bu sorunu nasıl çözebilirim?

  10. Ömer dedi ki:

    Hocam mesela bir uygulamayı apk olarak çıkardığımızda kullanmadığımız gereksiz sahnelerde onunlar birlikte buid oluyor eğer build settings deki scenes ın build bölümünde ekliyse bu performansı olumsuz etkiler mi yoksa etkisi olmaz mı?

  11. ahmet dedi ki:

    Merhaba, 2 boyutlu içinde sadece Sprite Renderer bulunan bir objenin scale ile en ve boyunu arttırmanın performansa herhangi bir etkisi var mıdır acaba? 1×1 beyaz renk 2d bir obje ile aynı objenin 1000×1000 scale edilmiş halinin performansa etkisi aynı mıdır acaba?

    • yasirkula dedi ki:

      Overdraw diye bir olay var, objelerin pikselleri ne kadar üst üste denk gelirse grafik kartı o kadar yorulur. Ama tek bir objenin 1000×1000 olmasının bir etkisi olacağını sanmıyorum.

  12. Squ4re dedi ki:

    Hocam benim bilgisayarda unity çok kasıyor.Kasmayı azaltmak için neler yapabilirim ?

    • yasirkula dedi ki:

      Edit-Project Settings-Quality’den düşük bir grafik ayarı seçmeyi deneyebilirsiniz. Buna rağmen boş bir projede bile program kasıyorsa, bilgisayarınızın güçsüz olması ihtimali de mevcut.

  13. alparslan bilgin dedi ki:

    merheba hocam oyun mobilde çok fazla kasma yapıyor istatistiklerde Tris değeri 750 k verts değeri 380 k bu durum sebep olabilirmi kasma sorununa

    • yasirkula dedi ki:

      Evet olabilir, modelleri olabildiğince low-poly yapmaya çalışın. Buna ilaveten, kullandığınız materyallerin shader’ları da çok önemli. Standard shader kullanmanızı hiç önermem, onun yerine Legacy-Diffuse gibi shader’lar kullanın. Eğer sahnenizde çok fazla model varsa LOD sistemini de inceleyebilirsiniz.

  14. volkan dedi ki:

    Merhaba,sorum apk boyutunun yüksek olmasıyla ilgili.Basit bir oyun yapsam bile boyutu gereksiz büyük oluyor(25-30mb).Sıkıştırma yöntemlerini doğru yaptığımı düşünüyorum. Bu yüzden boş bir proje açıp içine hiç bir şey eklemeden build yaptım ve oluşturduğum apk 15mb geldi.(sadece armv7 seçili, x86 kapalı).Projenin içinde hiçbir şey yokken bu kadar yer kaplamasına çözüm bulamadım. İnternetteki birçok kaynağa bakmama rağmen çözümü yok. Sizin bir çözümünüz varsa şimdiden teşekkür ederim.

    • yasirkula dedi ki:

      Unity’nin her yeni sürümünde boş APK’nın boyutu maalesef artıyor çünkü Unity’nin kütüphanesi genişliyor. İsterseniz Mono yerine IL2CPP Scripting Backend’ini kullanmayı deneyebilirsiniz (Player Settings’ten).

  15. huseyin fe dedi ki:

    ben küçük bir mobil oyun yaptım oyunumda reklamlar var ama bazı telefonlarda internet açılınca kasaya başlıyor internet kapalı olunca kasmıyor .
    reklamlar yüklenirken bir sorun oluyor olabilirmi

    • yasirkula dedi ki:

      Reklam kodlarınızı comment’leyip tekrar build alın ve internet açıkken oyunu test edin. Eğer artık kasma olmuyorsa, dediğiniz gibi reklamlar oyunu kastırıyordur. Ama bu kasma genelde birkaç saniye sürer, yani daha ana menüdeyken kasmanın bitmesi lazım normalde.

  16. yusuf dedi ki:

    hocam benim projemde sahne değiştirmek için butona basıyorum bir iki saniye bekleyince sahne değişiyor sanırım ses dosyasından kaynaklı acaba mp3 kullanmam yada audiosource’u yanlış kullanmamdan mı kaynaklanıyor.

  17. esaddkra dedi ki:

    Merhaba. Unity ile yaptığım projede ağaç kesme işlemi yapmak istiyorum. Ağaçları prefab olarak tek tek eklediğim zaman fps konusunda büyük sıkıntı yaşıyorum. Terrain de bulunan ağaç ekle bölümünden oluşturduğum ağaçları eklediğim zaman fps sorunu ortadan kalkıyor ancak bu seferde ağaçlarla etkileşim kuramıyorum. Prefabtan eklediğim zaman oluşan fps sorununu çözebilir miyim veya terrain üzerinden eklediğim ağaçlarla etkileşim kurabilir miyim ?

  18. ahmet dedi ki:

    Ben ilk başta farklı bir oyun yapıyordum. O oyundan vazgeçip daha farklı bir oyun yapmaya karar verdim. Yeni proje açmak yerine aynı proje üzerinden ilerledim. Sahnelerimde kullanmadığım assetsler yok. Fakat daha önce kullanıp sahneden sildiğim assetsler ve onların animasyonları proje klasörümün içinde duruyor.

    • yasirkula dedi ki:

      Kullanmadığınız asset’ler, Resources veya StreamingAssets klasörlerinde olmadığı sürece boyuta etki etmez. Unity sürümünüz güncelse, dediğim asset’i kullanarak hangi asset’lerin oyununuzda yer kapladığını tespit edebilirsiniz.

esaddkra 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.