Unclaimed Project Is this your project? Claim it to unlock full analytics and manage your listing.
Claim This Project

Unity Project

Download the source from GitHub

UI Toolkit Binding Support

Dependencies (5)

README

UI Toolkit Binding Support

Languages available in README: [ํ•œ๊ตญ์–ด (๋Œ€ํ•œ๋ฏผ๊ตญ)] [English (US)]

์†Œ๊ฐœ

์œ ๋‹ˆํ‹ฐ์˜ ์ฐจ์„ธ๋Œ€ UI ์‹œ์Šคํ…œ (์ด๋ผ๊ณ  ์ฃผ์žฅํ•˜๋Š”) UI Toolkit์—์„œ ์ปค์Šคํ…€ ํด๋ž˜์Šค์˜ SerializedProperty ๋ฐ”์ธ๋”ฉ์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ๊ณ 
์ •๋ง ํฐ ์ถฉ๊ฒฉ์„ ๋ฐ›์•„์„œ ๋งŒ๋“  ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค.


์ด ํŒจํ‚ค์ง€๋Š” ์œ ๋‹ˆํ‹ฐ๋ฅผ HarmonyX ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋Ÿฐํƒ€์ž„์— ์œ ๋‹ˆํ‹ฐ ๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ํŒจ์น˜ํ•˜์—ฌ
์œ ๋‹ˆํ‹ฐ ๋ฐ”์ธ๋”ฉ ์‹œ์Šคํ…œ์ด ์ปค์Šคํ…€ ํด๋ž˜์Šค๋ฅผ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

๋‹น์—ฐํžˆ? ์–ด๋–ป๊ฒŒ ๋ฐ”์ธ๋”ฉ ๋  ๊ฒƒ์ธ์ง€, ์›ํ•˜๋Š” ํƒ€์ž…์„ null๋กœ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ์„ค์ •ํ•˜๋Š”๊ฒƒ ๋˜ํ•œ ์ „๋ถ€ ์ปค์Šคํ…€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
ํŠน์ • ํƒ€์ž…์ด null๋กœ ํ• ๋‹น ๊ฐ€๋Šฅ์œผ๋กœ ์ธ์‹ํ•˜๊ฒŒ๋” ์„ค์ •ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ์ด์œ ๊ฐ€, Serializable Nullable ๊ฐ™์ด, null์„ ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋˜ํ•œ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.

๊นƒ์œผ๋กœ ์„ค์น˜ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค!
URL : https://github.com/Rumi727/UI-Toolkit-Binding-Support.git?path=Packages/com.rumi.custombinding

์ฐธ๊ณ  ์‚ฌํ•ญ

HarmonyX ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋Ÿฐํƒ€์ž„์—์„œ ์œ ๋‹ˆํ‹ฐ ์ฝ”๋“œ์— ๋ฐ”์ธ๋” ์ฝ”๋“œ๋ฅผ ์ง‘์–ด๋„ฃ๋Š”๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•ด์ฃผ์„ธ์š”!
์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ์œ ๋‹ˆํ‹ฐ๋ฅผ ๋ชจ๋”ฉํ•œ๊ฒ๋‹ˆ๋‹ค!
๋ถˆ์•ˆ์ •์ ์ผ ์ˆ˜ ์žˆ์œผ๋‹ˆ, ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”!

๋ฒ„๊ทธ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ผญ! ์ด์Šˆ์— ์˜ฌ๋ ค์ฃผ์„ธ์š”!

์ง€์›๋˜๋Š” ๋ฒ„์ „

Unity 2021.3 ๋ฒ„์ „ ๋ถ€ํ„ฐ ์ž‘๋™ํ•˜์ง€๋งŒ, ๋งˆ์ด๋„ˆํ•œ ๋ฒ„๊ทธ ์†Œ์ˆ˜ ์กด์žฌํ•˜์—ฌ ์ถ”์ฒœํ•˜์ง„ ์•Š์Šต๋‹ˆ๋‹ค.
๊ผญ ํ•„์š”ํ•˜๋‹ค ์‹ถ์„๋•Œ๋งŒ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”.

๊ณต์‹์ ์œผ๋ก  Unity 6 ๋ฒ„์ „ ๋ถ€ํ„ฐ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค!

์‚ฌ์šฉํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๋ฏธ๋ฆฌ๋ณด๊ธฐ

image

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

๊ธฐ๋ณธ์ ์œผ๋กœ, ObjectPropertyBinder ๋ฐ”์ธ๋”๊ฐ€ ํฌํ•จ๋˜์–ด์žˆ๊ธฐ์— ๊ทธ๋ƒฅ ํŒจํ‚ค์ง€ ์„ค์น˜๋งŒ ํ•˜์‹œ๋ฉด
์ปค์Šคํ…€ ์ปจํŠธ๋กค์— (์˜ˆ: BaseField ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•œ MyStructField ๋“ฑ) ๋ฐ”์ธ๋”ฉ์ด ์ •์ƒ์ ์œผ๋กœ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‹ˆ๊นŒ field.bindingPath = "myStruct"; ์ด๋Ÿฐ์‹์œผ๋กœ ํ‰์†Œ์ฒ˜๋Ÿผ Vector3Field, ObjectField ๊ฐ™์€ ๋‚ด์žฅ ์ปจํŠธ๋กค์— ๋ฐ”์ธ๋”ฉํ•˜๋“ฏ์ด ํ•˜๋ฉด ๋ชจ๋“ ๊ฒŒ ์•Œ์•„์„œ ๋ ๊ฑฐ์˜ˆ์š”!

ํ•˜์ง€๋งŒ, SerializedProperty.boxedValue ์†์„ฑ์ด ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ฑฐ๋‚˜ (๋งค์šฐ ํŠน์ดํ•œ ์ผ€์ด์Šค์ด๊ธด ํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ์ด๊ฑธ ์ถฉ๋Œ๋กœ ๊ฒฝํ—˜ํ•ด๋ดค์–ด์š”...),
Serializable Nullable ๊ฐ™์ด ๋ฐ”์ธ๋”๋ฅผ ์ปค์Šคํ…€ํ•ด์•ผํ•  ์ƒํ™ฉ๋„ ์žˆ์œผ์‹ค๊ฑฐ์˜ˆ์š”.
(์•„, ๊ฒŒ๋‹ค๊ฐ€ ์ด๊ฑฐ ๋งŒ๋“ค๋ฉด์„œ ์ฒ˜์Œ ์•Œ์•˜๋Š”๋ฐ SerializedProperty.boxedValue๋Š” 2022.1๋ถ€ํ„ฐ ์žˆ๋”๋ผ๊ณ ์š”?, ๊ทธ ์ „์—” ์–ด๋–ป๊ฒŒ ๊ฐ’ ์–ป์œผ๋ผ๋Š”๊ฑฐ์˜€์ง€...)

์ž, ์ด๋Ÿฐ MyStruct ๊ตฌ์กฐ์ฒด๊ฐ€ ํ•˜๋‚˜ ์žˆ๋‹ค๊ณ  ํ•ด๋ณผ๊ฒŒ์š”.

#nullable enable
using System;

[Serializable]
public struct MyStruct
{
    public string? name;
    public float value;
}

๊ทธ๋Ÿฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”์ธ๋”๋ฅผ ์งœ๋ฉด ๋ฉ๋‹ˆ๋‹ค. PropertyDrawer ์ด๊ฑฐ๋ž‘ ๋น„์Šทํ•˜์ฃ ?

#nullable enable
using Rumi.CustomBinding.Editor;
using System;
using UnityEditor;
using UnityEngine.UIElements;

[CustomPropertyBinder(typeof(MyStruct))] // CustomPropertyBinder ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๊ฐ€ ์žˆ์–ด์•ผ ๋ฐ”์ธ๋”๋กœ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค!
public class MyStructPropertyBinder : PropertyBinder // ๋ฌผ๋ก  PropertyBinder ์ด๊ฒƒ๋„ ๊ฐ™์ด ์ƒ์†ํ•ด์•ผํ•ด์š”!
{
    // SerializedProperty์—์„œ ๊ฐ’์„ ์ฝ์–ด์„œ MyStruct๋กœ ์—ญ์ง๋ ฌํ™” ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
    public override object Read(VisualElement element, SerializedProperty property, Type propertyType)
    {
        property.Next(true); // name
        string name = property.stringValue;
        
        property.Next(false); // value
        float value = property.floatValue;

        return new MyStruct { name = name, value = value };
    }
    
    // MyStruct์—์„œ ๊ฐ’์„ ์ฝ์–ด์„œ SerializedProperty์— ์ง๋ ฌํ™” ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
    public override void Write(VisualElement element, SerializedProperty property, Type propertyType, object? value)
    {
        if (value is MyStruct myStruct)
        {
            property.Next(true); // name
            property.stringValue = myStruct.name;
            
            property.Next(false); // value
            property.floatValue = myStruct.value;
        }
    }
}

์ž ๊น! ์ค‘์š”ํ•œ๊ฒŒ ์žˆ์–ด์š”!
๊ธฐ๋ณธ์ ์œผ๋กœ PropertyDrawer๋ž‘์€ ๋‹ค๋ฅด๊ฒŒ ์ •ํ•œ ํƒ€์ž…๊ณผ (์—ฌ๊ธฐ์„  MyStruct) ๋ฐ”์ธ๋”ฉํ•  ํ”„๋กœํผํ‹ฐ์˜ ํƒ€์ž…์ด ์„œ๋กœ ๋™์ผํ•œ ํƒ€์ž…๋งŒ ๋ฐ”์ธ๋”๊ฐ€ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค!

ํ•˜์ง€๋งŒ, CustomPropertyBinder ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๋Š” ์ˆจ๊ฒจ์ง„ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํ•˜๋‚˜ ๋” ์žˆ๋Š”๋ฐ,
๋ฐ”๋กœ isSubtypeCompatible ์ž…๋‹ˆ๋‹ค!

[CustomPropertyBinder(typeof(MyStruct), true)] ์ด๋Ÿฐ์‹์œผ๋กœ isSubtypeCompatible ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ true๋กœ ์„ค์ •ํ•ด์ฃผ๋ฉด,
๊ทธ ๋ฐ”์ธ๋”๋Š” MyStruct์˜ ํ•˜์œ„ ํƒ€์ž…๋„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ”์ธ๋”๋กœ ์ธ์‹ํ•ด์š”! (๊ตฌ์กฐ์ฒด๊ฐ€ ์–ด๋–ป๊ฒŒ ํ•˜์œ„ ํƒ€์ž…์ด ์žˆ๋Š”๊ฑฐ์ง€?)
์ด๋•Œ๋Š”, PropertyDrawer์™€ ์™„์ „ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค! (์ธํ„ฐํŽ˜์ด์Šค, ์ œ๋„ค๋ฆญ ํƒ€์ž… ์ •์˜ (์˜ˆ: List<>) ๋“ฑ๋„ ์˜ฌ๋ฐ”๋ฅธ ๋ฐ”์ธ๋”๋กœ ์ธ์‹ํ•จ)

ํ•˜์ง€๋งŒ! ๊ทธ๋ ‡๊ธฐ์— ํ•˜์œ„ ํƒ€์ž…๊ณผ ํ˜ธํ™˜๋˜๋Š” ๋ฐ”์ธ๋”๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ๋” ์กฐ์‹ฌํ•˜์…”์•ผํ•ด์š”!
๋‹น์—ฐํ•œ ์†Œ๋ฆฌ์ด๊ธด ํ•˜์ง€๋งŒ, ๋ฐ”์ธ๋”์˜ ๋ฐ˜ํ™˜ ๊ฐ’์ด object๋ผ๊ณ  ์•„๋ฌด ์˜ค๋ธŒ์ ํŠธ๋‚˜ ๋ฐ˜ํ™˜ํ•ด๋„ ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ,
ํ•ญ์ƒ ๋ฐ”์ธ๋”ฉ ํ•  ํ”„๋กœํผํ‹ฐ์˜ ํƒ€์ž…์— ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํƒ€์ž…์ด์—ฌ์•ผํ•ฉ๋‹ˆ๋‹ค! (์—ฌ๊ธฐ์„  MyStruct ๋˜๋Š” MyStruct๋ฅผ ์ƒ์†(??)ํ•˜๊ณ  ์žˆ๋Š” ํƒ€์ž…)

์•„๋ž˜๋Š” ํ•˜์œ„ ํƒ€์ž…๊ณผ ํ˜ธํ™˜๋˜๋Š” ๋ฐ”์ธ๋”์˜ ์˜ˆ์‹œ์˜ˆ์š”!

#nullable enable
using System;

[Serializable]
public class MyClass
{
    public string name = string.Empty;
    public float value;
}
#nullable enable
using Rumi.CustomBinding.Editor;
using System;
using UnityEditor;
using UnityEngine.UIElements;

[CustomPropertyBinder(typeof(MyParent), true)]
public class MyParentPropertyBinder : PropertyBinder
{
    public override object Read(VisualElement element, SerializedProperty property, Type propertyType) => property.boxedValue;
    public override void Write(VisualElement element, SerializedProperty property, Type propertyType, object? value) => property.boxedValue = value;
}

์•„! ๋ฐ”์ธ๋”ฉ ๊ฐ€๋Šฅํ•œ ํƒ€์ž…์ด ๋˜๋ ค๋ฉด MyStruct, MyParent ์ฒ˜๋Ÿผ ๊ผญ ๋งค๊ฐœ๋ณ€์ˆ˜ ์—†๋Š” public ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์–ด์•ผํ•˜๋‹ˆ๊นŒ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”!

Nullable ํƒ€์ž…์„ ๋“ฑ๋กํ•˜๊ณ  ์‹ถ์–ด์š”!

์šฐ์„ , NullableType.isNullable ๋”œ๋ฆฌ๊ฒŒ์ดํŠธ์— ์›ํ•˜๋Š” ํƒ€์ž…์ด Nullable์ด๋ผ๊ณ  ์ธ์‹ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋“ฑ๋กํ•ด์ฃผ์…”์•ผํ•ด์š”!
NullableType.isNullable += x => Nullable.GetUnderlyingType(x) != null;
์ด๋Ÿฐ์‹์œผ๋กœ ๋ฐ˜ํ™˜๊ฐ’์ด true์ด๋ฉด ํ•ด๋‹นํ•˜๋Š” ํƒ€์ž…์€ Nullable ์ฒ˜๋Ÿผ ์ทจ๊ธ‰ํ•ด์š”!

๊ทธ๋ฆฌ๊ณ , Nullable์˜ ๊ธฐ๋ณธ ํƒ€์ž…์„ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ NullableType.getNullableUnderlyingType ๋˜ํ•œ ๋“ฑ๋กํ•ด์ฃผ์…”์•ผํ•ด์š”!
NullableType.getNullableUnderlyingType += static x => Nullable.GetUnderlyingType(x);
์ด๋Ÿฐ์‹์œผ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋“ค์–ด์˜จ ํƒ€์ž…์˜ ๊ธฐ๋ณธ ํƒ€์ž…์ด ์žˆ์œผ๋ฉด ๊ทธ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•ด์ฃผ์‹œ๋ฉด ๋˜๊ณ , Nullable ํƒ€์ž…์ด ์•„๋‹ˆ๋ผ์„œ ๊ธฐ๋ณธ ํƒ€์ž…์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋ฉด ๊ผญ null์„ ๋ฐ˜ํ™˜ํ•ด์ฃผ์„ธ์š”!

๋˜ํ•œ ์ฃผ์˜ํ•˜์‹ค์ ์ด ์žˆ์–ด์š”!
Nullable๋กœ ํ‘œ์‹œ๋œ ํƒ€์ž…์€ ๊ผญ๊ผญ ๊ธฐ๋ณธ ํƒ€์ž…์œผ๋กœ ์•”์‹œ์  ํ˜•๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค
์•ˆ๊ทธ๋Ÿฌ๋ฉด ์บ์ŠคํŒ… ์˜ˆ์™ธ๋‚˜์š”!!

Comments

No comments yet. Be the first!