ํฌ์ŠคํŠธ

๐ŸŒ” Unity - UI Toolkit

๐Ÿ’ซ UI Toolkit


๊ธฐ์กด IMGUI, UGUI ์™€๋Š” ๋‹ค๋ฅธ ๋˜ ๋‹ค๋ฅธ UI ์‹œ์Šคํ…œ
UI Elements๊ฐ€ ์œ ๋‹ˆํ‹ฐ 2020 ๋ฒ„์ „์œผ๋กœ ๋„˜์–ด์˜ค๋ฉด์„œ UI Toolkit์œผ๋กœ ์ด๋ฆ„์ด ๋ฐ”๋€œ

์›น ๊ฐœ๋ฐœ์˜ ๋ ˆ์ด์•„์›ƒ, ์Šคํƒ€์ผ, ๋กœ์ง ๊ฐœ๋… ๊ทธ๋Œ€๋กœ
UI Toolkit์—์„œ๋Š” ๋ ˆ์ด์•„์›ƒ - UXML, ์Šคํƒ€์ผ- USS, ๋กœ์ง - C# ์œผ๋กœ UI๋ฅผ ๊ตฌํ˜„

๋Ÿฐํƒ€์ž„ UI, ์—๋””ํ„ฐ UI ๋ชจ๋‘ ๊ตฌํ˜„ ๊ฐ€๋Šฅ

๋Ÿฐํƒ€์ž„ UI์˜ ๊ฒฝ์šฐ, UI Toolkit์œผ๋กœ ๊ตฌํ˜„๋œ UI๋Š” ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„์— ๋ Œ๋”๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์ผ๋‹จ ์›”๋“œ ์ŠคํŽ˜์ด์Šค์—๋Š” UI๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์—†๋Š” ๊ฒƒ์œผ๋กœ

๊ธฐ์กด ์—๋””ํ„ฐ UI๋“ค๋„ UI Toolkit์œผ๋กœ ๋‹ค์‹œ ๋งŒ๋“ค์–ด์ง (์•„๋งˆ 2022๋ฒ„์ „๋ถ€ํ„ฐ?)
๊ทธ๋ž˜์„œ UI Toolkit Debugger๋ฅผ ํ†ตํ•ด ๊ธฐ์กด ์—๋””ํ„ฐ์˜ UI๋ฅผ ์ง์ ‘ ๋””๋ฒ„๊น…ํ•  ์ˆ˜๋„ ์žˆ์Œ

  • ํ•„์ž๋Š” ์ปค์Šคํ…€ ์—๋””ํ„ฐ UI ๊ตฌํ˜„์„ ๋ชฉ์ ์œผ๋กœ ๊ณต๋ถ€
    • ํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ, ๋Ÿฐํƒ€์ž„ UI์™€ ์—๋””ํ„ฐ UI๋Š” ๋‹จ์ง€ ๋ณด์—ฌ์ง€๋Š” ๊ณณ์˜ ์ฐจ์ด์ผ ๋ฟ, ๊ตฌํ˜„์€ ๋˜‘๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํ•˜๋Š” ๊ฑฐ์˜€์Œ
    • ๊ทธ๋ž˜์„œ ๋Ÿฐํƒ€์ž„ UI ์šฉ์œผ๋กœ ๋งŒ๋“  ๋ ˆ์ด์•„์›ƒ, ์Šคํƒ€์ผ, ๋กœ์ง์„ ๊ทธ๋Œ€๋กœ ์—๋””ํ„ฐ UI ์— ์ ์šฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ณ , ๋ฐ˜๋Œ€๋„ ๋งˆ์ฐฌ๊ฐ€์ง€
    • ์ด๋•Œ, ๋กœ์ง์˜ ๊ฒฝ์šฐ 2023๋ถ€ํ„ฐ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”๋“ฏ?

๐Ÿ’ซ SerializedObject


Serialize๋œ ๋ฐ์ดํ„ฐ๋ฅผ Unity์—์„œ ๋‹ค๋ฃจ๊ธฐ ์‰ฝ๊ฒŒ ๊ฐ€๊ณตํ•œ ๊ฒƒ

์œ ๋‹ˆํ‹ฐ ์—๋””ํ„ฐ ์ƒ์˜ ๋ชจ๋“  ์˜ค๋ธŒ์ ํŠธ๋Š” SerializedObject๋กœ ๋ณ€ํ™˜๋˜์–ด ๋‹ค๋ฃจ์–ด์ง
UnityEngine Object/์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํŽธ์ง‘ํ•˜๋Š” ์˜์—ญ <-> SerializedObject/์—๋””ํ„ฐ๊ฐ€ ํŽธ์ง‘ํ•˜๋Š” ์˜์—ญ

UnityEngine Object๋ฅผ Asset์œผ๋กœ ๋งŒ๋“ค ๋•Œ,
UnityEngine Object๋Š” Serialized Object๋กœ ๋ณ€ํ™˜๋œ ์ดํ›„, Serialized Object์—์„œ Asset๊ณผ .metaํŒŒ์ผ ์ƒ์„ฑ

๋ฌดํŠผ Editor์—์„œ๋„ SerializedObject๋ฅผ ๋‹ค๋ฃธ

๐Ÿ’ซ SerializedProperty


SerializedObject๋ณ€์ˆ˜.FindProperty()

C#์˜ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ†ตํ•ด,
SerializedObject์—์„œ SerializedProperty์„ ์–ป์„ ์ˆ˜ ์žˆ์Œ

๐Ÿ’ซ VisualElement


UI Toolkit์˜ ๋ชจ๋“  Element๋“ค์˜ Base๊ฐ€ ๋˜๋Š” Element
VisualElement ์ž์ฒด๋Š” ์•„๋ฌด ๊ธฐ๋Šฅ์ด ์—†๊ณ , ๊ตฌ์ฒดํ™”๋œ VisualElement๋“ค์˜ ๋‹จ์ˆœ ์ปจํ…Œ์ด๋„ˆ ์šฉ์œผ๋กœ ์“ฐ์ž„

C#์œผ๋กœ ์น˜๋ฉด Object?

๋ชจ๋“  VisualElement๋Š” generateVisualContext ์ฝœ๋ฐฑ์„ ๊ฐ€์ง

๐Ÿ’ซ Property Drawer


์ปดํฌ๋„ŒํŠธ/์Šคํฌ๋ฆฝํŠธ์˜ ์†์„ฑ์ด ์ธ์ŠคํŽ™ํ„ฐ์— ๋ณด์ด๋Š” ๋ฐฉ๋ฒ•์„ ์ œ์–ด/์ปค์Šคํ…€

1
2
3
4
5
6
7
8
9
[CustomPropertyDrawer(typeof(Something))]
public class SomethingEditor : PropertyDrawer
{
	public override VisualElement CreatePropertyGUI(SerializedProperty property)
	{
		return new PropertyField(property);
		// ์œ„ ์ฝ”๋“œ๋Š” ๊ธฐ์กด ๋ชจ์–‘ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ
	}
}


๐Ÿ’ซ Custom Editor


์ปดํฌ๋„ŒํŠธ/์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ธ์ŠคํŽ™ํ„ฐ์— ๋ณด์ด๋Š” ๋ฐฉ๋ฒ•์„ ์ œ์–ด/์ปค์Šคํ…€

1
2
3
4
5
6
7
8
9
10
11
[CustomEditor(typeof(Something))]
public class SomethingEditor : Editor
{
	public override VisualElement CreateInspectorGUI()
	{
		var root = new VisualElement();
		InspectorElement.FillDefaultInspector(root, serializedObject, this);
		// ์œ„ ์ฝ”๋“œ๋Š” ๊ธฐ์กด ๋ชจ์–‘ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ
		return root;
	}
}


๐Ÿ’ซ viewDataKey


ํŠน์ • VisualElement์˜ Uniqueํ•œ ๊ฐ’
VisualElement์˜ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ ์‚ฌ์šฉ๋จ

์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด VisualElement๊ฐ€ ํฌํ•จ๋œ ์œˆ๋„์šฐ๋ฅผ ์—ด๊ฑฐ๋‚˜ ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑ = ๊ธฐ๋ณธ ์ƒํƒœ
์ง€์ •ํ•˜๋ฉด ๋งˆ์ง€๋ง‰ ์ƒํƒœ๋กœ ๋ณต๊ตฌ

i.e.

Foldout์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ ‘ํ˜€์ง„ ์ƒํƒœ
๋งŒ์•ฝ viewDataKey๋ฅผ ์ง€์ •ํ•˜๋ฉด, ๋งˆ์ง€๋ง‰์œผ๋กœ Foldout๋ฅผ ํŽผ์ณค์„ ๊ฒฝ์šฐ ๋‹ค์‹œ ์œˆ๋„์šฐ๋ฅผ ์—ด๊ฑฐ๋‚˜ ํ•  ๋•Œ ์ ‘ํ˜€์ง„ ์ƒํƒœ๋กœ ๋ณต๊ตฌ

ScrollView์˜ ๊ฒฝ์šฐ, ๋งˆ์ง€๋ง‰์œผ๋กœ ์Šคํฌ๋กคํ•œ ์œ„์น˜๋ฅผ ๋ณต๊ตฌํ•œ๋‹ค๋˜์ง€ ๋“ฑ


๐Ÿ’ซ Foldout


์ ‘๊ฑฐ๋‚˜ ํŽผ์น  ์ˆ˜ ์žˆ๋Š” ๋ฐ•์Šค

1
2
3
4
5
6
var foldout = new Foldout()
{
	viewDataKey = "*Foldout",
	text = "์ธ์ŠคํŽ™ํ„ฐ์—์„œ ๋ณด์—ฌ์งˆ Foldout Text",
	InspectorElement.FillDefaultInspector(root, serializedObject, this);
}


๐Ÿ’ซ UXML ์—ฐ๊ฒฐํ•˜๊ธฐ


1
2
3
4
5
6
7
8
9
10
public VisualTreeAsset someUXML;

// CustomEditor๋ผ๋ฉด
public override VisualElement CreateInspectorGUI()
{
	var root = new VisualElement();
	someUXML.CloneTree(root);

	// ...
}

Project ์ฐฝ์—์„œ ํ•ด๋‹น Editor ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์„ ํƒํ•˜๊ณ , UXML ํŒŒ์ผ ํ• ๋‹น

๐Ÿ’ซ ์ปค์Šคํ…€ UI Shape?


BindableElement

UxmlFactory UXML ํŒŒ์ผ์—์„œ ๋ถˆ๋Ÿฌ์˜จ ๋ฐ์ดํ„ฐ๋กœ Image ์ธ์Šคํ„ฐ์‹ฑ?

generateVisualContext ์ฝœ๋ฐฑ์— MeshGenerationContext๋ฅผ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•˜๋ฉด ๋ฉ”์‰ฌ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Œ
Unity์—์„œ ์ง€์›ํ•˜๋Š” Painter2D API ํ™œ์šฉ

๐Ÿ’ซ Editor Window


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SomethingEditor : EditorWindow
{
	[SerializeField] Something something;

	[MenuItem("SomePath/Something")]
	static void CreateMenu()
	{
		var window = GetWindow<SomethingWindow>();
		window.titleContent = new GUIContent("Complex");
	}

	public void OnEnable() { // ... }
	public void CreateGUI() { // ... }

	// https://youtu.be/J2KNj3bw0Bw?t=2519
}


๐Ÿ’ซ ์ฐธ๊ณ 


[Extending the Unity Editor with custom tools using UI ToolkitUnite 2022](https://www.youtube.com/watch?v=J2KNj3bw0Bw)

์—๋””ํ„ฐ ํ™•์žฅ ์ž…๋ฌธ - ๋ฒˆ์—ญ 5์žฅ SerializedObject์— ๋Œ€ํ•ด์„œ
์ฐธ๊ณ 
์ฐธ๊ณ 

์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.