Yayınlanan ve güncellenmeye devam eden oyunlar sağlam bir mimariye ve disiplinli bir geliştirme pratiğine dayanır. Unity hızlı başlamayı kolaylaştırır; aynı kolaylık, proje büyüdükçe dağınık sahnelere, yavaşlayan kare hızına ve çözülemeyen birleştirme çakışmalarına dönüşebilir. Bu yazı bunu önleyen uygulamaları sıralıyor.
Mimari
Oyun mantığını sunumdan ayırın
Oyun kurallarını (can, puan, envanter) ekranda gösterilmelerinden ayırmak, tasarımcının arayüzü oyun koduna dokunmadan değiştirmesini ve kuralların test edilebilmesini sağlar. MVC, MVP ya da olay tabanlı bir yaklaşım seçebilirsiniz; önemli olan bir MonoBehaviour’ın hem kuralı hem görüntüyü yönetmemesidir.
Bileşen tabanlı yapı
Unity bileşen tabanlıdır: büyük, her şeyi yapan sınıflar yerine tek işi yapan küçük bileşenler yazın.
using UnityEngine;
public class Health : MonoBehaviour
{
[SerializeField] private int maxHealth = 100;
public int Current { get; private set; }
public event System.Action<int> Changed;
public event System.Action Died;
private void Awake() => Current = maxHealth;
public void TakeDamage(int amount)
{
if (Current <= 0) return;
Current = Mathf.Max(0, Current - amount);
Changed?.Invoke(Current);
if (Current == 0) Died?.Invoke();
}
}
Can çubuğu Changed olayını dinler, ölüm animasyonu Died olayını; Health ikisini de bilmez. Ayarları kodda değil, ScriptableObject varlıklarında tutmak da dengeyi tasarımcının elinde bırakır.
Performans
Nesne havuzu
Mermi, efekt gibi sık oluşturulup yok edilen nesneler çöp toplayıcıyı (GC) yorar ve kare atlamalarına yol açar. Unity’nin yerleşik ObjectPool<T> sınıfı bunun için hazırdır:
using UnityEngine;
using UnityEngine.Pool;
public class BulletSpawner : MonoBehaviour
{
[SerializeField] private Bullet prefab;
private ObjectPool<Bullet> pool;
private void Awake()
{
pool = new ObjectPool<Bullet>(
createFunc: () => Instantiate(prefab),
actionOnGet: b => b.gameObject.SetActive(true),
actionOnRelease: b => b.gameObject.SetActive(false),
defaultCapacity: 32);
}
public void Fire(Vector3 position, Quaternion rotation)
{
var bullet = pool.Get();
bullet.transform.SetPositionAndRotation(position, rotation);
bullet.Init(release: () => pool.Release(bullet));
}
}
Her karede iş yapmayın
Update içinde GetComponent, Find ya da yeni nesne oluşturmak her karede bedel öder. Referansları Awake içinde önbelleğe alın; zamanlı işleri her karede değil, gerektiğinde çalıştırın. Bekleme gerektiren akışlarda eşyordamlar (coroutine) ya da Awaitable kullanılabilir; bekleme nesnelerini (WaitForSeconds) her döngüde yeniden oluşturmak yerine bir kez oluşturup yeniden kullanın.
Önce ölçün
Tahmine göre optimizasyon zaman kaybıdır. Unity Profiler ve Frame Debugger ile darboğazı bulun ve ölçümü hedef cihazda yapın: editördeki performans, düşük donanımlı bir telefondakinin göstergesi değildir. İşlemci mi, grafik işlemci mi, bellek mi; önce hangisinin sınır olduğunu belirleyin.
Proje düzeni
Klasör yapısı
Assets/
├── _Project/
│ ├── Scripts/
│ │ ├── Gameplay/
│ │ ├── UI/
│ │ └── Systems/
│ ├── Prefabs/
│ ├── Scenes/
│ ├── Art/
│ └── Audio/
└── ThirdParty/
Kendi varlıklarınızı üçüncü taraf paketlerden ayırmak, güncellemeleri ve temizliği kolaylaştırır. Kodu Assembly Definition dosyalarıyla bölmek derleme süresini kısaltır.
Sürüm kontrolü
- Unity’nin önerdiği
.gitignoreileLibrary/,Temp/ve derleme çıktılarını dışarıda tutun - Editörde “Visible Meta Files” ve “Force Text” serileştirmesini açın
- Büyük ikili dosyalar (doku, ses, model) için Git LFS kullanın
- Sahne çakışmalarını azaltmak için sahneleri küçük parçalara ve prefab’lara bölün
Kod kalitesi
Adlandırma: C# kurallarını izleyin; sınıf ve yöntemler PascalCase, alanlar camelCase. Inspector’da görünmesi gereken özel alanlar public değil [SerializeField] private olmalı.
Belgeleme: açık olmayan kararların nedenini kodun yanında yazın.
Test: Unity Test Framework ile kuralları Edit Mode testleriyle, sahne davranışlarını Play Mode testleriyle doğrulayın. Kuralı MonoBehaviour’dan bağımsız düz bir C# sınıfına koymak testi kolaylaştırır; Edit Mode’da bileşenlerin Awake yöntemi çağrılmaz:
using NUnit.Framework;
public class HealthModel
{
public int Current { get; private set; }
public HealthModel(int max) => Current = max;
public void TakeDamage(int amount) => Current = System.Math.Max(0, Current - amount);
}
public class HealthTests
{
[Test]
public void Health_never_goes_below_zero()
{
var health = new HealthModel(100);
health.TakeDamage(500);
Assert.AreEqual(0, health.Current);
}
}
Mobil oyunlar
- Hedef kare hızını cihaz sınıfına göre belirleyin; pil ve ısınma da birer kısıttır
- Doku sıkıştırma ve atlas kullanın, çizim çağrılarını (draw call) azaltın
- Bellek bütçesi koyun; düşük bellekli cihazlarda uygulama işletim sistemi tarafından kapatılabilir
- Adresable varlıklarla ilk indirme boyutunu küçük tutun
- Gerçek cihazlarda, en düşük desteklenen modelde test edin
Sonuç
Unity’de başarılı bir proje; ayrık sorumluluklar, ölçümle yapılan optimizasyon, düzenli bir proje yapısı ve testle ayakta durur. Bu alışkanlıklar baştan kurulduğunda ekip büyüse de proje yönetilebilir kalır.
Oyun projeniz için teknik değerlendirme isterseniz bize yazın.
ByteGurus
Yazar