UI 비법서 (1)

 

당신의 레이아웃에 딱맞는 Content Size Fitter

 

 

작성 기준 버전 :: 2018.3 -2019.4

 

[이 포스트의 내용은 유튜브 영상으로도 시청하실 수 있습니다]

 

UI를 제작할 때, 보기 좋은 레이아웃을 만들어 내는 일은 매우 어렵다. 어렵사리 보기 좋은 레이아웃을 만들어 냈다고 해도 UI에 들어가는 내용의 크기가 바뀔 수 있는 상황이라면 정해진 텍스트나 이미지 등의 콘텐츠가 정해진 레이아웃을 벗어나는 경우가 쉽게 발생한다.

이러한 유동적인 상황에 대처하는 UI를 만드는 것은 이전까지는 스크립트에서 일일이 처리해야하는 번거로움이 있었다. 그런 번거로움을 해소하기 위한 기능으로 유니티는 콘텐츠 사이즈 피터(Content Size Fitter)라는 컴포넌트를 지원한다. 이 컴포넌트를 이용해서 콘텐츠의 크기에 따라 자유롭게 변화하는 UI를 제작하는 방법에 대해서 알아보도록 하자.

 

 

콘텐츠 사이즈 피터(Content Size Fitter)

 

콘텐츠 사이즈 피터는 단어 그대로 컴포넌트가 붙어있는 오브젝트의 크기를 내용물, 콘텐츠의 크기에 맞게 만들어주는 역할을 한다.

 

 

콘텐츠 사이즈 피터 컴포넌트를 게임 오브젝트에 추가하는 방법은 위의 이미지처럼 씬에서 Add Component 버튼을 누르고 "Content"를 검색해서 나오는 Content Size Fitter 컴포넌트를 선택하는 것이다.

 

 

콘텐츠 사이즈 피터를 사용하지 않으면 위의 이미지처럼 내용이 UI의 사이즈보다 커지면 옵션에 따라 뒷 부분이 잘리거나 영역을 넘어가서 표시되는 바람에 다른 UI를 침범하는 문제가 발생한다.

 

 

콘텐츠 사이즈 피터를 사용하면 전과 다르게 UI의 사이즈가 텍스트의 내용에 따라 자동으로 변하는 것을 확인할 수 있다.

 

 

추가된 콘텐츠 사이즈 피터는 인스펙터 뷰에서 위의 이미지와 같은 모습으로 보이며, Horizental Fit과 Vertical Fit라는 두 가지 프로퍼티를 설정할 수 있다.

 

선택 가능한 옵션으로는 Unconstrained, Min Size, Preferred Size 이렇게 세 가지가 있다. Unconstrained는 UI의 크기를 콘텐츠에 맞추지 않는 옵션으로 UI의 크기를 직접 맞추거나 고정된 크기를 사용해야 하는 경우 사용하면 되는 옵션이다. Min Size는 레이아웃 요소의 최소 크기를 기준으로 UI의 크기를 맞추는 옵션인데, 이 옵션의 경우에는 단독으로 사용되는 경우는 거의 없고 Layout group 계열의 컴포넌트와 함께 주로 사용된다. 마지막으로 Preferred Size 옵션은 기본적인 콘텐츠의 크기에 따라 UI의 크기를 맞추는 옵션으로 위의 예시 이미지처럼 텍스트나 이미지에 단독으로 콘텐츠 사이즈 피터를 사용하는 경우에 주로 사용된다.

 

 

 

 

 

콘텐츠 사이즈 피터와 피봇

 

콘텐츠 사이즈 피터로 인해 Rect Transform의 크기가 변경될 때는 UI의 피벗을 중심으로 조정되기 때문에, 피벗을 사용해서 UI의 크기가 조정되는 방향을 조절할 수 있다. 위의 이미지 중에서 콘텐트 사이즈 피터를 붙인 gif를 보면 피벗이 UI의 중심에 있어서 피벗을 중심으로 양 쪽 방향을 향해서 UI가 커지는 것을 볼 수 있다.

 

 

위의 이미지를 보면 X Pivot을 0으로 변경해서 UI의 피봇을 가장 좌측으로 옮긴 결과, UI의 트랜스폼이 왼쪽에서 오른쪽으로 확장되는 것을 볼 수 있다.

 

 

콘텐츠 사이즈 피터와 스크롤 뷰(Content Size Fitter & Scroll View)

 

콘텐츠 사이즈 피터를 가장 잘 응용할 수 있는 부분은 바로 스크롤 뷰이다. 이전에 스크롤 뷰에 대해서 설명했던 글에서는 스크롤 뷰에 들어가는 내용에 따라서 Content 오브젝트의 크기를 맞춰주는 방법을 스크립트로 설명했지만 콘텐츠 사이즈 피터를 사용하면 스크립트를 작성하지 않고도 훨씬 편하게 Content 오브젝트의 크기를 맞출 수 있다.

 

이제 콘텐츠 사이즈 피터를 사용해서 스크롤 뷰의 Content의 크기를 조절하는 방법을 알아보자.

 

 

우선 (200, 200) 사이즈의 스크롤 뷰를 생성한다.

 

 

 

그리고 스크롤 뷰의 Content 오브젝트 아래에 (150, 50) 사이즈의 이미지를 하나 만들어주자.

 

 

콘텐츠 사이즈 피터가 없는 상태에서 플레이 버튼을 눌러서 실행해보면 스크롤 뷰의 크기는 이미지의 크기나 갯수에 상관없이 동작하는 것을 확인할 수 있다.

 

다음에는 이 스크롤 뷰에 콘텐츠 사이즈 피터를 적용해보자.

 

 

우선 아이템들이 들어갈 Content 오브젝트를 선택하고 여기에 오브젝트들을 자동으로 정렬해주는 Vertical Layout Group 컴포넌트를 추가한다.

 

원하는 정렬 방법에 따라 Grid Layout Group이나 Horizontal Layout Group을 사용해도 된다. 각 프로퍼티의 값은 보기 좋아보이는 값을 찾아서 입력하도록 하자. 이 예시에서는 위 아래의 패딩을 10씩 주고 각 아이템 간의 간격을 5로 설정했다. 그리고 아이템의 정렬은 상단 중앙으로 설정했다.

 

그 다음에 내용물에 따라 오브젝트의 크기를 조정해주는 콘텐츠 사이즈 피터 컴포넌트를 추가한 뒤에 사용한 Layout Group에 맞게 Fit 옵션을 Min Size로 변경한다.

 

 

스크롤 뷰에 콘텐츠 사이즈 피터와 레이아웃 그룹을 적용하고 플레이를 해보면 아이템이 추가/제거됨에 따라서 자연스럽게 내부 Content의 크기가 늘어나서 스크롤이 가능하게 되는 것을 확인할 수 있다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

제대로 따라가기 (5) C++ 프로그래밍 튜토리얼 :: UMG와 유저 인터페이스

 


작성버전 :: 4.21.0

언리얼 엔진 튜토리얼인 UMG와 유저 인터페이스에서는 언리얼 모션 그래픽(UMG)을 사용해서 여러가지 화면과 버튼이 있는 기본적인 메뉴 시스템을 만드는 법을 배울 수 있다.

 

튜토리얼대로 하면 문제가 발생해서 제대로 따라갈 수 없는 부분으로 동작이 가능하게 수정해야하는 부분은 빨간 블럭으로 표시되어 있다.


이번 튜토리얼에서 새로 배우게 되는 내용은 글 제일 끝에 "이번 섹션에서 배운 것"에 정리된다.

 

 

1. UMG용 모듈 종속성 구성(문서)

 

C++탭에서 기본 코드를 선택하고 "HowTo_UMG"라는 이름으로 프로젝트를 새로 생성한다.

 

 

언리얼 모션 그래픽(UMG)을 사용하는 코드를 작성할 것이기 때문에 비주얼 스튜디오(Visual Studio)에 들어가서 기본으로 포함되어 있지 않은 몇 가지 모듈을 포함시키도록 하자.

 

언리얼 에디터의 파일 드롭다운 메뉴에서 Visual Studio 열기를 선택해서 비주얼 스튜디오를 연다.

 

 

UMG는 몇 가지 모듈에 종속되어 있는데, 그 모듈을 "HowTo_UMG.Build.cs"에 추가해야 한다.

 

 

"HowTo_UMG.Build.CS"에서 포함된 퍼블릭 모듈 목록에 "UMG"를, 포함된 프라이빗 모듈 목록에 "Slate" 와 "SlateCore" 를 추가해야 한다.

 

PublicDependencyModuleNames에 UMG를 다음과 같이 추가한다.

 

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });

 

비어있는 PrivateDependencyModuleNams를 주석 처리하고 "Slate"와 "SlateCore"가 있는 부분을 주석 해제 한다.

 

//PrivateDependencyModuleNames.AddRange(new string[] {  });

// Uncomment if you are using Slate UI
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

 

UMG 구성이 완료되었다면, 프로젝트의 커스텀 게임 모드에 코드를 추가하여 게임 메뉴를 만들고 표시할 수 있다.

 

 

2. 게임 모드 확장(문서)

 

게임이 시작되면 유저 위젯(User Widget)을 새로 만들어 표시하거나, 나중에 제거할 수 있도록 하기 위해서 Game Mode(게임 모드) 클래스에 함수와 프로퍼티를 추가하자. 각 프로젝트에는 커스텀 게임 모드 클래스가 딸려오므로, HowTo_UMGGameMode.h에 다음 코드를 추가하면 된다.

 

버전이 바뀌면서 GameMode 클래스 파일의 이름이 "ProjectNameGameMode.h", "ProjectNameGameMode.cpp"에서 "ProjectNameGameModeBase.h", "ProjectNameGameModeBase.cpp"로 바뀌었다. 비주얼 스튜디오의 솔루션 탐색기에서 HowTo_UMGGameModeBase.h를 열어서 작업하자.

 

public:
    UFUNCTION(BlueprintCallable, Category = "UMG_Game")
    void ChangeMenuWidget(TSubclassOf NewWidgetClass);

protected:
    virtual void BeginPlay() override;

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UMG_Game")
    TSubclassOf StartingWidgetClass;

    UPROPERTY()
    UUserWidget* CurrentWidget;

 

TSubclassOf 클래스를 사용할 때, [클래스 템플릿 "TSubclassOf"에 대한 인수 목록이 없다]는 에러가 발생한다. 전체 코드를 보고 유추해보건데, BeginPlay() 함수에서 StartingWidgetClass의 내용물을 CurrentWidget 변수에 넣어주거나 ChangeMenuWidget() 함수가 동작할 때, 매개변수로 받은 NewWidgetClass를 CurrentWidget에 대입해주는 방식으로 동작할 것임을 알 수 있다. 그렇기 때문에 여기에서는 TSubclassOf의 템플릿 인수로 UUserWidget 타입을 넣어주는 것이 올바른 해결책일 것이다.

 

다음과 같이 코드를 수정해주자.

 

public:
    UFUNCTION(BlueprintCallable, Category = "UMG_Game")
    void ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass);

protected:
    virtual void BeginPlay() override;

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UMG_Game")
    TSubclassOf<UUserWidget> StartingWidgetClass;

 

이제 HowTo_UMGGameModeBase.cpp로 가서, 선언한 두 함수의 구현을 해야 한다. 우선 BeginPlay()를 덮어쓰는 것으로 시작하자.

 

void AHowTo_UMGGameModeBase::BeginPlay()
{
    Super::BeginPlay();
    ChangeMenuWidget(StartingWidgetClass);
}

 

부모 클래스의 함수를 덮어쓸 때는, 여기 BeginPlay에서 하듯이 해당 함수의 부모 클래스 버전을 호줄하는 것이 중요한 경우가 많다. 우리가 구현하는 함수의 버전은 기존의 절차의 끝 부분에 한 단계를 추가하기 위한 것이므로, 함수 첫 줄에 Super::BeginPlay()를 호출한다.

 

그리고 여기서는 UUserWidget을 사용하기 위해서, HowTo_UMGGameModeBase.cpp 상단에 "Blueprint/UserWidget.h"를 포함시켜 주어야 한다.

 

#include "Blueprint/UserWidget.h"

 

계속 해서, 메뉴 간의 전환 방식을 구현해야 한다. 뷰포트에 활성화된 유저 위젯이 있다면 제거하고 난 다음에 유저 위젯을 새로 만들어 뷰포트에 추가해주도록 구현한다.

 

void AHowTo_UMGGameModeBase::ChangeMenuWidget(TSubclassOf NewWidgetClass)
{
    if (CurrentWidget != nullptr)
    {
        CurrentWidget->RemoveFromViewport();
        CurrentWidget = nullptr;
    }

    if (NewWidgetClass != nullptr)
    {
        CurrentWidget = CreateWidget(GetWorld(), NewWidgetClass);
        if (CurrentWidget != nullptr)
        {
            CurrentWidget->AddToViewport();
        }
    }
}

 

함수 구현할 때도, 변경된 선언에 맞춰서 매개변수 타입을 아래와 같이 수정하자.

 

void AHowTo_UMGGameModeBase::ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass)

 

이 코드는 제공된 위젯 인스턴스를 만들어 화면에 넣는다. 언리얼 엔진은 한 번에 다수의 위젯을 표시하고 상호작용처리가 가능하며, 한 번에 하나만 활성화 되도록 제거를 할 수도 있다. 하지만 위젯을 직접 소멸시킬 필요는 없는데, 뷰포트에서의 제거 밑 레퍼런싱하는 모든 변수 소거(또는 변경) 작업은 언리얼 엔진의 가비지 컬렉션 시스템에서 해주기 때문이다.

 

마지막으로 Player Controller 클래스에 입력 모드를 설정해야 한다. 그러기 위해서 Player Controller를 기반으로 새로운 C++ 클래스를 추가하자. 이 클래스 안해서 게임이 시작될 때 함수 하나를 추가로 호출해주기만 하면 UI 요소와 상호작용이 가능하도록 할 수 있다.

 

 

 

HowTo_UMGPlayerController.h에서 클래스에 다음 오버라이드를 추가하고 .cpp에 구현한다.

 

public:
    virtual void BeginPlay() override;

 

void AHowTo_UMGPlayerController::BeginPlay()
{
    Super::BeginPlay();
    SetInputMode(FInputModeGameAndUI());
}

 

메뉴 생성 및 표시와 필요없어진 메뉴를 제거하기 위한 코드 프레임워크를 완성했다. 이제 언리얼 에디터로 돌아가 메뉴 에셋을 디자인 해보자.

 

 

 

 

 

3. 메뉴 위젯 블루프린트 생성(문서)

 

언리얼 에디터에서 컴파일 버튼을 누르면 수정된 코드가 빌드된다. 이를 통해서 유저 위젯을 메뉴로 사용할수 있게 된다.

 

 

이제 게임 모드가 메뉴로 사용할 유저 위젯을 생성해보자. 콘텐츠 브라우저의 "신규 추가" 버튼을 누르고 유저 인터페이스 카테고리에서 위젯 블루프린트(Widget Blueprint) 클래스를 선택해서, "MainMenu"와 "NewGameMenu"라는 이름으로 두 개의 유저 위젯을 만든다.

 

 

방금 만든 "MainMenu" 위젯을 더블클릭하면 블루프린트 디자이너 창이 열리며, 여기서 메뉴 레이아웃을 만들 수 있다.

 

팔레트 패널의 일반 섹션에서 버튼(Button)과 텍스트(Text)를 끌어 그래프에 배치한다. 이 버튼은 새 게임 메뉴를 여는데 사용될 것이다.

 

 

버튼의 위치와 크기를 다음과 같이 수정하고, 함수성 연결을 해줄 때 알아보기 쉽게 하기 위해서 이름을 "NewGameButton"으로 변경한다.

 

 

그리고 이 버튼이 무슨 버튼인지 보여주기 위해서 텍스트 블록(Text Block)을 버튼 위로 끌어다 놓고 디테일을 다음과 같이 수정한다.

 

- Text를 "New Game"으로 변경

 

- Visibility를 Hit Test Visibility로 변경한다. 그러면 버튼을 누르려는 클릭을 텍스트 블록이 막지 않는다.

 

- 이름을 "NewGameText"로 변경한다. 필수는 아니지만 나중에 계층구조에서 어떤 UI인지 찾기 쉬워지기 때문에 들여두면 좋은 습관이 된다.

 

 

 

두 번째 버튼과 텍스트 블록을 만들어서 "Quit"(종료) 기능을 만든다. 버튼 이름은 "QuitButton", 버튼 위치는 (600, 100), 텍스트 블록 이름은 "QuitText"로 설정한다.

 

그 다음은, 버튼을 클릭했을 때, 코드가 실행되도록 버튼에 이벤트를 추가하는 작업을 해야한다. 디테일 패널에서 적합한 이벤트의 이름 옆에 "+"버튼을 찾아서 누르면 되는데 이 경우에는 "OnClicked" 이벤트를 추가하면 된다.

 

 

NewGameButton의 OnClicked 이벤트를 다음과 같이 구성한다.

 

 

QuitButton의 OnClicked 이벤트를 다음과 같이 구성한다.

 

 

 

메인 메뉴를 만들었으니, 레벨이 시작되면 메인 메뉴를 로드하는 게임 모드 애셋을 구성하면 된다.

 

 

4. 게임 모드 환경설정(문서)

 

콘텐츠 브라우저에서 프로젝트의 게임 모드에 맞는 블루프린트 클래스를 두 개 추가할 것이다. 그러면 그 두 클래스에 노출된 변수를 원하는 값으로 설정하는 것이 가능하다.

 

콘텐츠 브라우저에서 신규 추가버튼에서 블루프린트 클래스를 클릭한다.

 

 

부모 클래스로 HowTo_UMGGameModeBase를 선택해서 "MenuGameMode" 블루프린트 클래스를 만든다.

 

 

그리고 게임 내에서 마우스 커서를 보이게 하기 위해서, 플레이어 컨트롤러의 블루프린트 클래스도 만들어 주어야 한다. 콘텐츠 브라우저에서 블루프린트 클래스를 클릭하고 Player Controller 클래스 상속받아서 "MenuPlayerController"라는 이름으로 클래스를 생성하자.

 

"MenuPlayerController" 클래스가 생성되었으면, 콘텐츠 브라우저에서 블루프린트 파일을 더블클릭해서 블루프린트 에디터를 연다. 그리고 디테일 창에서 "Show Mouse Cursor" 박스를 체크한다.

 

 

다음은 "MenuGameMode"를 편집한다.

 

Starting Widget Class를 "MainMenu" 애셋으로 설정해서 게임 시작시 메뉴가 뜨도록 만든다.

 

Default Pawn Class를 Default Pawn이 아닌 Pawn으로 설정해서 플레이어가 메뉴에 있을 때, 이리저리 날아다니지 않도록 만든다.

 

Player Controller Class를 방금 만든 "MenuPlayerController" 애셋으로 설정해서 메인 메뉴에서 마우스 커서가 표시되도록 만든다.

 

 

우리가 만든 게임 모드 블루프린트를 사용하려면, 레벨 에디터 창으로 돌아와 세팅 버튼을 통해 현재 레벨에 대한 월드 세팅을 변경해야 한다.

 

 

프로젝트 세팅 메뉴의 맵 & 모드에서도 기본 게임 모드 설정이 가능하다. 이 방법을 사용하면 따로 덮어쓰지 않는 한 모든 레벨에서 기본 게임 모드로 설정된다. 어느 방법을 사용할지는 프로젝트 구성에 따라 달라질 수 있다.

 

월드 세팅 패널에서 Game Mode Override 항목을 "MenuGameMode" 애셋으로 설정한다.

 

 

이제 레벨에 메인 메뉴를 로드하고, 마우스 커서를 표시하는 플레이어 컨트롤러를 사용하도록 환경설정된 커스텀 게임 모드 애셋이 적용되었다. 이제 게임을 실행하면 Quit 버튼은 정상적으로 작동하지만, 아직 New Game 버튼은 빈 메뉴 화면으로 이동한다. 다음 단계에서는 New Game Menu를 구성해주자.

 

 

 

 

 

5. 2차 메뉴 제작(문서)

 

콘텐츠 브라우저에서 아까 만든 "NewGameMenu" 애셋을 연다. 이 메뉴는 이름을 입력할 수 있는 텍스트 박스와, 이름을 입력하기 전에는 누를 수 없는 '게임 플레이' 버튼, 메인 메뉴로 돌아가는 버튼으로 구성된다.

 

이름 입력 박스를 만들기 위해, 레이아웃에 Text Box(텍스트 박스)를 배치한다.

 

 

텍스트 박스의 설정은 다음과 같다.

 

 

이전 메뉴에서 버튼을 만들었던 것과 같은 방식으로 텍스트 블록 라벨이 있는 게임 플레이 버튼을 만든다.

 

버튼 : 이름은 PlayGameButton, 위치는 200, 300, 크기는 200, 100으로 변경한다.

 

텍스트 블록 : 이름은 PlayGameText, Visibility는 Hit Test Visible로, 내용은 Play Game으로 변경한 다음 PlayGameButton위에 배치한다.

 

게임 플레이 버튼의 경우, 만약 플레이어 이름 입력란이 비어있다면 작동하지 않도록 특수한 기능을 추가한다. UMG의 바인드 기능을 사용하여 (Behavior섹션 아래) "Is Enabled" 칸에 새로운 함수를 만들면 된다.

 

 

텍스트 박스가 공백이 아니어서 버튼이 활성화 될 수 있는 상태인지 확인하려면, 텍스트 박스에서의 텍스트를 스트링으로 변환한 다음 길이가 0보다 큰지 검사하면 된다.

 

 

이제 메인 메뉴로 돌아갈 수 있도록 버튼을 하나 추가해보자. 메인 메뉴에서 게임 플레이 버튼과 비슷하지만, 위치 기준이 좌상단이 아닌 우하단 구석이 될 것이다. 그러기 위해서는 디테일 패널에서 "앵커" 드롭다운을 클릭한 다음, 팝업 메뉴에서 우하단 부분을 나타내는 모양을 선택한다.

 

버튼 이름을 MainMenuButton으로 설정한다.

 

위치를 -400, -200으로 설정한다.

 

크기를 200x100으로 설정한다.

 

 

이제 NewGameMenu 위젯의 버튼들에도 OnClicked 이벤트들을 추가하자

 

 

메인 메뉴 버튼의 경우, 다시 메인 메뉴 위젯을 열어주지만, 게임 플레이 버튼은 누르면 메뉴를 비활성시킨 후, 게임에서 더 이상 아무것도 할 수 없게 만든다. 보통 이 시점에서 첫 레벨을 로드하고, 오프닝 동영상을 재상하거나 폰을 스폰시켜 빙의하는 등의 처리를 하게 된다.

 

모든 작업을 마치고 플레이 해보면 다음 스크린샷과 같은 장면을 얻을 수 있다.

 

 

 

 


 

이번 섹션에서 배운 것

 

1. 언리얼 모듈 종속성

언리얼 엔진의 기능은 다수의 모듈로 나누어져 있고, 그 중에 필요한 모듈을 묶어서 사용하는 방식이다. 이번 섹션 처음 부분에 build.cs파일에서 모듈 종속성을 구성할 때도 보았겠지만, 기본적으로 언리얼은 Core, CoreUObject, Engine, InputCore 모듈을 사용하고 있었고, UI와 관련된 기능을 사용하기 위해서 UMG 모듈과 Slate, SlateCore 모듈을 구성에 추가해주었다.

 

추후의 일이지만, 언리얼 엔진을 커스터마이징하고자 할 때, 새롭게 추가하는 기능을 이러한 모듈로 만들어 덧붙이게 될 것이다.

 

2. TSubClassOf<T>

UClass 타입 안정성을 보장하는 템플릿 클래스. TSubClassOf에 전달된 인수가 템플릿 인자로 받은 타입과 일치하거나 템플릿 인자로 받은 타입을 상속받은 타입인지를 런타임 중에 확인하도록 도와주는 클래스이다.

 
3. UUserWidget
UUserWidget* UserWidget;

Widget Blueprint를 통해서 확장할 수 있는 사용자 위젯.

UserWidget->AddToViewport();

유저 위젯을 뷰 포트에 추가하는 함수.

UserWidget->RemoveToViewport();

유저 위젯을 뷰 포트에서 제거하는 함수.

 

4. AActor::GetWorld()

GetWorld();

UWorld 객체를 가져오는 함수. UWorld는 액터나 컴포넌트들을 포함하는 맵이나 샌드박스의 최상위 객체이다.

 

5. CreateWidget()

CreateWidget(GetWorld(), newWidget);

위젯을 생성하는 함수

 

6. APlayerController::SetInputMode()

SetInputMode(FInputModeGameAndUI());

플레이어 컨트롤러의 입력 모드를 설정하는 함수. Game 입력만 받을지, UI 입력만 받을지, 아니면 둘 다 받을지를 정할 수 있다.

 

7. UPROPERTY()

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UMG_Game")

클래스의 멤버 변수에 붙이는 매크로. 에디터의 노출 수준을 정할 수 있다.

 

EditAnywhere는 에디터 디테일 패널에 노출이 가능하다.

 

BlueprintReadOnly는 블루프린트에서 수정을 불가능하고 읽기만 가능하다.

 

Category는 블루프린트에서 불러올 때, 카테고리를 분류해서 찾기 쉽게 만들어준다.

 

8. UFUNCTION()

UFUNCTION(BlueprintCallable, Category = "UMG_Game")

함수에 붙이는 매크로.

 

BlueprintCallable은 블루프린트에서 호출만 가능하다는 의미이다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

UI 리소스 최적화로 메모리와 용량 최적화 잡기

 

최근 게임을 제작할 때, PC를 타깃으로 한 평범한 수준의 게임은 유저들의 평균적인 메모리 사양이 4~8GB 가량 되기 때문에 메모리에 크게 구애받는 일은 적은 편이지만, 모바일을 타깃으로 하거나, PC나 콘솔 타깃이더라도 고사양의 게임은 메모리에 대한 최적화가 필요하다.
 
특히 모바일의 경우, 하이엔드 모델은 3~4GB 이상의 넉넉한 메모리를 지원하는 모델이지만, 대다수의 사용자들이 사용하는 보급형 모델은 1~2GB 수준으로 메모리 최적화를 고려하지 않고 게임을 만들었다면, 저사양의 유저들은 게임을 원활하게 게임을 플레이할 수 없을 것이다.
 
물론 애초에 고사양 타깃으로 만들어진 게임이면 어쩔 수 없는 일이지만, 중저사양의 모델 역시 타깃으로 잡았고 메모리를 제외하면 분명 중저사양에서도 돌아갈 수 있는 게임임에도 불구하고 메모리 최적화 때문에 중저사양 모델에서 돌리지 못한다면 문제가 있다.
 
이런 메모리 최적화 문제에 대한 해결책은 여러가지가 있지만 그 중에서도 제일 먼저 살펴보아야할 부분이 UI다. 그럼 UI 텍스처 최적화를 통한 메모리 최적화에 대해서 알아보도록 하자.
 

 

 

 

 

유니티의 텍스처 압축 지원 받기

 
유니티에서는 텍스처 리소스에 대해서 기본적인 압축 기능을 자체적으로 제공한다. 이러한 압축을 지원받는 것 만으로도 압축되지 않은 리소스에 비해서 상당한 수준의 용량을 아낄 수 있게 된다.
 
유니티의 텍스처 압축은 모든 텍스처에 적용되는 것은 아니고 한 가지 제약사항이 존재한다.
 

 

그것은 바로 텍스처의 너비와 높이 둘 다 4의 배수가 되어야 한다는 것이다. 만약 너비나 높이 둘 중 하나라도 4의 배수가 되지 못하면, 해당 텍스처는 무압축 상태로 빌드된다.

 

 

 

위의 이미지를 보면 텍스처 압축을 지원받지 못한 900x359 크기의 텍스처는 1.2MB지만 900x360 크기의 텍스처는 RGBA Compressed DXT5 포맷으로 압축되어 316.4KB로 엄청나게 크기가 줄어든 것을 확인할 수 있다.

 

단, DXT5 포맷의 압축 방식은 iOS에서는 지원되지 않기 때문에, 다른 압축 포맷을 직접 지정하는것이 좋다.

 

 

 

 

스프라이트 패킹(Sprite Packing)

 

UI 텍스처 압축을 통해 용량을 아꼈다면 이번에는 메모리를 아껴볼 차례다.

 

 

유니티에서 이미지는 메모리에 올라갈 때, 정사각형의 형태나 각 변의 길이가 POT(2의 승수, 128 256 512 같은..)인 형태로 올라가게 되는데, 만약 이미지가 정사각형이 아니거나 각 변의 길이가 POT인 형태라면 이미지의 가로변과 세로변의 길이 중 긴 변의 길이에 맞춰서 정사각형의 크기의 메모리를 할당받기 때문에 낭비되는 메모리 공간이 많아진다. 이 문제는 한 쪽변이 다른 한 쪽변의 길이에 비해서 매우 길어질 수록 심해진다.

 

 

 

이러한 문제를 해결하기 위한 것이 바로 스프라이트 패킹이다. 스프라이트 패킹이란 여러개의 이미지를 같은 패키지로 패킹해서 메모리에 함께 올리는 것으로 모든 UI 텍스처를 따로 메모리에 올렸을 때보다 메모리의 낭비를 많이 줄일 수 있게 된다.

 

유니티에서 스프라이트 패킹 방법으로 레거시 스프라이트 패커(Legary Sprite Packer)와 스프라이트 아틀라스(Sprite Atlas) 두 가지를 제공한다.

 

 

 

 

슬라이스드 이미지 사용하기(Sliced Image)

 

사실 UI 리소스의 경우에는 특별한 이미지가 많이 사용된다기 보다는 사용되는 이미지가 반복되어서 사용되는 경우가 많다. UI를 묶어주는 패널(Pannel), 입력을 받는 인풋 필드(Input Field), 버튼(Button) 등이 여기에 속한다.

 

이런 것에 사용되는 리소스는 재사용성을 높여야 되지만, 초보 개발자나 초보 UI 디자이너는 예쁘거나 멋지게 만들어야 되는다는 집착에 빠지거나, 최적화에 대한 신경을 못쓰고 만들어서, 패널이나 버튼에 들어갈 이미지를 크기에 맞춰서 필요한 모든 사이즈 별로 만드는 경우가 종종 있다.

 

 

예를 들어 760x960 크기의 UI가 필요해서 거기에 해당하는 리소스를 만들어 냈다고 해보자. 이걸 게임에 그대로 적용해버리면 패널을 꾸미기에 따라서는 UI가 예뻐보일 수는 있을 것이다. 하지만 압축된 리소스임에도 불구하고 0.7MB라는 엄청난 용량을 자랑하는 것을 볼 수 있다. 이게 겨우 하나여서 0.7MB지, 여러 종류의 많은 UI를 띄워야 하는 게임이어서 해당 UI의 크기 별로 패널 리소스를 새로 만들어서 적용한다면 그리고 그 와중에 몇몇 리소스가 압축되지 않는 불상사가 발생한다면 게임의 용량이나 메모리 소모는 엄청난 수준이 될 것이다.

 

버튼이나 패널 같은 UI의 리소스의 경우에는 일반적으로 리소스의 모서리 부분을 제외한 중심 부분은 반복되는 경우가 많다. 슬라이스드 이미지란 바로 이 점에서 착안한 아이디어로 모서리 부분과 반복될 중심 부분 조금만 있으면 유니티 엔진이 중심 부분을 자동으로 채워주는 기능이다. 그렇기 때문에 패널 리소스 중에서도 모서리 부분과 반복될 중심 부분 약간 만으로 리소스를 만들면 위 이미지와 같은 커다란 패널 UI 리소스는 아래 이미지와 같이 아주 작게 줄일 수 있다.

 

 

 

불필요한 중심 부분을 제거하는 것만으로도 이미지의 크기와 용량이 700분의 1로 줄어들었다.

 

 

 

 

리소스의 중심 부분을 제거한 뒤에는 유니티의 스프라이트 에디터(Sprite Editor)에서 원형을 유지할 부분과 크기가 늘어났을 때 자동으로 채워질 부분을 설정해주면된다. 위의 그림처럼 설정하면 UI의 크기가 늘어났을 때 모서리 부분은 원형을 유지하고 가운데 십자 부분만 자동으로 채워지는데 씬에서 UI Image를 추가할 때, Image Type를 Sliced로 변경해주면 작은 이미지가 전혀 확대되거나 이상한 모양으로 늘어지지 않음을 확인할 수 있다.

 

 

 

 

 

오른쪽의 패널이 슬라이스드 이미지를 사용한 UI이고 오른쪽은 그냥 패널 이미지를 통째로 사용한 것이다. 둘이 차이가 없음을 확인할 수 있다. 하지만 패널 리소스에 그라데이션이나 디자인이 들어갔다면 그 퀄리티는 다를 수도 있다. 하지만 그 부분은 저사양에서는 단순한 패널만 보여주고 유저가 고사양을 선택한다면 패널 UI 위에 디자인 이미지를 올려서 보여줄 수도 있다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

'Unity3D > Optimize' 카테고리의 다른 글

[Unity] 유니티 공식 지원 오브젝트 풀링  (4) 2022.08.09

+ Recent posts