INI 파일 설정과 런타임 에셋 로딩

1000·2020년 12월 4일
0

언리얼 기초 공부

목록 보기
11/16

1. 언리얼의 INI 기능을 사용해보자

부모클래스를 Pawn을 선택해 플레이어가 조종할 수 있는 액터인 폰을 제작합시다.블루프린트나 별도의 에디터 작업이 없어도 C++ 클래스의 기본 값을 코드가 아닌 외부에서 유연하게 설정할 수 있도록 언리얼 엔진은 INI 파일 설정 기능을 제공하고 있습니다.

원래 INI파일은 윈도우 관련 운영체제에서 시스템 구성요소의 설정을 위해서 고안된 파일 형식입니다. INI 파일의 형식은 속성과 값, 그리고 이들을 포괄하는 섹션으로 구성되어 있습니다. 언리얼 엔진에서는 이 형식을 이렇게 사용합니다.

1. 섹션 : 현재 프로젝트에서 사용하는 C++ 클래스 식별자.
2. 속성 : C++ 클래스에서 INI를 사용하도록 지정한 UPROPERY 멤버 변수.
3. 값 : 속성에 지정할 값.

C++클래스 식별자는 현재 프로젝트에서 고유한 식별자로 구분되어 있으며 아래와 같은 형식을 가지게 됩니다.

{스크립트경로}/{모듈이름}.{클래스이름}

스크립트 경로는 항상 /Script로 시작되며, 모듈이름은 언리얼 오브젝트가 속한 모듈 이름,
클래스이름은 언리얼 오브젝트의 이름이 됩니다

언리얼 엔진에서의 INI의 실제 사례를 확인하기 위해 프로젝트 폴더의 Config 폴더에 가봅시다.
Config 폴더에 있는 DefaultEngine.ini 파일을 메모장으로 열어보면 이미 아래와 같은 설정이 들어 있는 것을 확인할 수 있습니다.

[/Script/EngineSettings.GameMapsSettings]
GameInstanceClass=/Script/ArenaBattle.ABGameInstance

게임 인스턴스 역할을 하는 클래스가 GameInstanceClass라는 프로퍼티에 설정되어 있음을 유추할 수 있을 겁니다. 현재 이 값이 설정된 섹션을 보면 /Script/EngineSettings.GameMapsSettings로 되어 있습니다.

이는 EngineSettings라는 모듈에 있는 GameMapSettings라는 C++ 언리얼 오브젝트 클래스의 GameInstanceClass 변수의 값을 /Script/ArenaBattle.ABGameInstance로 지정하겠다라는 의미가 되겠습니다. 정말 소스코드에서 이런 언리얼 오브젝트와 변수가 있는지 다같이 찾아봅시다.

비주얼 스튜디오의 언리얼 소스 프로젝트인 UE4 프로젝트를 열고 Source의 Runtime 폴더로 갑시다. 이 폴더는 언리얼 엔진에서 에디터/게임이 사용하는 기본 모듈들을 포함하고 있습니다. Runtime의 하단에 있는 폴더를 잘 살펴보면 이전에 WebService 모듈 구조를 만들면서 설명드린 언리얼 엔진의 모듈 규칙을 정확히 준수하고 있음을 확인할 수 있습니다. 여기서 EngineSettings라는 폴더를 살펴보면 눈에 익숙한 Build.cs 파일이 보일겁니다. 따라서 이 폴더는 EngineSettings라는 모듈임을 유추할 수 있습니다.

다시 EngineSettings 폴더를 뒤져보면 일반적으로 언리얼 오브젝트 선언을 담아두는 Classes 폴더안에 GameMapSettings라는 언리얼 오브젝트가 있습니다.

이제 GameMapSettings.h 헤더파일을 열어서 UGameMapSettings 클래스 선언을 살펴봅시다. 클래스에 멤버 변수로 GameInstanceClass라는 변수가 UPROPERTY 매크로와 함께 정의된 것이 보일겁니다. 자세히보면 UPROPERTY에는 config라는 키워드가 선언되어 있습니다. 이는 언리얼 오브젝트 선언에서 지정한 INI 설정 파일에서 기본 값을 읽어오겠다는 의미입니다.

다시 올라가 언리얼 오브젝트 클래스 선언을 보면 상단 UCLASS 매크로에 config=Engine이라고 지정되어 있습니다. 이는 이 언리얼 오브젝트의 config 설정을 DefaultEngine.ini 파일에서 읽어온다는 의미가 되겠습니다. ( 참고로 언리얼 엔진 설치 폴더 내 Config 폴더에는 BaseEngine.ini 파일이 있으며, 여기에 모든 기본값이 세팅되어 있습니다. 이 기본 값을 오버라이드(override)하려면 DefaultEngine.ini 내 동일한 섹션에 필드와 값을 지정해주면 기본 값을 덮어쓰게 됩니다. )

아래 코드에서 관련된 부분을 노란색으로 색칠했으니 확인해보시면 언리얼 엔진이 INI 파일을 어떻게 활용하는지 대략 이해가 되실겁니다.

언리얼 엔진에서는 INI를 여러개 제공하고 있으며 엔진 기능에 관련된 INI는 DefaultEngine.ini 파일로, 게임 로직에 관련된 INI는 DefaultGame.ini 파일을 통해 관리하도록 구성을 제공하고 있습니다. 우리는 엔진 로직이 아니고 게임에 관련된 세팅이기 때문에 DefaultGame.ini 파일을 사용하도록 예제를 진행해봅시다.

메모장으로 DefaultGame.ini 파일을 생성하고 아래와 같이 입력합시다.

[/Script/ArenaBattle.ABPawn]
MaxHP=1000.0

그리고 ABPawn 클래스에 UCLASS에 config를 설정하고
MaxHP 변수의 UPROPERTY에 cofig를 입력합니다.

UCLASS(config=Game)
class ARENABATTLE_API AABPawn : public APawn
{
    GENERATED_BODY()

public:
    UPROPERTY(config, BlueprintReadWrite, EditDefaultsOnly, Category = "Stat")
    float MaxHP;

    UPROPERTY(BlueprintReadWrite, EditInstanceOnly, Category = "Stat")
    float CurrentHP;

};

이렇게 플레이하면 MaxHP가 INI 파일에 설정한 값대로 1000으로 초기화 된것을 볼 수 있습니다.

2. Soft Referencing

이 예제에서 알 수 있듯이 CDO 생성 시점에서는 INI 값이 적용되지 않습니다.
BeginPlay 함수와 같이 게임플레이 런타임에서야 비로소 INI 값은 효력을 발휘하게 되지요.

이번에는 게임플레이 런타임에서 랜덤하게 다른 캐릭터 애셋을 하나 골라서 로딩하도록 코드를 업데이트해봅시다. 이전 강좌에서 애셋을 로딩하기 위해 사용한 생성자 코드의 ConstructorHelpers 클래스 용도는 게임 시작 전 단계에서 애셋이 정확히 있는지 검증하기 위한 목적이 있다고 설명드렸습니다. 하지만 시작전에 검증되어야 할 필수 애셋이 있는 반면, 있던 없던 스트리밍 방식으로 천천히 로딩되도 큰 상관이 없는 애셋도 있습니다. 전자를 하드 레퍼런싱(Hard Referencing), 후자를 소프트 레퍼런싱(Soft Referencing)이라고 하는데, 이번 강좌에서는 소프트 레퍼런싱 방식으로 로딩해봅시다.

소프트 레퍼런싱 방식을 사용하려면 애셋의 정보를 가져올 때에는 FStringAssetReference 구조체를, 애셋의 클래스 정보를 가져올 때에는 FStringClassReference 구조체에 경로 정보를 지정해주면 됩니다.

ABPawn 클래스에 아래와 같이 캐릭터 에셋들을 담을 TArray를 선언합니다.

private:
    UPROPERTY(config)
    TArray<FStringAssetReference> CharacterAssets;

그리고 DefaultGame.ini 파일에 로드할 에셋 정보들을 넣어줍니다.

[/Script/ArenaBattle.ABPawn]
MaxHP=1000.0
+CharacterAssets=/Game/InfinityBladeWarriors/Character/CompleteCharacters/SK_CharM_Barbarous.SK_CharM_Barbarous 
+CharacterAssets=/Game/InfinityBladeWarriors/Character/CompleteCharacters/sk_CharM_Base.sk_CharM_Base 
+CharacterAssets=/Game/InfinityBladeWarriors/Character/CompleteCharacters/SK_CharM_Bladed.SK_CharM_Bladed 

이제 게임플레이 로직에서 CharacterAssets에 들어간 변수 중 하나를 랜덤으로 선택해 로딩하도록 지정합시다. 언리얼 엔진에서 런타임에서 애셋을 로딩하기 위해서는 FStreamableManager라는 구조체를 언리얼 오브젝트에서 선언하고 이를 통해 로딩하도록 고안되어 있습니다. 게임 인스턴스 클래스에서 이를 지정해 모든 액터들이 이를 사용할 수 있도록 지정하겠습니다.

GameInstance.h

UPROPERTY()
FStreamableManager AssetLoader;

ABPawn.cpp의 BeginPlay()

    int32 NewIndex = FMath::RandRange(0, CharacterAssets.Num() - 1);
    UABGameInstance* ABGameInstance = Cast<UABGameInstance>(GetGameInstance());
    if (ABGameInstance)
    {
        TAssetPtr<USkeletalMesh> NewCharacter = Cast<USkeletalMesh>(ABGameInstance->AssetLoader.SynchronousLoad(CharacterAssets[NewIndex]));
        if (NewCharacter)
        {
            Mesh->SetSkeletalMesh(NewCharacter.Get());
        }
    }

에셋을 동기화방식으로 로드해서 에셋이 로드되는 동안 게임 플레이 로직이 멈추는 문제점이 있습니다. 이를 해소하기 위해서는 비동기방식 로드가 필요합니다.

profile
Game Programming

0개의 댓글