Yine ve yeniden merhabalar,

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

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

Script Optimizasyonu

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

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

Optimizasyon öncesi:

void Update()
{
	// İleri yönde hareket et
	transform.Translate( Vector3.forward * Time.deltaTime );
}

void FixedUpdate()
{
	// Yukarı yönde güç uygula
	GetComponent<Rigidbody>().AddForce( Vector3.up );
}

public void MuzikCal()
{
	GetComponent<AudioSource>().Play();
}

Optimizasyon sonrası:

private Transform cachedTransform;
private Rigidbody cachedRigidbody;
private AudioSource cachedAudioSource;

void Awake()
{
	cachedTransform = transform;
	cachedRigidbody = GetComponent<Rigidbody>();
	cachedAudioSource = GetComponent<AudioSource>();
}

void Update()
{
	// İleri yönde hareket et
	cachedTransform.Translate( Vector3.forward * Time.deltaTime );
}

void FixedUpdate()
{
	// Yukarı yönde güç uygula
	cachedRigidbody.AddForce( Vector3.up );
}

public void MuzikCal()
{
	cachedAudioSource.Play();
}
  • GameObject.Find ile sahnedeki bir objeye erişmek yavaş bir işlemdir. Bunun daha optimize versiyonu GameObject.FindWithTag‘dır. Ancak daha daha iyi bir optimizasyon, ilgili objeyi bir değişkende tutmaktır. Özellikle Object.FindObjectOfType kullanıyorsanız bu optimizasyon daha çok önem arz etmektedir.

Optimizasyon öncesi:

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

void Obje3uDeaktifEt()
{
	GameObject.FindWithTag( "Obje3unTagi" ).SetActive( false );
}

Optimizasyon sonrası:

private Transform cachedTransform;
private Transform obje2ninTransformu;
private GameObject obje3;

void Awake()
{
	cachedTransform = transform;
	obje2ninTransformu = GameObject.Find( "Obje2" ).transform;
	obje3 = GameObject.FindWithTag( "Obje3unTagi" );
}

void Update()
{
	// Bu obje'yi, Obje2'nin olduğu yere ışınla
	cachedTransform.position = obje2ninTransformu.position;
}

void Obje3uDeaktifEt()
{
	obje3.SetActive( false );
}

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

  • Bazen oyun boyunca bir objeyi sürekli Instantiate ve Destroy etmeniz gerekebilir (örneğin bir FPS oyunundaki mermi prefabı veya bir patlama partikül efekti). Ancak bu Instantiate ve Destroy komutlarının çok fazla kullanımı performans açısından iyi bir şey değil. Tam olarak da böyle durumlar için pooling pattern dediğimiz bir teknik geliştirilmiş. Bunun çalışma prensibi basit: sürekli kullandığınız bir prefab’ın klonunu Destroy etmek yerine deactivate edip pool’a (havuz) atıyorsunuz. Daha sonradan bu prefab’ın başka bir klonuna ihtiyaç duyduğunuzda önce havuzda hazırda bir klon var mı diye bakıyorsunuz. Varsa havuzdaki klonu kullanıyor, yoksa ancak o zaman yeni bir klon Instantiate ediyorsunuz. Bu basit teknik, FPS oyunlarından tutun infinite runner oyunlarına kadar pek çok oyun türünde kullanılmaya müsaittir.

Eğer dilerseniz kendi oyununuz için özellikle optimize edilmiş pool scriptinizi yazabilirsiniz. Hazır script kullanmak isterseniz internette “unity pool script” diye aratırsanız pek çok kaynak bulabilirsiniz. Ben de bir generic pool script yazdım; eğer dilerseniz kendi oyunlarınızda kullanabilirsiniz: https://github.com/yasirkula/UnityGenericPool

Optimizasyon öncesi:

/*
 * Silah kodu
 */
public class SilahScript : MonoBehaviour
{
	public GameObject patlamaPartikulu;
	public Rigidbody mermiPartikulu;

	public void AtesEt()
	{
		// Yeni bir mermi oluştur
		Rigidbody mermiKlonu = Instantiate( mermiPartikulu, transform.position, transform.rotation ) as Rigidbody;
		
		// Mermiye ileri yönde güç uygula
		mermiKlonu.AddForce( transform.forward * 1000f );
	}

	public void PatlamaEfekti( Vector3 pozisyon )
	{
		// Yeni bir patlama partikülü objesi oluştur
		GameObject patlamaEfekti = Instantiate( patlamaPartikulu, pozisyon, Quaternion.identity ) as GameObject;
		
		// Patlama efektini 10 saniye sonra otomatik olarak yok et
		Destroy( patlamaEfekti, 10f );
	}
}
/*
 * Mermi kodu
 */
public class MermiScript : MonoBehaviour
{
	void Start()
	{
		// Bu mermiyi 3 saniye sonra otomatik olarak yok et
		Destroy( gameObject, 3f );
	}

	void OnCollisionEnter( Collision temasObjesi )
	{
		// Temas edilen obje Dusman ise
		if( temasObjesi.gameObject.CompareTag( "Dusman" ) )
		{
			// Düşmana 10 hasar ver
			temasObjesi.GetComponent<SaglikScript>().HasarVer( 10 );
			
			// Bu mermiyi yok et
			Destroy( gameObject );
		}
	}
}

Optimizasyon sonrası (kendi PoolScript’imi kullandım):

/*
 * Silah kodu
 */
public class SilahScript : MonoBehaviour
{
	private Transform cachedTransform;
	
	public GameObject patlamaPartikulu;
	public Rigidbody mermiPrefab;
	
	// Patlama prefab'ının klonlarını tutan pool (havuz)
	private SimplePool<GameObject> patlamaPartikulHavuzu;
	
	// Mermi prefab'ının klonlarını tutan pool (havuz)
	// Bu havuza MermiScript'ten de erişildiği için kendisi
	// bir public static değişken
	public static SimplePool<Rigidbody> mermiHavuzu;

	void Start()
	{
		cachedTransform = transform;
		
		// Havuzları oluştururken parametre olarak prefab objelerini veriyoruz
		// Böylece havuzdan obje çekmek isteyince havuz boş olsa bile 
		// bu prefab havuz tarafından otomatik olarak Instantiate ediliyor
		patlamaPartikulHavuzu = new SimplePool<GameObject>( patlamaPartikulu );
		mermiHavuzu = new SimplePool<Rigidbody>( mermiPrefab );
	}
	
	public void AtesEt()
	{
		// Havuzdan bir mermi objesi çek
		Rigidbody mermiKlonu = mermiHavuzu.Pop();
		
		// Mermiyi konumlandır
		Transform mermiKlonuTransform = mermiKlonu.transform;
		mermiKlonuTransform.position = cachedTransform.position;
		mermiKlonuTransform.rotation = cachedTransform.rotation;
		
		// Mermiye ileri yönde güç uygula
		mermiKlonu.AddForce( cachedTransform.forward * 1000f );
	}
	
	public void PatlamaEfekti( Vector3 pozisyon )
	{
		// Havuzdan bir patlama partikülü objesi çek
		GameObject patlamaEfekti = patlamaPartikulHavuzu.Pop();
		
		// Patlama partikülünü konumlandır
		Transform patlamaTransform = patlamaEfekti.transform;
		patlamaTransform.position = pozisyon;
		patlamaTransform.rotation = Quaternion.identity;
		
		// Patlama efektini 10 saniye sonra otomatik olarak yok et
		// Bu sefer coroutine kullanmak zorundayız çünkü, Destroy'un 
		// aksine, havuza objeyi belli bir süre sonra atmanın kısa
		// bir yolu yok
		StartCoroutine( PatlamaEfektiYokEtCoroutine( patlamaEfekti ) );
	}
	
	private IEnumerator PatlamaEfektiYokEtCoroutine( GameObject patlamaEfekti )
	{
		// 10 saniye bekle
		yield return new WaitForSeconds( 10f );
		
		// Patlama efektini havuza geri yolla (böylece sonradan havuzdan  
		// tekrar bir patlama efekti objesi çekmek istediğimizde, bu partikül
		// objesini tekrar kullanabileceğiz)
		patlamaPartikulHavuzu.Push( patlamaEfekti );
	}
}
/*
 * Mermi kodu
 */
public class MermiScript : MonoBehaviour
{
	// Start'ın bir coroutine (IEnumerator) olduğuna dikkat edin
	IEnumerator Start()
	{
		// 3 saniye bekle
		yield return new WaitForSeconds( 3f );
		
		// Mermiyi havuza geri yolla
		SilahScript.mermiHavuzu.Push( GetComponent<Rigidbody>() );
	}

	void OnCollisionEnter( Collision temasObjesi )
	{
		// Temas edilen obje Dusman ise
		if( temasObjesi.gameObject.CompareTag( "Dusman" ) )
		{
			// Düşmana 10 hasar ver
			temasObjesi.GetComponent<SaglikScript>().HasarVer( 10 );
			
			// Mermiyi havuza geri yolla
			SilahScript.mermiHavuzu.Push( GetComponent<Rigidbody>() );
			
			// Artık merminin 3 saniye sonra otomatik olarak
			// yok olmasına gerek kalmadı
			StopAllCoroutines();
		}
	}
}
  • Vector struct’larında (mesela Vector3) Distance adında bir fonksiyon ve magnitude adında bir değişken bulunmakta. Bu iki komut da esasında bir vektörün uzunluğunu ölçmeye yarıyor. Ancak hesaplama sırasında karekök kullanmak gerektiğinden aslında bu işlem sanıldığından daha zahmetli bir şey. Eğer ki bu uzunluk değerini direkt kullanıcıya göstermeyecekseniz o zaman daha hızlı çalışan sqrMagnitude değişkenini kullanmayı düşünebilirsiniz. Bu fonksiyon, bir vektörün uzunluğunun karesini döndürür ve bu değeri hesaplamak magnitude’a (ve Distance’a) göre daha hızlıdır.

Optimizasyon öncesi:

public float maksimumMesafe = 5f;
Vector3 vektor1, vektor2, vektor3;

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

Optimizasyon sonrası:

public float maksimumMesafe = 5f;
private float maksimumMesafeKaresi;
Vector3 vektor1, vektor2, vektor3;

void Start()
{
	maksimumMesafeKaresi = maksimumMesafe * maksimumMesafe;
}

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

Optimizasyon öncesi:

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

Optimizasyon sonrası:

private Transform cachedTransform;

void Awake()
{
	cachedTransform = transform;,
}

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

Optimizasyon öncesi:

public Transform[] hareketEttirilecekObjeler;

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

Optimizasyon sonrası:

public Transform[] hareketEttirilecekObjeler;

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

Fizik Optimizasyonu

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

Optimizasyon öncesi:

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

Optimizasyon sonrası:

// Bu değişkene Inspector'dan değer olarak "Zemin" layer'ı verilmeli
public LayerMask zeminLayer;

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

Grafik Optimizasyonu

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

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

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

Doğru Bilinen Yanlışlar-1: Oyununuzdaki texture’ların boyutu düşük olsun diye onları Unity’e .jpeg formatında atmayın. Ya da benzer şekilde, aslı 512×512 olan kaliteli bir texture’u, boyutu düşük olsun diye resim editörünüzden (Photoshop vb.) 256×256’ya ufaltıp öyle Unity’e yollamayın. Unity’e elinizdeki en kaliteli kaynak dosyayı yollayın (örneğin .psd veya .png ve 512×512). Siz bir resim dosyasını Unity’e hangi formatta atarsanız atın bu resim dosyası Unity içerisinde otomatik olarak bir texture‘a dönüştürülür. Yani bu resim dosyanın boyutu bir önem arz etmezken resim dosyasının kalitesi, Unity tarafından oluşturulan texture’un kalitesine direkt etki eder. Benzer şekilde, 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 dönüştürebilirsiniz.

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

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

Window-Lighting-Skybox: “None”

Window-Lighting-Ambient Source: “Color”

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

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

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

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

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

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

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

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

Oyun Boyutu (Build) Optimizasyonu

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

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

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

    Evet, o asseti ben de kullanmıştım. Daha iyi FPS veriyor. Fakat mit lisansı kullanılıyor diyor. “If you’d try to sell it on Asset Store, then I’m gonna find you.” demiş. Bu asseti oyunda kullanmamız için ne yapmalıyız ki 😀 Oyunda credits menüsü ekleyip blur efektin sahibini mi belirtmeliyiz acaba 🙂

    • yasirkula dedi ki:

      Evet yasal olarak bir sıkıntıya girmemek için şurada yazan lisans metnini, oyununuzdaki Credits veya About gibi bir menüye eklemeniz lazım: https://github.com/PavelDoGreat/Super-Blur/blob/master/LICENSE (metnin başına Super-Blur Effect gibi bir ekleme yaparak, oyunun hangi parçasının MIT lisansı kullandığını daha açık belirtebilirsiniz)

      Eğer mobil değil de PC oyunu yapıyorsanız, lisans metnini oyunun olduğu klasörde LICENSES veya DOCUMENTATION gibi bir alt klasöre eklemeniz yeterli oluyor diye biliyorum. Yine de emin olmak için bir Google araması yapabilirsiniz.

  2. ahmet dedi ki:

    Evet, harika bir yazı. Yasir KULA sayesinde kodlamayı öğrendik 😀 Artık her kod yazarken yukarıdaki optimizasyon kurallarına uyuyorum. Yasir hocam, size küçük bir sorum olacak.
    https://forum.unity.com/threads/post-process-mobile-performance-alternatives-to-graphics-blit-onrenderimage.414399/#post-3542411
    Burada yorum yapmışsınız. Acaba mobilde 60 FPS çalışan blur efekti yapmak mümkün müdür? Alto’s Adventure adlı oyun da Unity ile yapılmış. Onda da tam ekran blur efekti var ve çok akıcı.

    • yasirkula dedi ki:

      Ben de o forumda yazılanlardan gaza gelip bu sistemi öğrenmek ve sitemde ders olarak vermek için konuya yorum attım. Ancak basit bir blur scriptinde ilgili değişiklikleri yapsam bile, fps’imde bir artış olmadı ve oyun takılmaya devam etti. Yani mobilde 60 FPS blur efekti nasıl yapılır ben hâlâ bilmiyorum.

      • ahmet dedi ki:

        https://forum.unity.com/threads/are-there-any-2d-blur-effect-that-run-smooth-on-mobile-devices.515737/#post-3388532
        Burada da Kumo-Kairo nickli kişi nasıl yapılacağını anlatmış fakat ben şahsen hiçbir şey anlamadım 😀

      • yasirkula dedi ki:

        Orada anlatılan yönteme benzer bir yöntem kullanan bir asset buldum: https://github.com/PavelDoGreat/Super-Blur

        Bu asset’i Unity’e attıktan sonra kamerama Super Blur Fast component’ini verdim. Ardından SuperBlur/Material’ın içindeki materyallerin shader’larını Custom/SuperBlurPostEffect ve UI/Super Blur olarak değiştirip kameramdaki component’in “Blur Material” ve “UI Material” değişkenlerine değer olarak verdim. Downsample ve Iterations değerlerini de 2 yaptım. Son olarak da, kameramın Allow HDR ve Allow MSAA değerlerini kapatıp SuperBlurFast.cs’nin OnEnable’ına “m_Camera.forceIntoRenderTexture = false;” satırını ekledim.

        Bu raddede sahnemi Galaxy S3 cihazımda test ettiğimde, FPS değerlerim 60’dan 48’e düştü. Yani kabul edilebilir bir performanstı. Ancak sahnemde sadece birkaç küp ve bir partikül efekti vardı ve neredeyse hiç script yoktu. Yani dolu dolu bir oyunda bu değer daha da düşecektir. Ancak FPS’i biraz daha artırmak için Downsample’ın değerini artırıp “Gamma Correction”ı kapatabilirsiniz. Ben Downsample 3 iken ve “Gamma Correction” kapalıyken S3’te 60 FPS’te blur elde edebildim.

  3. İbrahim Kara dedi ki:

    Harika bir yazı olmuş teşekkürler

  4. onur dedi ki:

    Hocam boş bir sahneden 27 fps alıyorum neden olabilir(mobil)

    • yasirkula dedi ki:

      Application.targetFrameRate 30 olabilir veya vsync açık olabilir (Edit-Project Settings-Quality-Default ayarlanmış olan Quality seviyesi-V Sync Count). Eğer sorun buralarda değilse Build Settings’teki “Development Build” ve “Autoconnect Profiler” seçeneklerini açıp Profiler üzerinden sıkıntıyı bulmanız lazım.

  5. ahmet dedi ki:

    Sonunda mermiyi havuza atmayı yapabildim. Mermi için farklı bir script hazırlayıp böyle yapınca sırayla hepsi pasif olmaya başladı. Yukarıda github linkini atmama rağmen yoruma komple scripti paylaştı. Kusura bakma.
    public float lifetime;
    void Update ()
    {
    this.lifetime -= Time.deltaTime;
    if (this.lifetime < 0) {
    SimplePool.Despawn(this.gameObject);
    lifetime = 3;
    }
    }

    • yasirkula dedi ki:

      Yok sıkıntı değil. Ben de böyle bir şeyin mümkün olduğunu yeni gördüm. Benim yazdığım scriptin isminin SimplePool olduğunu unutmuşum, hiç aklıma gelmedi 😀 Niçin benim scriptim havuzdaki objeleri tekrar kullanmamış onu henüz keşfedemedim ama siz sorunu kendi başınıza çözdüğünüz için elinize sağlık.

  6. ahmet dedi ki:

    Enemy’e dokunulduğunda patlama efekti oluşuyor ama bu efekt kaybolmuyor. Yani Pushlama çalışmıyor. Sizce nerede yanlışlık var? Kodlar bu şekilde:

    private Transform cachedTransform;
    public GameObject patlamaPartikulu;
    private SimplePool patlamaPartikulHavuzu;

    void OnTriggerEnter2D(Collider2D other)
    { if (other.CompareTag(“Enemy”))
    {

    GameObject patlamaEfekti = patlamaPartikulHavuzu.Pop();
    Transform patlamaTransform = patlamaEfekti.transform;
    patlamaTransform.position = transform.position;
    patlamaTransform.rotation = Quaternion.identity;
    StartCoroutine( PatlamaEfektiYokEtCoroutine( patlamaEfekti ) );
    }
    }

    private IEnumerator PatlamaEfektiYokEtCoroutine( GameObject patlamaEfekti )
    {

    yield return new WaitForSeconds( 1f );

    patlamaPartikulHavuzu.Push( patlamaEfekti );
    }

    • ahmet dedi ki:

      Tabi ki void Start da yazılı. Yorumuma eklemeyi unutmuşum.
      void Start () {

      cachedTransform = transform;
      patlamaPartikulHavuzu = new SimplePool( patlamaPartikulu );
      }

    • yasirkula dedi ki:

      Belki Push fonksiyonu otomatik olarak objeyi inaktif yapmıyordur. Bu yüzden Push’tan önce patlamaEfekti.SetActive(false); yapabilirsiniz. Buna karşılık Pop() fonksiyonundan sonra da patlamaEfekti.SetActive(true); yapmanız lazım.

      • ahmet dedi ki:

        https://hizliresim.com/6JV16v

        SetActive(false) ve SetActive(true) ekleyince objeler pasif oluyor fakat resimde görüldüğü gibi 1 dakikalık oyun boyunca ortaya yüzlerce patlama efekti çıkıp pasif oluyor. Yani havuza atıp tekrar havuzdan almıyor sanırım.

      • yasirkula dedi ki:

        Evet öyle duruyor. Pop fonksiyonunda objenin activeSelf’i veya activeInHierarchy’si false ise objeyi es geçen bir koşul olabilir; eğer kaynak koduna erişiminiz varsa o koşulu kapatabilirsiniz. Aksi taktirde, SetActive(true) yerine GetComponent<ParticleSystem>().Play() ve SetActive(false) yerine GetComponent<ParticleSystem>().Stop() yazmayı deneyebilirsiniz. O da olmazsa ve bu ücretli bir pool asset’i ise asset’in yapımcısına mail atabilir, değilse başka bir pool scripti kullanabilirsiniz.

      • ahmet dedi ki:

        Sizin github’ta paylaştığınız poolscripti kullanmıştım.

      • ahmet dedi ki:

        Örneğin bu scripti şimdi indirip denedim. Void Start’a SimplePool.Preload(mermi, 10); ekledim ve oyun başladığında 10 tane pasif mermi oluşturdu. Her ateş tuşuna bastığımda ise
        Gameobject a = SimplePool.Spawn(mermi, transform.position, Quaternion.identity);
        kodu çalışıyor ve mermi atıyor. Böylece Void Start’a eklediğim koddaki oluşan 10 pasif mermi her ateş ettiğimde sırayla aktif olmaya başladı. Buraya kadar da sorun yok. Tek sorun bu mermilerin tekrar pasif olarak havuza dönmesi.
        /// Instead of destroying an object, use this:
        /// SimplePool.Despawn(myGameObject);
        Bunu kullanmam gerek sanırım. Fakat ateş ettikten sonra StartCoroutine (“Destroy”); çalıştırıp IEnumerator Destroy()
        {

        yield return new WaitForSeconds (5f);
        SimplePool.Despawn(a);
        }
        Bu kodu çalıştırdığımda sadece en son atılan mermi 5 saniye sonra pasif oluyor. Yani seri seri 9 mermi attıysak sadece 9. mermi pasif olurken ilk 8 mermi hâlâ aktif kalıyor.

      • ahmet dedi ki:

        Yine yazmayı unutmuşum. Düzenle butonu olsaymış iyi olurmuş 😀
        En son indirdiğim pool script bu:
        https://gist.github.com/quill18/5a7cfffae68892621267

  7. ahmet dedi ki:

    Hocam, blur optimized hakkında bir bilginiz var mı? PC’de sorun yok fakat mobilde blur efekti felaket fps düşürüyor. Kameraya ekleyip ekranı komple bulanık yapmak istiyorum. Bir fikriniz var mıydı acaba?

    • yasirkula dedi ki:

      Açıkçası ben de henüz performans sorunu olmayan bir blur efekti bulamadım. Post-processing efektleri çok basit bile olsa mobil cihazlarda performans sorununa yol açıyor gibi duruyor. Belki Unity 2018 ile beraber gelecek olan Scriptable Render Pipeline ile post-processing performans sıkıntıları bir nebze giderilir.

  8. Onur dedi ki:

    Hocam bu mobilde ekranı otomatik ayarlayamıyor muyuz ya freeaspect yapıyorum UI denen zımbırtı çok büyük oluyor telefona atınca canvasdan dışardaymış gibi gözüküyo, 16:9 yapıyorum çok küçük oluyor, kendim büyüklüklerini ayarlıyorum telefona atıp deniyorum hiçbi değişiklik olmuyo, 1280 800 yapıyorum 16 9 daki gibi oluyor ne yapmam gerek? Teşekkürler…
    Hocam birde pc de 2D oyunumda 500 600 fps alırken telefonda takılmalar yaşıyorum nedeni nedir bütün spritelari 256 bit ve normal quality yaptım fakat kasma oluyor yine.

    • yasirkula dedi ki:

      İlk aşama Canvas Scaler’ın “Ui Scale Mode” değişkenini ayarlamak olmalı. Ben bunun değerini “Scale With Screen Size” yapıp “Match” değerini 1’e çekiyorum. Böylece ekranın yüksekliği değiştikçe UI da scale oluyor. İkinci aşama ise, UI elemanlarının anchor’larını doğru ayarlamak olmalı. Örneğin ekranın daima solunda olmasını istediğiniz UI elemanlarının anchor x’lerini 0, daima sağında olmasını istediğiniz UI elemanlarının anchor x’lerini 1 yapmalısınız.

      Oyunun takılma sebebini en doğru şekilde tespit edebilmek için oyun esnasında Window-Profiler’dan faydalanabilirsiniz.

  9. ahmet dedi ki:

    Merhaba hocam, hareket eden collider’de rigidbody yoksa performansa olumsuz etki eder demişsiniz. Benim bir objem (insan vücudu) var ve hareket ediyor. Bu objede collider ve rigidbody de var. Fakat bu objeye bağlı bir objem (insanın kafası) daha var ve bu objede sadece collider var. Performans açısından bu insan kafasına rigidbody eklemeli miyim yoksa eklememeli miyim?

    • yasirkula dedi ki:

      Kafa objesi, vücut objesinin child’ı veya child’ının child’ı veya daha derin bir child’ı ise (yani kafa objesinin vücut objesi ile uzaktan yakından parent ilişkisi varsa) kafada rigidbody’e gerek yok.

      • ahmet dedi ki:

        Cevabınız çok teşekkür ederim. Son bir sorum daha olacak. 2 boyutlu bir oyunda aynı boyutlardaki boxcollider2d, circlecollider2d, capsulecollider2d ve edgecollider2d’lerden performans olarak sıralamayı biliyor musunuz acaba?

      • yasirkula dedi ki:

        Maalesef bir bilgim yok.

  10. ahmet dedi ki:

    Hocam merhaba.Hocam admob da sdk güncelleme yaparak 7.0.0 sürümü kullanın diyor. Unity google play services içinde bende version.xml içinde version aşağıdaki gibi geçiyor.
    Günecelleme yapmaya gerek varmı.Teşekürler.

    8298000

  11. ahmet dedi ki:

    Merhaba, basit bir 2d yandan ilerlemeli platform oyunu yapmaya çalışıyorum. Karşıdan gelen düşmanlar Player’a çok yaklaştığında (veya dokunduğunda) atak yapmasını istiyorum. Bu kodlar ile mi daha performanslı olur?
    ———————————————————————————————————————–
    public bool atak;

    void Update () {
    if ((player.transform.position.x – gameObject.transform.position.x >= -1) && (player.transform.position.x – gameObject.transform.position.x <= 1))
    {
    atak=true;

    }else {

    atak= false;

    } }

    ———————————————————————————————————————–

    Yoksa OnTriggerEnter2D ile mi yapmak mı daha performanslı olur? OnTriggerEnter2D ile de nasıl yapılacağını anlamadım. Böyle yapınca atak yapıyor ama Player'a dokunmayınca atak yapmayı durdurmasını yani atak=false yapmayı nasıl yapacağım?

    void OnTriggeEnter2D (Collider2D other){
    if (other.tag == "Player")
    { atak=true;}
    }

    • yasirkula dedi ki:

      Performans açısından kesin bir şey diyemiyorum, test edip görmeniz lazım gibi duruyor. İkinci yöntemde atak değişkenini OnTriggerExit2D fonksiyonunda resetleyebilirsiniz:

      void OnTriggeExit2D (Collider2D other){
      if (other.tag == “Player”)
      { atak=false;}
      }

  12. enceweb dedi ki:

    Merhaba Hocam,
    Build alınan oyundaki grafik kalitesi unity editördeki gibi olmuyor. Bunun çözümü için bazı forum sitelerinde unity’nin tam sürümünü almamız gerektiğini kimileri ise ek uygulamam kullanmamız gerektiği gibi şeyler söylüyor.(Grafiği artıran uygulamaların boyutu ise oyunun boyutundan daha fazla 🙂 )Söylenenler doğru mu hocam yoksa başka bir yolu var mıdır?

    • yasirkula dedi ki:

      Editördeki grafik kalitesi daha iyi diye anlıyorum dediğinizden. Edit-Project Settings-Quality’de her platformun altındaki yeşil tik, o platforma alınan build’in varsayılan grafik ayarını belirtiyor. O anda seçili olan grafik ayarı ise editörün kullandığı ayarı temsil ediyor. Eğer hedef platformunuzun grafik ayarı daha düşükse, aşağısındaki oka tıklayarak varsayılan ayarı artırabilirsiniz. Ancak daha iyi grafikler tahmin edeceğiniz üzere daha güçlü cihazlar gerektirir.

      Eğer oyunu mobile çıkarıyorsanız, PC’de olan her grafik ayarının her mobil cihazda desteklenmediğini de bilmeniz iyi olabilir. Örneğin Linear Rendering veya Deferred Rendering özellikleri her mobil cihazda desteklenmiyor. Veya bazen sadece DX11 destekleyen shader’lar veya post-processing effect’ler olabiliyor, bu durumda bu efektler mobil cihazlarda veya sadece DX9 destekleyen PC’lerde çalışmayacaktır (ancak Unity’nin kendi shader’larında bildiğim kadarıyla böyle bir sıkıntı yok).

  13. enceweb dedi ki:

    Merhaba Hocam

    Oyunu Facebook’a bağlayabilmek için bir öneriniz var mıdır? Bu konuda biraz araştıma yaptım ama yeterli kaynak bulamadım.

    Saygılar…

  14. Ramazan dedi ki:

    Merhaba, oyunuma bir güncelleme yayınlayacağım. Güncellemede Türkçeden, almanca ve ingilzce dillerine çeviriler yapmam gerekiyor. Sorunum şu ki ücretsiz ingilizce keywords araştırma siteleri mevcutken Türkçe (veya almanca) keywords sitesi bulamadım. Bu konuda bilginiz varmı yardımcı olabilirmisiniz?

  15. enceweb dedi ki:

    Merhaba Hocam,
    Vertex Color nedir nasıl kullanılır ile ilgili Türkçe bir kaynak bulamadım sizin öneriniz var mıdır veya nasıl kullanılır kısaca bahsedebilir misiniz?
    Saygılarımla….

    • yasirkula dedi ki:

      Öncelikle modeli 3D modelleme programında modellerken vertex paint uygulamanız lazım. Unity’de ise vertex color destekleyen bir shader kullanmanız lazım. Maalesef Unity’de varsayılan olarak gelen vertex color destekli bir shader yok ancak “unity vertex color shader” şeklinde arama yaparak vertex color destekli hazır shader’lar bulabilirsiniz.

  16. Mucahit dedi ki:

    selamun aleykum abi ben yeni bi oyun yapıp google playe attım benim telefonda test ederken hiç bir sorun yoktu ama bazı telefonlarda ya oyun açılmıyor ya da oyunda atıyor sebebi nedir

    • yasirkula dedi ki:

      Oyun çok RAM istiyor olabilir. Bu durumda texture ve ses dosyaları ile modellerin kalitelerini düşürmekten başka çareniz olduğunu sanmıyorum. Tabi sıkıntı bu olmayabilir de. Mesela oyunu ARM için build aldıysanız oyun x86 cihazlarda çalışmaz, x86 için build aldıysanız da ARM cihazlarda çalışmaz. FAT build aldıysanız hepsinde çalışması lazım. Eğer ki oyun 32-bit ARM işlemcilerde çalışıyor ama yeni nesil 64-bit ARM işlemcilerde çalışmıyorsa o zaman kullandığınız plugin’ler sıkıntı çıkarıyor olabilir. Bunun çözümü biraz karmaşık olduğundan önce sorunun gerçekten de bit’le alakalı olup olmadığından emin olmanız lazım.

  17. Ramazan dedi ki:

    Merhaba Yasir bey. Hazırladığım puzzle oyunumda zamanı time.timescale=0 kullanarak durdururuyorum ancak sonrasında puan hesaplamak için zamanı kullanmak istediğimde, ekrana yazdırdığım ve oyun kontrolcüsü haricinde bir scriptle hesaplattığım zamana erişemiyorum. getcompenent ile inspectordan almaya çalıştığımdada “NullReferenceException: Object reference not set to an instance of an object” hatası alıyorum. Puzzle ın bittiğine dair fonksiyonun içinde, oyun sonundaki zamana erişmek istiyorum. Hazır zaman da durmuşken 🙂 şuanki zamanı veren bir kod vs varmıdır? İlk oyunumda yüzüp yüzüp kuyruğuna geldim burda takıldım yabancı kaynaklardanda bişey bulamadım. Yardımcı olabilirmisiniz?

    • yasirkula dedi ki:

      Zaman değişkenini static yaparsanız ScriptAdi.degiskenZamani diye değişkene kolayca erişebilirsiniz. Ancak oyun başlarken ScriptAdi.degiskenZamani = 0; diye zamanı reset’lemeyi unutmayın.

      Şu anki zamanı veren ise 2 değişken var: Time.time ve Time.realtimeSinceStartup. İlki Time.timeScale’den etkilenirken ikincisi etkilenmez.

  18. osman dedi ki:

    herkese merhaba öncelikle muhtemelen farklı bir konudan bahsedeğim için özür dilerim:).
    1- ilk girişte anamenü dik olsun sonra oyun yatay olacak nasıl olur scene oluşturdum.
    2- Arkaplanı free aspecte göre düzenledim fakat unity UI kullanarak eklediklerimin yerleri değişiyor nasıl engellerim.
    3-oyunun boyutunu her telefona nasıl ayarlarım unity otomatik yapıyor diyorlar fakat benimki yapmıyor herhalde..

    teşekkürler.

    • yasirkula dedi ki:

      1- Player Settings’te Resolution and Orientation’daki “Default Orientation”ı Portrait yapın. Ardından kodunuzda ne zaman Screen.orientation = ScreenOrientation.LandscapeLeft; yazarsanız ekran yatay olur, ne zaman Screen.orientation = ScreenOrientation.Portrait; yazarsanız da geri dikey olur.
      2- Anchor’ları doğru bir şekilde yerleştirmemiş olabilirsiniz. UI dersimde anchor’lardan biraz bahsetmiştim, belki işinize yarar: https://yasirkula.com/2015/01/21/unity-ui-arayuz-sistemi/
      3- Eğer arayüzün her çözünürlükte düzgün gözükmesinden bahsediyorsanız Canvas’taki Canvas Scaler’ın parametreleri ile oynamayı deneyebilirsiniz.

  19. Ahmet dedi ki:

    if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit))
    Böyle yapınca tüm tuşlar sorunsuz çalışıyor ama huawei g8 ile denediğimde sanki butonlara basınca bir performans düşüklüğü hissediyorum. Lg g4’te ise sıkıntısız çalışıyor. Aşağıdaki gibi 4 şekilde de denedim fakat yine button çalışmadı malesef.

    if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit, buttonsLayer.value))

    if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit, Mathf.Infinity, buttonsLayer.value))

    if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit, 1<<buttonsLayer))

    if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit, Mathf.Infinity, 1<<buttonsLayer))

    • yasirkula dedi ki:

      Yazdığınız 2. veya 4. kodlardan en azından birinin doğru çalışmasını beklerdim açıkçası. Şu an neden böyle bir sorun olabileceği ile ilgili bir fikrim yok :/

      • Ahmet dedi ki:

        Sanırım kasmanın nedeni raycast değil de farklı bir şey. Profiler sekmesine baktım da unityde acemi olduğum için pek bir şek anlamadım açıkçası 🙂 Bu resimden kasmanın nedeni anlaşılabiliyor mu acaba?
        https://i.hizliresim.com/6XGYEv.jpg
        Yoksa teker teker tüm objeleri, scriptleri falan kaldırarak denemeli miyim? Bu arada yardımlarınız için çok teşekkürler.

      • yasirkula dedi ki:

        O frame’lerde CPU 2.25ms ve GPU 10.30ms zaman harcamış. Yani grafik kartı daha çok çalışmış ama yine de toplamda 12.55ms kabul edilebilir bir rakam sanki, saniyede 80 kareye denk geliyor.

        Attığınız ilk resimdeki BehaviourUpdate şeysi 2.25ms’lik CPU’nun büyük kısmını bir scriptinizin yediğini söylüyor. Oradaki oka basınca daha çok bilgi alabilirsiniz. Sıkıntılı noktaları en kesin şekilde tespit edebilmek için ise “Deep Profile” seçeneğini seçmelisiniz, bu durumda Profiler daha çok CPU yer ama daha çok bilgi sağlar.

      • Ahmet dedi ki:

        Yardımın için çok teşekkür ederim. Deep profile ile daha iyi anlaşılır oldu. Son olarak bir sorum daha olacaktı. 2d bir oyun yapıyorum. Resim dosyalarında en iyi performansı ve görüntü kalitesini alabilmem için bir ince ayar var mı? Mesela generate mipmapiing işaretleyince resimlerin kenarları bulanıklaşıyor ve daha hoş görünüyor. Bunun performansa bir zararı olur mu? Ayrıca Filter Mode olarak Blinear mı yoksa Trilinear mı seçmeliyim?
        https://i.hizliresim.com/nWaR3B.jpg

      • yasirkula dedi ki:

        Generate Mip Maps seçeneğinin performansa zararı olmaz, hatta 3D oyunlarda performans açısından faydalıdır bile. Yalnızca texture’un kapladığı yer biraz artar. Ancak dediğiniz gibi resmin kenarlarını bazen bulanık yaptığı için, bunun olmasının istenmediği durumlar için iyi değildir.

        Filter Mode’daki Trilinear’ın henüz Bilinear’dan bir farkını göremedim o yüzden ben Bilinear takılıyorum.

  20. Ahmet dedi ki:

    Merhaba, raycast ile sizin gösterdiğiniz şekilde buttonlar oluşturmuştum. Fakat optimizasyon için layer oluşturup kodlara ekleyince ve buttonlara da layerları ekleyince buttonlar çalışmadı. Acaba nerede hata yaptım, anlamadım. Optimizasyon öncesi kodları aynen şöyle:

    void Update ()
    {
    Camera camera = Camera.main;
    RaycastHit hit;
    if( camera == null )
    return;
    Camera camera = Camera.main;
    RaycastHit hit;
    for( int i = 0; i < Input.touchCount; i++ )
    {Touch parmak = Input.GetTouch( i );
    if (parmak.phase == TouchPhase.Stationary && this.grounded) {
    if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit)) { if (hit.transform.gameObject.name == "JumpButton")
    this.Jump ();
    }
    }
    }
    }
    ———————–Optimizasyon Sonrası———————–
    public LayerMask buttonsLayer;
    void Update ()
    {
    Camera camera = Camera.main;
    RaycastHit hit;
    if( camera == null )
    return;
    Camera camera = Camera.main;
    RaycastHit hit;
    for( int i = 0; i < Input.touchCount; i++ )
    {Touch parmak = Input.GetTouch( i );
    if (parmak.phase == TouchPhase.Stationary && this.grounded) {
    if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit, buttonsLayer)) { if (hit.transform.gameObject.name == "JumpButton")
    this.Jump ();
    }
    }
    }
    }

    • yasirkula dedi ki:

      Şu çalışıyor mu peki: Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit, Mathf.Infinity, buttonsLayer)

      buttonsLayer’ı yanlış yere parametre olarak vermişsiniz gibi duruyor: public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)

      • Ahmet dedi ki:

        Cevabınız için çok teşekkür ederim. Fakat malesef olmadı 😦
        public static bool Raycast (Ray ray, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers);
        void Update ()
        {
        Camera camera = Camera.main;
        RaycastHit hit;
        if( camera == null )
        return;
        for( int i = 0; i < Input.touchCount; i++ )
        {Touch parmak = Input.GetTouch( i );
        if (parmak.phase == TouchPhase.Stationary && this.grounded) {
        if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit, Mathf.Infinity, buttonsLayer))
        { if (hit.transform.gameObject.name == "JumpButton")
        this.Jump ();
        }
        }
        }
        }

        Böyle yapınca bu hatayı veriyor: "error CS0501: `PlayerController.Raycast(Ray, out RaycastHit, float, int)' must have a body because it is not marked abstract, extern, or partial"
        Zaten DefaultRaycastLayers kısmı kırmızı renk olarak duruyor. Ayrıca şöyle de denedim. DefaultRaycastLayers yazan yeri buttonLayers olarak değiştirdim. Yine aynı hatayı verdi:

        public static bool Raycast (Ray ray, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = buttonLayers);
        public LayerMask buttonsLayer;
        void Update ()
        {
        Camera camera = Camera.main;
        RaycastHit hit;
        if( camera == null )
        return;
        for( int i = 0; i < Input.touchCount; i++ )
        {Touch parmak = Input.GetTouch( i );
        if (parmak.phase == TouchPhase.Stationary && this.grounded) {
        if (Physics.Raycast (camera.ScreenPointToRay (parmak.position), out hit, Mathf.Infinity, buttonsLayer))
        { if (hit.transform.gameObject.name == "JumpButton")
        this.Jump ();
        }
        }
        }
        }

        Son olarak sadece public static bool ile başlayan kısmı (yani hatayı veren kısmı) komple sildim. Geriye kalan kodları ellemedim. Hata falan vermedi ama button çalışmadı 🙂
        Not: Buttona buttonsLayer ekli 😀

      • yasirkula dedi ki:

        public static bool Raycast (Ray ray, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = buttonLayers); kısmını fonksiyonun nasıl bir şey olduğunu görmeniz için atmıştım, koda yapıştırmamanız lazım 😀 Şurdan almıştım: https://docs.unity3d.com/ScriptReference/Physics.Raycast.html

        Ancak onu sildikten sonra çalışmaması garip geldi, isterseniz buttonsLayer’ı buttonsLayer.value veya 1<<buttonsLayer olarak değiştirmeyi deneyebilirsiniz.

  21. Barış dedi ki:

    Unity içindeki shaderlerden hangisinden en iyi kaliteyi alırım?

    • yasirkula dedi ki:

      Unity ile gelen shader’lar arasında en kalitelileri Standard ve Standard (Specular setup) shader’ları. Ama mobilde performans olarak çok iyi oldukları söylenemez.

  22. Ahmet dedi ki:

    Merhaba abi. öncelikle C# kullanıyorum ve yapmaya çalıştığım projede zamanla skor artıyor. ölüncede ana menu paneli açılıyor ve joystick falan kapanıyor. SceneManager.LoadScene (0); ile sonlandırıyorum. fakat öldükten sonra SceneManager.LoadScene (0); komutunu kullanmadan hemen önce skorumu kaydetmek istiyorum. PlayerPrefs ile denedim araştırdım çözemedim. senden istediğim abi şu son salise skoru kaydedip. SceneManager.LoadScene (0); komutundan sonra tekrar kullanmak. abi nolur yardım edermisin :))

    • Ahmet dedi ki:

      abi lütfen yardım edermisin

    • yasirkula dedi ki:

      En kolay çözüm yolu static bir değişken kullanmak. Örneğin oyunda A scriptiniz varsa bu scripte “public static int skor;” isminde bir değişken ekleyip oyun bitince A.skor = oyunsonuskoru; yaparak oyun sonu skorunu bu değişkene atayın. Ardından ilk scene’den bu skora A.skor diyerek erişebilirsiniz.

  23. xboxlive99 dedi ki:

    Selamun aleyküm hocam, kolay gelsin. Scriptlerin başında “using” ile başlayan kütüphaneleri kullanmak çıktımızın dosya boyutunu etkiliyor mu? Tüm scriptlerimde system.collections kütüphanesini kullanmadığımı gördüm ve sildim. Dosya boyutunu etkiler mi?

  24. KeremD dedi ki:

    Yasir bey benim bir sorum olacaktı oyunumda 18 e yakın sahne var hepsi çok güzel çalışırken bir sahnede donma yaşıyorum bu sahneye bağlı script uzun biraz fakat bu sahnedeki scriptte PlayerPrefs gibi fazla CPU yiyen şeyleri kullanmadım ve Profiler penceresinde de değerler normal görünüyo fakat oyunda iken ufak kasma ve donmalar oluyor yardım edersniz sevinirim şimdiden teşekkürler

    • yasirkula dedi ki:

      Belki grafik anlamında takılıyordur. Bunu test etmek için oyun esnasında Game panelinin boyutunu küçültün (böylece ekrana çizilen piksel sayısı azalacak); eğer oyunun takılması azalıyorsa sıkıntı model, materyal, shader ve(ya) texture’lardandır yoksa script’lerdendir.

      • KeremD dedi ki:

        Hocam baktım da galiba kastıran Tris and Verts kısmı stats dan falan o kanıya vardım fakat bunu nasıl önleyebilirim yardımcı olurmsunuz?

      • yasirkula dedi ki:

        O değerler modellerin poligon sayısıyla alakalı. Yapabiliyorsanız modellerin poligon sayılarını düşürebilir, yoksa objelerin materyallerine verdiğiniz shader’ları basitleştirebilirsiniz (mesela Mobile kategorisindeki shader’ları tercih edebilirsiniz). İlaveten, sahnede gölgeler açıksa gölgeleri kapatarak da Tris ve Verts sayısını neredeyse yarıya indirebilirsiniz.

      • KeremD dedi ki:

        Cok tesekkurler bunu hallettim bundan once hemen biraz ustte bir sorum vardi cok fazla resim kullanmakla alakalı oyunum icin col fazla resim gerekiyordu ve boyut cok fazla artiyordi 20 resimde 2 mb civari siz buna cevap verdiniz fakat ona dair bir ufak sorum var resimleri poolingle havuzdan cikartirsam isime yarar mı? Oyunum offline

      • yasirkula dedi ki:

        Pool sistemi sadece RAM kullanımınıza yardımcı olabilir, oyunun boyutuna etki etmez.

  25. KeremD dedi ki:

    Ben yapacağım oyun için 1000 i aşkın sprite a ihtiyaç duyuyorum haliyle oyun çok büyük oluyo ben bunu nasıl engelleyebilirim

    • yasirkula dedi ki:

      Sprite’ların Max Size’larını düşürüp “Compression”larını “Normal Quality”e çekebilirsiniz. Aksi taktirde bunun önüne geçmenin bir yolu olduğunu sanmıyorum, sonuçta o sprite’lar bir yerde depolanmak zorunda.

      • KeremD dedi ki:

        Teşekkürler onları yaptım fakat yinede 1000+ sprite cok buyuk oluyo sadece 20 resimle 3 GB buyuyebiliyo ben bu olmaz diye de düşündüm bi an sizin gibi depolanacak yer açısından fakat the higher and lower game e baktım orada oldukca fazla sprite var benim bahsettigimden de fazladır hatta ama boyutu 20Mb bile etmiyor bu neyle olgili olabilir unitynin bir azizligi midir yoksa bir kodlama mı gerektirecek bilginiz varsa aydinlatabilirseniz sevinirim

      • yasirkula dedi ki:

        Oyun offline çalışıyor mu bilmiyorum ama sadece online çalışıyorsa büyük olasılıkla resimleri internetten indiriyordur.

      • KeremD dedi ki:

        Teşekkürler fakat şimdi konudan bağımsız bir sorum ve bir maruzatım olacak Sorum şöyle ki: Benim yüksek skor değişkenim var static yaparak sahne geçişlerinde kaybolmasını engelledim fakat oyunun kapanıp açıldığında da ben aynı şekilde kalmasını istiyorum bunu nasıl yapabilirim Maruzatım ise Google play services altında leaderboard sistemini anlattığınız bir ders yapmayı düşünürmüsünüz eğer konuya hakimseniz bu konu bir çok insana zor geldiğinden hem dersiniz çok ilgi görür hem de bir çok insana yardım etmiş olursunuz.

      • yasirkula dedi ki:

        Yüksekskor kaydetmenin en kolay yolu PlayerPrefs kullanmak, ancak çok güvenli bir yöntem değil. Belki Application.persistentDataPath’te oluşturacağınız bir dosyaya yüksekskoru encrypted bir şekilde kaydetmek biraz daha güvenli olabilir.

        İlerleyen zamanlarda Google Play Games Services ile alakalı bir ders hazırlayabilirim, ancak şu sıralar değil.

      • KeremD dedi ki:

        Tekrardan teşekkürler dediğiniz Application.persistentDataPath e bakacağım fakat bunları yüksek skordan bağımsız olarak oyun kapandığında değerlerin gitmemesini sağlayabileceğimiz başka basit yollar varmı ?

      • yasirkula dedi ki:

        Serialization diye bir konsept var; çeşitli varyasyonları mevcut: xml serialization, binary serialization gibi… Json veri formatını da araştırabilirsiniz.

  26. Halil dedi ki:

    Unity de script açıyorum , visual studio da project yok diyor ve Unity compilerı çalışmıyor nasıl yapabilirim. Yani şöyle tr yazıyorum altta transform diye seçenek gelmiyor .

  27. Mucahit dedi ki:

    Selamun aleykum abi oyunumu google playe yukledim cogu telefonda sorunsuz calisiyo calisiyo ama bazi telefonlarda kasiyo oyun 15 mb sorunu bi turlu cozemedim neyden kaynaklaniyo olabilir

    • yasirkula dedi ki:

      Oyunun boyutuyla alakalı olmamalı. Belki bazı script’ler cihazı çok yoruyordur ya da oyundaki grafik/3D modellerin kalitesi/boyutu/vertex sayısı telefonun zorlanmadan çizebileceği kapasiteyi aşıyordur. Unity editöründeyken Profiler kullanarak en çok kim performans harcıyor görmeye çalışın, belki oradan yola çıkarak bir şeyler bulabilirsiniz.

  28. bthnosma dedi ki:

    Merhaba.Yazdığınız object pooling scriptinde populate tam olarak ne işe yarıyor.pop fonksiyonu zaten pooldan çıkarıyor istenilen şeyi yani neden populate etmeliyiz tam anlamadım.ve bir pool da birbirinden farklı objeleri saklayabilir miyiz ?Biraz uzun oldu fakat cevaplarsanız sevinirim.

    • yasirkula dedi ki:

      Pop fonksiyonu eğer pool’da hazırda obje varsa onu döndürüyor, yoksa yeni bir obje Instantiate edip onu döndürüyor. Oyun esnasında maksimum performans için Instantiate’in minimum kullanılması gerektiğinden Populate fonksiyonu koydum. Populate, pool’un içinde belirlenen sayıda kullanıma hazır obje oluşturmaya yarıyor. Eğer oyunun en başında havuz yeterli miktarda obje ile populate edilirse, Pop fonksiyonunun çoğu zaman yeni obje Instantiate etmesine gerek kalmaz.

      Pool’da birbirinden farklı objeler saklayabilirsiniz ancak bu objeleri kodda elle oluşturup havuza Push’lamanız lazım. Pop yaptığınız zaman havuzun en başında hangi obje varsa o obje döndürülür. Bir başka alternatif ise her obje için ayrı bir havuz kullanmak.

      • bthnosma dedi ki:

        Bende ikinci alternatifi düşünüyordum.Yalnız populate ettiğimizde bi nevi o objeyi havuza push etmiş oluyoruz değil mi?Yani sadece objeyi yok etmek istediğimizde mi push edecez yoksa ilk başta bir kere push etmemiz gerekiyor mu?.Cevabınız için saolun.

      • yasirkula dedi ki:

        Sadece objeyi yok ederken push etmeniz yeterli. Populate’in yaptığı şey havuzu kullanıma hazır birkaç obje ile doldurmak. Bu sayede oyun esnasında Pop yapınca havuzda hazırda obje olacağı için Instantiate’e gerek kalmayacak. Tabii çok fazla Pop yaparsanız ve havuz boş olursa illa ki otomatik olarak Instantiate gerçekleşecek.

  29. iran dedi ki:

    tekrar merhaba
    abi uzunluk “Y”e mi diyorsun?
    yoksa x+y?
    yada x+z?

  30. iran dedi ki:

    selamlar
    abi oncelikle sorumu burda sordugum icin uzgunum 😦
    sizin unity notlarinizda okudum Vector3.Normalize() ne ise yarar diye anlamadim.

    siz dediniz li bu komut vectorun uzunlugunu 1 yapae ancak su urnekter olmuyor 😦

    var vektor : Vector3 = Vector3( 1, 2, 2 );
    vektor.Normalize();

    yani 3/2 bir mi yapiyor!?

  31. Yusuf Erdoğan dedi ki:

    ellerine sağlık çok faydalı olmuş 🙂

  32. ayse dedi ki:

    Merhaba
    Konudan bağımsız olarak unity de networking işlemleriyle ilgili bilginiz var mı acaba

  33. Barış dedi ki:

    Lod Group hakkında bilgi verebilir misiniz? O da optimizasyon için kullanılabilir mi

  34. Berkay dedi ki:

    Çok yararlı bir yazı olmuş. Teşekkürler.

  35. serhan dedi ki:

    Yasir Güzel Yazı Olmuş Ellerine Sağlık .

    Peki Ben bişey Sormak İstiyorum. Adamlar 30 Levelli Araba Park Etme , yada 10 levelli yük taşıma simulasyon oyunları yapıyorlar , Oyunların Boyutları 100 mb gibi bir yer kaplıyor.

    Ben 3 levelli Bir Oyun Yapıyorum Ayrı ayrı 3 Sahneden Oluşuyo Birbirinden Bağımsız , bir bölümü geçince diğerr bölüme gidiyosun , oyun boyutu 120 mb oluyooo onuda Google Play Yüklemede Kabul Etmiyor.

    Bunun Çözümü ince Detayı ne ??? Bende 15 Levelli Oyun Yapmak İstiyorum ama boyutu çok aşıyor texturelere dikkat etmeme rağmen .

    • yasirkula dedi ki:

      Oyunun boyutunu düşürmek için en çok boyutu hangi asset’lerin kapladığını bulmanız ve bu asset’leri compress veya optimize etmeniz lazım. Genelde bu asset’ler müzik, texture ve 3D modeller olmakta. Ben kendim Asset Store’daki şu eklentiyi kullanarak kimin ne kadar boyut kapladığını öğreniyorum ama bunu öğrenmenin ücretsiz bir alternatif yolu da vardır muhakkak: https://www.assetstore.unity3d.com/en/#!/content/8162

      Oyunun boyutunu düşüremezseniz de oyunu build alırken Player Settings’ten “Split Application Binary” yaparak .apk ve .obb dosyaları oluşturup bu iki dosyayı da Google Play’e yükleyebilirsiniz (bu konuda tecrübem yok, daha başka adımlar da gerekebilir).

  36. Kadir Danışman dedi ki:

    Her zamanki gibi çok yararlı bir makale olmuş, ellerinize sağlık 🙂

  37. Yine çok güzel bir yazı olmuş. Teşekkür ederiz

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

w

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.