Version: 2023.1
언어: 한국어
요소 관리를 위한 베스트 프랙티스
UXML 요소 레퍼런스

로직으로 UXML 문서 캡슐화

프리팹은 씬에서 여러 번 인스턴스화할 수 있는 미리 만들어진 게임 오브젝트입니다.프리팹은 재사용 가능한 컴포넌트 생성에 유용합니다.UI 툴킷의 시각적 요소는 게임 오브젝트가 아니므로 프리팹은 적용되지 않습니다.그러나 로직으로 요소의 특정 계층 구조를 캡슐화하는 재사용 가능한 UI 컴포넌트로 커스텀 컨트롤을 생성할 수 있습니다.UI 툴킷은 게임 또는 애플리케이션 코드에서 UI를 분리하도록 권장하므로 UXML을 사용하여 구조를 정의하고, USS를 사용하여 겉모습을 정의하고, C#을 사용하여 커스텀 컨트롤의 로직을 정의할 수 있습니다.

재사용 가능한 UI 컴포넌트 생성

아래와 같이 캐릭터의 이미지와 생명력 및 공격 통계를 표시하는 카드에 CardElement라는 커스텀 컨트롤과 같은 재사용 가능한 UI 컴포넌트를 생성할 수 있습니다.

예시 카드
예시 카드

다음은 이를 수행하기 위한 일반적인 단계입니다.

  1. C#에서 CardElement라는 커스텀 요소 타입을 선언합니다.

  2. UXML에서 커스텀 컨트롤의 계층 구조를 정의합니다.두 가지 방식을 사용할 수 있습니다.두 방법 모두 C#과 부모 UXML에서 CardElement 인스턴스화를 지원합니다.

  3. 커스텀 컨트롤의 자식 요소에 대한 레퍼런스를 찾습니다.

  4. 프로퍼티와 메서드를 노출하고 C# 클래스에서와 동일한 방식으로 커스텀 컨트롤에 로직을 캡슐화합니다.

  5. 커스텀 컨트롤을 게임이나 애플리케이션 코드와 연결합니다.또한 이벤트 콜백을 등록하여 사용자 상호 작용을 구현할 수도 있습니다.

UXML 우선 방식

이 방식을 사용하면 커스텀 요소 CardElement를 계층 구조 UXML 문서에 포함시키고 그 바로 아래에 자식 요소를 선언한 다음 계층 구조 UXML 문서를 템플릿으로 사용합니다.이 방식은 계층 구조 UXML 문서 내에 고정된 UI 구조를 포함하는 더 간단한 솔루션을 제공합니다.

다음 C# 및 UXML 예시에서는 UXML 우선 방식을 사용하여 재사용 가능한 UI를 생성하는 방법을 보여줍니다.

커스텀 컨트롤 클래스 생성

CardElement 커스텀 컨트롤을 정의하는 C# 스크립트를 생성합니다.커스텀 컨트롤 클래스는 이미지 및 배지 값을 CardElement에 할당합니다.

using UnityEngine;
using UnityEngine.UIElements;

// Define the custom control type.
public class CardElement :VisualElement
{
    // Expose the custom control to UXML and UI Builder.
    public new class UxmlFactory :UxmlFactory<CardElement> {}

    private VisualElement portraitImage => this.Q("image");
    private Label attackBadge => this.Q<Label>("attack-badge");
    private Label healthBadge => this.Q<Label>("health-badge");

    // Use the Init() approach instead of a constructor because 
    // we don't have children yet.
    public void Init(Texture2D image, int health, int attack)
    {
        portraitImage.style.backgroundImage = image;
        attackBadge.text = health;
        healthBadge.text = attack;
    }

    // Custom controls need a default constructor.
    public CardElement() {}
}

커스텀 컨트롤의 계층 구조 정의

CardElement의 계층 구조를 정의하는 UXML 문서(CardElement.uxml)를 생성합니다.이 예시에서는 USS 파일로 CardElement의 스타일을 지정합니다.

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="CardElementUI.uss" />
    <CardElement> 
        <ui:VisualElement name="image" />
        <ui:VisualElement name="stats">
            <ui:Label name="attack-badge" class="badge" />
            <ui:Label name="health-badge" class="badge" />
        </ui:VisualElement>
    </CardElement> 
</ui:UXML>

커스텀 컨트롤을 게임에 연결

다음을 통해 커스텀 컨트롤을 게임에 연결할 수 있습니다.

  • 부모 UXML 문서 내에 CardElement.uxml을 인스턴스화합니다.UI 빌더에서 계층 구조 UXML과 이 UXML 문서 간에 앞뒤로 탐색할 수 있습니다.
  • MonoBehaviour C# 스크립트에서 CardElement를 포함하는 CardElement.uxml을 인스턴스화합니다.씬에 추가하기 전에 UQuery를 사용하여 CardElement를 찾아야 합니다.

커스텀 컨트롤을 씬에 추가한 후 Init()를 호출합니다.

UXML 우선 방식의 워크플로
UXML 우선 방식의 워크플로

또한 요소와 상호 작용하는 클릭 이벤트와 같은 게임플레이 관련 행동도 추가할 수 있습니다.

부모 UXML 내에 인스턴스화

다음은 UXML에서의 인스턴스화 예시입니다.

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <ui:Template name="CardElement" src="CardElement.uxml"/>
    <ui:Instance template="CardElement"/>
    <ui:Instance template="CardElement"/>
    <ui:Instance template="CardElement"/>
</ui:UXML>

게임에서 UXML 문서를 렌더링하는 방법에 대한 내용은 게임(Game) 뷰의 렌더 UI를 참조하십시오.

C#에서 직접 인스턴스화

참고:학습하기 위해 이 페이지의 예시 코드에서는 리소스 폴더 메서드를 사용하여 편리하게 UXML 파일을 로드합니다.그러나 이 메서드는 확장성이 좋지 않습니다.기타 메서드를 사용하여 프로덕션 프로젝트에 대한 레퍼런스를 로드하는 것이 좋습니다.

다음은 C#에서의 인스턴스화 예시입니다.

using UnityEngine;
using UnityEngine.UIElements;

public class UIManager :MonoBehaviour
{
    public void Start()
    {
        UIDocument document = GetComponent<UIDocument>();

        // Load the UXML document that defines the hierarchy of CardElement.
        // It assumes the UXML file is placed at the "Resources" folder.
        VisualTreeAsset template = Resources.Load<VisualTreeAsset>("CardElement");

        // Create a loop to modify properties and perform interactions 
        // for each card.It assumes that you have created a function 
        // called `GetCards()` to get all the cards in your game.
        foreach(Card card in GetCards())
        {
            // Instantiate a template container.
            var templateContainer = template.Instantiate();

            // Find the custom element inside the template container.
            var cardElement = templateContainer.Q<CardElement>();

            // Add the custom element into the scene.
            document.rootVisualElement.Add(cardElement);

            // Initialize the card.
            cardElement.Init(card.image, card.health, card.attack);

            // Register an event callback for additional interaction.
            cardElement.RegisterCallback<ClickEvent>(SomeInteraction);
        }
    }

    private void SomeInteraction(ClickEvent evt)
    {
        // Interact with the elements here.
    }
}

요소 우선 방식

이 방식을 사용하면 계층 구조 UXML 문서에 자식 요소만 포함시키고 C#을 사용하여 CardElement 클래스 정의에 계층 구조 UXML 문서를 로드합니다.이 방식은 커스텀 컨트롤에 대해 유연한 UI 구조를 제공합니다.예를 들어 특정 조건에 따라 다른 계층 구조 UXML 문서를 로드할 수 있습니다.

다음 C# 및 UXML 예시에서는 요소 우선 방식을 사용하여 재사용 가능한 UI를 생성하는 방법을 보여줍니다.

커스텀 컨트롤 클래스 생성

CardElement 커스텀 컨트롤을 정의하는 C# 스크립트를 생성합니다.커스텀 컨트롤은 이미지 및 배지 값을 CardElement에 할당하는 생성자 정의 외에도 클래스 정의에서 계층 구조 UXML 문서를 로드합니다.

using UnityEngine;
using UnityEngine.UIElements;

// Define the custom control type.
public class CardElement :VisualElement
{
    // Expose the custom control to UXML and UI Builder.
    public new class UxmlFactory :UxmlFactory<CardElementA> {}

    private VisualElement portraitImage => this.Q("image");
    private Label attackBadge => this.Q<Label>("attack-badge");
    private Label healthBadge => this.Q<Label>("health-badge");


    // Custom controls need a default constructor.This default constructor 
    // calls the other constructor in this class.
    public CardElement() {}

    // Define a constructor that loads the UXML document that defines 
    // the hierarchy of CardElement and assigns an image and badge values.
    public CardElement(Texture2D image, int health, int attack)
    {
        // It assumes the UXML file is called "CardElement.uxml" and 
        // is placed at the "Resources" folder.
        var asset = Resources.Load<VisualTreeAsset>("CardElement");
        asset.CloneTree(this);

        portraitImage.style.backgroundImage = image;
        attackBadge.text = health.ToString();
        healthBadge.text = attack.ToString();
    }
}

참고:성능에 문제가 있는 경우 지연 초기화를 사용해 필드를 유지하여 참조를 캐시하고 쿼리를 너무 자주 다시 평가하지 않도록 하십시오.

커스텀 컨트롤의 계층 구조 정의

CardElement 자식 요소의 계층 구조를 정의하는 UXML 문서(CardElement.uxml)를 생성합니다.예시에서는 USS 파일로 CardElement의 스타일을 지정합니다.

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="CardElementUI.uss" /> 
    <ui:VisualElement name="image" />
    <ui:VisualElement name="stats">
        <ui:Label name="attack-badge" class="badge" />
        <ui:Label name="health-badge" class="badge" />
    </ui:VisualElement>
</ui:UXML>

커스텀 컨트롤을 게임에 연결

다음을 수행하여 커스텀 컨트롤을 게임에 연결할 수 있습니다.

  • 부모 UXML 문서 내에 CardElement.uxml을 인스턴스화합니다.UI 빌더에서는 자식 요소가 C#에서 로드되기 때문에 계층 구조 UXML 간에 앞뒤로 탐색할 수 없습니다.
  • MonoBehaviour C# 스크립트에서 CardElement를 포함하는 CardElement.uxml을 인스턴스화합니다.

커스텀 컨트롤을 씬에 추가하기 전에 생성자를 호출합니다.

요소 우선 방식의 워크플로
요소 우선 방식의 워크플로

또한 요소와 상호 작용하는 클릭 이벤트와 같은 게임플레이 관련 행동도 추가할 수 있습니다.

부모 UXML 내에 인스턴스화

다음은 UXML에서의 인스턴스화 예시입니다.

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
   <CardElement />
   <CardElement />
   <CardElement />
</ui:UXML>

게임에서 UXML 문서를 렌더링하는 방법에 대한 내용은 게임(Game) 뷰의 렌더 UI를 참조하십시오.

C#에서 직접 인스턴스화

다음은 C#에서의 인스턴스화 예시입니다.

using UnityEngine;
using UnityEngine.UIElements;

public class UIManager :MonoBehaviour
{
    public void Start()
    {
        UIDocument document = GetComponent<UIDocument>();

        // Create a loop to modify properties and perform interactions 
        // for each card.It assumes that you have created a function 
        // called `GetCards()` to get all the cards in your game.
        foreach(Card card in GetCards())
        {
            var cardElement = new CardElement(card.image, card.health, card.attack);

            // Register an event callback for additional interaction.
            cardElement.RegisterCallback<ClickEvent>(SomeInteraction);

            // Add the custom element into the scene.
            document.rootVisualElement.Add(cardElement);
        }
    }

    private void SomeInteraction(ClickEvent evt)
    {
        // Interact with the elements here.
    }
}

더 복잡한 요소 빌드

프로젝트의 UI가 복잡해질 수록 로직을 더 높은 레벨의 컴포넌트로 분리하는 것이 좋습니다.이렇게 하면 나머지 게임이나 애플리케이션에서 UI를 더 쉽게 오케스트레이션할 수 있습니다.

이 페이지의 개념을 적용하여 더 작고 일반적인 컴포넌트 이상의 특수 컴포넌트를 점진적으로 빌드할 수 있습니다.예를 들어 사용자가 Options 메뉴와 About 섹션에 액세스할 수 있는 기본 타이틀 화면을 빌드하려면 세 개의 서로 다른 자식 UXML 문서가 포함된 TitleScreenManager 요소를 생성하면 됩니다.각각 고유한 요소(Title, Options, About)를 정의합니다.

An example main title screen
An example main title screen

추가 리소스

요소 관리를 위한 베스트 프랙티스
UXML 요소 레퍼런스