이 페이지는 UI 툴킷을 사용하여 간단한 캐릭터 선택 화면을 설정하는 방법을 단계별로 설명합니다. 또한 UI 요소 및 템플릿을 만드는 방법, 씬을 설정하는 방법 및 UI에 스크립팅 로직을 연결하는 방법도 다룹니다. 이 가이드는 USS를 통한 스타일링은 다루지 않으며, 기본 스타일과 테마만 사용합니다.
이 가이드의 최종 소스 코드는 이 페이지 하단에서 확인할 수 있습니다.
다루는 주제: UI 빌더, ListView, 레이블, PanelSettings, UIDocument, 선택 처리
이 가이드는 다음 단계를 설명합니다.
최종 UI 화면은 두 개의 개별 UI 템플릿(UXML)로 구성됩니다. 메인 뷰 템플릿은 캐릭터 이름이 나열된 리스트, 선택한 캐릭터의 세부 사항이 표시되는 보다 작은 패널 및 버튼 하나를 포함합니다. 이 섹션에서는 UI 빌더를 사용하여 이 템플릿을 설정합니다.
참고: UI 빌더에 익숙하며 이 단계를 건너뛰고 싶다면 이 페이지 맨 아래에서 메인 뷰를 위한 UXML 코드를 복사하여 새 파일에 바로 붙여넣고 Assets/UI/MainView.uxml로 저장하십시오.
Window > UI Toolkit > UI Builder 메뉴에서 UI 빌더 창을 여십시오. 뷰포트 왼쪽 상단에 있는 파일 메뉴를 사용하여 새 UXML 문서를 만드십시오.
게임 UI를 개발할 때는 항상 UI 빌더 뷰포트 오른쪽 상단에 있는 Unity Default Runtime Theme
을 선택하십시오. 에디터 및 런타임 테마의 기본 폰트 크기와 컬러는 서로 다르며, 이는 레이아웃에 영향을 미칩니다.
계층 구조에서 새 UXML 파일을 선택하고 Match Game View 체크박스를 활성화하십시오. 이미 설정되어 있지 않은 경우 Unity 에디터를 가로 해상도로 설정해야 할 수 있습니다.
이제 UI 요소를 만들어야 합니다. Library에서 계층 구조로 새 VisualElement를 드래그하여 생성하십시오.
새 요소는 화면 전체를 커버해야 하므로, flex-grow
프로퍼티를 1로 설정해야 합니다. 계층 구조에서 요소를 선택하고 오른쪽 인스펙터 패널에서 Flex라는 레이블이 있는 폴드아웃을 찾으십시오. Grow의 값을 0에서 1로 변경하십시오.
이 VisualElement의 모든 자식을 화면 중앙에 정렬하려면 VisualElement의 Align 프로퍼티를 변경하십시오. Align Items와 Justify Content를 모두 center로 설정해야 합니다.
마지막으로 Background > Color에서 배경색을 선택할 수 있습니다. 이 단계는 선택 사항입니다. 이 예에서는 #732526
을 컬러로 사용했습니다.
다음으로 기존 VisualElement 아래에 새 VisualElement를 만드십시오. 이는 UI의 왼쪽 및 오른쪽 섹션을 위한 부모 컨테이너가 됩니다.
이 새 요소의 flex-direction
프로퍼티를 row
로 설정하십시오(기본값: column). 또한 픽셀 높이를 350픽셀로 고정해야 합니다.
현재 UI는 이렇게 표시되어야 합니다. 화면은 게임 뷰의 해상도와 종횡비에 따라 다르게 보일 수 있습니다.
캐릭터 이름의 리스트를 만들려면 Library에서 ListView 컨트롤을 선택하고 방금 만든 VisualElement 아래에 자식으로 추가하십시오. 요소를 선택하고 인스펙터의 CharacterList
이름을 할당하십시오. 이 작업을 수행해야 추후 컨트롤러 스크립트를 통해 이 리스트에 액세스할 수 있습니다.
리스트의 너비를 230픽셀로 고정하십시오. 또한 다음으로 만들 요소와 어느 정도 간격을 둘 수 있도록 오른쪽에 6픽셀 너비의 마진을 설정하십시오.
리스트에 배경색을 할당하고 모서리가 둥근 테두리를 설정할 수도 있습니다. 이 가이드에서는 배경색을 #6E3925
로, 테두리 컬러를 #311A11
로 설정했으며, 테두리 너비는 4픽셀로, 반지름은 15픽셀로 설정했습니다. 이 단계는 선택 사항입니다.
CharacterList
와 같은 부모 아래에 새 VisualElement를 추가하십시오. 이 VisualElement는 캐릭터 세부 사항 패널과 버튼을 포함합니다. Align 폴드아웃 아래에서 Align Items 설정을 flex-end
로, Justify Content를 space-between
으로 변경하십시오.
이 새 컨테이너에 새로운 VisualElement를 추가하십시오. 이 VisualElement는 캐릭터 세부 사항 패널이 됩니다. 사용자가 왼쪽 리스트에서 캐릭터를 선택하면 이 패널은 캐릭터의 초상, 이름과 클래스를 표시합니다.
요소의 너비를 276픽셀로 고정하고, Align Items와 Justify Content를 center로 전환하십시오. 또한 자식들이 컨테이너 테두리와 최소한의 거리를 유지하도록 요소에 8픽셀 너비의 패딩을 추가하십시오.
패널에 스타일을 적용하여 배경색을 #AA5939
로, 테두리 컬러를 #311A11
로, 테두리의 너비와 반지름을 각각 4px와 15px로 설정할 수 있습니다. 이 단계는 선택 사항입니다.
그러면 UI 레이아웃이 아래 이미지처럼 표시됩니다.
다음으로 캐릭터 세부 사항에 개별 UI 컨트롤을 추가합니다. 첫 번째로 추가할 캐릭터 초상은 배경 프레임과 전경 이미지의 두 요소로 구성됩니다.
먼저 배경 프레임을 위해 캐릭터 세부 사항 컨테이너에 새 VisualElement를 추가하십시오. 120x120픽셀의 고정된 크기를 할당하고, 포함된 이미지가 테두리에 직접 닿지 않도록 4픽셀의 패딩을 설정하십시오.
컬러가 #311A11
이고 배경색이 #FF8554
이며, 너비와 반지름이 각각 2픽셀, 15픽셀인 테두리로 요소를 꾸밀 수 있습니다. 원하는 컬러와 스타일링을 적용해도 됩니다.
실제 이미지에서는 방금 만든 프레임에 새 VisualElement를 자식으로 추가하고, 나중에 컨트롤러 스크립트에서 액세스할 수 있도록 이름을 CharacterPortrait
로 지정하십시오.
이미지가 가용 공간을 모두 사용하도록 Flex > Grow를 1로 설정하십시오. 또한 올바른 종횡비를 유지하면서 요소 크기에 맞춰 이미지 크기를 확대하거나 축소할 수 있도록 Background > Scale Mode에서 확대/축소 모드를 scale-to-fit
으로 변경하십시오.
다음으로 캐릭터 세부 사항 컨테이너에 레이블 컨트롤 두 개를 추가하십시오. 이러한 컨트롤은 추후 선택한 캐릭터의 이름과 클래스를 표시하는 데 사용합니다. 두 컨트롤의 이름은 각각 CharacterName
과 CharacterClass
로 지정하십시오.
캐릭터의 이름이 클래스보다 더 눈에 잘 띄게 하려면 해당 레이블의 폰트 크기를 18로 변경하고 스타일을 bold로 설정하십시오.
그러면 UI 화면이 아래 이미지처럼 표시됩니다.
마지막으로 오른쪽의 UI 컨테이너에 버튼 컨트롤을 추가하십시오. 추후 컨트롤러 스크립트에서 이 버튼에 액세스하고 캐릭터가 선택되거나 선택 취소되면 이 버튼을 활성화 또는 비활성화하게 됩니다. 버튼의 이름을 SelectCharButton
으로 지정하고 너비를 150픽셀로 고정하십시오. 또한 버튼의 레이블 텍스트를 Select Character
로 지정하십시오.
버튼을 스타일링하려면 배경색을 #FF8554
로 설정하고, 너비 2픽셀, #311A11
컬러의 테두리를 추가하십시오. 이 단계는 선택 사항입니다.
그러면 완성된 메인 뷰가 아래 이미지처럼 표시됩니다.
UXML 템플릿을 Assets/UI/MainView.uxml로 저장하십시오. 여기의 페이지 하단에 이 템플릿의 최종 UXML 코드가 있습니다.
이 섹션에서는 이전 섹션에서 만든 UI 템플릿을 런타임 시 게임에서 로드하고 표시하는 방법을 알아봅니다.
시작하려면 PanelSettings 에셋을 만들어야 합니다. 이 에셋은 확대/축소 모드와 렌더링 순서와 같은 화면 설정을 정의하며, UI 툴킷 디버거에 UI가 어떤 이름으로 표시되는지도 결정합니다.
프로젝트 뷰에서 마우스 오른쪽 버튼을 클릭하여 새 Panel Settings Asset
을 만드십시오. Create > UI Toolkit > Panel Settings Asset을 선택하십시오. 새로 만든 파일의 이름을 GameUI_Panel
로 지정하십시오. 이 가이드에서는 모든 설정을 기본값으로 둡니다.
이전 섹션의 메인 뷰 UI 템플릿을 표시하려면 씬에서 새 게임 오브젝트를 만들어야 합니다. 해당 게임 오브젝트에 UIDocument 컴포넌트를 부착하십시오.
UIDocument
는 Unity에서 플레이 모드에 진입할 때 할당된 VisualTreeAsset
을 자동으로 로드합니다. VisualTreeAsset
은 UXML 템플릿입니다. MainView.uxml
과 새로운 GameUI_Panel
패널 설정을 모두 컴포넌트에 할당하십시오.
참고: PanelSettings
에셋을 UI Document 컴포넌트에 할당하지 않으면 프로젝트를 자동으로 검색하여 처음으로 찾은 패널 설정 에셋을 자동으로 사용합니다. 에셋의 이름을 변경하거나 에셋을 이동할 때 이 점에 유의하십시오.
이제 Unity 에디터에서 플레이 모드에 진입하여 게임 뷰에 표시된 UI를 확인할 수 있습니다.
참고: 씬에 여러 개의 UI 문서가 있는 경우, 모든 UI 문서에 같은 패널 설정을 할당할 수 있습니다. 그러면 UI가 같은 패널에 렌더링되어 성능이 최적화됩니다.
이 섹션에서는 UI의 캐릭터 리스트에 데이터를 채우는 데 사용되는 샘플 데이터를 만들어 봅니다.
캐릭터 리스트를 위해 캐릭터 이름, 클래스와 초상 이미지가 포함된 간단한 클래스가 필요합니다. 새 ScriptableObject 스크립트 Assets/Scripts/CharacterData.cs를 만들고 파일에 다음 코드를 붙여넣으십시오.
using UnityEngine;
public enum ECharacterClass
{
Knight, Ranger, Wizard
}
[CreateAssetMenu]
public class CharacterData : ScriptableObject
{
public string m_CharacterName;
public ECharacterClass m_Class;
public Sprite m_PortraitImage;
}
[CreateAssetMenu]
속성이 Create 메뉴에 자동으로 엔트리를 추가합니다.
프로젝트 뷰의 폴더를 마우스 오른쪽 버튼으로 클릭하여 새 ScriptableObject의 인스턴스를 만드십시오.
이제 CharacterData
인스턴스를 몇 개 만들고 무작위 데이터로 채워야 합니다. 모든 CharacterData
인스턴스를 Resources/Characters 폴더에 넣으십시오. 추후 이 폴더에서 모든 캐릭터 데이터를 자동으로 파싱하고 로드하는 스크립트를 작성합니다.
이 섹션에서는 리스트의 개별 엔트리를 위한 UI 템플릿을 만듭니다. 런타임 시 컨트롤러 스크립트가 각 캐릭터에 대해 이 UI의 인스턴스를 만들고 리스트에 추가합니다. 캐릭터 리스트 엔트리의 UI는 컬러가 적용된 배경 프레임과 캐릭터 이름으로 구성됩니다.
참고: 이 단계를 건너뛰고 싶다면 이 페이지 맨 아래에서 리스트 엔트리를 위한 UXML 코드를 복사하여 새 파일에 바로 붙여넣고 Assets/UI/ListEntry.uxml로 저장하십시오.
Window > UI Toolkit > UI Builder 메뉴에서 UI 빌더 창을 여십시오. File > New*를 선택하여 새 UXML 템플릿을 만드십시오.
배경을 위한 VisualElement를 추가하고, 높이를 41픽셀로 고정하십시오. 엔트리 내 텍스트는 왼쪽으로 정렬하여 요소 중앙에 배치해야 하므로, Align 폴드아웃을 열고 Align Items를 left로, Justify Content를 center로 설정하십시오. 또한 10픽셀의 왼쪽 패딩을 설정하여 레이블이 프레임의 왼쪽 테두리와 최소한의 간격을 유지하도록 하십시오.
스타일링의 경우, 배경색을 #AA5939
로 설정하고 너비가 2픽셀, 반지름이 15픽셀이며 컬러가 #311A11
로 설정된 테두리를 추가할 수 있습니다. 이 단계는 선택 사항이며, 원하는 컬러와 스타일링을 적용할 수 있습니다.
기존 VisualElement의 자식으로 레이블을 추가하고 향후 컨트롤러 스크립트에서 액세스할 수 있도록 이름을 CharacterName
으로 지정하십시오. Font Style을 bold로 설정하고 폰트 크기는 18로 설정하십시오.
UXML 템플릿을 Assets/UI/ListEntry.uxml로 저장하십시오. 여기의 페이지 하단에 이 템플릿의 최종 UXML 코드가 있습니다.
이 섹션에서는 리스트 엔트리를 위한 컨트롤러 스크립트를 만듭니다. 스크립트의 목적은 리스트 엔트리의 UI에 캐릭터 인스턴스의 데이터를 표시하는 것입니다. 이 스크립트는 캐릭터 이름을 위한 레이블에 액세스하여 주어진 캐릭터 인스턴스의 이름을 표시하도록 설정해야 합니다.
새 스크립트 Assets/Scripts/UI/CharacterListEntryController.cs를 만들고 다음 코드를 붙여넣으십시오.
using UnityEngine.UIElements;
public class CharacterListEntryController
{
Label m_NameLabel;
public void SetVisualElement(VisualElement visualElement)
{
m_NameLabel = visualElement.Q<Label>("CharacterName");
}
public void SetCharacterData(CharacterData characterData)
{
m_NameLabel.text = characterData.m_CharacterName;
}
}
이 클래스에는 두 개의 함수가 있으며, 두 함수 모두 Set
함수입니다.
SetVisualElement(VisualElement visualElement)
이 함수는 이전 섹션에서 만든 ListEntry
UI 템플릿의 인스턴스인 시각적 요소를 수신합니다. 메인 뷰 컨트롤러가 이 인스턴스를 만듭니다. 이 함수의 목적은 UI 요소 내에서 캐릭터 이름 레이블에 대한 레퍼런스를 검색하여 가져오는 것입니다.
SetCharacterData(CharacterData characterData)
이 함수는 이 리스트 요소가 표시해야 할 캐릭터 이름을 수신합니다. ListView
의 요소 리스트는 모아서 재사용되므로, 표시할 캐릭터의 데이터를 변경하려면 Set
함수가 있어야 합니다.
CharacterListEntry
클래스는 MonoBehaviour
가 아니라는 점에 유의하십시오. UI 툴킷의 시각적 요소는 게임 오브젝트가 아니므로, 이러한 시각적 요소에 컴포넌트를 부착할 수 없습니다. 대신 이 클래스는 다음 섹션의 userData
프로퍼티에 부착됩니다.
이 섹션에서는 메인 뷰의 캐릭터 리스트를 위한 컨트롤러 스크립트를 만들고, 이러한 컨트롤러 스크립트를 인스턴스화하고 시각적 트리에 할당하는 MonoBehaviour 스크립트를 만듭니다.
시작하려면 Assets/Scripts/UI/CharacterListController.cs 아래에 새 스크립트를 만들고 다음 코드를 붙여넣으십시오.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class CharacterListController
{
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
}
}
InitializeCharacterList()
메서드는 나중에 입력합니다. 하지만 다음 섹션에서 호출할 수 있도록 지금 빈 메서드를 추가해야 합니다.
메인 뷰에 컨트롤러 스크립트 부착
CharacterListEntryController
와 마찬가지로 CharacterListController
도 MonoBehaviour
가 아니며, 다른 방식으로 시각적 트리에 부착해야 합니다.
UIDocument
와 같은 게임 오브젝트에 부착할 수 있는 MonoBehaviour 스크립트를 만들어야 합니다. 이 스크립트는 CharacterListController
를 인스턴스화하고 시각적 트리에 부착됩니다.
새 스크립트 Assets/Scripts/UI/MainView.cs를 만들고 다음 코드를 붙여넣으십시오.
using UnityEngine;
using UnityEngine.UIElements;
public class MainView : MonoBehaviour
{
[SerializeField]
VisualTreeAsset m_ListEntryTemplate;
void OnEnable()
{
// The UXML is already instantiated by the UIDocument component
var uiDocument = GetComponent<UIDocument>();
// Initialize the character list controller
var characterListController = new CharacterListController();
characterListController.InitializeCharacterList(uiDocument.rootVisualElement, m_ListEntryTemplate);
}
}
Unity 에디터로 이동하여 UIDocument
가 있는 게임 오브젝트에 스크립트를 부착하십시오. ListEntry.uxml
을 List Entry Template 프로퍼티에 할당하십시오.
MainView UXML 인스턴스화는 같은 게임 오브젝트의 UIDocument
컴포넌트에서 자동으로 수행되므로, 스크립트 컴포넌트가 이를 수행할 필요가 없습니다. MainView
스크립트는 이미 인스턴스화된 시각적 트리의 레퍼런스를 얻기 위해 UI Document 컴포넌트에 액세스합니다. 그런 다음 이 스크립트는 CharacterListController
의 인스턴스를 생성하여 시각적 트리의 루트 요소와 개별 리스트 요소에 사용되는 UXML 템플릿을 전달합니다.
참고: UI가 리로드되면 UIDocument
컴포넌트를 포함하는 같은 게임 오브젝트의 컴패니언 MonoBehaviour 컴포넌트가 리로드 전에 비활성화된 다음 리로드 후 다시 활성화됩니다. 따라서 이러한 MonoBehaviour의 OnEnable
및 OnDisable
메서드에 UI와 상호작용하는 코드를 넣는 것이 좋습니다.
모든 캐릭터 데이터 인스턴스 열거
컨트롤러 스크립트에 가장 먼저 추가해야 할 기능은 앞서 만든 모든 캐릭터 데이터 인스턴스를 열거하는 함수입니다. 이러한 인스턴스는 리스트를 채우는 데 사용됩니다.
아래 코드를 복사하여 CharacterListController
클래스에 넣으십시오.
List<CharacterData> m_AllCharacters;
void EnumerateAllCharacters()
{
m_AllCharacters = new List<CharacterData>();
m_AllCharacters.AddRange(Resources.LoadAll<CharacterData>("Characters"));
}
참고: 이 코드는 Resources/Characters 폴더에 캐릭터 인스턴스가 생성되었음을 가정합니다. 캐릭터를 다른 폴더에 넣었다면 폴더 이름을 Resources/Characters로 변경해야 할 수 있습니다.
이제 초기화 중 EnumerateAllCharacter
메서드를 호출해야 합니다. InitializeCharacterList
메서드의 맨 위에 EnumerateAllCharacter
의 호출을 추가하십시오.
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
EnumerateAllCharacters();
}
UI 요소에 대한 참조 획득하기
이 섹션에서는 InitializeCharacterList
메서드의 콘텐츠를 채워봅니다. 이 메서드에 필요한 첫 번째 사항은 정보를 표시하기 위해 액세스해야 하는 모든 개별 UI 컨트롤에 대한 레퍼런스를 획득하는 것입니다. 이름, USS 클래스, 타입 또는 이의 조합으로 개별 UI 컨트롤을 검색해서 가져오려면 UQuery API 패밀리를 사용하십시오.
CharacterListController
클래스 내 코드를 아래 코드로 확장하십시오.
// UXML template for list entries
VisualTreeAsset m_ListEntryTemplate;
// UI element references
ListView m_CharacterList;
Label m_CharClassLabel;
Label m_CharNameLabel;
VisualElement m_CharPortrait;
Button m_SelectCharButton;
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
EnumerateAllCharacters();
// Store a reference to the template for the list entries
m_ListEntryTemplate = listElementTemplate;
// Store a reference to the character list element
m_CharacterList = root.Q<ListView>("CharacterList");
// Store references to the selected character info elements
m_CharClassLabel = root.Q<Label>("CharacterClass");
m_CharNameLabel = root.Q<Label>("CharacterName");
m_CharPortrait = root.Q<VisualElement>("CharacterPortrait");
// Store a reference to the select button
m_SelectCharButton = root.Q<Button>("SelectCharButton");
}
엔트리로 리스트 채우기
다음으로 화면의 리스트를 앞서 열거하고 로드한 캐릭터로 채워야 합니다. 이를 수행하려면 CharacterListController
클래스 안에 FillCharacterList
메서드를 새로 만들어야 합니다.
ListView를 요소로 채우는 작업은 다음 4단계를 따르십시오.
makeItem
함수를 만듭니다.bindItem
함수를 만듭니다.makeItem 콜백 함수의 목적은 UI에 하나의 리스트 항목을 나타내는 작은 시각적 트리를 만들고, 이 트리의 루트 VisualElement를 반환하는 것입니다.
이 경우에는 makeItem
콜백이 리스트 엔트리를 위해 만든 UXML 템플릿을 인스턴스화해야 합니다. 또한 UI를 CharacterData
의 데이터로 채우는 CharacterListEntryController
컨트롤러 스크립트의 인스턴트도 만들어야 합니다.
클래스 안에 FillCharacterList
메서드를 만들고 아래 코드를 붙여넣으십시오.
void FillCharacterList()
{
// Set up a make item function for a list entry
m_CharacterList.makeItem = () =>
{
// Instantiate the UXML template for the entry
var newListEntry = m_ListEntryTemplate.Instantiate();
// Instantiate a controller for the data
var newListEntryLogic = new CharacterListEntryController();
// Assign the controller script to the visual element
newListEntry.userData = newListEntryLogic;
// Initialize the controller script
newListEntryLogic.SetVisualElement(newListEntry);
// Return the root of the instantiated visual tree
return newListEntry;
};
}
makeItem
콜백의 일부로 인스턴스화된 시각적 요소의 userData
프로퍼티 안에 컨트롤러 스크립트를 저장하십시오. 그러면 추후 스크립트에 액세스하여 리스트 요소에 여러 캐릭터를 할당할 수 있습니다.
// Assign the controller script to the visual element
newListEntry.userData = newListEntryLogic;
ListView
는 메모리 및 성능 최적화를 위해 리스트의 엔트리마다 별도의 요소를 인스턴스화하는 대신 리스트 요소를 재사용합니다. 또한 보이는 영역을 채울 만큼의 시각적 요소만 만들고, 리스트를 스크롤하면 만든 시각적 요소를 모아 재사용합니다.
따라서 데이터의 인스턴스(이 경우에는 CharacterData
)를 개별 리스트 요소에 바인드하는 bindItem 콜백을 제공해야 합니다.
하단의 코드를 추가하여 FillCharacterList
메서드를 확장하십시오.
// Set up bind function for a specific list entry
m_CharacterList.bindItem = (item, index) =>
{
(item.userData as CharacterListEntryController).SetCharacterData(m_AllCharacters[index]);
};
bindItem
콜백은 리스트 엔트리의 시각적 트리를 위한 루트 시각적 요소의 레퍼런스를 수신하며, 데이터를 위한 인덱스도 수신합니다. 시각적 요소의 userData
프로퍼티에 CharacterListEntryController
에 대한 레퍼런스를 저장했으므로, 코드가 이에 액세스하여 CharacterData
를 바로 설정할 수 있습니다.
마지막으로 요소의 항목 높이를 설정하고 리스트를 위한 데이터 소스의 레퍼런스를 제공해야 합니다. 이는 리스트에 포함된 요소의 수를 리스트에 알립니다.
하단의 코드를 추가하여 FillCharacterList
메서드를 확장하십시오.
// Set a fixed item height
m_CharacterList.fixedItemHeight = 45;
// Set the actual item's source list/array
m_CharacterList.itemsSource = m_AllCharacters;
마지막으로 초기화 끝에 FillCharacterList
메서드를 호출해야 합니다. 아래대로 InitializeCharacterList
메서드의 하단에 호출을 추가하십시오.
FillCharacterList();
지금 플레이 모드에 진입하면 캐릭터 리스트가 만든 캐릭터의 이름으로 채워집니다.
여기 이 가이드의 하단에서 CharacterListController
스크립트의 최종 코드를 확인하실 수 있습니다.
사용자가 캐릭터를 선택하면 캐릭터의 세부 사항, 즉 초상, 성명과 클래스가 화면 오른쪽의 캐릭터 세부 사항 섹션에 표시되어야 합니다. 또한 캐릭터가 선택되면 선택 버튼이 활성화되어야 합니다. 선택된 캐릭터가 없으면 버튼이 다시 비활성화되어야 합니다.
이미 리스트의 캐릭터를 클릭하고 선택할 수 있음에 유의하십시오. 선택 및 강조 표시 기능은 ListView 컨트롤의 일부입니다. 사용자가 리스트의 선택 항목을 변경하면 응답하는 콜백 함수만 추가하면 됩니다. ListView
컨트롤은 이를 위한 onSelectionChange
이벤트를 포함합니다.
다음 코드를 InitializeCharacterList
메서드 하단에 추가하십시오.
// Register to get a callback when an item is selected
m_CharacterList.onSelectionChange += OnCharacterSelected;
이제 상기 코드에서 설정한 OnCharacterSelected
콜백 함수를 구현해야 합니다. 이 함수는 리스트에서 선택한 모든 항목을 수신합니다. 그러나 리스트는 단일 항목 선택만 허용하므로, 리스트의 selectedItem 프로퍼티를 통해 선택한 항목에 직접 액세스할 수 있습니다.
아래 코드를 복사하여 클래스에 넣으십시오.
void OnCharacterSelected(IEnumerable<object> selectedItems)
{
// Get the currently selected item directly from the ListView
var selectedCharacter = m_CharacterList.selectedItem as CharacterData;
}
selectedItem
프로퍼티가 null을 반환할 수 있습니다. 아무것도 선택되지 않았거나 사용자가 모든 항목을 선택 취소하기 위해 ESC
키를 누르는 경우가 이에 해당합니다. 이 사례는 우선적으로 처리해야 합니다.
아래대로 OnCharacterSelected
메서드를 확장하십시오.
void OnCharacterSelected(IEnumerable<object> selectedItems)
{
// Get the currently selected item directly from the ListView
var selectedCharacter = m_CharacterList.selectedItem as CharacterData;
// Handle none-selection (Escape to deselect everything)
if (selectedCharacter == null)
{
// Clear
m_CharClassLabel.text = "";
m_CharNameLabel.text = "";
m_CharPortrait.style.backgroundImage = null;
// Disable the select button
m_SelectCharButton.SetEnabled(false);
return;
}
}
선택이 유효하면 UI에 캐릭터의 세부 사항을 표시해야 합니다. 클래스의 InitializeCharacterList
메서드에서 검색해서 가져온 레퍼런스를 통해 레이블 및 세로 이미지 시각적 요소에 액세스할 수 있습니다.
아래 코드를 복사하여 OnCharacterSelected
메서드에 넣으십시오.
// Fill in character details
m_CharClassLabel.text = selectedCharacter.m_Class.ToString();
m_CharNameLabel.text = selectedCharacter.m_CharacterName;
m_CharPortrait.style.backgroundImage = new StyleBackground(selectedCharacter.m_PortraitImage);
// Enable the select button
m_SelectCharButton.SetEnabled(true);
이제 플레이 모드에 진입하여 실제 캐릭터 선택 리스트를 확인할 수 있습니다. 캐릭터를 선택 취소하려면 Escape
키를 누르십시오.
아래는 이 가이드에서 만든 모든 파일의 소스 코드 전문입니다.
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<ui:VisualElement style="flex-grow: 1; align-items: center; justify-content: center; background-color: rgb(115, 37, 38);">
<ui:VisualElement style="flex-direction: row; height: 350px;">
<ui:ListView focusable="true" name="CharacterList" style="width: 230px; border-left-color: rgb(49, 26, 17); border-right-color: rgb(49, 26, 17); border-top-color: rgb(49, 26, 17); border-bottom-color: rgb(49, 26, 17); border-left-width: 4px; border-right-width: 4px; border-top-width: 4px; border-bottom-width: 4px; background-color: rgb(110, 57, 37); border-top-left-radius: 15px; border-bottom-left-radius: 15px; border-top-right-radius: 15px; border-bottom-right-radius: 15px; margin-right: 6px;" />
<ui:VisualElement style="justify-content: space-between; align-items: flex-end;">
<ui:VisualElement style="align-items: center; background-color: rgb(170, 89, 57); border-left-width: 4px; border-right-width: 4px; border-top-width: 4px; border-bottom-width: 4px; border-left-color: rgb(49, 26, 17); border-right-color: rgb(49, 26, 17); border-top-color: rgb(49, 26, 17); border-bottom-color: rgb(49, 26, 17); border-top-left-radius: 15px; border-bottom-left-radius: 15px; border-top-right-radius: 15px; border-bottom-right-radius: 15px; width: 276px; justify-content: center; padding-left: 8px; padding-right: 8px; padding-top: 8px; padding-bottom: 8px;">
<ui:VisualElement style="border-left-color: rgb(49, 26, 17); border-right-color: rgb(49, 26, 17); border-top-color: rgb(49, 26, 17); border-bottom-color: rgb(49, 26, 17); border-left-width: 2px; border-right-width: 2px; border-top-width: 2px; border-bottom-width: 2px; height: 120px; width: 120px; border-top-left-radius: 13px; border-bottom-left-radius: 13px; border-top-right-radius: 13px; border-bottom-right-radius: 13px; padding-left: 4px; padding-right: 4px; padding-top: 4px; padding-bottom: 4px; background-color: rgb(255, 133, 84);">
<ui:VisualElement name="CharacterPortrait" style="flex-grow: 1; -unity-background-scale-mode: scale-to-fit;" />
</ui:VisualElement>
<ui:Label text="Label" name="CharacterName" style="-unity-font-style: bold; font-size: 18px;" />
<ui:Label text="Label" display-tooltip-when-elided="true" name="CharacterClass" style="margin-top: 2px; margin-bottom: 8px; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:Button text="Select Character" display-tooltip-when-elided="true" name="SelectCharButton" style="width: 150px; border-left-color: rgb(49, 26, 17); border-right-color: rgb(49, 26, 17); border-top-color: rgb(49, 26, 17); border-bottom-color: rgb(49, 26, 17); background-color: rgb(255, 133, 84); border-left-width: 2px; border-right-width: 2px; border-top-width: 2px; border-bottom-width: 2px;" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<ui:VisualElement style="height: 41px; align-items: flex-start; justify-content: center; padding-left: 10px; background-color: rgba(170, 89, 57, 255); border-left-color: rgba(49, 26, 17, 255); border-right-color: rgba(49, 26, 17, 255); border-top-color: rgba(49, 26, 17, 255); border-bottom-color: rgba(49, 26, 17, 255); border-left-width: 2px; border-right-width: 2px; border-top-width: 2px; border-bottom-width: 2px; border-top-left-radius: 15px; border-bottom-left-radius: 15px; border-top-right-radius: 15px; border-bottom-right-radius: 15px;">
<ui:Label text="Label" display-tooltip-when-elided="true" name="CharacterName" style="-unity-font-style: bold; font-size: 18px;" />
</ui:VisualElement>
</ui:UXML>
using UnityEngine;
public enum ECharacterClass
{
Knight, Ranger, Wizard
}
[CreateAssetMenu]
public class CharacterData : ScriptableObject
{
public string m_CharacterName;
public ECharacterClass m_Class;
public Sprite m_PortraitImage;
}
using UnityEngine.UIElements;
public class CharacterListEntryController
{
Label m_NameLabel;
public void SetVisualElement(VisualElement visualElement)
{
m_NameLabel = visualElement.Q<Label>("CharacterName");
}
public void SetCharacterData(CharacterData characterData)
{
m_NameLabel.text = characterData.m_CharacterName;
}
}
using UnityEngine;
using UnityEngine.UIElements;
public class MainView : MonoBehaviour
{
[SerializeField]
VisualTreeAsset m_ListEntryTemplate;
void OnEnable()
{
// The UXML is already instantiated by the UIDocument component
var uiDocument = GetComponent<UIDocument>();
// Initialize the character list controller
var characterListController = new CharacterListController();
characterListController.InitializeCharacterList(uiDocument.rootVisualElement, m_ListEntryTemplate);
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class CharacterListController
{
// UXML template for list entries
VisualTreeAsset m_ListEntryTemplate;
// UI element references
ListView m_CharacterList;
Label m_CharClassLabel;
Label m_CharNameLabel;
VisualElement m_CharPortrait;
Button m_SelectCharButton;
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
EnumerateAllCharacters();
// Store a reference to the template for the list entries
m_ListEntryTemplate = listElementTemplate;
// Store a reference to the character list element
m_CharacterList = root.Q<ListView>("CharacterList");
// Store references to the selected character info elements
m_CharClassLabel = root.Q<Label>("CharacterClass");
m_CharNameLabel = root.Q<Label>("CharacterName");
m_CharPortrait = root.Q<VisualElement>("CharacterPortrait");
// Store a reference to the select button
m_SelectCharButton = root.Q<Button>("SelectCharButton");
FillCharacterList();
// Register to get a callback when an item is selected
m_CharacterList.onSelectionChange += OnCharacterSelected;
}
List<CharacterData> m_AllCharacters;
void EnumerateAllCharacters()
{
m_AllCharacters = new List<CharacterData>();
m_AllCharacters.AddRange(Resources.LoadAll<CharacterData>("Characters"));
}
void FillCharacterList()
{
// Set up a make item function for a list entry
m_CharacterList.makeItem = () =>
{
// Instantiate the UXML template for the entry
var newListEntry = m_ListEntryTemplate.Instantiate();
// Instantiate a controller for the data
var newListEntryLogic = new CharacterListEntryController();
// Assign the controller script to the visual element
newListEntry.userData = newListEntryLogic;
// Initialize the controller script
newListEntryLogic.SetVisualElement(newListEntry);
// Return the root of the instantiated visual tree
return newListEntry;
};
// Set up bind function for a specific list entry
m_CharacterList.bindItem = (item, index) =>
{
(item.userData as CharacterListEntryController).SetCharacterData(m_AllCharacters[index]);
};
// Set a fixed item height
m_CharacterList.fixedItemHeight = 45;
// Set the actual item's source list/array
m_CharacterList.itemsSource = m_AllCharacters;
}
void OnCharacterSelected(IEnumerable<object> selectedItems)
{
// Get the currently selected item directly from the ListView
var selectedCharacter = m_CharacterList.selectedItem as CharacterData;
// Handle none-selection (Escape to deselect everything)
if (selectedCharacter == null)
{
// Clear
m_CharClassLabel.text = "";
m_CharNameLabel.text = "";
m_CharPortrait.style.backgroundImage = null;
// Disable the select button
m_SelectCharButton.SetEnabled(false);
return;
}
// Fill in character details
m_CharClassLabel.text = selectedCharacter.m_Class.ToString();
m_CharNameLabel.text = selectedCharacter.m_CharacterName;
m_CharPortrait.style.backgroundImage = new StyleBackground(selectedCharacter.m_PortraitImage);
// Enable the select button
m_SelectCharButton.SetEnabled(true);
}
}