Unity Scriptable Objects: İyileştirilmiş Veri Yönetimi

Oyun geliştirme sürecinde benzer objelerin farklı verilere ihtiyaca duyduğu anlarla karşılaşırız. Örneğin 3 farklı düşmanın farklı hareket hızları, saldırı menzilleri gibi çeşitli verileri olur. İşte Scriptable Objects bu durumda harika bir tercihtir. Scriptable Objetcs, projemizde asset olarak bulunurlar ve çok az yer kaplarlar. Genel olarak veri depolama amacıyla kullanılırlar. Avantaj olarak sayabileceğim yönleri şunlardır;

  • Sahne ve Play Mode’dan bağımsızdırlar. (oyunumuz çalışırken yapılan değişikler kaydedilir.)
  • Sürükle & Bırak yöntemiyle çalıştıkları için kullanımı kolaydır.
  • Kolayca test edilebilirler.
  • Scriptler arası bağlılığı azaltırlar.

Üstte bahsettiğim durumu örnek alalım. Oyunumuzda birden fazla düşmanın olduğu ve her birinin farklı statlara sahip olduğu bir oyunumuz olsun. Bunların her birinin statlarını kendi classlarında tutmak yerine, Scriptable objeler oluşturup, hızlıca düşmanlarımıza assign edebiliriz. Böylece bir düşmanın statını diğeriyle hızlıca değiştirebilir veya yeni statlar oluşturabiliriz.

Peki Neden Prefab’ımızı Çoğaltıp Direkt Olarak Oradan Veriyi Değiştirmiyoruz?

Bir prefab’ı çoğalttığımızda, orada oluşan veriler de instance olarak kopyalanır. Bu verilerin kopyalanmasına gerek yoktur. Ortak bir veriden istedikleri değerleri çekebilirler. Yani, çok büyük bir sorun olarak gözükmese de programlamada dikkat etmemiz gereken şeylerden biri de tekrardan kaçınmamızdır. Bunun yerine ortak bir data bulundurup bu datanın verilerini değiştirmek çok daha kullanışlı bir yöntemdir. Ayrıca yeni bir düşman oluştururken hali hazırda olan bir veriyi kullanmak istersek, kolayca objeye atamayı yapabiliriz. O zaman hadi scriptable obje oluşturalım!

Scriptable Obje ve Data Management

Öncelikle bir scriptable obje sınıfı oluşturmak için, sınıfımızın ScriptableObject classından inherit alması gerekiyor ve bu oluşturduğumuz objenin unity menülerinde görünmesi için CreateAssetMenu attribute’unu kullanmamız gerekli.:

[CreateAssetMenu(menuName = "Stats/EnemyStats")]
public class EnemyStats : ScriptableObject
{
}

Görüldüğü üzere, Stats sekmesinde EnemyStats adlı bir asset menüsü oluşturduk. Şimdi gidip Unity’de istediğimiz klasöre sağ tıkladığımızda (nerede oluşturmak istiyorsak) objemizin orada bulunduğunu göreceğiz, EnemyStats’s tıklayıp ilk scriptable objemizi yaratalım:


Şu anda içerisine herhangi bir şey yazmadığımız için doğal olarak içi boş. Haydi istediğimiz statları içine doldurmaya başlayalım:
[CreateAssetMenu(menuName = "Stats/EnemyStats")]
public class EnemyStats : ScriptableObject
{
    public string enemyName;
    public int health;
    public int mana;
    public float moveSpeed;
    public float attackSpeed;
    
}

İsim, can, mana, hareket hızı ve saldırı hızı değişkenleri tanımladık. Şimdi scriptable objemize baktığımızda ise bu değerleri görüp düzenleyebiliriz. Bu datayı başka bir yerde kullanacağımız için değişkenlerimizi public yaptık.

Bu değerleri, istediğimiz düşmanlara veri olarak artık aktarabiliriz. Düşmanlarımızın sınıflarında, oluşturduğumuz scriptable objenin tipinde bir değişken yaratmalıyız. [SerializeField] attribute’u ile editörde atama yapabilmek için değişkenimizi açığa çıkarabiliriz. [SerializeField] hakkında bilginiz yoksa buraya tıklayarak bilgi edinebilirsiniz;

public class Enemy : MonoBehaviour
{
    [SerializeField] private EnemyStats stats;
}

Değişkenimizi oluşturduk, şimdi editörden oluşturduğumuz scriptable objenin atamasını yapalım.

Artık Enemy sınıfımız, oluşturduğumuz tüm datalara ulaşabilir hale geldi. Denemek için stat sınıfımızın içerisinde hızlıca bir PrintStats(); methodu oluşturacağım.
public class EnemyStats : ScriptableObject
{
    public string enemyName;
    public int health;
    public int mana;
    public float moveSpeed;
    public float attackSpeed;

    public void PrintStats()
    {
        Debug.Log($"My name is {enemyName}, my health is: {health}, my mana is {mana}, my move speed is {moveSpeed} & my attack speed is {attackSpeed}");
    }
}

Şimdi ise bu methodu oluşturduğumuz stats değişkenini kullanarak Enemy sınıfımızda çağıralım. Böylece gerekli değerleri doğru alabiliyor muyuz test edelim;

public class Enemy : MonoBehaviour
{
    [SerializeField] private EnemyStats stats;

    private void Start()
    {
        stats.PrintStats();
    }

Oyunu başlatıp editörde test edelim;

Scriptable obje yardımıyla karakterimiz kendi verilerini kolayca çekebildi. Şimdi ise bir başka Scriptable obje oluşturalım ve aynı düşmanımıza atamasını yapalım. Bunun statları daha farklı olsun;

Şimdi tekrar oyunu çalıştıralım ve değerleri kontrol edelim;

İşte, artık statlarımızı değiştirmek bu kadar basit. Dilediğimiz kadar düşman oluşturalım, bu düşmanlarda var olan verilerimizi kullanabilir ya da yeni scriptable objeler oluşturup atamalarını yapabiliriz. Gerekli ayarlamalar sonrası sadece atama yaparak işimizi hızlandırdık. Üstelik bunları oyun çalışıyorken değiştirdiğinizde bile çalıştığını farkedeceksiniz.
İşte Scriptable Objeleri kullanmak bu kadar kolay. Data yönetimi için en ideal kullanımlardan birini öğrendik. Tabii ki scriptable objelerin bunlardan farklı kullanımları da var. Bir sonraki yazımızda ise Scriptable objelerin nasıl oyun mimarisini kolaylaştırdığı ile ilgili konuya değineceğiz.

Sağlıcakla kalın.