Oyun Geliştiricilerin En Sık Yaptığı Mimari Hatalar

Oyun geliştirmeye başlayan ekiplerin çoğu ilk 6 ayda aynı soruna çarpıyor: başta hızlı ilerliyorsunuz, ama 3-4 ay sonra yeni özellik eklemek cehennem oluyor. Kod o kadar karışmış ki, bir yeri değiştirince başka üç yer kırılıyor. Bunun nedeni genellikle mimari değil, mimarinin hiç olmayışıdır.

Oyun geliştiricilerin ilk hamlesinden biri GameManager'ı singleton yapıp içine her şeyi atmaktır. Ses, girdi, fizik, UI, ağ, hepsi bir sınıfın içinde. Başta işe yarar; hızlı. Ama 50 bin satır koda ulaştığında bu nesne 30 farklı sisteme bağlanmıştır ve bir değişiklik yapamaz duruma gelirsiniz.

Çözüm basit ama disiplin gerektirir: singleton'u sadece servis konteyneri olarak kullanın. GameManager sadece diğer sistemlere erişim sağlasın; mantık kendisinde olmasın. Ses sistemi ayrı, fizik ayrı, girdiler ayrı. Singleton sadece onları bağlasın. Böylece bir sistemi test etmek veya değiştirmek izole hale gelir.

Birçok oyun, özellikle mobil, sahneler arası nesneleri düzgün temizlemiyor. Bir sahne yüklendiğinde eski sahnedeki 200 tane GameObject hala bellekte dolaşıyor. Ses klipleri, tekstürler, particle sistemler, hiçbirisi silinmiyor.

Bunun nedeni çoğu zaman event listener'ları unsubscribe etmemektir. A sistemi B sistemini dinliyor, B sahne değiştiğinde yok ediliyor ama A hala B'yi beklemeye devam ediyor. Garbage collector bu "ölü" nesneleri temizleyemiyor.

Pratik çözüm: Her subscribe işleminin yanında unsubscribe kodu yazın. Sahneden çıkışta tüm listener'ları temizleyin. Unity'de OnDestroy metodunu kullanın; Unreal'de BeginDestroy. Düzenli olarak memory profiler açın ve sahneler arası bellek artışını izleyin. 100 MB'dan fazla fark varsa, bir şeyler temizlenmiyordur.

UI butonu direkt olarak GameLogic'i çağırıyor, GameLogic direkt olarak Physics'i çağırıyor, Physics direkt olarak Audio'yu çağırıyor. Görülüyor mu? Değişim imkansız hale gelir.

Reklam · TEST
AdSense Test ReklamıYazı arası (gönderi içi) · Yazı arası

Bu sorunu çözmek için event sistemi veya command pattern kullanın. Buton basıldığında "PlayerAttackRequested" event'i yayınlansın; kim dinlemek isterse dinlesin. Attack logic bu event'i dinler, ses sistemi de. Böylece UI ile logic hiç konuşmaz; iletişim event'ler üzerinden olur.

Oyun durumunu kaydederken, veya ağ üzerinden göndererken, sınıfları doğrudan serialize etmeye çalışmak sonunda bite gelir. Veri yapısı değişince, eski kayıtlar okunamaz. Ağ protokolü update edilince, eski sürüm istemciler bağlanamaz.

Çözüm: Veri taşıması için DTO (Data Transfer Object) kullanın. Oyun mantığı ile veri gösterimi ayrı olsun. Kayıt dosyasında versiyon numarası ekleyin. Yeni sürüm eski veriyi okuyabilmeli; migration yapabilmeli. Bu biraz daha iş, ama 6 ay sonra çok sevineceksiniz.

Dinamik olmayan her nesneyi kinematic olarak ayarlamak, ya da tam tersine static olan nesneleri dynamic yapıp masa hesabı yapmak, oyunun FPS'ini yere serer. Collision layer'ları düzgün kurulmamışsa, gereksiz fizik hesaplamaları yapılır.

Bir sahneyi optimize etmeden önce profiler açın. Fizik güncellemesinin ne kadar zaman aldığını görün. Çoğu zaman problem, 500 tane dynamic nesnenin hepsi bir diğeriyle çarpışma kontrol etmesidir. Collision layer'ları kullanarak, hangi nesnelerin çarpışmasını kontrol edeceğini belirleyin.

Mimari hata değildir bu, ama mimariye dayalı bir sorundur: sistem tasarımında fizik engine'in sınırlarını dikkate almamak. Bir sistem tasarlarken, performans bütçesini önceden belirleyin. "Bu sahneye max 100 dynamic nesne, max 50 raycast/frame" gibi.

Oyun kodunun sağlıklı kalması için mimari kararlar erken alınmalı. Hızlı prototip yaparken esnekliğe ihtiyaç var, ama ilk "release candidate"'ye gelmeden önce kodu yeniden yapılandırın. Yoksa 2 sene sonra baştan yazmanız gerekir.

Reklam · TEST
AdSense Test ReklamıGönderi altı · Görüntülü
konu hakkında tepkinizi verebilirsiniz
  • birazsessiz

    GameManagera her şey atmak çok kolay başlıyor ama sonra oradan bir şey çıkarmak imkansız hale geliyor değiştiremiyorsun hiçbir şeyi