Yazılarımız

Veri Akademi

UNİTY İLE PROJE YAPISINI KURMAK VE UZUN VADELİ ÖLÇEKLENEBİLİRLİK SAĞLAMAK

Bir Unity projesi büyüdükçe “küçük kararların” etkisi katlanır: klasör isimleri, bağımlılıkların yönü, sahnelerin sorumlulukları, prefab hiyerarşisi ve veri akışı… İlk hafta sorun çıkarmayan şeyler, üçüncü ayda ekip hızını düşüren birer sürtünme noktası haline gelir.

Bu yazıda, Unity proje yapısı kurarken uzun vadede ölçeklenebilirliği destekleyen pratik bir yaklaşım anlatıyorum. Amaç; yeni birinin projeye hızlı girebilmesi, geliştirme sürecinin öngörülebilir olması ve değişikliklerin daha az riskle yayımlanabilmesidir.

Okurken kendi ürününüzü düşünün: farklı disiplinlerin aynı repo üzerinde çalıştığı, sürüm çıktılarının düzenli alındığı ve “işi bugün bitirelim” ile “yarın bakımını yapabilelim” dengesinin kritik olduğu senaryolarda, aşağıdaki adımlar doğrudan karşılık bulur.


Unity proje yapısı için net bir klasör düzeni kurmak

Ölçeklenebilirlik için ilk adım, içeriklerin rastgele değil amaç odaklı yerleşmesidir. “Assets içinde her şey var” yaklaşımı kısa vadede hızlı görünür; uzun vadede ise arama, taşıma ve bağımlılık yönetimini zorlaştırır. Burada hedef; “bir şey nerede olmalı?” sorusunun ekip içinde tartışma konusu olmamasıdır.

Kök dizinde okunabilir bir hiyerarşi oluşturmak

Pratik bir başlangıç şablonu şu şekilde olabilir:

  • Assets/_Project: Projeye özel tüm kod ve içerikler
  • Assets/_Project/Art: Model, materyal, shader, VFX, UI görselleri
  • Assets/_Project/Audio: Sesler ve miks ayarları
  • Assets/_Project/Prefabs: Prefab varlıkları
  • Assets/_Project/Scenes: Oyun sahneleri
  • Assets/_Project/Scripts: Runtime ve Editor kodları
  • Assets/_Project/Settings: ScriptableObject ayar varlıkları

Üçüncü parti paketleri ise ayrı tutmak iyi bir alışkanlıktır. Paketleri Packages altında yönetmek ve proje içi kodu “_Project” altında toplamak, güncelleme/geri dönüş süreçlerini daha güvenli hale getirir.

Unity editöründe düzenli klasör isimleri ve ekipte ortak çalışma düzenini yansıtan proje yapısı görünümü

İsimlendirme kurallarını yazılı hale getirmek

Dosya isimlerinde karışıklık çıktığında, zaman kaybı çoğu zaman arama değil karar verme aşamasında yaşanır. Bu yüzden küçük bir “kural seti” belirlemek önemlidir: Prefab’lar “PF_”, ScriptableObject ayarları “SO_”, sahneler “SC_” gibi ön ekler; UI varlıklarında “UI_” gibi net ayrımlar. Böyle bir standardın amacı estetik değil, sürtünmeyi azaltmaktır.

Bağımlılık yönetimini doğru yönde kurgulamak

Projeler büyürken en sık görülen sorunlardan biri, kodun birbirine “yanlış yönde” bağlanmasıdır. UI’nın gameplay’e, gameplay’in altyapıya, altyapının da UI’ya bağlandığı döngüsel bir yapı test etmeyi, yeniden kullanımı ve refaktörü zorlaştırır. Burada ana hedef; bağımlılıkları tek yöne akıtmaktır.

Assembly Definition ile modülerleşmeyi planlamak

Assembly Definition (asmdef) kullanmak, derleme sürelerini düşürmenin yanı sıra modüler tasarım için de güçlü bir araçtır. Örnek bir ayrım:

  • Project.Core (genel altyapı, yardımcılar)
  • Project.Gameplay (oyun kuralları ve sistemleri)
  • Project.UI (arayüz katmanı)
  • Project.Editor (sadece editor araçları)

Bu ayrımın içinde, UI’nın doğrudan Gameplay sınıflarına bağlanması yerine, araya bir “Application” katmanı koymak veya mesajlaşma/olay sistemi kullanmak daha sürdürülebilir olur. Burada önemli olan, kararın en baştan verilmesi değil; bilinçli bir yönlendirme ile büyümeye izin vermektir.

Bağımlılıkları görünür kılan pratik kontrol listesi çıkarmak

Her büyük eklemeden sonra şu soruları sormak iyi çalışır: “Bu sınıf hangi katmana ait?”, “Bu referans ters yönde mi?”, “Bu sistem tek sorumluluk mu taşıyor?”. Bu tür kontrol listeleri, ekip içi code review’larda aynı dili konuşmayı sağlar ve sürprizleri azaltır.

ScriptableObject ve veri akışıyla esnek bir mimari kurmak

Unity’de veri odaklı yaklaşım, hem tasarım hem de geliştirme tarafında hız kazandırır. Burada ScriptableObject’ler, ayarları koddan ayırmak, farklı varyasyonları hızlıca denemek ve sahne bağımlılığını azaltmak için ideal bir araçtır. Özellikle dengeleme (balance), item/skill tanımları, UI temaları ve yapılandırma değerleri için güçlü bir temel sunar.

Konfigürasyonu sahneden ayırmak ve tekrar kullanılabilir yapmak

Bir sahne kapandığında kaybolmaması gereken veriler (örneğin oyun ekonomisi ayarları) sahne objelerine değil, ayrı varlıklara taşındığında bakım kolaylaşır. Ayrıca aynı ayar seti farklı sahnelerde tutarlı davranış sağlar.

// Örnek: Oyun ayarlarını ScriptableObject ile yönetmek
using UnityEngine;

[CreateAssetMenu(menuName = "Project/Settings/GameConfig")]
public class GameConfig : ScriptableObject
{
    public int targetFrameRate = 60;
    public float masterVolume = 0.8f;
    public string defaultLanguage = "tr";
}

Bu yaklaşımın yanında, runtime’da bu ayarları uygulayan tek bir başlangıç noktası tanımlamak önemlidir. Dağınık “Start() içinde ayar uygulama” yaklaşımı yerine, merkezi bir bootstrap akışı daha kontrollüdür.

Olay tabanlı iletişimle katmanlar arası bağı gevşetmek

UI’nın gameplay’den haberdar olması gerekir; ama bu, doğrudan sınıf referansı anlamına gelmek zorunda değildir. Olaylar, sinyaller veya basit bir “event bus” yaklaşımıyla UI’yı daha bağımsız hale getirebilirsiniz. Böylece farklı ekranların eklenmesi veya kaldırılması daha düşük risk taşır.

Adreslenebilir varlıklarla içerik ölçeklemek

İçerik sayısı arttıkça asset yönetimi de zorlaşır. Bu noktada adreslenebilir varlıklar yaklaşımı, içerikleri gruplamak, yükleme stratejisini netleştirmek ve paketleme süreçlerini kontrol etmek için önemli avantajlar sağlar. Özellikle çok sayıda prefab, büyük doku setleri veya çok dilli içerikler olan projelerde etkisi belirginleşir.

Yükleme stratejisini tasarım kararı olarak ele almak

“Her şeyi başta yükleyelim” yaklaşımı, ilk çalıştırma süresini ve bellek kullanımını şişirebilir. Bunun yerine ekran akışına göre yükleme planı yapmak, kullanıcı deneyimini daha tutarlı kılar. Örneğin ana menü, oyun içi, mağaza gibi bölümler ayrı gruplar olarak ele alınabilir.

Adreslenebilir varlık gruplarıyla içerik yükleme planını gösteren ekip içi kontrol paneli ve görev dağılımı

Basit bir Addressables kullanım örneğiyle riskleri azaltmak

Teknik ekip için önemli nokta, yükleme/boşaltma döngüsünü kontrol altında tutmaktır. Basit bir örnek:

// Örnek: Addressables ile prefab yüklemek ve instantiate etmek
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class SpawnWithAddressables : MonoBehaviour
{
    [SerializeField] private AssetReferenceGameObject enemyRef;

    public void Spawn(Vector3 position)
    {
        enemyRef.InstantiateAsync(position, Quaternion.identity)
            .Completed += OnSpawned;
    }

    private void OnSpawned(AsyncOperationHandle<GameObject> handle)
    {
        if (handle.Status != AsyncOperationStatus.Succeeded) return;

        GameObject enemy = handle.Result;
        // Burada gerekli init işlemleri yapılabilir
    }
}

Bu örnekte en sık unutulan parça, ihtiyaç bittiğinde varlıkları serbest bırakmaktır. Ölçek büyüdükçe bellek yönetimi bir “sonradan bakarız” konusu olmaktan çıkar; planlı bir disiplin haline gelir.

Versiyon kontrolü ve proje hijyeniyle çatışmaları azaltmak

Birden fazla kişinin aynı projede çalıştığı ortamlarda, versiyon kontrolü sadece “dosya saklama” değil, ekip verimliliğini belirleyen bir süreçtir. Unity projelerinde özellikle meta dosyaları, sahne/prefab değişiklikleri ve büyük dosyalar doğru yönetilmediğinde günlük çalışma temposu ciddi şekilde düşebilir.

Unity ayarlarını versiyon kontrolüne uygun hale getirmek

En temel adımlardan biri, meta dosyalarının ve text serialization ayarlarının doğru kurgulanmasıdır. Böylece sahne/prefab farklarını daha okunur hale getirir, merge çatışmalarını azaltırsınız. Ek olarak, “hangi klasörler repoya girer” sorusunun net cevabı olmalıdır. Library gibi türetilen klasörleri repo dışında tutmak, repoyu daha sağlıklı hale getirir.

Branch stratejisini ekip ritmine göre seçmek

Basit bir akış çoğu ekipte iyi çalışır: ana dal (main) her zaman çalışır durumda, geliştirme dalı (develop) günlük entegrasyon için, feature branch’ler ise izole geliştirme için. Burada kritik olan, “çok uzun yaşayan branch” alışkanlığını azaltmaktır. Küçük ve sık entegre edilen değişiklikler, sürprizleri düşürür.

CI/CD ve build otomasyonuyla teslim süreçlerini standartlamak

Ölçeklenebilirlik yalnızca kodun düzeni değildir; teslim ve test süreçlerinin tekrarlanabilir olması da aynı ölçüde önemlidir. Build’in “birinin bilgisayarında” alınması, hata ayıklamayı zorlaştırır ve sürüm güvenilirliğini düşürür. Otomasyon ise bu riski azaltır.

Build pipeline’ı yazılı bir süreç haline getirmek

Hangi platformlara build alınacak, sürüm numarası nasıl artacak, hangi sahneler build’e girecek, hangi ayarlar ortam bazlı değişecek… Bu soruların cevabı net değilse, teslimler kişiye bağımlı hale gelir. Süreci standartlaştırmak, ekibin her üyesine güven verir ve hız kazandırır.

Test otomasyonu ve kalite kapıları eklemek

Her projede kapsamlı test kurmak mümkün olmayabilir; ama küçük bir başlangıç bile büyük fark yaratır. Örneğin temel oyun kuralları için editmode testleri, kritik akışlar için playmode testleri ve her merge öncesi otomatik çalıştırma. Burada amaç, hatayı erken yakalamaktır.

Build otomasyonu ve sürüm çıktısı akışını temsil eden terminal ekranı, görev listesi ve ekip koordinasyonu

Profiling, loglama ve bakım maliyetini azaltmak

Performans sorunları genellikle “en sonunda” değil, proje büyürken yavaş yavaş birikir. Düzenli performans profilleme alışkanlığı, sürprizleri azaltır. Aynı şekilde logların yapılandırılması, canlı ortamda hata yakalamayı kolaylaştırır.

Profiler kullanımını düzenli bir ritme oturtmak

Her sprint sonunda kısa bir profil kontrolü yapmak, özellikle mobil hedeflerde büyük kazanç sağlar. CPU, GPU, garbage collection, draw call gibi metrikler; “hangi değişiklik neyi etkiledi?” sorusunun cevabını somutlaştırır.

Log seviyeleriyle hata ayıklamayı yönetilebilir kılmak

Her yerde Debug.Log yazmak kısa vadede yardımcı olur; uzun vadede gürültüye dönüşür. Log seviyelerini (info/warn/error) düzenlemek, kritik hataları öne çıkarır ve ekip içi iletişimi kolaylaştırır. Üretim ortamında logların güvenliğini ve kişisel verileri de göz önünde bulundurmak gerekir.


Ekibin ortak dilini güçlendirmek için eğitimle süreci hızlandırmak

Yukarıdaki yapı taşları tek tek uygulanabilir; ancak asıl değer, ekipte herkesin aynı yaklaşımı benimsemesiyle ortaya çıkar. Proje yapısı, modülerleşme, ScriptableObject tabanlı veri yönetimi, Addressables stratejisi ve otomasyon başlıklarında ortak pratikler oluşturmak, geliştirme hızını doğrudan etkiler.

Bu konuları uygulamalı örneklerle ekip içinde standartlaştırmak isterseniz, Unity eğitimi kapsamında proje mimarisi, ölçeklenebilir yapı kurma ve üretim süreçleri üzerine hedefe yönelik içerikler planlanabilir. Hedef; herkesin aynı dili konuşması, daha az çatışma yaşaması ve daha öngörülebilir teslimler almasıdır.

Sonuçta iyi bir Unity proje yapısı; sadece düzenli klasörlerden ibaret değildir. Bağımlılıkların yönünü doğru kurgulamak, veriyi sahneden ayırmak, içerik büyümesini planlamak ve teslim süreçlerini otomatikleştirmekle birlikte anlam kazanır. Bu parçaları erken ele almak, ileride çok daha büyük maliyetleri önler.

 ANİMASYON AKADEMİ