SON GÜNCELLEME: 23 Eylül 2020 (Diğer Optimizasyonlar’a bir yeni optimizasyon eklendi)
Yine ve yeniden merhabalar,
Bu yazıda, kimilerini kendim tecrübe ettiğim, kimilerini de ordan burdan derlediğim Unity 3D optimizasyon tekniklerinden bahsedeceğim. Yeni şeyler öğrendikçe bu yazıyı 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.
Genel Kural: Oyununuzda sistemi en çok hangi bileşen(ler)in yorduğunu görmek için her daim Profiler kullanın. Profiler dersim için: https://yasirkula.com/2020/03/26/unity-profiler-kullanimi/
Yazıdaki optimizasyon tekniklerinin bazılarının başında [1], [2] gibi referanslar bulunmakta; bu referanslara yazının sonundaki “Yararlanılan Kaynaklar” kısmından ulaşarak, o optimizasyon tekniklerinin bahsi geçtiği orijinal dokümanları inceleyebilirsiniz.
Bazı optimizasyon tekniklerinde Garbage Collector (GC) terimini kullandım, bundan kısaca bahsetmek istiyorum. C++’ın aksine C#’ta, artık kullanılmayan hafıza otomatik olarak boşaltılır (automatic memory management). Bu işlemi Garbage Collector gerçekleştirir. Kullanılmayan hafızanın temizlenmesi önemlidir çünkü aksi taktirde bu hafıza tekrar kullanılamaz ve oyununuzun kullandığı RAM gitgide artar, ta ki oyun artık tüm RAM’i harcayıp çökene kadar. Garbage Collector çalıştığında, oyunun kullandığı tüm RAM’in üzerinden geçer ve RAM’in artık kullanılmayan kısımlarını bularak buraları temizler. Bu işlem biraz zaman alır ve bu esnada oyun donar (bu donma süresi, oyunun kullandığı RAM miktarı ile doğru orantılıdır); bu yüzden de garbage collector’ın olabildiğince az çalışması elzemdir. Peki garbage collector ne zaman çalışır? Oyuna atanmış olan RAM ağzına kadar doluyken, yeni bir obje için (Instantiate edilmiş bir obje, yeni bir array vs.) biraz daha hafıza gerektiğinde. İşte bu durumda garbage collector çalışır, kullanılmayan hafıza temizlenir ve eğer yeni oluşturulan objenin sığabileceği kadar yer temizlendiyse obje hafızanın o kısmını kullanmaya başlar. Yeteri kadar yer temizlenemediyse mecburen oyunun kullandığı RAM miktarı artar; bu da sonraki garbage collector operasyonlarının daha uzun sürmesi anlamına gelir. Tabi oyun RAM’i zaten tamamen kullanıyor idiyse o zaman maalesef oyun çöker (devasa bir oyun yapmıyorsanız buna çok kafanızı takmayın, bu sorun benim hiç başıma gelmedi). Özet geçecek olursak:
- Instantiate ile veya new takısı ile oluşturduğunuz objeler, array’ler vs. hafızada yer kaplar, eğer hafıza doluysa garbage collector çalışır. Buradaki new olayı aslında biraz daha karmaşık bir konu çünkü
new int[]
ile oluşturulan bir array garbage collector’ı çalıştırırken,new Vector3
ile oluşturulan bir vektör çalıştırmaz çünkü Vector3’ün kullandığı hafıza, garbage collector’ın çalıştığı heap‘te değil, ayrı bir hafıza olan stack‘te tutulur (dediğim gibi bu biraz karmaşık bir konu, siz sadece Unity’de çokça kullandığımız new Vector3‘ün herhangi bir sıkıntı olmadığını ve garbage collector’a bir etkisi olmadığını bilin yeter). - Garbage collector çalışırken oyun tamamen donar, bu süre oyunun kullandığı RAM miktarı ile doğru orantılı olarak artar.
Profiler penceresinin GC Alloc sütunu, o frame’de ne kadar yeni hafıza kullanıldığını gösterir. Özellikle mobil oyunlarda, bu değer oyunun büyük çoğunluğunda 0 byte olmalıdır. Eğer her frame’de belli bir miktar hafıza kullanılıyorsa, garbage collector’ın olabildiğince az çalışması için, hafızayı kullanan kodu Profiler ile tespit edin ve o kodu 0 byte‘a düşürecek şekilde optimize edin (diyelim sürekli yeni bir array oluşturuluyorsa, onun yerine bu array’i oyunun başında bir kere oluşturup tekrar tekrar kullanın).
Script Optimizasyonu
- [16] 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.
- Kodunuzda 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.
Optimizasyon öncesi:
void FixedUpdate()
{
// Yukarı yönde güç uygula
GetComponent<Rigidbody>().AddForce( Vector3.up );
}
public void MuzikCal()
{
GetComponent<AudioSource>().Play();
}
Optimizasyon sonrası:
private Rigidbody cachedRigidbody;
private AudioSource cachedAudioSource;
void Awake()
{
cachedRigidbody = GetComponent<Rigidbody>();
cachedAudioSource = GetComponent<AudioSource>();
}
void FixedUpdate()
{
// Yukarı yönde güç uygula
cachedRigidbody.AddForce( Vector3.up );
}
public void MuzikCal()
{
cachedAudioSource.Play();
}
- [15] 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 çok daha önem arz etmektedir.
Optimizasyon öncesi:
void Update()
{
// Bu objeyi, Obje2'nin olduğu yere ışınla
transform.position = GameObject.Find( "Obje2" ).transform.position;
}
void Obje3uDeaktifEt()
{
GameObject.FindWithTag( "Obje3unTagi" ).SetActive( false );
}
Optimizasyon sonrası:
private Transform obje2ninTransformu;
private GameObject obje3;
void Awake()
{
obje2ninTransformu = GameObject.Find( "Obje2" ).transform;
obje3 = GameObject.FindWithTag( "Obje3unTagi" );
}
void Update()
{
// Bu objeyi, Obje2'nin olduğu yere ışınla
transform.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 kullanın.
- GetComponents veya GetComponentsInChildren fonksiyonları, belli bir türdeki tüm component’leri bulup bir array olarak döndürürler. Her yeni array, garbage collector için biraz daha iş demektir. Bunun yerine, bu fonksiyonların List parametre alan versiyonlarını kullanın. Bu şekilde, bulunan sonuçlar halihazırda var olan List’te depolanır, yeni bir array oluşmamış olur.
Optimizasyon öncesi:
void ColliderlariKapat()
{
Collider[] colliderlar = GetComponents<Collider>();
for( int i = 0; i < colliderlar.Length; i++ )
colliderlar[i].enabled = false;
}
Optimizasyon sonrası:
private readonly List<Collider> colliderlar = new List<Collider>();
void ColliderlariKapat()
{
GetComponents<Collider>( colliderlar );
for( int i = 0; i < colliderlar.Count; i++ )
colliderlar[i].enabled = false;
}
- 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ıyorsunuz. Havuzda klon 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/
- Oyununuzun başında Application.targetFrameRate‘e bir değer verin. Bu değişken, oyunun hedeflediği FPS değerini belirler. Diyelim değerini 60 yaparsanız, oyun 60 FPS’in üzerine çıkmaz. Oyunun 120 FPS’te çalıştırılmasına göre 60 FPS’te çalıştırılması, özellikle mobil cihazlarda bataryayı daha yavaş harcamaya, cihazın daha yavaş ısınmasına ve CPU’nun daha az yorulmasına yardımcı olur. Mobil cihazlarda targetFrameRate’i mümkünse 30, oyununuz göze çok akıcı gelmiyorsa da 60 yapın. Daha da yüksek bir rakamın size bir faydası olmayacaktır.
- 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 (örneğin iki vektörün uzunluğunu kıyaslamaya çalışıyorsanız), o zaman daha hızlı çalışan sqrMagnitude değişkenini kullanmayı düşünebilirsiniz. Bu değişken, 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
}
// 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
}
// 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:
public float surtunme = 2f;
void Update()
{
// Objeyi ileri yönde hareket ettir
transform.Translate( Vector3.forward * Time.deltaTime / surtunme );
}
Optimizasyon sonrası:
public float surtunme = 2f;
private float _1BoluSurtunme;
void Start()
{
_1BoluSurtunme = 1f / surtunme;
}
void Update()
{
// Objeyi ileri yönde hareket ettir
transform.Translate( Vector3.forward * Time.deltaTime * _1BoluSurtunme );
}
- [15] SendMessage/BroadcastMessage fonksiyonları performans için hiç iyi değildir ve bu yüzden kullanılmamalıdır. Onun yerine, çağırmak istediğiniz fonksiyon hangi objedeyse o objeyi bir değişkende tutup, bu değişken vasıtasıyla fonksiyonu direkt olarak ç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, sadece bir satır fazla kod yazarak 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 );
}
}
- Eğer build alacağınız platform Mono yerine IL2CPP destekliyorsa (Player Settings‘teki Scripting Backend ayarından bunu öğrenebilirsiniz), IL2CPP ile build almayı deneyin. Mono’da alınan build’lar, script’leri intermediate language (IL) denilen formata çevirirken, IL2CPP’de alınan build’lar ise kodu bu IL formatından C++ (CPP) formatına çevirir (farkettiyseniz, IL2CPP’nin açılımı da “intermediate language to C++“tır). Genel olarak bu bedavadan performans artışı anlamına gelir. Android’de IL2CPP ile build alabilmek için NDK kurulu olması gereklidir, eğer kurulu değilse Unity sizi download linkine yönlendirecektir.
- [5][14]
public int Degisken { get; set; }
şeklinde tanımlanmış property‘leripublic int Degisken;
şeklinde normal değişkenlere çevirin. Eğer değişkenin Inspector’da gözükmesini istemiyorsanız, başına[System.NonSerialized]
attribute‘ü ekleyin. - [14] Bir fonksiyonda sürekli yeni bir List veya array oluşturuyorsanız, onun yerine bu List veya array’i oyunun başında bir kere oluşturup tekrar tekrar kullanın. Bu optimizasyon özellikle garbage collector‘ı çok sevindirecektir.
- [14] Dokunmatik ekranlar için kod yazarken Input.touches kullanıyorsanız, onun yerine Input.touchCount ve Input.GetTouch kullanın. Dokunmatik ekran dersim için: https://yasirkula.com/2013/07/17/unity-ile-androide-uygulama-gelistirmek-1-dokunmatik-ekran-entegrasyonu/
Optimizasyon öncesi:
for( int i = 0; i < Input.touches.Length; i++ )
{
Touch parmak = Input.touches[i];
}
Optimizasyon sonrası:
for( int i = 0; i < Input.touchCount; i++ )
{
Touch parmak = Input.GetTouch( i );
}
- [14] OnCollisionEnter vs. fonksiyonlara parametre olarak gelen Collision‘ın contacts değişkenini her kullandığınızda yeni bir array oluşturulur. Onun yerine, eğer Unity 2018.3 veya sonrasını kullanıyorsanız, contactCount ve GetContact kullanın (tıpkı Input.touches optimizasyonu gibi).
- Bir objenin tag‘inin belli bir değere eşit olup olmadığına bakarken
obje.tag == "Player"
kodu yerineobje.CompareTag("Player")
kodunu kullanın. - [15] Kodunuzun belli bir noktasında, pek çok string’i birleştirerek yeni bir string oluşturmanız gerekiyorsa, string’leri + ile birbirine eklemek yerine StringBuilder kullanın. Sadece iki string’i birleştirecekseniz + kullanmaya devam edin, üç veya dört string’i birleştirecekseniz string.Concat fonksiyonunu kullanın, daha fazlası için StringBuilder kullanın. Hatta eğer mümkünse her seferinde yeni bir StringBuilder objesi oluşturmak yerine, tek bir StringBuilder objesi oluşturup onu tekrar tekrar kullanın. Her kullanımdan sonra, StringBuilder.Length‘i 0’a eşitleyerek StringBuilder’ın içini boşaltın.
Optimizasyon öncesi:
public void StringBirlestirme( int skor, int para )
{
string s1 = "Skor: " + skor;
string s2 = "Para: " + para + "TL";
string s3 = "Skor " + skor + " ve para " + para + "TL";
}
Optimizasyon sonrası:
// StringBuilder, kapasitesi string'i depolamaya yetmeyince
// otomatik olarak büyür ama dilersek StringBuilder'ın ilk kapasitesini
// objeyi oluştururken elle belirleyebiliriz (bu örnekte 100)
private readonly StringBuilder sb = new StringBuilder( 100 );
public void StringBirlestirme( int skor, int para )
{
string s1 = "Skor: " + skor;
string s2 = string.Concat( "Para: ", para, "TL" );
string s3 = sb.Append( "Skor " ).Append( skor ).Append( " ve para " ).Append( para ).Append( "TL" ).ToString();
sb.Length = 0; // Sonraki kullanımlar için StringBuilder'ın içini boşalt
}
- [15] String.StartsWith veya String.EndsWith fonksiyonlarının tek bir string parametre alan versiyonlarını kullanıyorsanız, onlar yerine şu fonksiyonları kullanın:
// "asd".StartsWith( "a" ) yerine PerformansliStartsWith( "asd", "a" ) kullanın
bool PerformansliStartsWith( string a, string b )
{
int aLen = a.Length;
int bLen = b.Length;
int ap = 0; int bp = 0;
while( ap < aLen && bp < bLen && a[ap] == b[bp] )
{
ap++;
bp++;
}
return ( bp == bLen );
}
// "asd".EndsWith( "a" ) yerine PerformansliEndsWith( "asd", "a" ) kullanın
bool PerformansliEndsWith( string a, string b )
{
int ap = a.Length - 1;
int bp = b.Length - 1;
while( ap >= 0 && bp >= 0 && a[ap] == b[bp] )
{
ap--;
bp--;
}
return ( bp < 0 );
}
- TextMesh Pro objelerinizde bir int, float vb. değişkenin değerini gösterecekseniz, bunu garbage collector’u yormadan yapın: https://yasirkula.com/2020/09/13/unity-cerez-ders-textmesh-pro-yazilarinda-sayi-gostermenin-efektif-yolu/
- [15] Bu maddeyi direkt örnek üzerinden göstereceğim.
Optimizasyon öncesi:
void Update()
{
for( int i = 0; i < array.Length; i++ )
{
if( boolDegisken )
BirFonksiyon( array[i] );
}
}
Optimizasyon sonrası:
void Update()
{
if( boolDegisken )
{
for( int i = 0; i < array.Length; i++ )
{
BirFonksiyon( array[i] );
}
}
}
- [15] Update‘te çağırdığınız bir fonksiyonu, her frame’de çalıştırmak yerine 3-4 frame’de bir veya her 0.1 saniyede bir çalıştırmanız kafiyse (kullanıcı aradaki farkı anlamayacaksa), o fonksiyonu boş yere her frame’de çalıştırmayın.
Optimizasyon öncesi:
void Update()
{
PahaliBirFonksiyon();
}
Optimizasyon sonrası:
void Update()
{
if( Time.frameCount % 3 == 0 ) // Her 3 frame'de bir çalışır
PahaliBirFonksiyon();
}
// VEYA
private float fonksiyonCagrilmaAni;
void Update()
{
if( Time.time >= fonksiyonCagrilmaAni )
{
PahaliBirFonksiyon();
fonksiyonCagrilmaAni = Time.time + 0.1f; // Her 0.1 saniyede bir çalışır
}
}
- [15] Bir kodun sadece obje kameranın görüş alanı içindeyse çalışmasını istiyorsanız, o kodu rendererComponenti.isVisible true ise çalıştırabilirsiniz. Görüş alanından kasıt, objenin kameranın field of view‘ının içinde olmasıdır, yani objenin bir duvarın arkasında gizleniyor olması isVisible’ın değerine etki etmez.
- [14][16] LINQ kullanmayın. LINQ performans olarak hiç iyi değildir. Gerekirse biraz daha fazla kod yazın ama LINQ’ten kaçının.
- [14] Regular Expression‘ları mümkünse kullanmayın, illa kullanmanız gerekiyorsa da bu regular expression’ı
new Regex
ile oluşturup bir değişkende tutun ve regular expression’ın sağlanıp sağlanmadığınaregexDegisken.Match
fonksiyonuyla bakın; yani static olanRegex.Match
fonksiyonunu kullanmayın. - [8] transform.SetParent fonksiyonunu gereksiz kullanmayın; bu fonksiyonun sık kullanımı kolaylıkla performansı düşürebilir. Bu yüzdendir ki Instantiate fonksiyonunun
Transform parent
parametre alan versiyonları bulunmaktadır: eğer bir objeyi Instantiate edip hemen ardından SetParent ile parent’ını değiştiriyorsanız, bu parent belirleme kısmını direkt Instantiate’in içinde yaparak performansı biraz daha iyileştirebilirsiniz. Eğer object pooling tekniğinden faydalanıyorsanız da, objeleri havuza eklerken mümkünse SetParent kullanmayın. - transform.position ve transform.eulerAngles yerine mümkünse transform.localPosition ve transform.localEulerAngles kullanın. İlk saydıklarım objenin global konum ve rotasyonunu döndürürken, ikinci saydıklarım ise objenin local konum ve rotasyonunu döndürürler (Inspector’da Position ve Rotation olarak gözüken değerler aslında ikinci saydığım değerlerdir). position ve eulerAngles değerleri, objenin parent‘larının Position, Rotation ve Scale değerleriyle beraber değiştiği için, position ve eulerAngles’ı hesaplarken parent’ların Transform’larının da hesaba dahil olması gerekir; bu hesaplama hiçbir zaman localPosition ve localEulerAngles kadar hızlı değildir. Eğer position ve eulerAngles’ına erişmek istediğiniz objenin bir parent’ı olmadığını veya tüm parent’larının (parent’ının parent’ı vs.) 0,0,0 Position, Rotation ve Scale değerlerine sahip olduğunu kesin olarak biliyorsanız, o zaman localPosition ve localEulerAngles kullanın.
- [15] Bir önceki maddede değindiğim gibi, transform.position ve transform.eulerAngles‘ı hesaplamak nispeten yavaştır. Bu yüzden bu değerlere mümkün olduğunca az erişmeye çalışın (örneğe bakınca anlayacaksınız).
Optimizasyon öncesi:
private void Update()
{
// Objeyi daima Y=10 koordinatında tut
// transform.position'a 2 kere erişiyoruz: x ve z koordinatlarını alırken
transform.position = new Vector3( transform.position.x, 10f, transform.position.z );
}
Optimizasyon sonrası:
private void Update()
{
// Bu sefer transform.position'a sadece bir kere erişiyoruz
Vector3 konum = transform.position;
konum.y = 10f;
transform.position = konum;
}
- [3] Hem transform.position‘a hem de transform.rotation‘a (veya transform.eulerAngles‘a) aynı anda değer veriyorsanız, bunun için transform.SetPositionAndRotation fonksiyonunu kullanın.
Optimizasyon öncesi:
private void Update()
{
obje1.transform.position = obje2.transform.position;
obje1.transform.eulerAngles = new Vector3( 0f, 90f, 0f );
}
Optimizasyon sonrası:
private void Update()
{
obje1.transform.SetPositionAndRotation( obje2.transform.position, Quaternion.Euler( new Vector3( 0f, 90f, 0f ) ) );
}
- [3] Bir kodu sadece objenin Transform‘u değiştiyse çalıştırmak istiyorsanız (örneğin konumu değiştiyse), kodu o Transform’un hasChanged değeri true olduğunda çalıştırın. Bir Transform’un herhangi değeri değiştiğinde, o Transform’un hasChanged’i otomatik olarak true olur. Ancak hasChanged true ise kodunuzu çalıştırdıktan sonra, hasChanged’i elle false yapın çünkü Unity bu değeri kendisi false yapmaz.
- [12][14][16] Oyununuzun bazı parametrelerini bir JSON veya XML dosyasından çekiyorsanız, onun yerine ScriptableObject kullanmayı düşünebilirsiniz. ScriptableObject’in dezavantajı, içinde saklanan değerlerin oyunu build aldıktan sonra değiştirilememesidir ancak bu sizin için bir sıkıntı değilse, ScriptableObject kullanarak daha performanslı bir şekilde parametreleri okuyabilirsiniz. Kullanıcının parametreleri değiştirebilmesini istediğiniz için özellikle JSON kullanıyorsanız, bu JSON’u parse etmek için mümkünse Unity’nin JsonUtility sınıfını kullanın. JsonUtility dersim için: https://yasirkula.com/2020/04/03/unity-jsonutility-kullanimi/
- Mobil oyunlarınızda, script’lerinizde OnMouseDown gibi fonksiyonlar bırakmayın. Bu fonksiyonlar varken build alırsanız Unity konsola bir uyarı basarak bunun mobilde performansı düşürebileceğinden yakınacaktır. İlaveten, Input.GetMouseButtonDown gibi içerisinde Mouse kelimesi geçen bir Input fonksiyonu veya değişkeni kullanmıyorsanız, oyunun başında
Input.simulateMouseWithTouches = false;
yapın. - [14] Materyallerin SetColor veya SetTexture gibi fonksiyonları ilk parametre olarak bir string veya int kabul eder. Eğer bu fonksiyonları birden çok kez çağırıyorsanız, performans için int’li fonksiyonları tercih edin. “_Color” gibi herhangi bir string parametrenin int karşılığını öğrenmek için, Shader.PropertyToID fonksiyonunu kullanabilirsiniz. Bu fonksiyon aynı string değeri için daima aynı int değeri döndürür, o yüzden bu fonksiyonun döndürdüğü int’i oyunun başında bir değişkende tutun ki birden fazla kez bu fonksiyonu çağırmak zorunda kalmayın.
- [14] Yukarıda bahsettiğim durum Animator için de geçerli. Animator’ın SetFloat veya SetBool gibi fonksiyonlarını da int parametre ile çağırın. Bu int’in değerini öğrenmek için ise, Animator.StringToHash fonksiyonunu kullanın.
- Kodlarınızda kullandığınız Debug.Log‘ların oyunu build aldığınızda da çalışmaya devam ettiğini biliyor muydunuz? Android’de bu log’lara çeşitli yollarla bakabilirken iOS’ta ise Xcode’un konsolu ile bakabilirsiniz. Debug.Log’lar, özellikle sık kullanılması halinde (mesela Update‘te), oyunun performansına gözle görülür derecede olumsuz etki edebilirler. Bu durumda yapabileceğiniz üç alternatif var:
- Sadece editörde gözükmesini istediğiniz Debug.Log’ları
#if UNITY_EDITOR
ve#endif
satırlarıyla çevreleyin:
#if UNITY_EDITOR
Debug.Log( "Bu log sadece editörde gözükecek" );
#endif
- [4][16] Hiçbir Debug.Log’un build alınan oyunda gözükmesini istemiyorsanız, şöyle bir Debug2 class’ı oluşturun:
using System.Diagnostics;
using Debug = UnityEngine.Debug;
public static class Debug2
{
[Conditional( "UNITY_EDITOR" )] public static void Log( object message ) { Debug.Log( message ); }
[Conditional( "UNITY_EDITOR" )] public static void LogWarning( object message ) { Debug.LogWarning( message ); }
[Conditional( "UNITY_EDITOR" )] public static void LogError( object message ) { Debug.LogError( message ); }
[Conditional( "UNITY_EDITOR" )] public static void LogException( System.Exception exception ) { Debug.LogException( exception ); }
}
Ardından artık Debug.Log yerine Debug2.Log fonksiyonunu kullanın. Bu fonksiyon System.Diagnostics.Conditional("UNITY_EDITOR")
attribute’üne sahiptir, yani UNITY_EDITOR condition’ının sağlanmadığı durumlarda (build alırken), Debug2.Log fonksiyonları script’lerinizden otomatik olarak silinir.
- Debug.Log’ların oyunda gözükmesini istiyor ama stacktrace‘lerinin (o log’un olduğu fonksiyonu hangi fonksiyonların çağırdığını gösteren bilgi) gözükmesini istemiyorsanız, Player Settings‘te Logging‘in altında yer alan değerleri ScriptOnly‘den None‘a çekin. Yalnız bu durum editörde aldığınız log’ları da etkiler, o yüzden bu değişikliği sadece build alırken yapıp, build aldıktan sonra değişikliği geri almak isteyebilirsiniz. Burada işin güzel yanı, normal log’ların stacktrace’lerini gizleyip sadece Debug.LogError‘ların veya hata mesajlarının (Debug.LogException) stacktrace’lerinin gözükmesini sağlayabilirsiniz (hataların kaynağını daha kolay tespit edebilmek için). Bunun için Logging ayarlarında Error ve Exception‘ı ScriptOnly’de bırakmanız yeterli.
- [1] Bir MeshRenderer veya SkinnedMeshRenderer objenin materyaline erişmek için iki değişken vardır: material ve sharedMaterial. İlki, objenin mevcut materyalini klonlar, bu klon materyali objeye atar ve ardından bu materyali döndürür; ikincisi ise objenin materyalini klonlamadan direkt döndürür. Her yeni materyal performans için kötü olduğu için, mümkün olduğunca material‘den kaçının. Örneğin objenin materyalini herhangi bir şekilde değiştirmeyecek ama sadece materyalin rengini vs. kontrol edeceksiniz daima sharedMaterial kullanın. Yok ama sahnedeki sadece belli bir objenin rengini değiştirecekseniz, o zaman material kullanabilirsiniz. Eğer materyalin alabileceği renkler belliyse (mesela ya kırmızı ya da beyaz), bunun daha iyi bir yolu, materyalin rengini değiştirmek yerine direkt materyali değiştirmektir. Yani elinizde aynı materyalin hem beyaz hem kırmızı versiyonu olur ve objenin direkt sharedMaterial‘inin değerini değiştirerek objenin rengini değiştirirsiniz. Maalesef sharedMaterial ile ilgili dikkat etmeniz gereken önemli bir husus var: editördeyken objenin sharedMaterial’ine yapılan değişiklikler, direkt Project’teki materyal asset’ini değiştirir, yani yaptığınız değişiklik Play moddan çıkınca geri alınmaz.
- [6] Vektör hesaplaması yaparken işlem önceliğine dikkat ederek bedavadan optimizasyon yapabilirsiniz:
// İki (Vector3*float) hesaplaması var: YAVAŞ
Vector3 v1 = Vector3.forward * 5f * Time.deltaTime;
// Bir (float*float) ve bir (Vector3*float) hesaplaması var: DAHA HIZLI
Vector3 v2 = Vector3.forward * ( 5f * Time.deltaTime );
Vector3 v3 = 5f * Time.deltaTime * Vector3.forward;
- [6] Bir List‘in elemanlarının üzerinden for döngüsü ile geçiyorsanız ve bu List’in eleman sayısı for’dayken değişmeyecekse, List.Count‘u sadece bir kere çağırmaya çalışın:
Optimizasyon öncesi:
float ListElemanlariniTopla( List<float> list )
{
float sonuc = 0;
for( int i = 0; i < list.Count; i++ )
sonuc += list[i];
return sonuc;
}
Optimizasyon sonrası:
float ListElemanlariniTopla( List<float> list )
{
float sonuc = 0;
for( int i = 0, elemanSayisi = list.Count; i < elemanSayisi; i++ )
sonuc += list[i];
// Veya list'i baştan sona değil sondan başa gez, Count yine bir kere kullanılır
//for( int i = list.Count - 1; i >= 0; i-- )
// sonuc += list[i];
return sonuc;
}
- [15] StartCoroutine ile bir coroutine‘i başlatırken mutlaka bir miktar RAM kullanılır (garbage collector için kötü), gereksiz coroutine’lerden kaçının.
- [15] Bir coroutine‘i bir frame bekletmek için
yield return 0;
kullanmayın, onun yerineyield return null;
kullanın. İlk kod, boxing’den dolayı RAM harcar. - [15] Coroutine‘lerde kullanılan WaitForSeconds‘ı birden çok kez kullanabilirsiniz, her seferinde
new WaitForSeconds
ile yeni bir obje oluşturmanıza gerek yok. - [4] Kodunuzda 2 boyutlu array kullanıyorsanız, bu array’leri
int[,]
şeklinde değil deint[][]
şeklinde tanımlayın. - [4] İnternetten veri indirmek için Unity’nin WWW sınıfını kullanıyorsanız, onun yerine UnityWebRequest kullanın. WWW her seferinde yeni bir thread oluştururken, UnityWebRequest’in kendi içinde bir thread havuzu bulunmaktadır.
- [7] Eğer oyununuzu Profiler ile debug ettiğinizde, oyundaki takılmaların büyük bir kısmının garbage collector (GC) tarafından kaynaklandığını tespit ederseniz, Player Settings‘teki Use incremental GC seçeneğini (Unity 2019.1+) açıp oyunu bir de öyle test edin.
- [5] Dictionary ve HashSet veri türlerinin tüm elemanlarının üzerinden foreach ile geçmek nispeten yavaştır. Eğer bu veri türlerinin sağladığı kolaylıklara özelliklere ihtiyacınız yoksa List kullanın veya aynı veriyi hem List hem de Dictionary/HashSet’te tutup tüm elemanların üzerinden geçerken List’i kullanın.
- [5] Key‘i UnityEngine.Object türünde olan çok sık kullandığınız bir Dictionary varsa, key’in türünü int yapıp, bir sorgu yaparken de
obje.GetInstanceID()
‘i key olarak kullanın. Hatta mümkünse bu GetInstanceID’nin sonucunu cache’leyin, örneğin objelerinizi bir List<Object>‘te tutuyorsanız, bunlara karşılık gelen GetInstanceID’leri de bir List<int>‘te tutabilirsiniz. - [5] Key‘i bir enum türünde olan Dictionary‘ler, sorgulama esnasında hafızayı bir miktar harcarlar ve bu da garbage collector‘ı yorar. Bundan kaçınmak için Dictionary’i oluştururken bir IEqualityComparer kullanın.
Optimizasyon öncesi:
private enum TestEnum { A, B };
private readonly Dictionary<TestEnum, int> dict = new Dictionary<TestEnum, int>();
private void Update()
{
int deger;
dict.TryGetValue( TestEnum.A, out deger ); // Her seferinde 20 byte RAM harcar
}
Optimizasyon sonrası:
private class TestEnumComparer : IEqualityComparer<TestEnum>
{
public bool Equals( TestEnum x, TestEnum y ) { return x == y; }
public int GetHashCode( TestEnum obj ) { return (int) obj; }
}
private enum TestEnum { A, B };
// Dictionary'i oluştururken IEqualityComparer kullanıyoruz
private readonly Dictionary<TestEnum, int> dict = new Dictionary<TestEnum, int>( new TestEnumComparer() );
private void Update()
{
int deger;
dict.TryGetValue( TestEnum.A, out deger ); // Artık hiç RAM harcamaz
}
- [8] Hierarchy’de sahnenin root’unda yer alan (parent’ı olmayan) tüm Transform‘ların hierarchyCapacity adında bir değişkeni bulunmaktadır. Unity sahnedeki Transform’ları kendi içinde şu şekilde barındırmaktadır: Her root Transform içerisinde List gibi bir yapı barındırır ve bu List’te de Transform’un tüm child objeleri yer alır (child objelerinin child objeleri, onların da child objeleri vs. bu listeye dahildir). Bu root Transform’a veya onun child’larından birine eklenen yeni bir child obje, root Transform’un içindeki bu List’e dahil olur. Eğer List ağzına kadar doluysa, List’in boyutu artar. Root Transform’ların sahip olduğu hierarchyCapacity, bu List’in kapasitesini belirlemektedir. Gelelim buradaki optimizasyona: eğer sahnedeki bir root Transform’a SetParent ile pek çok child obje ekleyecekseniz ve toplamda kaç child obje ekleyeceğinizi kabaca biliyorsanız, her birkaç SetParent’ta bir Transform’un hierarchyCapacity’sinin otomatik olarak artırılması yerine (kapasite her arttığında bu Garbage Collector’a yük olur), en başta root Transform’un hierarchyCapacity’sini elle belirleyerek otomatik olarak kapasitenin artmasının önüne geçebilirsiniz. Yalnız root Transform’a kaç child ekleyeceğinizi bileceğiniz gibi aynı zamanda her bir child’ın da kaç child’dan oluştuğunu bilmeniz ve hierarchyCapacity’i ona göre artırmanız lazım çünkü başta da dediğim gibi, root Transform’un içindeki List, onun tüm child’larını, child’larının child’larını, onların child’larını vs. kısaca objenin altında yer alan her şeyi içinde barındırır.
- [11] ParticleSystem‘ın Play, Pause gibi fonksiyonları
bool withChildren
isminde opsiyonel bir parametre alırlar; bu parametre varsayılan olarak true‘dur. Parametre true olduğunda, ParticleSystem GetComponentsInChildren ile alt objelerindeki ParticleSystem component’lerini de bulur ve Play/Pause komutlarını onlara da uygular. Tahmin edeceğiniz üzere, GetComponentsInChildren kullanımından dolayı bu performans için iyi değildir. Eğer withChildren parametresinin değeri false ise, GetComponentsInChildren çağrılmaz ve Play/Pause fonksiyonu sadece mevcut ParticleSystem’a uygulanır. Eğer bir ParticleSystem’ın child’ı olmadığını biliyorsanız withChildren’ı daima false yapın, aksi taktirde ParticleSystem’ı ve içindeki tüm child’ları oyunun başında bir array’e alıp, Play/Pause yaparken array’deki tüm ParticleSystem’ların Play/Pause fonksiyonlarını withChildren false parametre ile tek tek çalıştırın. - [9] Bir AudioSource‘un sesini Mute ile kıssanız bile, AudioSource hâlâ müziği sessiz bir şekilde çalarak CPU yemeye devam eder. Mümkünse Stop fonksiyonu ile çalmakta olan sesi durdurun veya
audioSource.enabled=false;
ile Audio Source’u disable edin. - [14] Bir fonksiyon params şeklinde bir parametre alıyorsa (mesela
params int[]
), bu fonksiyonu her çağırdığınızda arkaplanda yeni bir array oluşturulur ve parametreleriniz bu array’e kopyalanır. Örneğin Mathf.Max ve Mathf.Min fonksiyonlarının 2’den fazla parametre alan versiyonları params kullanır. Bu, özellikle garbage collector için kötü olduğundan, bu fonksiyonları kullanmamaya çalışın. - [15] Garbage Collector‘u
System.GC.Collect();
ile elle çalıştırabilirsiniz. Örneğin bir loading ekranı esnasında bu fonksiyonu çağırırsanız, garbage collector’un oyun esnasında çalışıp da oyunu dondurma şansını azaltırsınız. - [4] Çok uzun bir
const string
kullanıyorsanız, bunustatic readonly string
yapın. “const string” build esnasında kodda her kullanıldığı yere kopyala-yapıştır yapıldığı için, kodun şişmesine sebep olabilir. - [9] IL2CPP ile build alırken, compiler otomatik olarak kodunuza şu eklemeleri yapar (kaynak):
- Bir objeye erişmeden önce, o objenin null olup olmadığına bakan bir if koşulu
- Array’in herhangi bir elemanına erişmeden önce, o index’in array’in
[0,array.Length)
sınırları dahilinde olup olmadığına bakan bir if koşulu
IL2CPP kodunuzu C++’a çevirdiği için, bu if koşullarını otomatik olarak eklemeseydi, bu koşullardan birinde bir sıkıntı çıkınca (eriştiğiniz obje null ise veya array’in -1. index’teki elemanına erişmeye çalışırsanız) oyununuz çökerdi. Ancak bazen bu ekstra if koşullarının performans harcamaktan başka işe yaramadığı, yazdığınız kodun bu iki sıkıntıyı kesinlikle yaşamayacağını bildiğiniz durumlar olacaktır. Bu durumlarda isterseniz IL2CPP’nin kodunuzun o güvenli kısımlarına otomatik olarak if koşulları eklemesini engelleyebilirsiniz. Bunun için Unity’nizin kurulu olduğu konumdaki Editor\Data\il2cpp\Il2CppSetOptionAttribute.cs dosyasını kopyalayıp projenizin Assets klasörüne yapıştırın. Ardından şu iki attribute ile, istediğiniz class, fonksiyon veya property‘de bu if koşullarının oluşmasını engelleyebilirsiniz (kodun başına using Unity.IL2CPP.CompilerServices;
da ekleyin):
private float[] array = new float[5];
[Il2CppSetOption( Option.NullChecks, false )] // Null kontrolünü kapat
[Il2CppSetOption( Option.ArrayBoundsChecks, false )] // Array index'inin sınırlar dahilinde olup olmadığı kontrolünü kapat
private float ArrayElemaniDondur( int index )
{
return array[index];
}
Fizik Optimizasyonu
- [10][16] 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. Bu değeri ne kadar artırırsanız, bu performans için o kadar iyidir ama eğer değeri çok artırırsanız da o zaman fiziksel etkileşimler istendiği gibi çalışmamaya başlar, o yüzden deneye yanıla ince ayar yapmanız en iyisi.
- [10] 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.
- [10][16] 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 taklit etme durumu, çoğu fiziksel etkileşim için yeterli olur). O halde napıyoruz? Objelerimizdeki Mesh Collider’ları, Unity’nin sağladığı primitive collider’lar ile taklit etmeye çalışıyoruz (ardından Mesh Collider component’ini silmeyi unutmayın). İhtiyaç halinde bu collider’ları, objenin child GameObject’lerine de verebilirsiniz (mesela collider’a biraz eğim vermeniz gerekiyorsa).
- Raycast fonksiyonlarınızda, bu 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, tahmin edebileceğiniz üzere daha hızlı sonuç alırsınız. Ö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 );
}
- Physics.RaycastAll ve Physics.OverlapSphere gibi fonksiyonlar her seferinde yeni bir array döndürerek cihazın hafızasını yavaş yavaş sömürürler (bu sömürme durumu Physics.Raycast için geçerli değil). Bu fonksiyonların çoğunun, hafızadan yemeyen NonAlloc isimli versiyonları bulunmaktadır: Physics.RaycastNonAlloc ve Physics.OverlapSphereNonAlloc. Bu NonAlloc fonksiyonlar parametre olarak bir RaycastHit[] array’i alırlar ve buldukları RaycastHit sonuçları bu array’de depolarlar. Fonksiyonun döndürdüğü int değeri ise, kaç RaycastHit bulunduğunu (haliyle array’e kaç RaycastHit eklendiğini) belirtir. Diyelim fonksiyona kapasitesi 10 olan bir RaycastHit[] array’i verirseniz ve fonksiyon 3 döndürürse, fonksiyonun döndürdüğü RaycastHit’ler array’in 0, 1 ve 2. index’lerinde depolanır. Eğer fonksiyona çok küçük bir array verirseniz (bu örnekte diyelim 2 elemanlı bir array), fonksiyon 2 RaycastHit bulduktan sonra otomatik olarak durur, diğer RaycastHit’leri bulmaz.
- [16] Edit-Project Settings-Physics‘teki Layer Collision Matrix, hangi layer’larda yer alan objelerin birbiriyle temas edebileceğini belirler. Birbiriyle kesinlikle temas etmeyecek layer’ların tiklerini kaldırarak, boş yere bu layer’lar arası temas hesaplamaları yapılmasını engelleyebilirsiniz. Eğer 2D fizik kullanıyorsanız, aynı matrix Edit-Project Settings-Physics 2D‘de de mevcuttur.
- [16] Edit-Project Settings-Physics‘teki Auto Sync Transforms‘u kapatın. Bu değer açık olduğunda, bir objenin Transform’u ne zaman değişirse fizik motoru bu değişikliği anında fizik dünyasına uygularken, bu değeri kapatırsanız tüm Transform değişiklikleri FixedUpdate’ten hemen önce toplu bir şekilde fizik dünyasına uygulanır. Oyununuzda çok fazla Rigidbody varsa ve Update’te sürekli Transform değerlerini değiştiriyorsanız, bu optimizasyonun performansa büyük etkisi olabilir.
- [16] Edit-Project Settings-Physics‘teki Reuse Collision Callbacks‘i açın. Kodlarınızdaki OnCollisionEnter gibi fonksiyonların aldıkları
Collision temas
parametresi, her seferinde yeni bir Collision objesi oluşturarak yavaş yavaş hafızadan yer. Ancak eğer bu seçeneği açarsanız, fizik motoru tek bir Collision objesini tekrar tekrar kullanır, sürekli yeni Collision objesi oluşturmaz. Bu durumun tek dezavantajı, eğer Collision parametresini script’inizde daha sonra kullanmak üzere bir yerde depoluyorsanız, bu Collision objesinin içeriği bir sonraki temasta değişeceği için kodunuz düzgün çalışmayabilir. Ancak ben şimdiye kadar hiç Collision objesini depolama ihtiyacı hissetmediğimden, bu handikapın çoğu proje için önemsiz olduğunu düşünüyorum.
UI Optimizasyonu
- [2] UI’ın daha performanslı çalışması ve sprite’larınızın daha az yer kaplaması için, UI’de sprite atlas kullanın. Neyse ki Unity bu konuda bize oldukça yardımcı oluyor. Edit-Project Settings-Editor‘de yer alan Sprite Packer‘ı Always Enabled‘a veya Enabled For Builds‘a çekin. İlki, Unity’nin editörde de sprite atlasları kullanmasını sağlarken ikincisi, sprite atlas’ların sadece oyunu build alırken oluşmasını sağlar; ben ilkini kullanıyorum. Bundan sonraki aşama, Unity sürümünüze göre değişiklik gösteriyor:
- Unity 2017.1 ve sonrası: Project panelinde Create-Sprite Atlas ile yeni bir sprite atlas oluşturun. Allow Rotation ve Tight Packing değerlerini kapatın (UI’da sıkıntı çıkarabiliyor) ve sprite atlas’a eklemek istediğiniz sprite’ları, Objects for Packing kısmına ekleyin. Buraya bir klasör eklerseniz, klasörün içindeki tüm sprite’lar otomatik olarak sprite atlas’a dahil olur. Dilerseniz Pack Preview butonuna tıklayarak, oluşan sprite atlas’ı görüntüleyebilirsiniz.
- Daha eski sürümler: Sprite Atlas’a eklemek istediğiniz sprite’ların Inspector’larındaki Packing Tag‘lerine istediğiniz gibi bir değer verin (mesela “UIAtlas“). Aynı Packing Tag’i taşıyan sprite’lar, otomatik olarak aynı sprite atlas’a eklenirler.
- [5][13][16] Tüm UI objelerinizi tek bir Canvas‘ın içine yığmayın. Canvas’ın içindeki tek bir UI elemanının tek bir özelliği değişse (konumu, rengi vs.), tüm canvas yenilenmek zorunda kalır (evet, TÜM CANVAS). Örneğin dinamik UI objeleriniz ile statik (hiç değişmeyen) UI objelerinizi ayrı canvas’larda tutun. Dinamik UI objelerinizden bazıları çok sık değişiyor ama bazıları ender değişiyorsa, bu çok sık değişen UI objelerinizi de ayrı bir canvas’a alın. Burada işin güzel yanı, bu iki canvas’ın birbirinden tamamen bağımsız olmasına gerek yok, bir canvas objesi başka bir canvas objesinin child objesi olabilir; buna nested canvas denir. Parent canvas’taki bir UI objesi değişse bile, nested canvas’taki UI objelerinde bir değişiklik olmadığı sürece nested canvas yenilenmez, boş yere CPU harcanmaz. Nested canvas’larla ilgili dikkat etmeniz gereken tek şey, eğer nested canvas’ın içinde Button gibi, mouse veya parmak ile etkileşime girebilen bir UI objesi varsa, nested canvas’ta Graphic Raycaster component’i olduğundan emin olun; parent canvas’taki Graphic Raycaster nested canvas’ı etkilemez.
- [2] Canvas’ınızın Hierarchy’sinde objelerinizin sırası Image-Text-Image-Text diye gidiyorsa, bunu mümkün olduğunca Image-Image-Text-Text şeklinde yeniden düzenlemeye çalışın. Yani Text’leri Hierarchy’de elinizden geldiğince alt alta tutmaya çalışın, araya Image vari başka bir component sokmayın. Bu şekilde Unity’nin UI’ı daha iyi optimize ederek draw call sayısını azaltma şansı daha fazla olacak.
- [5][16] Horizontal Layout Group ve Grid Layout Group gibi Layout Group component’lerini mümkün olduğunca az kullanın, canvas her kendini yenilediğinde bu component’ler canvas’a ekstra yük olurlar. Arayüzünüzün düzenli olması için bu Layout Group’lar kritik öneme sahipse ama içerikleri dinamik değilse (bir kere oluştuktan sonra değişmeyecekse), arayüzünüzü ekranda gösterdikten sonra bu component’leri disable ederek (
.enabled=false
), canvas’ın bundan sonraki kendini yenileme sürecinde bu component’lerin boş yere aynı hesaplamaları yapmalarının önüne geçebilirsiniz. - Yeni bir Scroll View oluşturduğunuzda, oluşan objenin Viewport isimli child’ında Mask da dahil olmak üzere bir takım component’ler yer alır. Mask’ın amacı, scroll view’ın içeriğinin, Viewport’un dışında kalan kısmını ekrana çizdirmemektir. Artık aynı işi daha performanslı bir component ile yapmak mümkündür: Rect Mask 2D. Bu değişiklik için Viewport’taki RectTransform hariç tüm component’leri silip ardından Add Component ile objeye Rect Mask 2D component’i eklemeniz yeterli.
- [2] Text component’inin Best Fit seçeneğini çok lüzumlu olmadıkça kullanmayın çünkü yazının font boyutunun hesaplamak için CPU’nun bir takım deneme yanılma yapması gerekmektedir.
- (Unity 2018.3+) [1][16] İçerisinde çok fazla transparan piksel olan bir sprite arayüzde büyük bir yer kaplıyorsa, bu overdraw sıkıntısına zemin hazırlarlar. Overdraw, objelerin üst üste çizilmesinin GPU’yu zorlaması sıkıntısıdır. Ne kadar çok obje üst üste çizilirse, grafik performansı o kadar düşer. Image‘larınızda kullandığınız sprite‘lardaki transparan pikseller her ne kadar görünmez olsalar da, GPU tarafından işlendikleri için overdraw’dan kaçamazlar. Neyse ki Unity’nin son sürümleriyle beraber, Image component’ine Use Sprite Mesh değişkeni eklendi (yalnızca Image Type‘ın değeri Simple iken gözükür). Bu ayarı aktif ettiğinizde, artık Image ekrana bir quad (dikdörtgen) geometri ile çizilmek yerine, sprite import edilirken otomatik olarak oluşturulan optimize geometri ile çizilir (sprite’ın Inspector’undaki Mesh Type‘ın Tight olması lazım, oluşan geometri transparan piksellerin büyük kısmını dışarıda bırakır). Bu, ekrana biraz daha fazla vertex ve triangle çizilmesi anlamına gelir ama overdraw genelde vertex sayısından daha büyük bir sıkıntı olduğu için, bu bir sorun olmaz.
- [16] Yukarıda bahsettiğim overdraw (objelerin üst üste çizilmesi) sıkıntısından dolayı, bir Image‘ın üzerine başka bir Image veya Text‘i olabildiğince az çizdirmeye çalışın. Örneğin Hearthstone vari bir kart oyununda, kartın her bir bileşenini ayrı bir Image veya Text yapmak, kartı kişiselleştirmeyi kolaylaştırır ama bir yandan da overdraw’u artırır. Böyle bir durumda, sizi de çok zora sokmayacak şekilde, üst üste çizilen sprite ve text’leri olabildiğince birleştirmeye, daha az Image ve Text kullanmaya çalışın. Örneğin her kartın PNG dosyası, o kartın üzerindeki yazıları vs. içine dahil ederse, tek bir Image ile kart ekrana çizilebilir.
- [5] Canvas‘ın Pixel Perfect‘i ekstra performans harcar, lüzumlu değilse bu seçeneği kapalı tutun.
- [5] Canvas‘larınızda Render Mode olarak Screen Space – Camera yerine mümkünse Screen Space – Overlay‘i tercih edin.
- [5][13] Render Mode‘u World Space olan Canvas‘larınızın Event Camera‘sını asla boş bırakmayın, buraya kameranızı değer olarak verin. Aksi taktirde Unity arkaplanda defalarca kez Camera.main‘i çağırmak zorunda kalır.
- [13][16] Mouse veya parmak ile herhangi bir şekilde etkileşime geçmeyen Image, Text vs. objelerin Raycast Target‘larını kapatın. Örneğin yeni bir Button oluşturunca, içinde otomatik olarak bir Text de oluşur; bu Text’te Raycast Target’a gerek yoktur.
- [5][13] Bir Canvas‘ı gizlemek için SetActive(false) yerine enabled=false‘u tercih edin, canvas’ı bu şekilde gösterip gizlemek daha performanslıdır. Ancak enabled=false yaptığınızda, canvas’ın child objelerinde Update fonksiyonu olan script’ler varsa bunlar çalışmaya devam edecektir, bu yüzden Canvas’ın enabled’ını değiştirdikten hemen sonra bu script’lerin enabled’ını da değiştirmek isteyebilirsiniz.
- [5][16] Scroll View‘ınızda çok fazla (yüzlerce veya binlerce) eleman varsa, sadece ekranda gözüken elemanlar için UI objesi oluşturan bir sistem kullanın (list view diye de geçer); aksi taktirde o yüzlerce eleman, canvas’ın sınırları dışında kalsalar bile bir miktar performans yerler. Bu konuda yazdığım ders: https://yasirkula.com/2020/10/14/unity-scroll-viewda-cok-sayida-objeyi-performansli-bir-sekilde-gostermek/
- [5] Scroll Rect component’inin Inertia değeri açık olduğunda, scroll view’ı biraz kaydırıp parmağınızı çektiğinizde, scroll view hâlâ kaymaya devam eder ve bir süre sonra yavaşlayarak durur. Eğer Inertia kullanıyorsanız, Deceleration Rate‘i olabildiğince düşürün (mesela 0.001). Bu değer ne kadar düşerse, scroll view o kadar hızlı yavaşlayıp durur. Scroll view kaydığı sürece canvas sürekli kendini yenilemek zorunda olduğu için, bu süreyi kısaltmak canvas’ı rahatlatır.
- [5] Eğer UI elemanlarınızdan bazılarında object pooling‘den faydalanıyorsanız ve bir UI objesini havuza atarken aynı zamanda SetParent yapıyorsanız (illa gerekli değilse yapmayın), SetParent’tan hemen önce objeyi inaktif yapın. Havuzdan objeyi çekerken ise önce SetParent yapın, ardından objede herhangi bir değişiklik yapacaksanız bu değişiklikleri yapın ve ancak ondan sonra objeyi aktif hale getirin.
Grafik Optimizasyonu
- 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.
- Mobil platformlarda Player Settings‘teki Resolution Scaling Mode‘u Fixed DPI yapın ve Target DPI değerini de 320 yapın. Bu ayar, oyunun maksimum 320dpi çözünürlüğünde ekrana çizilmesini sağlar. Özellikle retina ekranlar ufak olmalarına rağmen bilgisayar çözünürlüklerinin bile üzerinde çözünürlüklere sahip olabildikleri için, bu ekranlarda render almak çok zaman alabilir. Ancak oyunu maksimum 320dpi’da render alarak bu süreci epey hızlandırabilirsiniz. Dilerseniz 320dpi değerini, oyunun görselliğinde büyük bir fark olmadığı sürece daha da düşürmeyi deneyebilirsiniz.
- Minimum sayıda materyal kullanın. Oyun esnasında kameranın görüş alanındaki farklı materyal sayısı ne kadar çok olursa, “SetPass calls” dediğimiz değer de o kadar çok olur; bu da oyunun yavaşlamasına sebep olur.
- [16] Shader’larınızı olabildiğince basit tutun/seçin. Yeni oluşturulan materyallere otomatik olarak atanan Standard Shader‘ı minimum düzeyde kullanın (mobil oyunlarda hiç kullanmamaya çalışın). Bu shader, PBR adı verilen gerçekçi ışıklandırmaya yönelik hesaplamalar üzerine kurulduğu için, özellikle mobil platformlarda çok fazla performans harcar. 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.
- 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 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.
- Game panelinin sağ üstündeki Stats kapalıysa açın ve oyun boyunca bir gözünüzü “Batches” ve “SetPass calls“ta tutun. Bu değerler ne kadar az olursa o kadar iyi. “SetPass calls” değeri, kameranın görüş alanındaki objelerde ne kadar çok farklı materyal varsa o kadar artar. “Batches” değeri ise, kameranın görüş alanındaki objelerin sayısı ile doğru orantılı olarak artar. Gölgelerin açık olduğu durumda Batches değeri yaklaşık ikiye katlanabilir. Bu iki değerin optimizasyonunda önceliği “SetPass calls”a vermelisiniz çünkü bu değerin yüksek olması, “Batches”ın yüksek olmasına göre performansa daha çok etki eder. “SetPass calls” değerini düşürmenin tek yolu var: olabildiğince az materyal kullanmak; yani olabildiğince çok objeye aynı materyali vermelisiniz.
- (Özellikle mobil) [15] Eğer sahnenizde aynı materyali taşıyan ve low-poly olan çok fazla obje varsa, Player Settings‘teki Dynamic Batching‘i açarak “Batches“ın azalmasına yardımcı olabilirsiniz. Bu ayar, Unity’nin kameranın görüş alanındaki aynı materyale sahip objeleri GPU’ya yollamadan önce birleştirerek tek bir obje yapmasını sağlar, böylece birden çok obje tek bir seferde çizilir. Bir frame’de kaç objenin birleştirildiğini, Stats ekranında “Saved by batching” olarak görebilirsiniz.
- Eğer oyununuz kapalı bir alanda veya bir şehirde geçiyorsa, Occlusion Culling kullanarak bir duvarın veya binanın arkasında kalan objelerin boş yere GPU’ya yollanmasını engelleyin. Occlusion Culling dersim için: https://yasirkula.com/2020/03/31/unity-occlusion-culling-sistemi/
- Farklı texture’lara sahip objelerin aynı materyali kullanmaları için “texture atlasing” tekniğinden faydalanın. Yalnız bunun için 3D modelleme konusunda biraz bilginizin olması ya da Asset Store’daki plugin’lerden birine sahip olmanız lazım.
- Çoğu 3D modelleme uygulaması, modeli “vertex color” ile boyamanıza imkan verir. Eğer ki modeliniz sadece düz renklerden oluşuyorsa, ona texture vermek yerine sadece vertex color uygulayabilirsiniz. Bu durumda objenin Unity’deki shader’ını, vertex color destekleyen bir shader ile değiştirmeyi unutmayın (bunun için “unity vertex color diffuse shader” gibi arama yapabilirsiniz).
- [1] 3D modellerinizde, oyuncunun hiç göremeyeceği yüzleri (triangle) silin. Örneğin bardak modelinizin alt yüzü oyun esnasında hiç gözükmüyorsa, oradaki triangle’ları silin gitsin.
- [1][16] Özellikle mobilde Image Effect (post processing) 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.
- [1] Unity’nin son sürümleri ile gelen dinamik skybox yerine, cubemap kullanılarak hazırlanmış eski usül skybox’lar kullanın. Yeni skybox dinamik olarak oluşturulduğu için performans açısından daha kötüdür.
- 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)
- Window-Lighting‘de “Precomputed Realtime GI” ve “Baked GI” adında 2 seçenek bulunmaktadır. Bu seçenekler lightmapping ile alakalıdırlar. 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
- [1] Lightmapping kullanıyorsanız, çok ufak statik objeleri bu sürece dahil etmeyin çünkü obje çok ufak olduğu için lightmapping’in etkisi belki de belli bile olmayacak, ama obje boş yere lightmap texture’larınızın boyutunu artıracak. Onun yerine bu objeleri light probe‘lar ile ışıklandırın (illa ışıklandıracaksanız). Bir statik objeyi lightmapping’den çıkarmak için, objenin Inspector’undaki Static işaretinin yanında yer alan oka tıklayın ve Lightmap Static‘i kapatın (yeni Unity sürümlerinde Contribute GI).
- (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 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.
- (Özellikle mobil) Player Settings‘teki Color Space‘i Gamma‘da tutmaya çalışın. Alternatif seçenek olan Linear daha gerçekçi ışıklandırma sağlar ama daha yavaştır.
- [1] Player Settings‘te Multithreaded Rendering açık değilse açın.
- 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, build aldığınız platformun varsayılan grafik düzeyine geçiş yapın (varsayılan düzey yeşil bir tik ile gösterilir). Ardından “V Sync Count“u “Every V Blank” yapın ve oyunu test edin.
- 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 ışıklarınızda “Hard Shadows” kullanın.
- Eğer gölge kullanıyorsanız, Edit-Project Settings-Quality‘den Shadow Distance‘ı olabildiğince düşürün. Bu değer, gölgelerin çizileceği en uzak mesafeyi belirler. Daha uzaktaki objelerin gölgeleri çizilmez. Quality ayarlarındaki Shadow Cascades‘i de, görsele çok büyük etkisi olmadığı sürece No Cascades yapın.
- [4][14] 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.
- [8][14] 3D model asset’lerinizin Inspector’daki Optimize Mesh değerinin açık, Read/Write Enabled değerinin kapalı olduğundan emin olun. Eğer objeye Mesh Collider verecekseniz o zaman “Read/Write Enabled”ı açık bırakmak isteyebilirsiniz.
- [15] Bir 3D modeli hiçbir zaman normal map ile kullanmıyorsanız, Inspector’daki Tangents‘ı None yapın. Eğer modeli Unlit materyal harici bir materyalle kullanmıyorsanız, o zaman Normals‘ı da None yapın. Böylece model, kullanmadığınız bu verilerden arınmış olacak.
- [8] Animasyona sahip 3D modellerinizin Inspector’daki Rig sekmesinde yer alan Animation Type değeri Generic veya Humanoid ise, aynı sekmedeki Optimize Game Objects‘i etkinleştirin. Bu şekilde 3D modelin child objeleri Unity tarafından optimize edilip Hierarchy’den gizlenir. Eğer belli başlı child objelerin Hierarchy’den gizlenmesini istemiyorsanız, onları Extra Transforms To Expose listesine ekleyebilirsiniz. Eğer ki “Optimize Game Objects” seçeneğini değiştirdiğiniz bir 3D model halihazırda bir prefab’ın parçası ise, 3D modeli o prefab’dan silip tekrar eklemeniz gerekebilir çünkü child objelerin Hierarchy’den gizlenmesi değişikliği prefab’lara otomatik olarak uygulanmaz.
- [9][14] Animasyona sahip olmayan 3D modellerinizin Rig sekmesindeki Animation Type‘ını None yapın. Animasyona sahip modellerde ise, inverse kinematics (IK) veya animation retargeting kullanmıyorsanız, Humanoid yerine Legacy (tercihen) veya Generic Animation Type kullanın.
- [16] LOD (Level of Detail) sistemini kullanarak, uzaktaki objelerin daha basit geometriler ile çizilmesini sağlayın. Bu optimizasyon, özellikle büyük çaplı PC oyunlarında çok önemlidir. LOD dersim için: https://yasirkula.com/2020/10/17/unity-lod-sistemi/
- 2D bir oyun yapıyorsanız, UI Optimizasyonu kısmında bahsettiğim Sprite Atlas tekniği 2D Sprite’larınızda da işe yarar. Bu sefer oluşturacağınız Sprite Atlas asset’lerinizin Allow Rotation ve Tight Packing değerlerini açık bırakabilirsiniz, bunlar UI dışında bir sıkıntı çıkarmazlar.
- [15] 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. Default grafik ayarı, yeşil bir tik ile gösterilir.
- Oyununuzun görseline büyük etkisi olmadığı sürece kameranızın Allow HDR ve Allow MSAA değerlerini kapatın. HDR genelde bloom vari post-processing efektlerde kullanılırken, MSAA ise anti-aliasing’i aktifleştirir. Eğer illa anti-aliasing kullanacaksanız, Edit-Project Settings-Quality‘de Anti Aliasing‘in açık olduğundan emin olun.
- (Android) Eğer anti-aliasing kullanmıyorsanız, Player Settings‘teki Blit Type‘ı Auto yapmayı deneyin (sadece 2018.3 ve üstü için).
- 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 (bu durum sprite’lar için geçerli değil). 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 (sıkıştırma) şansına da sahip olacaksınız. Yok illa ki 75×75 texture kullanacağım diyorsanız da 128-75=53 piksellik boş alanı başka bir texture için kullanmayı düşünebilirsiniz (texture atlasing).
- Kameranızın Far Clipping Plane değerini olabildiğince azaltın. Bu değer, kameranızın ekrana çizdireceği objelerin kameradan maksimum ne kadar uzakta olabileceğini belirler. Daha uzaktaki objeler ekrana çizilmez. Bu optimizasyon aynı zamanda z-fighting denilen sıkıntıyı gidermeye de yardımcı olur.
- [1] Camera‘nın çok bilinmeyen bir değişkeni bulunmaktadır: layerCullDistances. Bu değişken vasıtasıyla, kameranın Far Clipping Plane‘inin her layer için farklı olmasını sağlayabilirsiniz. Bu şekilde, sahnenizdeki ufak objeleri farklı bir layer’a alıp bu layer’ın ekrana çizilme mesafesini ufak bir değer yaparsanız, diğer objelerin aksine bu objeler görüş alanından daha çabuk kaybolurlar (ekrana çizilmeyen her obje artı performans demektir), ama zaten ufak oldukları için oyuncu bunu anlamaz bile. Daha fazla bilgi için: https://docs.unity3d.com/ScriptReference/Camera-layerCullDistances.html
- Eğer oyununuzdaki sahnelerde genel olarak hep aynı shader’ları kullanıyorsanız, Player Settings‘teki Keep Loaded Shaders Alive seçeneğini işaretleyerek, sahneler arası geçişlerde shader’ların hafızadan silinmesini engelleyebilirsiniz. Bu şekilde, her yeni sahne açılışında aynı shader’lar tekrar tekrar hafızadan silinip hemen ardından tekrar hafızaya alınmakla uğraşılmaz, shader’lar hep hafızada kalırlar.
Oyun Boyutu (Build) Optimizasyonu
NOT: Unity 2018.2‘den itibaren, şu plugin’i kullanarak oyununuzda en çok hangi asset’lerin yer kapladığını görebilirsiniz: https://assetstore.unity.com/packages/tools/utilities/build-report-inspector-for-unity-119923
- Oyunlarda en çok boyutu genellikle texture‘lar ve müzik dosyaları kaplar. Daha ufak bir build için bu dosyaların Inspector’daki compression ayarlarında olabildiğince ince ayar yapın.
- 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.
- Shader’larınızdaki kullanılmayan özellikleri kapatın (shader stripping): https://yasirkula.com/2020/09/06/unity-shader-boyutu-optimizasyonu/
- Alpha kanalını kullanmadığınız texture’ların Inspector’daki Alpha Source‘unu None yapın. Bu şekilde bazı texture’ların boyutu yarıya kadar inebilir (zaten alpha’sı olmayan texture’lara bir etkisi olmaz). Alpha kanalı genelde saydamlık için kullanılır, bu yüzden sadece tamamen opak objelerin texture’ları bu optimizasyondan faydalanabilir. Nadiren de olsa alpha kanalı, Standard shader’da objenin ışıklandırmasına etki etmek için kullanılabilir. Bu yüzden bu optimizasyonu uygularken, sahnenizde objenin bir klonu olsun ve optimizasyonun objeye görsel olarak bir etkisi olmadığından emin olun.
- [4][14] 3D modellerinizin Mesh Compression‘ına Off harici bir değer vermeyi deneyin. Eğer 3D modelin görüntüsünde gözle görülür bir bozulma olursa bu ayarı geri alın.
- [14] Player Settings‘teki Vertex Compression, 3D modellerinizi sıkıştırma konusunda yardımcı olur. Burada Position harici her şeyi işaretlemek isteyebilirsiniz. Benzer şekilde, Optimize Mesh Data‘yı açarak, 3D modelde kullanılmayan UV kanalları vs. varsa bunların build esnasında modelden silinmesini sağlayabilirsiniz. Hangi kanalların kullanıldığını hesaplamak için, Unity modelin materyaline atanmış shader’ı inceler.
- (Android) Unity ile x86 işlemcili Android cihazlara da build almak mümkündür. Bu her ne kadar kulağa güzel gelse de, oyunun x86 işlemci versiyonunu APK’ya eklemek, dosya boyutunu ciddi miktarda artırabilir. x86 işlemciyi APK’nıza dahil etmemek için, Player Settings-Other Settings‘teki Device Filter‘ın değerini ARMv7 yapabilirsiniz. Zaten artık Google Play’e x86’lı bir APK’yı upload edemediğiniz için (Unity x64 desteklemediğinden), x86’yı APK’nıza dahil etmeniz için bir sebep kalmıyor. Unity’nin ilerleyen sürümleriyle birlikte x86 desteğinin tamamen kesileceğinin de buradan altını çizelim.
- (Mobil platformlar) Oyununuzu build alırken, kodunuzda kullandığınız class’ları içeren dll‘ler de apk dosyasına eklenir ancak bu dll’ler ile gelen kullanmadığınız diğer class’lar boş yere fazlalık oluşturur. Unity’nin build alırken bu class’ları otomatik olarak yok saymasını sağlayan bir özellik var: “Stripping Level” (Player Settings-Other Settings)(yeni Unity sürümlerinde “Managed Stripping Level“). 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). Bu saydığım seçenekler yeni Unity sürümlerinde Low, Medium ve High olarak geçmekte. Ben bu seçeneği High yapıyorum ve script’lerle alakalı bir sorun yaşamadığım sürece de High’da bırakıyorum.
- Window-Package Manager‘ı açıp All packages‘ı Built-in packages yapın ve oradaki ihtiyacınız olmayan modülleri Disable edin. Örneğin çoğu oyun AI (NavMesh), Asset Bundle veya Cloth modüllerini kullanmaz. Bazen kullandığınız bir plugin, kapattığınız bir modülü kullanıyor olabilir. Neyse ki bu durumda konsola bir hata mesajı düşer ve hangi modülü tekrar Enable ederek sorunu çözebileceğiniz yazar.
- [1] Edit-Project Settings-Graphics‘te “Built-in Shader Settings” başlığı altında yer alan kullanmadığınız grafik özelliklerinin değerini “Built-in shader“dan “No Support“a çekin. Örneğin çoğu mobil oyunda Deferred, Motion Vectors, Light Halo ve Lens Flare kullanılmaz. Benzer şekilde, Always Included Shaders‘ta bariz kullanmadığınız shader’lar varsa bunları kaldırın; örneğin Video Player kullanmıyorsanız Video shader’larını ve UI sistemini kullanmıyorsanız UI shader’larını kaldırabilirsiniz.
Diğer Optimizasyonlar
- [3] Sırf Hierarchy düzenli dursun diye objelerinizi Empty GameObject‘lerin child’ı yapmayın. Bir child objenin Transform’u değiştiğinde, Unity kendi içinde bu Transform’un tüm kardeşlerini potansiyel olarak değişmiş işaretler ve bu işlem CPU’dan yer. Bunun için sahnenizdeki hareketli objeler Hierarchy’nin root’unda dursun, yani bir parent’ları olmasın. Ama bir Empty GameObject’in içindeki tüm objeler hareketsizse, o Empty GameObject o şekilde kalabilir, bir sıkıntı yok.
- [5][9][16] Çok basit animasyonlar için Animator component’i yerine Animation component’ini kullanın veya DOTween gibi kod bazlı bir animasyon plugin’i kullanın.
- Çok kısa ve sıklıkla kullandığınız AudioClip‘lerin Load Type‘ını Decompress On Load yapın. Bu AudioClip’ler hafızada çok daha fazla yer kaplar ama oyun esnasında decompression için CPU harcamazlar. Bu yüzden de bu ayar sadece çok kısa ses efektleri için idealdir. Müziklerde ise Streaming seçeneğini seçin. Bu ayar seçili olduğunda müzik diskten okunur ve müziğin tamamı hafızaya alınmaz, sadece çalmakta olan kısmı hafızaya alınır.
- [14][16] 3D uzayda çalan AudioClip‘lerin Force To Mono seçeneğini açın; aksi taktirde bu sesler oyun esnasında dinamik olarak mono’ya çevrilirler ve bu esnada CPU harcarlar. Aslında müzik gibi, 3D uzaydan bağımsız olarak çalan ses dosyalarını da Force To Mono yapmak isteyebilirsiniz çünkü AudioSource component’i, Mono sesleri daha kolay çalar.
- AudioClip‘lerle ilgili diğer optimizasyonlar için: https://yasirkula.com/2020/03/31/unity-audioclip-import-ayarlari/
- [3] Oyununuzda aynı anda pek çok AudioSource oynuyorsa, Edit-Project Settings-Audio‘daki Max Virtual Voices ve Max Real Voices‘ın değerlerini olabildiğince kısarak, Unity’nin tek bir frame’de hesaba kattığı AudioSource sayısına bir limit koyabilirsiniz.
- (iOS) [17] Eğer projenizde Input.acceleration ve Input.gyro‘dan faydalanmıyorsanız, Player Settings‘teki Accelerometer Frequency‘nin değerini Disabled yaparak, bu sensörün boş yere CPU harcamasını engelleyin.
- Oyun boyunca sürekli kullandığınız asset’leri, Player Settings‘teki Preloaded Assets listesine ekleyerek, asset’in oyunun başında hafızaya alınıp oyun bitene kadar hafızadan çıkmamasını sağlayabilirsiniz. Bu sayede sahneler arası geçişlerde bile asset hafızada kalır ve oyun esnasında asset’i yüklerken herhangi bir gecikme yaşanmaz.
Yararlanılan Kaynaklar
[1] https://learn.unity.com/tutorial/optimizing-graphics-in-unity
[2] https://learn.unity.com/tutorial/optimizing-unity-ui
[3] Unite Berlin 2018 – Unity’s Evolving Best Practices: https://www.youtube.com/watch?v=W45-fsnPhJY
[4] https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity.html
[5] Unite Europe 2017 – Squeezing Unity: Tips for raising performance: https://www.youtube.com/watch?v=_wxitgdx-UI
[6] https://www.habrador.com/tutorials/unity-optimization/
[7] https://makaka.org/unity-tutorials/optimization
[8] Unite 2016 – Let’s Talk (Content) Optimization: https://www.youtube.com/watch?v=n-oZa4Fb12U
[9] https://unity3d.com/how-to/unity-best-practices-for-engine-performance
[10] Unite 2012 – Performance Optimization Tips and Tricks for Unity: https://www.youtube.com/watch?v=jZ4LL1LlqF8
[11] https://unity3d.com/how-to/work-optimally-with-unity-apis
[12] https://unity3d.com/how-to/unity-common-mistakes-to-avoid
[13] https://unity3d.com/how-to/unity-ui-optimization-tips
[14] https://learn.unity.com/tutorial/mobile-optimization
[15] https://learn.unity.com/tutorial/fixing-performance-problems
[16] https://create.unity3d.com/nine-ways-to-optimize-game-development
[17] Optimization tips for maximum performance – Part 1 | Unite Now 2020: https://www.youtube.com/watch?v=ZRDHEqy2uPI
Yasir Bey merhabalar. Kendi oyunumda profiler ile analiz yaptığımda en büyük GC Alloc yaratan durumlardan birinin Unity’de On Play tuşuna basar basmaz kendiliğinden oluşan “DontDestroyOnLoad” objesi olduğunu gördüm. Bu objenin içindeki “[Debug Updater]” objesinin içindeki yine aynı adlı script belli ki sistemi zorluyor. Bu GameObject’in ve doğal olarak içindeki script’in play’e basar basmaz oluşmasını engellemenin yolu var mıdır? Şimdiden ilginiz için teşekkür ederim.
Yolu varmış: https://stackoverflow.com/questions/71341272/disable-unity-debug-canvas
Çok teşekkür ederim hocam 🙂
Mono ile 10 dakikada aldığım buildi il2cpp ile 3 saatte alamıyorum. Bir türlü build alamadım. Neden olur acaba?
IL2CPP build’leri gerçekten daha uzun sürüyor ama 3 saate henüz ben denk gelmedim. İsterseniz “%LOCALAPPDATA%\Unity\Editor\Editor.log” dosyasını inceleyip orada bir ipucu veriyor mu bakabilirsiniz. Veya google araması yapabilirsiniz ama bulacağınız çözümler ne kadar alakalı olur bilmiyorum. Benim maalesef aklıma bir şey gelmiyor.
https://gametorrahod.com/unity-android-app-icon-2019/
hocam bu sitede anlatılan Icon modeli nasıl olmalı cevabini bir de siz verirmisiniz?
bazı telefonlarda uygulama simgesi çok küçük çıkıyor
Yazıyı okudum, ben ikon sistemini hiç bu kadar karmaşık incelememiştim. Açıkçası yazıyı okuyunca benim de kafam karışmadı değil ama ben şimdiye kadar hep “Default Icon”u doldurup (transparan köşeleri olmayan bir ikon ile) diğerlerini boş bıraktım ve yazıdan anladığım kadarıyla bu sadece A2’de sorun çıkarmış. Şu anda olsa yine sadece “Default Icon”u doldururum sanırım.
Uygulamanızın şu an hedeflediği API düzeyi 29, ancak güvenlik ve performans açısından optimize edilmiş en son API’ ler üzerinde oluşturulduğundan emin olmak için hedeflemesi gereken en düşük API düzeyi 30. Uygulamanızın hedef API düzeyini en az 30 olacak şekilde değiştirin.
Android SDK Manager’dan Android API Level 30 veya yukarısını yüklemeniz gerekiyor. Akabinde Unity’i ve Unity Hub’ı tekrar başlatın.
The tree 1166 must use the Nature/Soft Occlusion shader. Otherwise billboarding/lighting will not work correctly.
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
böyle bir hata alıyorum ağaç modelimden sizce shader ı ne seçmeliyim?
Terrain ile çok uğraşmadım o yüzden maalesef bu hatanın neyi temsil ettiğini bile bilmiyorum.
Shader seçerken (mobil oyun için) Mobile sekmesindeki Bumped diffuse seçeneğini seçersem en iyi performans alırım sanırım? Bumped specular orta, Diffuse ise en az performans alırım?
Grafiklerde performansı daha yüksek olan shader da daha düşüktür mantıken?
“Mobile/Bumped Diffuse” ile “Legacy Shaders/Diffuse”u kıyaslayınca bence Bumped Diffuse daha pahalı olur çünkü normal map kullanıyor, düz Diffuse ise kullanmıyor. Benim düşünceme göre ucuzdan pahalıya: “Mobile/Diffuse”, “Legacy Shaders/Diffuse”, “Mobile/Bumped Diffuse”
OpenCL GPU device Intel(R) Iris(TM) Graphics 6100 from Intel Inc. has less than 4 GB of global memory, ignoring device for lightmapping. Böyle bir hata aldım ne dediğini anlamadım.
Ve Window-Lighting‘de “Precomputed Realtime GI” ve “Baked GI” bu seçeneği maalesef bulamadım. Mac de 2021.1.12 sürümünü kullanıyorum.
Bu iki sorunuma yardım ederseniz sevinirim. Şimdiden teşekkürler.
– Laptopta 2 ekran kartı varsa, NVIDIA Denetim Masası’nda Unity güçsüz ekran kartını kullanıyor olabilir. Veya lightmapping esnasında Unity tüm ekran kartlarından faydalanıyor olabilir ve cihazın Intel ekran kartını güçsüz bulduğu için, o ekran kartını kullanmayacağını söylüyor olabilir.
– Unity 2021 kullanmadım maalesef bilmiyorum. Precomputed Realtime GI deprecated olmuştu belki 2021’de kalkmıştır.
Just adding a little info :3
for CompareTag. Make sure to use gameObject.CompareTag() instead collision.CompareTag(). To prevent it Getting Gameobject Component
But if user is comparing the tag of collided object, then collision.gameObject.CompareTag should be used instead of gameObject.CompareTag (which compares this object’s tag, not the collided object’s tag).
Merhaba hocam profilerde camera render/ SetRenderTarget değeri 40-60 arası oluyor nasıl düşürebilrim bu değeri
Editörde Deep Profile’ı açıp bir bakın isterseniz çünkü direkt SetRenderTarget ile alakalı bir Profiler konusu forumlarda göremedim.
Selamlar oyunumda coinlere değdiğinde ekranda beliren bir effect vari text mevcut oyunu ilk açtığımda ve ilk coine değdiğinde anlık kasma yaratıyor bunu nasıl çözebilirim acaba.
Kameranın görüş alanına scale’i 0 olan bir text koyup Start’ta onu Destroy ederseniz, takılma olayı sadece oyunun başında olur.
hocam çok teşekkürler ilginiz için ben baya ileri görüşlülük yapıp loading ekranında gösterdim ve sanırım kasma sorunu ortadan kalktı çok teşekkürler. Web siteniz gerçekten çok yararlı bilgiler içeriyor. Umarım karşılığını fazlasıyla alıyorsunuzdur.
Rica ederim. İyi niyetiniz için teşekkürler.
Gerçekten emek harcayıp güzel içerikler ortaya çıkarıyorsunuz.
Merhaba Hocam, Yapılabilecek optimizasyonları yaptım. Hiç kasmadan oyun telefonda çalışıyor. Telefon bağlıyken test ettiğimde profiller GC değeri maksimum anlık 1.2kb oluyor ve sıfırlanıyor. Arada daha daha düşük değerlerde olup sıfırlanıyor. Bu değerleri aldığım kodlarıda optimize edemiyorum. Sizce sıkıntılı bir durum mu? Yoksa o kadarı da olur mu? Teşekkürler
Kodları optimize etme imkanınız yoksa, bence bunu düşünmeye gerek yok çünkü sıkıntı olsa bile yapacak bir şey yok. Ama oyunda anlık kasmalar olmuyorsa, şansınıza performansı etkilemiyordur.
Hocam selamlar. Bize sunduğunuz kaynaklar için çok teşekkür ederim. Müsaadenizle bir sorum var
Unityde oyunu “Maximize on Play” modunda çalıştırırken iki nokta arasında 2-3 saniyede giderken, normal küçük ekran yani editörler,inspectorler falan olan split ekranda çalıştırırken aynı noktaya 4-5 saniyede gidiyor. Bunun sebebi nedir? Çözümü var mıdır?
Yardımlarınız için teşekkürler.
Objenin hareket kodunda Time.deltaTime kullanmıyorsanız, oyunu büyütünce framerate düşüyor ve objenin hareket kodu daha az çalıştığı için daha az hareket ediyor olabilir.
Kullanmıyordum hocam, addforce kullanıp kendi belirlediğim çarpan ile hareket ettiriyordum. Hızlı cevap için çok sağolun 🙂
AddForce’u FixedUpdate’te kullanıyorsanız, Time.deltaTime yerine Time.fixedDeltaTime kullanabilirsiniz.
Ana menüde Application.targetFrameRate = 45; yapsam diğer açılacak sahnelere de etki eder değil mi? Yoksa bütün sahnelere kodu ekleyeceğim?
Kod tüm sahnelere etki eder ancak 45 targetFrameRate VSync açıkken çalışmayabilir. Ben 45 yapmayı denediğimde, onun yerine FPS limiti kalkmıştı (bu yüzden şimdi dersten 45 önerisini sildim). Ben artık sadece 30 veya 60 (genelde 60) FPS yapıyorum. Şu asset ile FPS’inizi kontrol edebilirsiniz: https://assetstore.unity.com/packages/tools/gui/graphy-ultimate-fps-counter-stats-monitor-debugger-105778
Evet 45 etki etmemiş gibi gelmişti zaten 😀 vsync ellemedim direk 60 yaptım.
Yasir Bey merhaba,
unity de Artırılmış gerçeklik oyunu yaptım, oyunu imzalamadan build edince hızlı açılırdı, lakin imzaladıktan sonra yaklaşık 14 sn siyah ekran göründükten sonra açılıyor,
bunun sebebi ne olabilir.
İlk defa duyuyorum. Google’da da bu konuya dair bir başlık bulamadım. Bence Profiler ile oyunu hem imzalı iken hem de imzalı değilken debug edip hangi unsurun başlangıç süresine en çok etki ettiğini tespit edin: https://yasirkula.com/2020/03/26/unity-profiler-kullanimi/. Ardından bu unsuru google’da aratın.
Merhabalar. Projem bir bilgisayar simülasyonu. Bilgisayar içinde her uygulama için bir panel var. Sorun şu: Android de oyun ilk açıldığında 100 150 mb ram tüketiyor. Fakat bilgisayardaki panelleri açtığımda ram tüketimi çok artıyor. Kimisi 50 kimisi, 100 mb arttırıyor ve düşmüyor. Ram kullanımı işlemler sonuncunda 500 – 600 mb kadar oluyor.
Şunları yaptım fakat neredeyse birşey değişmedi: Grafikler en düşüğe aldım, font render modunda yumuşatmayı kapattım, İmage max size 2048 > 512, ağır fonksiyonların çoğu 5 saniyede bir veya 10 karede bir olacak şekilde düzenledim.
Kullandığım buton ve panel resimleri istisnalar dışında en fazla 5 kb.. Profiler Gpu kullanımının yarısını render yarısını other oluşturuyor, Memory de ise kayda değer bir değişilik olmuyor. Sorun ne ile alakalı olabilir? Teşekkürler. (Bir canvas kullandım)
Şurada Memory modülünün Detailed sekmesinde bahsettiğim yöntem ile, RAM’i kaplayan objelerin ne olduğunu görebilirsiniz: https://yasirkula.com/2020/03/26/unity-profiler-kullanimi/. Daha göze hitap eden bir alternatif için, Unity’nin Memory Profiler package’ını öneririm: https://www.youtube.com/watch?v=I9wB4Cvgz5g
Tekrar merhaba. Sorunun sebebini bulabildim. Proje editörde hiç sıkıntı olamadan çalışıyor fakat androidde kullandığım her Text için bir tane kullanılan fontun kopyasını önbelleğe alıyor(Tanesi 4 mb). Fazla text kullandığım için bellek kullanımı 300 – 400 mb artıyor. Bunu nasıl düzeltebilirim.
Text’in fontunun kendi kendine klonlandığını daha önce görmedim. Kodunuzda nerede Text’in fontunu klonluyorsanız, o kodu silmeniz lazım. Asset Bundle kullanıyorsanız belki sorun aynı Asset Bundle’ı tekrar tekrar sıfırdan belleğe almanızdan kaynaklanıyordur.
Merhaba Yasir bey. Unity ile yaptığım oyunu telefona attığımda ve daha henüz bir dakika geçmeden ısınmaya başlıyor. Bu konuda bana nasıl yardımcı olabilirisiniz.
Çok eski bir cihazda test ediyorsanız bence o cihazın üzerine çok düşmeyin, ortalama seviye bir cihazda test edin. Ortalama cihazda da takılıyorsa oyun CPU ve/veya GPU’yu çok zorluyor demektir. Bu derste bahsettiğim optimizasyon teknikleri ve internetteki diğer optimizasyon teknikleri ile oyunu optimize etmek zorundasınız. Özellikle Application.targetFrameRate ve Resolution Scaling Mode optimizasyonlarını denemenizi öneririm. Profiler kullanarak da oyunu en çok zorlayan kodları tespit edebilirsiniz: https://yasirkula.com/2020/03/26/unity-profiler-kullanimi/
Hocam bunları videolarla anlatacak, video serileri oluşturacak mısınız?
Video ders vermeyi planlamıyorum ama ilginiz için teşekkür ederim.
Hocam ben unity de oyunu yapıyorum profiler dan baktığımda bilgisayarda 120 fps civarı alıyorum. Fakat mobile geçince, yani apk yapıp telefona atınca genelde oyun yavaş oluyor. Oyunun mobilde de hızlı çalışacağından nasıl emin olabiliriz ? Örneğin bilgisayarda kaç fps almam lazım ? Bu şekilde bir kıyaslama mümkün mü yoksa her seferinde mobilde denemem gerekir mi ?
Mobil cihazlarla PC arasında belli bir oran olduğunu sanmıyorum, bence her seferinde mobil cihazda denemeniz lazım.
Merhaba hocam, InvokeRepeating mi yoksa StartCoroutine kullanmak optimizasyon açısından daha verimlidir?
InvokeRepeating reflection kullandığı için, ben StartCoroutine’i tercih ediyorum. Ancak Unity arkaplanda InvokeRepeating’in kendini tekrar etmesini coroutine’den daha optimize bir şekilde mi sağlıyor bilmediğimden, oyun esnasındaki verimlerini kıyaslayamayacağım. 1,000,000 adet InvokeRepeating ve StartCoroutine’i ayrı ayrı çalıştırarak oyunun FPS’ini hangisinin daha çok düşürdüğünü kontrol edebilirsiniz. Sonucu burada da paylaşırsanız sevinirim.
Küçük çaplı bir test yaptım.Editörde InvokeRepeating daha iyi sonuç verdi ama build edince ikiside neredeyse aynı sonucu verdi.İnternetten araştırdığım kadarıyla büyük çoğunluk StartCoroutine kullanmanın daha mantıklı olduğunu söylemiş
Yasir bey iyi akşamlar
Benim oyunumdaki objelerde yuzeyiin altına gitmemesi için collider ekledim . objelerde rigidbody yerçekimi açık . Bu objeler enemy . Birbirlerine çarpınca yönleri ve açıları değişiyor .rigidbody de freeze positionu ve rotationa tik işaretli olduğunda yön değişmiyor fakat objeler yokuş veya benzer bir yuzeyde bir kısmı havada kalıyor . Colliderı olan enemy objelerinin birbirinden etkilenmemesi için ne yapabilirim .
Physics.IgnoreCollision ile 2 collider’ın birbiri ile temas etmesini engelleyebilirsiniz.
Hocam merhaba;
2d unity de Oyunda yukardan aşağıya sarkan bir zincir ve alt ucun da bir top var ve birbirine sabitledim, bu zincir yukardan sabit olacak şekil de topla sağa ve sola devamlı hareket ettirmek istiyorum fakat kod la meydana getiremedim, yardımcı olabilirseniz sevinirim.
Zincir ve top birbirine bağlıyken fiziksel hareketleri gerçekçi olsun istiyorsanız, Joint 2D component’leri kullanmanız gerekebilir. Ardından koda 2 saniye boyunca sağ yönde, 2 saniye boyunca sol yönde güç uygulamayı deneyebilirsiniz (bu döngüyü sürekli tekrar edin).
Hocam merhaba; aslında yukarda ki video da gösterilen sarkaç gibi unity 2d de aynısını kod yazıp ya da farklı bir şekil de uygulayabilir miyiz, bunun için kod yazmayı denedim ama çalışmadı, bununla ilgili video falan var mıdır, yardımcı olabilirseniz sevinirim.
Kod yazmadan, Animation veya Animator component’i ile bunu halledebilirsiniz. Bilginiz yoksa “unity animation ders” veya “unity animation tutorial” şeklinde arama yapabilirsiniz.
Yasir Bey Merhaba,
Bir oyun yapıyorum(endless race tarzı), başlangıçta her şey çok iyi, stats’ta fps 125-150 ile başlıyor ama bir süre sonra aniden 20 lere düşüyor. Neredeyse yapmadığım optimizasyon kalmadı.
Coroutinleri kaldırdım,
fixedUpdateleri kaldırdım, lateupdateleri kullandım gerekli yerlerde,
physics hesaplarını minimuma çektim,
etraftaki objeleri azalttım,
instantiate yerine objectpooling kullandım,
mp3 ayarını streaming + quality %70 yaptım,
find object veya start ya da awake harici getcompenent hiç kullanmadım, (bir yer hariç, o da mecburi)
daha aklıma gelmeyen bir çok şey, bunlar sadece düşüşün biraz daha geç başlamasını sağladı.
profiler görselleri de şu şekilde;
https://i.hizliresim.com/b9QnRV.jpg
https://i.hizliresim.com/zyQ6qD.jpg
https://i.hizliresim.com/OaJG4Z.jpg
önerebileceğiniz yöntemler var mıdır acaba?
Bir de şu EditorLoop nedir acaba, araştırdım fakat bir şey anlamadım.
Şimdiden teşekkür ederim, kolay gelsin.
EditorLoop, Unity editörünün Profiler için veya başka bir şeyler için harcadığı süreyi temsil ediyor. Niye o kadar çok süre harcıyor ben de anlayamadım ama eğer EditorLoop’suz Profiler kullanmak istiyorsanız, oyunu “Development Mode” ve “Autoconnect Profiler” seçenekleri açıkken Build&Run yapabilirsiniz. Bu şekilde, build aldığınız oyunu profile etmiş olacaksınız. EditorLoop’un içini görmek istiyorsanız da, yukarıdaki “Profile Editor” seçeneğini açabilirsiniz.
Yasir bey profilden baktım ençok ( %44 ) FixedUpdate.PhysicsFixedUpdate bu kasıyor .Fakat kodlarımda fixedUpdate yok . sabit ojelerdeki rigidbody kaldırdım .Unity 3d sürümüm 2017 neden bu kadar kasıyor . Nasıl fps artışı sağlarım .
Eğer Profiler Timeline görünümündeyse onu Hierarchy görünümüne alıp listeyi “Time ms”ye göre sıraladıktan sonra, en çok ms nerede harcanıyor bulmaya çalışın. Örneğin FixedUpdate satırının solunda bir ok varsa, o oka tıklayarak satırın içine girin ve bu şekilde nokta atışı yapmaya çalışın.
Yasir bey 35 mb çıktısı olan andoid oyunum 15 fps nin uzerine çıkmıyor unity 3d sorunlu bir motormu oyunu yaptıktan sonra optimizasyonla çok uğraşmak gerekiyor hiç bir yöntem fayda etmedi . kullandıpım texler bile 256×256 .
Yasir bey sizin yazdıınız optimizasyonları uyguladım ayarları değiştirdim .Unity nin game ekranında oyun sorunsuz oynanıyor fakat telefonda canvas ve partikul dışında hiçbirşey görenmuyor . Nasıl duzeltebilirim .
Yasir bey
Disable Depth and Stencil ın tikini kaldırınca duzeldi . Bu problemi yaşayan arkadaşlara yardımcı olmak ve sizi meşgul etmemek için yazıyorum .Bilgileriniz ve yardımlarınız için teşekkurler .
Selamlar Yasir Bey iki sorum olacaktı.Bir uygulama yapıyorum 2D olarak projeye başladım ama uygulama içinde asla hareket etmeyen 3D objeler kullandım (70 adet low poly ağaç) ve kamerada hareket etmiyor sadece belli bir açıda obje olarak uygulamada duruyorlar.Bu projeyi 2D değilde 3D olarak oluşturmamın bana bir faydası olur mu daha iyi performans alırmıyım ? (Uygulamam genelinde fazlaca UI elemanlarından oluşan 2D bazlı bir uygulama.)
Uygulamamızı 2D veya 3D proje olarak oluşturmak arasındaki farklar nelerdir ve Unity neden böyle bir seçenek sunuyor çünkü deneyimlerime göre 2D bir projedede üç boyuları objeleri kullanabiliyoruz farkları nelerdir ?
Performans konusunda emin olamadım. Eğer tüm ağaçlar aynı açıya sahipse, ağaçların 2D sprite olması daha iyi olabilir. Ağaçların eğimleri farklıysa, her bir eğim için ayrı bir sprite gerekeceğinden bu sefer 3D daha iyi olabilir. Tabi bir de overdraw konusu var: 2D oyunda sprite’lar üst üste çizildiği için overdraw artıp performansı olumsuz etkiler, 3D modeller depth buffer sayesinde overdraw’dan daha az etkilenirler. Ama en nihayetinde 3D veya 2D hangisi daha iyi olur sanırım test etmeden bilemeyeceksiniz.
2D oyunda 2 boyutlu görselleri Sprite Renderer kullanarak ekranda gösterebilirsiniz, 3D modele gerek yok.
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.
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?
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 .
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.
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.
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.
2d android bir oyunum var hocam optimizasyonlar için hangilerini önerirsiniz?
Burada yazdıklarım genel olarak hem 3D hem de 2D de geçerli maddeler, bu yüzden tüm maddeleri gözden geçirmenizi öneririm.
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.
Yeni sahnenin başında, CrossPlatformInputManager.SetButtonUp veya CrossPlatformInputManager.SetAxisZero gibi fonksiyonlarla o takılı kalan buton veya axis’i sıfırlamayı deneyebilirsiniz.
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.
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.
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.
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ı.
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.
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)
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
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.
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.
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
}
}
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
Hocam aksam eve gittiğimde değişiklikleri yapıp size döneceğim
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
}
}
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
}
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.
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ı?
her ekranda ayni cozunurlugu nasil sunarim bunun cozumunu bulamadim bir cok yontem denedim ama maalesef bir sonuca varamadim
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.
haklısınız evet biliyorum ama nasıl optimize edeceğiz bu sorunu nasıl aşarız
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
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?
Ya script ya da grafik anlamında oyun yoruluyor herhalde. Sıkıntının nerede olduğunu tespit etmenin en iyi yolu bence Profiler kullanmak. Bunun için build alırken “Development Build” ve “Autoconnect Profiler” seçeneklerini seçip öyle build alın. Ardından Window-Profiler penceresini açın ve en çok ms (milisaniye) kim harcıyor oradan kontrol edin. Profiler’a çok basit bir giriş için şu videoyu altyazılı izleyebilirsiniz: https://www.youtube.com/watch?v=a1l6YixAuw0
Development Build ve Autoconnect Profiler seçeneklerini seçip öyle build aldım ama profiller sekmesinde record açık olmasına rağmen herhangi değer işlemiyor.
“Script Debugging”i de açmayı deneyin. Build&Run yaptıktan sonra da, Profiler’ın tepesindeki “Active Profiler”a tıklayıp orada oyununuz var mı kontrol edebilirsiniz.
https://i.hizliresim.com/Lvbb60.png
Hocam böyle bir sonuç aldım.
Sol ortada Timeline yazar, onu Hierarchy yapın ve karşınıza gelen ekranda en çok ms. kimlerin harcadığını tespit edin. Diyelim çok fazla ms. harcayan bir satırın solunda ok varsa, o oka basarak satırı genişletip iyice nokta atışı yapmaya çalışın. Tahminimce WaitForTargetFPS en çok ms. harcıyor, bunun için Edit-Project Settings-Quality’den “VSync Count”u kapatmayı (Don’t Sync) deneyin.
https://i.hizliresim.com/7BDLOW.png
hocam vsync yi dont sync yaptım ama oyun hala takılıyor.
Oyunun takıldığı anlarda, Profiler penceresinin en üst grafiğinde bir zıplama olur (resimde grafiğin sağ kısmında gördüğünüz yükseltiler gibi). Grafiğin öyle bir anda zıpladığı bir ana mouse ile tıklarsanız o an çağrılan fonksiyonları görürsünüz. İşte bize o bilgiler lazım. Ayrıca oyunun başında “Application.targetFrameRate = 30;” yapmayı deneyebilirsiniz.
Hocam verdiğiniz kodu start fonksiyonun içine mi yazayım?
Olur.
https://i.hizliresim.com/5NaLWA.png
https://i.hizliresim.com/AODn9Q.png
https://i.hizliresim.com/00RlYV.png
https://i.hizliresim.com/4pj1EY.png
Hocam maalesef verdiğiniz kodda takılmaları düzeltmedi
bir düşmanı zıplayarak mario gibi öldürmek için nasıl bir yapı geliştirmem gerekir raycast ile aşağı ışın versek bu ışın düşmana çarparsa collideri aktif etsem gibi birşey yapmak yada daha kolay bir çözümü varmıdır
Direkt OnCollisionEnter2D fonksiyonunda, karakter düşmanın yukarısında mı yoksa yanında veya altında mı bakabilirsiniz: https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnCollisionEnter2D.html
Orada uzun ms. kullanan fonksiyonlara dikkat edin, mesela oyuncukont.OnTriggerEnter ve ses_control.Update niyeyse çok fazla ms. harcıyor gözüküyor. Bu fonksiyonları isterseniz yasirkula@gmail.com‘a atın.
Gönderdim hocam
benzere yakin problem bende var samsung s7 edge oyun akici huawei mate 20 lite da hafif kasma var
Benzer şekilde Profiler ile oyunu debug edip, genel olarak hangi fonksiyonların ms. (milisaniye) harcadığını tespit etmeniz lazım.
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ı?
Sahne yüklenmediği sürece performansa değil de sadece uygulamanın boyutuna etkisi olması lazım.
Tamam hocam teşekkürler
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?
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.
Hocam benim bilgisayarda unity çok kasıyor.Kasmayı azaltmak için neler yapabilirim ?
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.
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
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.
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.
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).
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
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.
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.
Ses dosyasının Inspector’undaki “Preload Audio Data” ve “Load In Background” seçenekleri ile oynamayı deneyebilirsiniz.
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 ?
Şurada yazdığına göre üzerinde Capsule Collider olan bir ağaç prefab’ı oluşturup onu terrain’de kullanmanız lazımmış: https://www.reddit.com/r/Unity3D/comments/3s6hjo/why_dont_my_trees_have_colliders/
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.
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.