Oyun geliştirme süreçlerinde, farklı sistemler ve bileşenler arasında iletişim kurma ihtiyacı kaçınılmazdır. Bir oyuncu canı azaldığında UI’ın güncellenmesi, bir düşman öldüğünde skorun artması veya bir kapı açıldığında ses çalması gibi senaryolar, sistemler arası bağımlılıkları yönetmeyi gerektirir. Geleneksel yaklaşımlar genellikle doğrudan referanslar veya Singleton desenleri gibi çözümler sunsa da, bu durum zamanla kod karmaşıklığına, test zorluğuna ve bakım güçlüklerine yol açabilir.
İşte tam da bu noktada, “Game Event Sistemi” devreye girer. Unity’de ScriptableObject tabanlı bir Game Event sistemi kurmak, oyununuzdaki bileşenler arasındaki bağımlılıkları önemli ölçüde azaltarak daha modüler, esnek ve ölçeklenebilir bir yapı oluşturmanızı sağlar. Bu rehberde, bu güçlü sistemi nasıl kuracağınızı ve projelerinize nasıl entegre edeceğinizi adım adım öğreneceksiniz.
Unity’de Game Event Sistemi Neden Önemli?
Büyük ve karmaşık oyun projelerinde, her bir bileşenin diğerini doğrudan bilmesi, “tight coupling” olarak adlandırılan sıkı bir bağımlılık yaratır. Örneğin, oyuncunun canını gösteren bir UI elemanının, oyuncu sınıfına doğrudan bir referansı varsa, oyuncu sınıfında yapılacak herhangi bir değişiklik, UI elemanını da etkileyebilir. Bu durum, özellikle çok sayıda bileşenin etkileşimde bulunduğu durumlarda bir “örümcek ağı” etkisi yaratır ve hata ayıklamayı kabusa çevirir.
Game Event sistemleri, bu sıkı bağımlılığı ortadan kaldırmak için bir “aracı” görevi görür. Bileşenler birbirlerini doğrudan bilmek yerine, belirli olayları (event) yayınlar ve bu olayları dinleyen diğer bileşenler, bu olaylar gerçekleştiğinde kendi işlevlerini yerine getirir. Bu yaklaşım, “loosely coupled” yani gevşek bağlı bir mimari sunar. Bu sayede, bir bileşeni değiştirdiğinizde veya sildiğinizde, sistemin geri kalanında çok daha az bozulma riski olur.
ScriptableObject Tabanlı Game Event Sisteminin Avantajları
Neden Game Event sistemi için özellikle ScriptableObject kullanmalıyız? İşte başlıca avantajları:
- Modülerlik ve Esneklik: Her bir olay, ayrı bir ScriptableObject varlığı olarak tanımlanır. Bu, olayları Unity Editörü üzerinden kolayca yönetmenizi ve farklı senaryolar için yeniden kullanmanızı sağlar. Kodunuz daha temiz ve anlaşılır hale gelir.
- Performans: Doğrudan referanslar veya string tabanlı olay sistemlerine kıyasla daha verimlidir. Unity’nin kendi UnityEvent yapısını kullanarak, doğrudan metot çağrıları kadar hızlı çalışabilir.
- Bakım Kolaylığı: Bağımlılıklar azaldığı için kod tabanı daha kolay anlaşılır ve bakımı yapılabilir hale gelir. Hata ayıklama süreçleri daha hızlıdır çünkü bir olayın kimden geldiğini ve kimlere gittiğini daha net görebilirsiniz.
- Editör Entegrasyonu: ScriptableObject’ler Unity Editörü’nde birer varlık (asset) olarak bulunur. Bu, tasarımcıların veya diğer ekip üyelerinin kod yazmadan olayları bağlamasına ve test etmesine olanak tanır.
- Runtime ve Editor Modunda Çalışma: ScriptableObject’ler hem oyun çalışırken hem de editör modunda kullanılabilir, bu da geliştirme ve test süreçlerinde büyük kolaylık sağlar.
Temel Bileşenler: GameEvent ve GameEventListener
Bir ScriptableObject tabanlı Game Event sisteminin iki ana bileşeni vardır:
GameEvent(ScriptableObject): Bu, gerçekleşen olayı temsil eden bir ScriptableObject varlığıdır. Sadece bir “olay tetikleme” metodu (Raise) içerir ve bu olayı dinleyen tümGameEventListener‘lara bildirir.GameEventListener(MonoBehaviour): Bu, belirli birGameEvent‘i dinleyen ve olay tetiklendiğinde önceden tanımlanmış tepkileri (örneğin, bir metodu çağırmak) tetikleyen bir MonoBehaviour bileşenidir. Genellikle, Unity’nin yerleşik UnityEvent yapısını kullanarak, bu tepkileri Unity Editörü’nde görsel olarak bağlamanıza olanak tanır.
Adım Adım Kurulum Rehberi
1. GameEvent ScriptableObject Oluşturma
Öncelikle, oyun olaylarınızı temsil edecek temel ScriptableObject sınıfımızı oluşturalım. Bu sınıf, olayı dinleyen tüm bileşenleri tutacak ve olayı tetiklemek için bir metot sağlayacaktır.
Yeni bir C# scripti oluşturun ve adını GameEvent olarak belirleyin. İçeriği aşağıdaki gibi olmalıdır:
using UnityEngine;
using System.Collections.Generic;
[CreateAssetMenu(fileName = "New Game Event", menuName = "Game Events/Game Event")]
public class GameEvent : ScriptableObject
{
private readonly List<GameEventListener> eventListeners = new List<GameEventListener>();
public void Raise()
{
for (int i = eventListeners.Count - 1; i >= 0; i--)
{
eventListeners[i].OnEventRaised();
}
}
public void RegisterListener(GameEventListener listener)
{
if (!eventListeners.Contains(listener))
eventListeners.Add(listener);
}
public void UnregisterListener(GameEventListener listener)
{
if (eventListeners.Contains(listener))
eventListeners.Remove(listener);
}
}
Bu kodda dikkat etmeniz gerekenler:
[CreateAssetMenu(...)]: Bu nitelik (attribute), Unity Editörü’nde sağ tıklama menüsüne “Game Events/Game Event” seçeneğini ekleyerek yeni birGameEventvarlığı oluşturmanızı sağlar.eventListeners: Bu liste, bu olayı dinleyen tümGameEventListenerörneklerini saklar.Raise(): Bu metot çağrıldığında, listesindeki tüm dinleyicilerinOnEventRaised()metodunu çağırır. Ters döngü kullanılması, listeden dinleyici çıkarılırken oluşabilecek hataları önler.RegisterListener()veUnregisterListener(): Bu metotlar, dinleyicilerin olay listesine kaydolmasını ve kaydını silmesini sağlar.
2. GameEventListener MonoBehaviour Oluşturma
Şimdi de olayları dinleyecek ve bunlara tepki verecek MonoBehaviour sınıfımızı oluşturalım. Yeni bir C# scripti oluşturun ve adını GameEventListener olarak belirleyin:
using UnityEngine;
using UnityEngine.Events;
public class GameEventListener : MonoBehaviour
{
[Tooltip("Dinlenecek GameEvent varlığı.")]
public GameEvent Event;
[Tooltip("Olay tetiklendiğinde çağrılacak eylemler.")]
public UnityEvent Response;
private void OnEnable()
{
if (Event != null)
Event.RegisterListener(this);
}
private void OnDisable()
{
if (Event != null)
Event.UnregisterListener(this);
}
public void OnEventRaised()
{
Response.Invoke();
}
}
Bu kodun ana özellikleri:
Event: Bu alana, Unity Editörü’nden dinlemek istediğinizGameEventvarlığını sürükleyip bırakacaksınız.Response: Bu, Unity’nin yerleşikUnityEventsınıfıdır. Editörde, bu olayın tetiklenmesiyle hangi metotların çağrılacağını (örneğin, bir UI Panelini etkinleştirmek veya bir ses çalmak) görsel olarak belirleyebilirsiniz.OnEnable()veOnDisable(): Bu metotlar, dinleyicinin etkinleştirilmesi ve devre dışı bırakılması durumundaGameEvent‘e kaydolmasını ve kaydını silmesini sağlar. Bu, gereksiz referansları önlemek ve performansı artırmak için önemlidir.OnEventRaised():GameEventtarafından çağrılan bu metot,Response.Invoke()ile kaydedilmiş tüm eylemleri tetikler.
3. Unity Editöründe Kullanım
Artık temel bileşenlerimiz hazır olduğuna göre, bunları Unity projenizde nasıl kullanacağınıza bakalım:
- GameEvent Varlığı Oluşturma: Proje penceresinde sağ tıklayın -> Create -> Game Events -> Game Event. Oluşturduğunuz varlığa anlamlı bir isim verin (örneğin, “PlayerDiedEvent”, “LevelCompletedEvent”).
- GameEventListener Ayarlama: Olayı dinlemesini istediğiniz herhangi bir GameObject’e
GameEventListenerbileşenini ekleyin. - Olayı Bağlama:
GameEventListenerbileşeninin “Event” alanına, adım 1’de oluşturduğunuzGameEventvarlığını sürükleyip bırakın. - Tepki Tanımlama: “Response” alanının altındaki ‘+’ butonuna tıklayarak, olayın tetiklenmesiyle çağrılacak metotları seçin. Örneğin, bir UI panelini aktif hale getiren bir metodu veya bir ses oynatan bir fonksiyonu buraya bağlayabilirsiniz.
- Olayı Tetikleme: Kodunuzda, olayın gerçekleşmesini istediğiniz herhangi bir yerden
GameEventvarlığınızınRaise()metodunu çağırın. Örneğin, oyuncunun canı sıfıra düştüğünde:public GameEvent PlayerDiedEvent; void TakeDamage(int amount) { currentHealth -= amount; if (currentHealth <= 0) { PlayerDiedEvent.Raise(); } }
Uygulama Örnekleri
Bu sistemin esnekliği sayesinde pek çok farklı senaryoda kullanabilirsiniz:
- Kullanıcı Arayüzü Güncellemeleri: Oyuncu canı, skoru, envanter değişiklikleri gibi durumlar için olaylar tanımlayarak, UI elemanlarının bu olaylara tepki vermesini sağlayın. Bu sayede, UI kodu ile oyun mantığı kodu birbirinden tamamen ayrılır.
- Oyuncu Durumu Değişiklikleri: Oyuncu öldüğünde, seviye atladığında veya yeni bir yetenek kazandığında bir olay tetikleyerek, bu durumlara tepki verecek farklı sistemleri (ses sistemi, efekt sistemi, oyun yöneticisi vb.) bilgilendirebilirsiniz.
- Seviye Geçişleri: Bir seviye tamamlandığında
LevelCompletedEventtetikleyerek, oyunun bir sonraki seviyeyi yüklemesini veya bir bitiş ekranı göstermesini sağlayabilirsiniz. - Etkileşimli Nesneler: Bir anahtar alındığında
KeyCollectedEventtetikleyerek, kilitli kapıların bu olayı dinlemesini ve açılmasını sağlayabilirsiniz.
En İyi Uygulamalar ve İpuçları
- Olay Parametreleri (Generic Game Events): Eğer olaylarınızın veri taşıması gerekiyorsa (örneğin, oyuncunun aldığı hasar miktarı),
GameEventveGameEventListenersınıflarınızı generic (jenerik) hale getirebilirsiniz. Örneğin,GameEvent<T>veGameEventListener<T>oluşturarak, olayın bir parametre (T tipi) taşımasını sağlayabilirsiniz. - Olay Adlandırma Kuralları: Olaylarınıza açıklayıcı isimler verin (örneğin,
OnPlayerDeathyerinePlayerDiedEvent). Bu, projenizin okunabilirliğini artırır. - Performans Göz Önünde Bulundurma: Her ne kadar ScriptableObject tabanlı sistemler genel olarak performanslı olsa da, çok sık tetiklenen veya çok sayıda dinleyiciye sahip olaylarda performans darboğazları yaşanabilir. Bu tür durumlarda, olaylarınızı optimize etmek veya alternatif çözümler düşünmek gerekebilir.
- Editör Uzantıları: Projeniz büyüdükçe, olayların kimden geldiğini ve kimleri etkilediğini gösteren basit editör uzantıları yazmak, hata ayıklama sürecini çok kolaylaştırabilir.
Sonuç
Unity’de ScriptableObject tabanlı bir Game Event sistemi kurmak, oyun geliştirme projelerinize inanılmaz bir modülerlik ve esneklik katacaktır. Bileşenler arasındaki bağımlılıkları azaltarak, kodunuzu daha temiz, bakımı daha kolay ve daha ölçeklenebilir hale getirirsiniz. Bu rehberdeki adımları takip ederek kendi oyunlarınızda bu güçlü deseni uygulamaya başlayabilir ve daha sağlam bir mimariyle daha keyifli oyunlar geliştirebilirsiniz. Unutmayın, iyi bir mimari, uzun vadede size zaman ve emek kazandırır!



