UIELements로 개발할 때의 문제점과 해결

13699 단어 UIElementsUnityC#
PONOS Advent Calendar 2019의 13일째 보도.
어제는 @nimitsu선생님의 Game Lift RealTime Server 같이 놀아요 for Unity(Unity 편)입니다.

입문


Unity 2019.1이 드디어 UIELements를 공식 발표했다.
앞으로 실행 시 UI에 대한 채택도 결정되고 Unity 전체의 UI 프레임워크로 활용될 것으로 생각하지만 아직 사용하기가 어려워서 유감입니다.
이번에는 제가 UIELements를 접하고 불편한 점을 공유하고 여러분의 UIELements 개발이 조금이나마 개선될 수 있도록 이 문제들의 해결 방법을 소개하고 싶습니다.
※ 앞으로 소개할 문제점에 대해서는 다른 해결책을 알고 계신 분이 있다면 댓글로 남겨주세요!
※ 소개 이외에 어려운 점이 있으면 꼭 댓글로 남겨주세요.같이 해결하자!
따로
  • Unity 2019.2.12f1
  • MacOS 10.14.6
  • 큰 장면을 렌더링하는 동안 이 고장이 발견되었습니다.

    UXML 다시 로드 허용


    문제


    공식 코드와 Assets/Create/UIElements Editor Window 메뉴에서 만든 코드에는 Editor Window의 On Enable () 에서 UXML과 USS를 읽어서 반영한다고 기술되어 있다.
    Editor Windows에 초점을 맞출 때마다 레이아웃을 다시 읽을 필요가 없습니다. Editor Windows를 열거나 컴파일해서 실행할 때 이 처리를 실행하는 것은 낭비가 없고 효율적이라고 생각합니다.
    그러나 개발 과정에서 UXML 편집이 빈번하게 이루어지면서 컴파일이 실행되는 시기와 다른 시기에 UXML을 다시 읽으려는 경우도 많다.
    물론 Editor Window를 다시 열 때마다 UXML을 다시 읽을 수 있지만 부동 창이라면 별론이다. 다른 창과 연결해서 사용할 창이라면 Editor Window를 다시 여는 것은 상당히 힘들다.
    (또한 USS는 실시간으로 변경 사항을 반영합니다)

    해결됨


    이제 EditorWindow 메뉴에서 UXML을 다시 로드할 수 있습니다.
    Editor Windows에서 IHAS Custom Menu를 구현하여 다시 로드할 메뉴 항목을 등록합니다.
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using UnityEngine.UIElements;
    
    public class ReloadableEditorWindow : EditorWindow, IHasCustomMenu
    {
        public void AddItemsToMenu(GenericMenu menu)
        {
            // メニューアイテムを登録。
            menu.AddItem(new GUIContent("Reload"), false, () =>
            {
                ReloadUxml();
            });
        }
    
        [MenuItem("Window/UIElements/ReloadableEditorWindow")]
        static void Open()
        {
            ReloadableEditorWindow wnd = GetWindow<ReloadableEditorWindow>();
            wnd.titleContent = new GUIContent("ReloadableEditorWindow");
        }
    
        public void OnEnable()
        {
            ReloadUxml();
        }
    
        void ReloadUxml()
        {
            // 一度ルートに紐付けられた要素を全て削除する。
            rootVisualElement.Clear();
    
            // UXMLの読み込み。
            var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("...");
            VisualElement labelFromUXML = visualTree.CloneTree();
            rootVisualElement.Add(labelFromUXML);
    
            // その他、レイアウトに関する各種処理。
        }
    }
    
    
    이런 느낌으로 선택하고 실행할 수 있습니다.

    UXML을 편집한 후 현재 열려 있는 Editor Windows에서 UXML을 다시 로드할 수 있습니다.

    UXML/USS 경로를 쉽게 검색


    문제


    현재 UXML과 USS는 Assets/부터 시작된 리소스 경로를 AssetDatabase.LoadAssetAtPath<T>() 에 전달하고 각각 VisualTreeAsset 및 StyleSheet 객체로 로드합니다.
    이것은 현재 완벽한 읽기 메커니즘이 없기 때문일 수 있지만 원본 파일의 경로를 하드코딩했기 때문에 파일 위치가 변경될 때 지원하기 어렵고 창설된 UIELements를 모듈화하여 다른 사람에게 나누어 줄 때도 불편하다.
    var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/Sample/SampleWindow.uxml");
    var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Editor/Sample/SampleWindow.uss");
    

    해결됨


    UXML 및 USS 경로를 동적으로 가져옵니다.
    AssetDatabase에서 지정된 파일 이름을 가진 파일을 찾아 리소스 경로로 되돌아오는 방법을 만듭니다.
    파일 이름에서 읽어들인 것이기 때문에 나중에 항목의 차원이 바뀌어도 파일 이름과 클래스 이름이 변경되지 않으면 계속 참조할 수 있습니다.
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using System.Linq;
    using System.IO;
    
    public static class UIElementsPath
    {
        public static string FindUxml(string name)
        {
            return Find(name, "uxml");
        }
    
        public static string FindUss(string name)
        {
            return Find(name, "uss");
        }
    
        static string Find(string name, string extension)
        {
            var assetHashs = AssetDatabase.FindAssets(name);
            var results = assetHashs
                .Select(hash => AssetDatabase.GUIDToAssetPath(Path.GetFileNameWithoutExtension(hash)))
                .Where(assetPath => Path.GetFileNameWithoutExtension(assetPath) == name)
                .Where(assetPath => Path.GetExtension(assetPath) == "." + extension);
    
            // 同じ名前のUIElementsが複数存在する(名前空間が違う等)場合の警告。
            if (1 < results.Count())
            {
                Debug.LogWarning($"\"{name}\"で{results.Count()}件の結果が見つかりました。\n\n{results.Aggregate((a, b) => a + "\n" + b)}");
            }
    
            return results.FirstOrDefault();
        }
    }
    
    이 방법을 사용하면 방금 코드를 다음과 같이 다시 쓸 수 있습니다.
    var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(UIElementsPath.FindUxml("SampleWindow"));
    var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>((UIElementsPath.FindUss("SampleWindow"));
    

    끝내다


    UIELements는 아직 시간이 좀 더 걸려야 효과적으로 개발을 진행할 수 있지만, 처음에 말한 바와 같이 미래의 Unity의 UI는 이 프레임워크를 이용하여 개발할 것이기 때문에 가능한 한 빨리 접촉하는 것을 권장합니다.
    내일은 @karizumai 선생님!

    좋은 웹페이지 즐겨찾기