Unity의 UIToolkit을 활용하여 유저가 실수로 작업을 수행하는 것을 방지하는 UI인 확인 대화상자를 만들고자 한다.
우선 하이라이키 창에서 마우스 오른쪽 버튼을 클릭해 [UIToolkit] > [UIDocument]를 생성해 줍니다.
생성된 Document의 이름을 ConfirmationModalUI라고 바꾸겠습니다.
UIDocument 컴포넌트에 필수로 넣어줘야 하는 것은 Source Asset으로 앞으로 만들 UI를 저곳에 넣어줄 것입니다.
PanelSettings는 레거시 UI의 캔버스 같은 역할로 캔버스에서 ScreenSize를 고정했던 것과 같이 고정해 줍시다.
PanelSettings는 Project 파일에서 마우스 오른쪽 버튼을 클릭해 [Create] > [UI Toolkit] > [Panel Settings Asset]을 클릭하면 PanelSettings가 생성됩니다.
생성된 Panel Settings Asset의 이름을 MobilePanelSettings로 바꿔주겠습니다.
Panel의 크기를 고정해 주기 의해선 ScaleMode를 Constant Physical Size에서 Scale With Screen Size 모드로 바꿔주어야 합니다.
바꾼 다음 Reference Resolution의 X를 900으로 Y를 1600으로 해서 9:16 사이즈로 설정해 줍니다.
설정해 준 다음 PanelSettings에서 방금 설정 Panel을 넣어줍니다.
드디어 UI를 만들 준비가 다 끝났습니다.
이제 Source Asset의 넣을 Visual Tree Asset을 만듭시다.
Visual Tree Asset 도 Project 파일에서 마우스 오른쪽 버튼을 클릭해 [Create] > [UI Toolkit] > [UIDocument]을 클릭하면 Visual Tree Asset이 생성됩니다.
생성된 Visual Tree Asset의 이름을 현재 우리가 만들 UI의 이름과 같게 바꿔주었습니다.
이제 Source Asset도 ConfirmaionModal을 넣어 채워줍니다.
진짜로 UI를 만들어 봅시다.
Source Asset의 넣어준 Visual Tree Asset을 클릭해 UI Builder창을 켜줍니다.
우선 Canvas의 사이즈를 게임씬에 맞게 설정해 봅시다.
[하이라이키 창] > [ConfirmationModal.uxml]을 클릭하면 [인스팩터 창]에 Canvas Size를 설정할 수 있는 창이 뜹니다.
Canvas Size는 [Width는 900] [Height는 1600]으로 설정해 줍니다.
설정해 주면 캔버스 사이즈가 변경될 것입니다.
이제 [Library] > [Containers] > [VisualElement]를 아래의 사진과 같이 [Hierarchy 창]에 넣어주고
이름을 [confirmation_modal_root_visual_contain-box]으로 바꿔줍니다.
먼저 확인창의 Background를 만들겠습니다. 위에서 만든 contain안에 새로운 VisualElement를 넣어줍니다.
그리고 그 Element의 이름을 [confirmation_modal_background_contain-box]으로 바꿔줍니다.
바꿔준 후 background의 부모 [Inspector] > [Align]의 [Align Items]와 [Justify Content]를 center로 바꿔줍니다.
이건 root 아래에 들어가는 UI를 중앙으로 정렬해 주는 겁니다.
그리고 background를 선택하고 [Flex] > [Grow]의 값을 0으로 바꿔줍니다.
[Grow]를 1로 설정하면 부모의 레이아웃 크기의 맞춰 Size가 확장되고
[Grow]를 0으로 설정하면 부모의 레이아웃 관계없이 자신의 Size에 맞게 확장됩니다.
background가 색이 없어 확인하기 힘드니 [Background] > [Color]를 흰색으로 바꿔줍니다.
바꿔준 다음
[Size] > [Width] 80%으로
[Size] > [Height] 25%으로
설정해 줍니다.
이제 버튼과 레이블을 담을 VisualElement를 background 아래의 생성해 주고
두 개 다 Grow를 0으로 설정해 줍니다.
레이블 박스의 [Size]는 [Width는 100%] [Height는 70%]으로 설정해 주고
버튼 박스의 [Size]는 [Width는 100%] [Height는 30%]로 설정해 줍니다.
그리고 버튼 박스의 컬러는 보기 편하게 회색으로 바꿔줍시다.
그리고 너무 각진 것이 보기 좋지 않으니 [Border] > [Radius]를 알잘 딱으로 설정해 줍니다.
저는 아래와 같이 설정해 주었습니다.
레이블 박스 [Top-Left & Top-Right = 20]
버튼 박스 [Bottom-Right & Bottom-Left = 20]
이제 확인 내용을 써줄 Label을 넣어줍시다.
[Library] > [Controls] > [Label]을 레이블 박스에 2개 넣어주고
2개의 이름을 [confirmation_modal_title-label]와 [confirmation_modal_description-label]으로 바꿔 줍니다.
TitleLabel 설정
[Size] > [Width = 100%] [Height = 35%]
[Text] > [Font = 한국어가 있는 폰트]
[Text] > [FontAsset = None]
[Text] > [Size = 60]
[Text] > [Align = middle]
[Margin & Padding] > [Padding] > [Top = 35] [Right = 60] [Left = 60]
Description 설정
[Size] > [Width = 100%] [Height = 65%]
[Margin & Padding] > [Padding] > [Right = 60] [Left = 60] [Bottom = 40]
[Text] > [Font = 한국어가 있는 폰트]
[Text] > [FontAsset = None]
[Text] > [Size = 40]
[Text] > [Align = middle]
[Text] > [Wrap = normal]
위에처럼 레이블 쪽 설정을 마치셨다면 이제 버튼 박스 안에 [Library] > [Controls] > [Button]을 2개 넣어줍니다.
그리고 버튼 박스 설정
[Flex] > [Direction = row]
[Align] > [Align Items = center]
[Align] > [Justify Content = space-around]
지금부터 버튼을 만들어 볼 겁니다.
버튼 같은 경우는 한 개만 만드는 게 아니라 2개 이상을 만들어야 하기에 Style Class를 사용하여 만들어 볼 겁니다.
여기서 Style Class는 UI의 스타일을 저장하여 다른 UI에도 클래스를 집어넣는 것으로 UI가 클래스의 스타일로 변경됩니다.
우선 버튼의 Style Class를 만들어 봅시다.
[StyleSheets] > [ + 버튼] > [Create New Uss] 버튼을 클릭하여 생성해 줍니다.
제대로 생성되었다면 [StyleSheets]의 무언가가 생겼을 겁니다.
이제 [Add new selector...]라고 써져 있는 인풋 필드를 클릭해 [.btn]을 입력하고 엔터를 눌러 만들어 줍니다.
만든 클래스를 방금 만들었던 2개의 버튼에 끌어다 넣어 주고 버튼의 이름을 바꿔줍시다.
저는 왼쪽 버튼의 이름은 [confirmation_modal_cancel-btn]으로 오른쪽 버튼의 이름은 [confirmation_modal_check-btn]으로 바꾸어 주었습니다.
바꾸어준 다음 StyleSheets의 방금 전 만든 [.btn]클래스를 클릭하고 [Inspector 창]을 봐줍니다.
[.btn]클래스 세팅
[Border] > [Color = Color(0,0,0,0)]
[Border] > [Radius = 5px]
[Background] > [Color = Color(0.93,0.93,0.93,1)]
[Size] > [Width = 30%] [Height = 35%]
버튼, 레이블 등 Text가 있는 곳은 [Inspector] > [Attributes] > [Text]에서 Text변경이 가능합니다.
저는 아래와 같이 Text를 바꾸어 주었습니다.
모든 UI 디자인이 끝났습니다. 이제 코드를 짜기 전에 창이 꺼졌다 켜졌다 하는 기능을 Toolkit의 Style Class를 이용해 만들어 보겠습니다. 우선 UI의 root VisualElement를 [.root]이라는 클래스로 빼줍니다.
[StyleSheet] > [Style Class List] > [인풋 필드에 root 작성]
[StyleSheet] > [Style Class List] > [Extract Inlined Styles to New Class 클릭]
위의 방법처럼 하면 클래스가 잘 빠지는 걸 확인할 수 있습니다.
[StyleSheets] > [인풋 필드] > [.root.on 입력]엔터 후
생성된 [.root.on]의 세팅을 아래와 같이 바꾸어 주겠습니다.
[Display] > [Opacity = 0]
[Margin & Padding] > [Margin] > [Top = 1600] [Bottom = -1600]
세팅을 바꾸어준 후 root VisualElement의 [Style Class List] > [인풋 필드 .on입력] 엔터
[.on] 클래스를 추가해 주면 위에서 설정한 값으로 변경되는 것을 확인할 수 있다.
여기서 [.on] 클래스를 추가하거나 지울 때 변경되는 것이 딱딱한 것을 볼 수 있다. 이것을 해결하기 위해선
root VisualElement의 [Transition Animations] > [Duration = 0.3]으로 설정해야 한다.
그러면 [.on] 을 적용하고 해제할 때 듀레이션이 적용되는 걸 볼 수 있다.
이제 이 UI를 제어해 줄 스크립트를 작성할 것이다. ConfirmationModal이라는 스크립트 하나를 생성해 주었다.
생성한 스크립트를 우선 UI가 있는 [ConfirmationModalUI]에 붙여 주었습니다.
스크립트의 내용은 아래와 같습니다.
최대한 코드를 이해할 수 있게 주석을 달아 놓았습니다.
그럼에도 모르겠다 싶다면 댓글로 적어주세요.
using System;
using UnityEngine;
using UnityEngine.UIElements; //UIToolkit 선언
public class ConfirmationModal : MonoBehaviour
{
//UI가 담겨져 있는 UIDocument
private UIDocument _doc;
//클래스를 적용했다 해제할 UI
private VisualElement _confirmationModal;
//타이틀 레이블
private Label _titleLabel;
//내용 레이블
private Label _descriptionLabel;
//취소 버튼
private Button _cancelBtn;
//확인 버튼
private Button _checkBtn;
//클릭 이벤트
private Action _currentCheckBtnClickEvent;
private void Awake()
{
//스크립트가 붙어있는 오브젝트에서 UIDocument를 가져오기
_doc = gameObject.GetComponent<UIDocument>();
//UI에서 가장 위에 존재하는 UI VisualElement로 가져오기
VisualElement root = _doc.rootVisualElement;
//<> 안에는 가져올 UI의 타입()안에는 가져올 UI의 이름
//rootUI 안에 있는 confirmation_modal_root_visual_contain-box 라는 이름의 VisualElement형식의 UI를 _titleLabel에 넣어준다.
_confirmationModal = root.Q<VisualElement>("confirmation_modal_root_visual_contain-box");
//rootUI 안에 있는 confirmation_modal_title-label라는 이름의 Label형식의 UI를 _titleLabel에 넣어준다.
_titleLabel = root.Q<Label>("confirmation_modal_title-label");
//rootUI 안에 있는 confirmation_modal_description-label라는 이름의 Label형식의 UI를 _descriptionLabel에 넣어준다.
_descriptionLabel = root.Q<Label>("confirmation_modal_description-label");
//rootUI 안에 있는 confirmation_modal_cancel-btn라는 이름의 Button형식의 UI를 _cancelBtn 넣어준다.
_cancelBtn = root.Q<Button>("confirmation_modal_cancel-btn");
//rootUI 안에 있는 confirmation_modal_check-btn라는 이름의 Button형식의 UI를 _checkBtn 넣어준다.
_checkBtn = root.Q<Button>("confirmation_modal_check-btn");
//취소 버튼을 클릭했을 떄
_cancelBtn.RegisterCallback<ClickEvent>(evt => ToggleUI()); //UI끄기
//확인 버튼을 눌렀을 때
_checkBtn.RegisterCallback<ClickEvent>(evt =>
{
if (_currentCheckBtnClickEvent != null)
{
//확인 이벤트 실행
_currentCheckBtnClickEvent?.Invoke();
_currentCheckBtnClickEvent = null;
}
//UI끄기
ToggleUI();
});
}
private void Update()
{
//디버깅용 코드
if (Input.GetKeyDown(KeyCode.Space))
ShowConfirmationModal("디버깅용 타이틀", "이건 디버깅용 내용으로 아무 연관 없습니다.", () => Debug.Log("확인 눌림"));
}
/// <summary>
/// 확인 대화창 화면에 보이기
/// </summary>
/// <param name="title">타이틀에 넣을 내용</param>
/// <param name="description">확인 대화창 내용</param>
/// <returns></returns>
public void ShowConfirmationModal(string title, string description, Action checkBtnCliekEvent)
{
//UI 키기
ToggleUI();
//title의 text에 넣기
_titleLabel.text = title;
//_descriptionLabel text에 넣기
_descriptionLabel.text = description;
//cliekEvent 업데이트
_currentCheckBtnClickEvent = checkBtnCliekEvent;
}
private void ToggleUI()
{
//Style Class가 붙어있다면 해제 안붙어 있다면 적용
_confirmationModal.ToggleInClassList("on");
}
}
완성본
지금까지 장문의 글을 읽어 주셔서 감사드립니다.
더 궁금한 점이나 안 되는 부분이 있다면 댓글에 적어주세요.
반응형