Version: Unity 6.0 (6000.0)
언어 : 한국어
요소 관리를 위한 베스트 프랙티스
시각적 요소 레퍼런스

로직으로 UXML 문서 캡슐화

프리팹은 씬에서 여러 번 인스턴스화할 수 있는 사전 제작된 게임 오브젝트입니다. 프리팹은 재사용 가능한 컴포넌트를 생성하는 데 유용합니다. UI 툴킷의 시각적 요소는 게임 오브젝트가 아니므로 프리팹은 적용되지 않습니다. 하지만 로직으로 요소의 특정 계층 구조를 캡슐화하는 재사용 가능한__ UI__(사용자 인터페이스) 사용자가 애플리케이션과 상호 작용하도록 해 줍니다. Unity는 현재 3개의 UI 시스템을 지원합니다. 자세한 정보
See in Glossary
컴포넌트로 커스텀 컨트롤을 생성할 수 있습니다. UI 툴킷은 게임이나 애플리케이션 코드에서 UI를 분리하도록 합니다. 따라서 UXML을 사용하여 구조를 정의하고, USS를 사용하여 모양을 정의하고, C#을 사용하여 커스텀 컨트롤의 로직을 정의할 수 있습니다.

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

예를 들어 카드 게임을 만들고 싶다고 가정해 보겠습니다. 생명력, 공격력과 같은 다양한 통계가 포함된 카드를 표시하려고 합니다.

카드 예시
카드 예시

캐릭터의 이미지, 생명력, 공격력 통계를 표시하는 CardElement라는 커스텀 컨트롤을 생성한 다음, 게임의 각 카드에 이 커스텀 컨트롤을 재사용할 수 있습니다.

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

  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.
[UxmlElement]
public partial class CardElement : VisualElement
{

    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)를 생성합니다. 이 예시에서는 CardElement의 스타일을 USS 파일로 지정합니다.

<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 문서를 렌더링하는 방법에 대한 내용은 게임 뷰에서 UI 렌더링을 참조하십시오.

C#에서 직접 인스턴스화

참고: 이 페이지에 있는 학습 목적의 예시 코드는 편리하게 UXML 파일을 로드하기 위해 Resources 폴더 방식을 사용합니다. 하지만 이 방법은 잘 확장되지 않습니다. 프로덕션 프로젝트에 대한 레퍼런스를 로드하려면 다른 방법을 사용하는 것이 좋습니다.

다음은 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.
[UxmlElement]
public partial class CardElement : VisualElement
{
    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)를 생성합니다. 이 예시에서는 CardElement의 스타일을 USS 파일로 지정합니다.

<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 문서를 렌더링하는 방법에 대한 내용은 게임 뷰에서 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 섹션에 액세스할 수 있는 메인 타이틀 화면을 빌드하려면 3개의 다른 자식 UXML 문서를 사용하여 TitleScreenManager 요소를 생성하면 됩니다. 각각은 고유한 Title, Options, About 요소를 정의합니다.

메인 타이틀 화면 예시
메인 타이틀 화면 예시

추가 리소스

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