Unity 3D Editör Scripti Yazmak – 2 – PropertyDrawer

Yayınlandı: 03 Kasım 2019 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde

Merhabalar,

Bu derste, Unity‘nin Inspector’undaki değişkenlerin görünümlerini, PropertyDrawer vasıtasıyla nasıl değiştirebileceğimize bakacağız. Örneğin Color türündeki değişkenlerin Inspector’a RGB değerleriyle birlikte çizilmelerini sağlayacağız:

Hazırsanız başlayalım!

Öncelikle Project panelinde Editor isminde bir klasör oluşturun. Editör script’leri daima Editor klasörü içerisine eklenmelidir. Klasörü oluşturduktan sonra, klasörün içinde ColorDrawer adında bir C# script’i oluşturun ve içeriğini şöyle değiştirin:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer( typeof( Color ) )]
[CustomPropertyDrawer( typeof( Color32 ) )]
public class ColorDrawer : PropertyDrawer
{
	public override void OnGUI( Rect konum, SerializedProperty property, GUIContent degiskenIsmi )
	{
		EditorGUI.BeginProperty( konum, degiskenIsmi, property );
		konum = EditorGUI.PrefixLabel( konum, degiskenIsmi );

		float ceyrekGenislik = konum.width / 4f;
		Rect rKonum = new Rect( konum.x, konum.y, ceyrekGenislik, konum.height );
		Rect gKonum = new Rect( konum.x + ceyrekGenislik, konum.y, ceyrekGenislik, konum.height );
		Rect bKonum = new Rect( konum.x + 2f * ceyrekGenislik, konum.y, ceyrekGenislik, konum.height );
		Rect renkKonum = new Rect( konum.x + 3f * ceyrekGenislik, konum.y, ceyrekGenislik, konum.height );
		Color renk = property.colorValue;

		EditorGUI.BeginChangeCheck();
		int r = EditorGUI.IntField( rKonum, (int) ( renk.r * 255 ) );
		if( EditorGUI.EndChangeCheck() )
		{
			renk.r = r / 255f;
			property.colorValue = renk;
		}

		EditorGUI.BeginChangeCheck();
		int g = EditorGUI.IntField( gKonum, (int) ( renk.g * 255 ) );
		if( EditorGUI.EndChangeCheck() )
		{
			renk.g = g / 255f;
			property.colorValue = renk;
		}

		EditorGUI.BeginChangeCheck();
		int b = EditorGUI.IntField( bKonum, (int) ( renk.b * 255 ) );
		if( EditorGUI.EndChangeCheck() )
		{
			renk.b = b / 255f;
			property.colorValue = renk;
		}

		EditorGUI.BeginChangeCheck();
		renk = EditorGUI.ColorField( renkKonum, renk );
		if( EditorGUI.EndChangeCheck() )
			property.colorValue = renk;

		EditorGUI.EndProperty();
	}
}

Artık Color veya Color32 türündeki değişkenlere Inspector’dan bakarken, değişkenlerin RGB değerleriyle beraber ekrana çizildiğini göreceksiniz. Peki bu script nasıl çalışıyor?

  • Kullandığımız CustomPropertyDrawer attribute‘ları, bu class’ın Color ve Color32 türündeki değişkenleri Inspector’a çizdirmeye yaradığını Unity’e söylemekte.
  • CustomPropertyDrawer eklemeye ilaveten, class PropertyDrawer sınıfından türemek zorunda.
  • Arayüz elemanlarını çizdirme işlemini OnGUI fonksiyonunda yapıyoruz. Bu fonksiyon 3 parametre almakta:
    • konum: bu değişkeni Inspector’da hangi koordinatlara çizeceğimizi depolar
    • property: değişkene erişmeye yarar, SerializedProperty aslında ayrı bir ders yazacak kadar kapsamlı bir konu ama biz bu derste üzerinden basitçe geçeceğiz
    • degiskenIsmi: değişkenin ismini tutar
  • PropertyDrawer’lar GUILayout desteklememekte, o yüzden arayüz elemanlarını GUI ve EditorGUI fonksiyonları vasıtası ile, konum‘un olduğu koordinatlarda çizdirmek zorundayız.
  • Sistemin düzgün çalışması için, OnGUI’nin başında EditorGUI.BeginProperty fonksiyonunu çağırmak zorundayız, bu fonksiyon ne işe yarar derseniz ben de tam bilmiyorum.
  • EditorGUI.PrefixLabel fonksiyonu, konum koordinatlarının sol tarafına değişkenin ismini yazar ve sağ tarafta kalan boş alanın koordinatlarını döndürür:
  • Sonraki satırlarda, rengin RGB değerlerini ve rengin kendisini hangi koordinatlarda çizeceğimizi hesaplıyoruz. Bu 4 bileşenin her biri, konum koordinatlarının genişliğinin 1/4’ü genişlik kaplıyor. konum.x, konum’un sol kenarının pozisyonunu tutarken konum.y de konum’un üst kenarının pozisyonunu tutar. konum.width konum’un genişliğini, konum.height da konum’un yüksekliğini döndürür. new Rect constructor‘ı ise, yeni konum değişkeninin x, y, genişlik ve yükseklik değerlerini parametre olarak alır.
  • Color renk = property.colorValue; satırında, değişkenin değerine erişiyoruz. Unity’de her şey SerializedProperty‘ler vasıtasıyla diske kaydedilmekte, yani oyununuzda yer alan her değişkenin değeri arkaplanda pek çok SerializedProperty objesinde depolanmakta. Color veya Color32 değişken tutan bir SerializedProperty’deki rengin değerine colorValue ile erişilir.
  • EditorGUI.BeginChangeCheck ve EditorGUI.EndChangeCheck fonksiyonları beraber kullanılırlar ve bu iki fonksiyon arasında ekrana çizdirdiğiniz arayüz elamanlarında bir değişiklik meydana gelirse (mesela kullanıcı oradaki input’un değerini değiştirirse), EditorGUI.EndChangeCheck fonksiyonu true döndürür.
  • Rengin RGB değerlerini sırasıyla kendi koordinatlarında çizdiriyoruz. Bunun için EditorGUI.IntField kullanıyoruz çünkü RGB değerleri [0,255] aralığında bir int değer alırlar. Ancak Color32’nin aksine Color türü, RGB değerlerini [0,1] aralığında float olarak tutar. Biz de bu aralığı [0,255]’e genişletmek için rengin RGB bileşenlerini 255 ile çarpıp int’e çeviriyoruz.
  • Eğer kullanıcı RGB bileşenlerinden herhangi birisini değiştirirse, EditorGUI.EndChangeCheck if koşulunun içine giriyoruz ve burada o bileşeni 255’e bölerek tekrar [0,1] aralığına daraltıyoruz. Ardından da değişkenin değerini SerializedProperty’nin colorValue‘si vasıtasıyla değiştiriyoruz.
  • RGB bileşenlerinden sonra, EditorGUI.ColorField vasıtasıyla rengin kendisini de ekrana çizdiriyoruz.
  • EditorGUI.BeginProperty ile başlattığımız kodu, EditorGUI.EndProperty ile bitiriyoruz.

Kabaca sistemi anlatabilmişimdir diye ümit ediyorum. Kodda hiç Undo fonksiyonu kullanmadık çünkü PropertyDrawer sınıflarında BeginProperty ve EndProperty fonksiyonları arasında SerializedProperty’e yapılan değişiklikler, otomatik olarak undo-redo destekliyor.

Yazdığımız bu script vasıtasıyla Color/Color32 array’lerinin elemanları da yeni stilde ekrana çizdiriliyorlar:

Buradaki tek sıkıntı, RGB bileşenlerinin hepsinin başında biraz boşluk olması; çünkü Unity, array elemanlarını otomatik olarak biraz sağda çizdirir (indentation). Ama eğer dilerseniz bu sorunu çözmek çok kolay; EditorGUI.PrefixLabel fonksiyonundan hemen sonra EditorGUI.indentLevel = 0; satırını ekleyerek indentation’ı sıfırlayabilirsiniz:

Şimdi dilerseniz bir başka PropertyDrawer örneği daha görelim. Bu örnekte, kendi yazdığımız basit bir class’ın Inspector’daki görünümünü değiştireceğiz. Class’ımız şu:

[System.Serializable]
public class TestClass
{
	public Vector3 vektor;
	public string yazi;
}

İsmi TestClass ve içerisinde bir Vector3 ile bir string tutuyor. TestClass türündeki değişkenlerin Inspector’da gözükebilmesi için, class’ın System.Serializable attribute’una sahip olması lazım (Serializable ve SerializedProperty kelimeleri arasındaki benzerliğe dikkat ettiniz mi 😉 ). Şu anda TestClass türünde değişkenler, Inspector’da şu şekilde gözükmekte:

Değişkenlerimizin başında açılıp kapanabilir bir başlık bulunmakta (Test Class, Element 0, Element 1). Hem bu başlığı elle açmak zorunda olmak vakit kaybı hem de bu başlığın kapladığı 1 satırlık alan, Inspector’da yer israfı. Bu sorunları çözmek için, biz bu TestClass değişkenlerimizin şöyle görünmesini istiyoruz:

Bunun için, Editor klasöründe TestClassDrawer adında yeni bir C# script’i oluşturalım:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer( typeof( TestClass ) )]
public class TestClassDrawer : PropertyDrawer
{
	public override void OnGUI( Rect konum, SerializedProperty property, GUIContent degiskenIsmi )
	{
		EditorGUI.BeginProperty( konum, degiskenIsmi, property );
		konum = EditorGUI.PrefixLabel( konum, degiskenIsmi );
		EditorGUI.indentLevel = 0;

		float yarimYukseklik = konum.height / 2f;
		Rect vektorKonum = new Rect( konum.x, konum.y, konum.width, yarimYukseklik );
		Rect yaziKonum = new Rect( konum.x, konum.y + yarimYukseklik, konum.width, yarimYukseklik );

		EditorGUI.PropertyField( vektorKonum, property.FindPropertyRelative( "vektor" ), GUIContent.none );
		EditorGUI.PropertyField( yaziKonum, property.FindPropertyRelative( "yazi" ), GUIContent.none );

		EditorGUI.EndProperty();
	}

	public override float GetPropertyHeight( SerializedProperty property, GUIContent label )
	{
		return EditorGUIUtility.singleLineHeight * 2f;
	}
}
  • Burada ekstradan GetPropertyHeight fonksiyonu bulunuyor. Değişken ekrana çizilirken birden çok satır kaplamasını istiyorsanız, bunu bu fonksiyon vasıtasıyla belirtiyorsunuz. EditorGUIUtility.singleLineHeight değişkeni, Unity’deki standart 1 satırın yüksekliğini döndürür. Biz değişkeni 2 satırlık bir alana çizmek istiyoruz. Böylece OnGUI‘ye aktarılan konum parametresi, 2 satır yüksekliğinde olacak.
  • OnGUI fonksiyonunda tek fark, değişkenleri ekrana EditorGUI.PropertyField ile çizdirmemiz. Bu fonksiyon parametre olarak bir SerializedProperty alır ve onu belirtilen koordinatlarda çizdirir. Normalde 3. parametre olarak değişkenin ismi girilir ve PropertyField fonksiyonu, değişkenin ismini de ekrana çizdirir ama biz zaten değişkenin ismini PrefixLabel fonksiyonu ile ekrana çizdiğimiz için, boş bir değişken ismi olan GUIContent.none‘ı 3. parametreye değer olarak veriyoruz.
  • PropertyField fonksiyonuna SerializedProperty parametresini verirken, property değişkenimizin FindPropertyRelative fonksiyonunu kullanıyoruz. Burada property bizim TestClass değişkenimizi tutmaktadır. Bu değişkenin alt değişkenlerine, yani TestClass’ın kendi değişkenlerine erişmek için FindPropertyRelative kullanıyoruz. Parametre olarak da değişkenin ismini giriyoruz. Bu fonksiyon ise bize, içerisinde o alt değişkeni tutan başka bir SerializedProperty döndürüyor.
  • PropertyField fonksiyonunu BeginChangeCheck ve EndChangeCheck ile çevrelemedik çünkü PropertyField fonksiyonunun bir avantajı, SerializedProperty’de yapılan değişiklikleri otomatik olarak algılayıp değişkeni gerektiğinde güncellemesi.

Böylece geldik bir dersin daha sonuna. O halde bir sonraki derste görüşmek üzere, esen kalın!

Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google fotoğrafı

Google hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.