🤍ref.
- 이득우의 언리얼 C++ 게임 개발의 정석, 이득우, 에이콘출판사, 2018
- Unreal Engine 4.27 Documentation
✨info
튜토리얼 교재는 4.19v을 사용하나, 4.27v으로 실습 후 게시글을 작성하였습니다.
새로 알게 된 내용과 추가로 공부한 내용 위주로 작성합니다. 배우는 과정이라 부족한 점이 많습니다.😊
1. 프로젝트 설정
1.1. 소스 파일 정리
언리얼 소스 코드 구조
Classes
: 언리얼 오브젝트 관련 헤더Public
: 외부에 공개하는 선언 파일Private
: 외부에 공개하지 않는 정의 파일
소스 코드 폴더에 Public과 Private 폴더를 만들어 .h 파일과 .cpp 파일을 옮겨보자. 솔루션을 재생성하면 프로젝트 구성이 바뀐 것을 확인할 수 있다.
*.Target.cs
: 게임 빌드와 에디터 빌드 설정 지정
1.2. 모듈 추가
주 게임 모듈 외에 다른 모듈을 게임 프로젝트에 추가하고 로직을 분리해 관리할 수 있다.
단, 추가 모듈은 자동으로 생성되지 않으므로 필요한 요소를 직접 추가해야 한다.
- 모듈 폴더와 빌드 설정 파일: 모듈 폴더와 모듈명으로 된 Build.cs 파일
- 모듈의 정의 파일: 모듈명으로 된 .cpp 파일
솔루션파일을 재생성한 후 VS에서 추가한 모듈을 빌드할 수 있도록 ArenaBattle.Target.cs
파일과 ArenaBattleEditor.Target.cs
파일 정보를 수정해야 한다. 모듈 설정까지 추가하고 나면 빌드 명령 시 새로운 모듈을 컴파일한다.
모듈 추가 과정
- 모듈을 생성하기 위한 필수 파일 생성 후 Source 폴더에 복사
- 솔루션 파일 재생성
ArenaBattle.Target.cs
와ArenaBattleEditor.Target.cs
정보 수정 후 빌드- 새 DLL 파일에 모듈 정보 기입
ArenaBattle
모듈의 의존성에ArenaBattleSetting
기입Object
를 부모로 하는ABCharacterSetting
클래스 생성- ArenaBattleSetting 모듈에 속한 오브젝트 필요
- 오브젝트의 모듈을
ArenaBattleSetting
으로 선택
- 프로젝트 재생성 후 빌드
ArenaBattle.Target.cs
1
2
3
4
5
6
7
8
9
public class ArenaBattleTarget : TargetRules
{
public ArenaBattleTarget(TargetInfo Target) : base(Target)
{
...
ExtraModuleNames.AddRange( new string[] { "ArenaBattle", "ArenaBattleSetting" } );
}
}
ArenaBattleEditor.Target.cs
1
2
3
4
5
6
7
8
9
public class ArenaBattleEditorTarget : TargetRules
{
public ArenaBattleEditorTarget(TargetInfo Target) : base(Target)
{
...
ExtraModuleNames.AddRange( new string[] { "ArenaBattle", "ArenaBattleSetting" } );
}
}
ArenaBattle.uproject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"FileVersion": 3,
"EngineAssociation": "4.27",
"Category": "",
"Description": "",
"Modules": [
{
"Name": "ArenaBattleSetting",
"Type": "Runtime",
// 다른 모듈모다 먼저 로딩되도록 함
"LoadingPhase": "PreDefault"
},
{
"Name": "ArenaBattle",
"Type": "Runtime",
"LoadingPhase": "Default",
"AdditionalDependencies": [
"Engine",
"UMG",
"AIModule",
// ArenaBattleSetting에 대한 의존성 부여
"ArenaBattleSetting"
]
}
]
}
추가 모듈 로딩 결과
2. INI 설정
2.1. 오브젝트 기본값 지정
지금까지 애셋 정보를 생성자 코드로 지정했지만, 애셋이 변경되면 코드를 다시 만들고 컴파일해야 한다. 애셋의 효율적인 관리를 위해 새로 추가한 ABCharacterSetting
모듈에 앞으로 사용할 애셋의 목록을 보관하자. 애셋의 경로는 FSoftObjectPath
클래스를 활용해 저장한다.
언리얼 오브젝트의 기본값을 유연하게 관리하도록 INI 파일에서 기본 속성 값을 지정하는 기능을 제공한다. 애셋 경로 정보를 참조해 프로그램을 로딩할 수 있다. 기본값을 INI 파일에서 불러들이도록 설정하면 언리얼 오브젝트를 초기화할 때 해당 속성의 값을 INI 파일에서 읽어온다.
기본값을 지정해 생성하는 객체를 클래스 기본 객체라고 한다. 언리얼 엔진 초기화 시 모든 클래스 기본 객체가 메모리에 올라간다. 메모리에 올라간 클래스 기본 객체는 엔진 종료 직전까지 상주하므로 항상 객체를 활용할 수 있다.
INI를 활용한 기본값 지정 과정
UCLASS
매크로에config
키워드 추가- 불러들일 INI 파일명 지정
UPROPERTY
속성에config
키워드 선언ArenaBattle.Build.cs
파일에 참조할 모듈 목록 추가- .cpp 파일에만 모듈을 사용하므로
PrivateDependencyModule
항목에 추가
- .cpp 파일에만 모듈을 사용하므로
ABCharacter
에서ABCharacterSetting
의 애셋 목록을 얻어오는 코드 작성- ArenaBattleSetting 모듈이 ArenaBattle 모듈보다 빨리 로딩되므로
GetDefault
함수를 사용해 애셋 정보 불러오기 가능GetDefault
: 클래스 기본 객체 가져옴
- ArenaBattleSetting 모듈이 ArenaBattle 모듈보다 빨리 로딩되므로
ABCharacterSetting.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "ABCharacterSetting.generated.h"
UCLASS(config=ArenaBattle)
class ARENABATTLESETTING_API UABCharacterSetting : public UObject
{
GENERATED_BODY()
public:
UABCharacterSetting();
UPROPERTY(config)
TArray<FSoftObjectPath> CharacterAssets;
};
ABCharacterSetting.cpp
1
2
3
4
UABCharacterSetting::UABCharacterSetting()
{
}
ArenaBattle.Build.cs
1
2
3
4
5
6
7
8
9
public class ArenaBattle : ModuleRules
{
public ArenaBattle(ReadOnlyTargetRules Target) : base(Target)
{
...
PrivateDependencyModuleNames.AddRange(new string[] { "ArenaBattleSetting" });
}
}
ABCharacter.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "ABCharacterSetting.h"
AABCharacter::AABCharacter()
{
...
auto DefaultSetting = GetDefault<UABCharacterSetting>();
if (DefaultSetting->CharacterAssets.Num() > 0)
{
for (auto CharacterAsset : DefaultSetting->CharacterAssets)
{
ABLOG(Warning, TEXT("Character Asset : %s"), *CharacterAsset.ToString());
}
}
}
INI파일 설정값 로딩 결과
2.2. 캐릭터 애셋 로딩
애셋 경로를 통해 NPC가 생성될 때 랜덤하게 목록 중 하나를 골라 캐릭터 애셋을 로딩하는 기능을 만들 수 있다. ABGameInstance
클래스에 FStreamableManager
클래스를 멤버 변수로 선언해 애셋 로딩 명령을 내린다.
FStreamableManager
: 게임 진행 도중 비동기 방식으로 애셋 로딩- 프로젝트에서 하나만 활성화하는 것을 권장
AsyncLoad
: 비동기 방식 에셋 로딩 명령
👀싱글톤 설정
게임 인스턴스는 싱글톤처럼 동작해 FStreamableManager
클래스를 멤버 변수로 선언했지만 별도로 싱글톤으로 지정할 수도 있다.프로젝트 설정
-> Default Classes
섹션의 고급 섹션에서 설정 가능하다!
ABGameInstance.h
1
2
3
4
5
6
7
8
9
10
11
#include "Engine/StreamableManager.h"
class ARENABATTLE_API UABGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
...
FStreamableManager StreamableManager;
}
ABCharacter.h
1
2
3
4
5
6
7
8
9
10
class ARENABATTLE_API AABCharacter : public ACharacter
{
private:
...
void OnAssetLoadComplete();
private:
FSoftObjectPath CharacterAssetToLoad = FSoftObjectPath(nullptr);
TSharedPtr<struct FStreamableHandle> AssetStreamingHandle;
}
ABCharacter.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "ABGameInstance.h"
void AABCharacter::BeginPlay()
{
Super::BeginPlay();
/*auto CharacterWidget = Cast<UABCharacterWidget>(HPBarWidget->GetUserWidgetObject());
if (nullptr != CharacterWidget)
{
CharacterWidget->BindCharacterStat(CharacterStat);
}*/
if (!IsPawnControlled())
{
auto DefaultSetting = GetDefault<UABCharacterSetting>();
int32 RandIndex = FMath::RandRange(0, DefaultSetting->CharacterAssets.Num() - 1);
CharacterAssetToLoad = DefaultSetting->CharacterAssets[RandIndex];
auto ABGameInstance = Cast<UABGameInstance>(GetGameInstance());
if (nullptr != ABGameInstance)
{
AssetStreamingHandle = ABGameInstance->StreamableManager.RequestAsyncLoad(CharacterAssetToLoad, FStreamableDelegate::CreateUObject(this, &AABCharacter::OnAssetLoadCompleted));
}
}
}
void AABCharacter::OnAssetLoadCompleted()
{
USkeletalMesh* AssetLoaded = Cast<USkeletalMesh>(AssetStreamingHandle->GetLoadedAsset());
AssetStreamingHandle.Reset();
if (nullptr != AssetLoaded)
{
GetMesh()->SetSkeletalMesh(AssetLoaded);
}
}
결과 화면
Leave a comment