๊ทธ๊ฒ์ ๋ฐ๋ก PropertyDrawer์ uitoolkit์ ํ์ฉํ๋ ๊ฒ์.
๊ฐ๋จํ ๋ฌธ์ ์ํฉ์ ๋ง๋ค๊ณ ์ปค์คํ ui๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์์ ๋ฅผ ๋ง๋ค์ด๋ณธ๋ค.
์ ๋ํฐ๋ก ๊ฒ์์ ๋ง๋ค๋ฉด์ ์ปค์คํ ํด๋์ค๋ฅผ ์ฌ์ฉํ ์ผ์ ๊ธธ๊ฐ์ ์ฑ์ด๋ ๋์ฒ๋ผ ๋ง๋ค.
๊ฐ๋ น ๊ฐ๋จํ ๊ฒ์ฌ( ๋ฌธ์ "$"๋ฅผ ํฌํจํ์ง ์๋๋ค.) ๊ฐ ํ์ํ ์คํธ๋ง์ด ํ์ํ ์ํฉ์ด๋ผ๊ณ ํด๋ณด๊ณ ์๋์ ๊ฐ์ ํด๋์ค๋ฅผ ๋ง๋ค์๋ค.
[Serializable]
public class MyString
{
// ์ด๊ฒ์ "$" ๋ฅผ ํฌํจํ๋ฉด ์๋๋ ์คํธ๋ง์ด๋ค. ์ด์ ๋ ์๋ค ์๋ํ๋ฉด ์ด ์ฝ๋๋ ์์ ์ฝ๋๋๊น.
// ์๋ํฐ์์ ์์ ํ ์ดํ์ ๋ฐํ์์์๋ ๋ณ๊ฒฝ๋ ํ์๊ฐ ์๋ ์คํธ๋ง.
[SerializeField] private string doNotUseDollar;
public string Value => doNotUseDollar;
#if UNITY_EDITOR
public static bool CheckValidation(string value)
{
return !value.Contains("$");
}
#endif
}
๊ทผ๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ์ด ์คํธ๋ง๊ฐ์ ์ฌ๋์ด ์๋ํฐ์์ ์ ๋ ฅํด์ค์ผ ํ๋ ๊ฐ์ธ๋ฐ, ์ ๋ ฅ๊ณผ์ ์์ ์ค์๊ฐ ์ผ์ด๋ ํ๋ฅ ์ด ๋๋ค.
๋ฐํ์์์ ์๋ํฐ๋ก๋ถํฐ ์ ๋ ฅ๋ ๊ฐ์ ํจ์์์ ์ฌ์ฉ ์ ์ ์ค๋ฅ ์ฌ๋ถ๋ฅผ ์ฒดํฌ๋ฅผ ํ ์๋ ์์ง๋ง,
์ฌ์ค ์ ๋ฐ๊ฒ์ ์ ์ด์ ์๋ชป๋ค์ด๊ฐ ์์ผ๋ฉด ์๋๋ ๊ฐ์ด๊ธฐ ๋๋ฌธ์ ์๋ํฐ์์๋ง ๊ฒ์ฌ๋ฅผ ํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
๊ทธ๋ ๋ค๋ฉด ์ค์๋ฅผ ์์ ๊ธฐ ์ํด ์๋ชป๋ ๊ฐ์ด ๋ค์ด๊ฐ๋ฉด ๊ฒฝ๊ณ ๋ฅผ ํด์ฃผ๋ฉด ์ข๊ฒ ์.
์ผ๋จ ์ด ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฐ๋จํ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด๋ณด์.
public class MyMonoComponent : MonoBehaviour
{
[SerializeField] private MyString test;
[SerializeField] private MyString[] testArray;
private void Awake()
{
Debug.Log(test.Value);
}
}
๊ทธ๋ฆฌ๊ณ ์ ๋ํฐ์ ์ถ๊ฐํ๋ฉด
์ด๋ ๊ฒ ๋์ค๊ฒ ์ง?
๊ทธ๋ฌ๋ฉด ์ด์ ๊ฐ์ ์ ๋ ฅํ ๋ ์๋ชป ์ ๋ ฅํ๋ฉด (์ฌ๊ธฐ์๋ $๋ฅผ ์ ๋ ฅํ ๊ฒฝ์ฐ)
์๋ ๊ทธ๋ฆผ์ฒ๋ผ ์๋ชป ์ ๋ ฅํ ํ๋๊ฐ ๋นจ๊ฐ๊ฒ ๋๋ฉด ๊ฐ์ ์ ๋ ฅํ๋ค๊ฐ ์ค์ํ์ ๋ ๋ฐ๋ก ์์์๊ฒ ์ง?
์ด๊ฑธ ๊ตฌํํ๋ ๊ฒ์ ์ฌ์ค ์์ฃผ ๊ฐ๋จํ๊ณ ์ฌ๋ฌ๊ฐ์ง ๊ตฌํ๋ฐฉ๋ฒ์ด ์์. ์ทจํฅ์ ๋ง๊ฒ ์ข์ํ๋ ๋ฐฉ๋ฒ์ ์ฐ๋ฉด ๋๋๊ฑด๋ฐ
๋๋ uitoolkit์ ์ฌ์ฉํ๊ณ ๋์๋ ์๋์ฒ๋ผ ๊ตฌํํ๋ ๊ฒ์ ๋งค์ฐ ์ข์ํ๊ฒ ๋์์.
uitoolkit์ฌ์ฉ ๋ฐฉ๋ฒ์ ๊ณต๋ถํ๋ค๋ฉด,
์๋น uitoolkit์์ ๋น๋ฉ๋ธ๋ก์ผ๋ก ์ฌ์ฉํ๋ VisualElement๋ผ๋ ํด๋์ค๋ฅผ ๋ค๋ฃจ๊ฒ ๋ ๊ฒ์.
uitoolkit์์ ์ ๊ณตํ๋ UIBuilder๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐํธํ๊ฒ uxmlํ์ผ์ ํธ์งํ ์ ์์
ํธ์ง๋ uxmlํ์ผ์ VisualTree๋ฅผ ์ด๋ป๊ฒ ๊ตฌ์ฑํ ์ง์ ๋ํด ์ ์ด๋ ผ ํ์ผ์.
VisualTree๋ VisualElement๋ค๋ก ๊ตฌ์ฑ๋์ด ์๊ณ
์ด๋ฆ์ฒ๋ผ ํธ๋ฆฌ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์ ์ต์๋จ root์์๋ถํฐ ํ์ VisualElement๊น์ง ์ ๊ทผํ๊ธฐ ์ํด
ํ์๊ตฌ์กฐ๋ฅผ ์ฟผ๋ฆฌํด์ค๊ธฐ ์ํ Q<T> ๋๋ Query(string id) ํจ์๋ฅผ ์ ๊ณตํจ.
๊ทธ๋ ๋ค๋ฉด ์ด๋ค VisualElement ํ์์์์ ์ ๊ทผํ๊ธฐ ์ํด, id๋, ํด๋น ์์์ ํด๋์ค์ด๋ฆ์ด ํ์ํ ๊ฒ์ ์๊ณ ์์๊ฑฐ์
์ด์ ์ด๊ฑธ ์ด์ฉํ ๊ฑฐ์.
UIBuilder์์ ๋ณด๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณต๋๋ UIElement ๋ค์ด ์๋ค.
์คํฌ๋ฆฝํธ์์๋ ์๋์ ๊ฐ์ ํด๋์ค๋ฅผ ์ฌ์ฉํ ์ ์๋ค. (๊ท์ฐฎ์์ ๋ฐ์ 3๊ฐ๋ง ์ ์)
UnityEngine.UIElements.Label
UnityEngine.UIElements.TextField
UnityEngine.UIElements.PropertyField
์ฌ๊ธฐ์ UnityEngine.UIElements.TextField์ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ณด์.
์ต์๋จ VisualElement๋ก "TextField"๊ฐ ์๊ณ ๊ทธ ํ์๋ก "Label"๊ณผ "#unity-text-input"์ด ์์์ ์ ์ ์๋ฐ.
๊ทธ๋ฌ๋ฉด MyString.doNotUseDollar ์ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ ๊ฒ์ ์ฝ๋ฐฑ์ผ๋ก ๋ฑ๋กํ๊ณ , ์คํธ๋ง์ ๊ฒ์ฌํ๋ ํจ์๋ฅผ ์คํํด์ ๊ทธ ๊ฐ์ ๋ฐ๋ผ
"#unity-text-input"์ background ์ปฌ๋ฌ๋ฅผ ๋ณ๊ฒฝํด์ฃผ๋ฉด ๋ ๊ฑฐ์.
๊ทธ๊ฒ์ ์ํด ์ผ๋จ Editorํด๋๋ฅผ ์์ฑํ๊ณ MyStringProperyDrawer.cs ํ์ผ์ ๋ง๋ค์.
(namespace์ ๋ง๋ ์์น์ ์์ฑํด์ฃผ์, ์ฌ๊ธฐ์๋ Asset/Editor/MyStringProperyDrawer.cs)
๊ทธ๋ฆฌ๊ณ ์๋์ ์ฝ๋๋ฅผ ์ ๋ ฅํด๋ณด์.
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
[CustomPropertyDrawer(typeof(MyString))]
public class MySerializableClassPropertyDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
}
}
์ผ๋ง์ ์ ๋์ฒ๋ผ PropertyDrawer๋ฅผ ์ฒ์ ์ฌ์ฉํด๋ณด๋ ์ฌ๋์ ์ํด ์ ๊น ์ค๋ช ํ๊ณ ๋์ด๊ฐ.
ProperyDrawerํด๋์ค๋ ํน์ Serializable Class๊ฐ ์ ๋ํฐ ์ธ์คํํฐ์์ ๊ทธ๋ ค์ง๋ ๋ฐฉ๋ฒ์ ํํํ ์ ์๊ฒ ํด์ฃผ๋ ํด๋์ค์.
๊ณต์๋ฌธ์๊ฐ ์ค๋ช ์ด ๋ ์๋์ด์์. ์ค๋ช ์ด ํ์ํ ์ฌ๋์ ์ฝ์ด๋ณด๊ณ ์ค๋๋ก
https://docs.unity3d.com/kr/2021.3/Manual/editor-PropertyDrawers.html
ํ๋กํผํฐ ๋๋ก์ด - Unity ๋งค๋ด์ผํ๋กํผํฐ ๋๋ก์ด๋ ์คํฌ๋ฆฝํธ์์ ์์ฑ์ ์ฌ์ฉํ๊ฑฐ๋ ํน์ Serializable ํด๋์ค๊ฐ ํ์๋์ด์ผ ํ๋ ๋ฐฉ๋ฒ์ ์ ์ดํ์ฌ ์ธ์คํํฐ(Inspector) ์ฐฝ ์ ํน์ ์ปจํธ๋กค์ด ํ์๋๋ ๋ฐฉ๋ฒ์ ์ปค์คํฐ๋ง์ด์ฆํ ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.docs.unity3d.comhttps://docs.unity3d.com/kr/2022.3/Manual/editor-PropertyDrawers.html
๊ทธ๋ฐ๋ฐ ์ฌ๊ธฐ์ ์ฌ๋ฏธ๋์ ์ ๋ด๊ฐ ์ฝฉ์ ์ ์ฃผ๋ก ๋งํฌ๋ฅผ ๋๋ฒ๋จ๊ฒ์ด ์๋๋ผ๋ ์ ์
๊ฐ๊ฐ 2021.3๊ณผ 2022.3 ๋ฒ์ ์ ๋ค๋ฅธ ๋ฌธ์์.
๊ทธ๋ฐ๋ฐ 2022.3 ๋ฒ์ ๋ฌธ์์๋ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ๊ตฌ๊ฐ ์ถ๊ฐ๋์ด์์.
์ปค์คํ ํด ๋ง๋ค ๋๋ uitoolkit์ ์จ๋ผ๋ ๊ฑฐ์. ๊ทธ๋ ๋ค๋ฉด ํ๋ฒ ์จ์ค์ผ ๋์ง ์์๊น??
(๋๋ ์ ๋ํฐ๊ฐ uitoolkit์ ๊ณ์ ๋ฐ์ ์์ผ์ ์ฅ๊ธฐ์ ์ผ๋ก๋ ์น/์ฑ๋์์ธ์ฒ ํ์ ์ธ๊ฒ์์๋ ์ ์ฉํ ์ ์๊ฒ ํ๊ณ ,
๋๋ถ์ด flutter๋ react native์ฒ๋ผ ๋ณต์กํ ui๊ธฐ๋ฅ์ ์ง์ํ๋ ์ ํ์ ๋ง๋ค์ด ๊ฒ์ ์ธ์๋ ๋ค์ํ ์์ฅ์ ๋ ธ๋ฆฌ๋ ค๋ ์๋๋ก ๋ณด๊ณ ์์)
๊ทธ๋ ๋ค๋ฉด ์ด๋ป๊ฒ ์ธ๊น?
๋ฌธ์์ ์์ ๋ ์๋์ ๊ฐ์ด OnGUIํจ์๋ฅผ ์ค๋ฒ๋ผ์ด๋ ํ๋ผ๊ณ ํ๋ค. ์ด๊ฒ์ ์ ํต์ ์ธ ๋ฐฉ๋ฒ์ด๋ค.
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawer :PropertyDrawer
{
// Draw the property inside the given rect
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
...์๋ต...
}
}
๊ทธ๋ฌ๋ CreatePropertyGUIํจ์๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ๋ฉด VisualElement๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
ย CreatePropertyGUIํจ์๋ย ํน์ Serializable Class๋ฅผ ํํํ๊ธฐ ์ํ VisualElement๋ฅผ ๋ฐํํ๋ ํจ์์ด๋ค.
์ผ๋จ์ MyString์ ํํํ๊ธฐ ์ํด
1) TextField๋ฅผ ์์ฑํ๊ณ
2) TextField์ MyString.doNotUseDollar ํ๋กํผํฐ๋ฅผ ๋ฐ์ธ๋ฉํ๊ณ
3) TextField์ ๊ฐ์ด ๋ณ๊ฒฝ ๋ ๋ 4) ์ญํ ์ ํ๋ ํจ์๋ฅผ ์ฝ๋ฐฑ์ผ๋ก ๋ฑ๋ก
4) string๊ฐ์ ๊ฒ์ฌํด์ "#unity-text-input"์ background๋ฅผ true๋ฉด ์งํํ์๋ฐฐ๊ฒฝ, false๋ฉด ๋นจ๊ฐ์์ผ๋ก ๋ณ๊ฒฝ.
์ ํ ๊ฑฐ์.
์๋ ์ฃผ์์ ๋ฌ์๋ ผ ์ฝ๋๋ฅผ ๋ณด๋ฉด์ ์ค๋ช ํ๊ฒ ์.
using UnityEditor;
using UnityEngine;
// VisualElement๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ํ์ํ๋ค.
using UnityEngine.UIElements;
// MyString์ ์ด๋ป๊ฒ ์ธ์คํํฐ์ ๊ทธ๋ฆด ๊ฒ์ธ๊ฐ.
[CustomPropertyDrawer(typeof(MyString))]
public class MySerializableClassPropertyDrawer : PropertyDrawer
{
// ์๋์์ ์ฌ์ฉํ ์์์ ๋ฏธ๋ฆฌ ์ ํด๋์๋ค.
private static readonly Color InvalidBackgroundColor = Color.red;
private static readonly Color BackgroundColor = new Color(0.157f, 0.157f, 0.157f);
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
// ๋ฃจํธ VisualElement ์์ฑ
var container = new VisualElement();
var doNotUseDollarProperty = property.FindPropertyRelative("doNotUseDollar");
// TextField (VisualElement) ์์ฑ.
var doNotUseDollarField = new TextField
{
// mvvm๋ชจ๋ธ์ binding์ด๋ผ๋ ๊ฐ๋ ์ ๊ณต๋ถํด๋ณด์. ์ฌ๊ธฐ์ ์ค๋ช ํ๊ธฐ์๋ ๊ธธ๋ค.
// ๋์ถฉ ๋งํ๋ฉด ๋ด๋ถ์ ์ผ๋ก ๋์ ๋๋ฆฌ๊ฐ ์๊ณ ๋ฐ์ธ๋ฉํจ์ค๋ ํค๊ฐ ๋๋ค๊ณ ์ดํดํ๋ฉด ๋๋ค.
bindingPath = doNotUseDollarProperty.propertyPath,
label = property.displayName
};
// TextField์ ์ฝ๋ฐฑ ๋ฑ๋ก.
doNotUseDollarField.Reg1sterValueChangedCallback(value =>
{
// ๋ณ๊ฒฝ๋ ๊ฐ์ ๊ฒ์ฌ.
var isValid = MyString.CheckValidation(value.newValue);
//UI Builder์์ TextField์ ๊ตฌ์กฐ๋ฅผ ๋ดค๋๊ฒ์ ๊ธฐ์ตํ๋๊ฐ? (๊ธฐ์ต ์๋๋ฉด ์์ ๊ทธ๋ฆผ ๋ค์ ๋ณด๊ณ ์ค๋ฉด ์ข์๋ฏ)
// TextField๋ ํ์์ unity-text-input๋ผ๊ณ ์ด๋ฆ๋ถ์ฌ์ง VisualElement๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
var textInput = doNotUseDollarField.Q("unity-text-input");
// ์์ค์ฒ๋ผ ํ๋ฉด "unity-text-input" VisualElement๋ฅผ ๋ฐํ๋ฐ์ ์ ์๋ค.
// ๊ทธ๋ฆฌ๊ณ ์คํ์ผ์ ์ค์ ํด์ฃผ์.
textInput.style.backgroundColor = isValid ? BackgroundColor : InvalidBackgroundColor;
});
// ๋งจ์ฒ์ ๋ง๋ ์ปจํ ์ด๋ ํ์์ ํ ์คํธํ๋๋ฅผ ์ถ๊ฐํ๋ค.
container.Add(doNotUseDollarField);
return container;
}
}
UIBuilder์์ VisualTree๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ฐธ์กฐํด propertyDrawer๋ฅผ ์ ์ํ ์ ์์๋ค.
์ ๋ณด์ถ
์์ฐ
์ ๊ฑฐ๋ก๋ ์ฐ๋๊ฑฐ๊ตฌ๋. ๊ฐ์ถ