오늘은 그동안 작업한 프로젝트를 정리하고 모듈을 추가해보겠다
사용한 예제 파일은 링크에서 받을 수 있다
그동안은 헤더 파일과 cpp 파일이 한 폴더에 같이 존재했다
언리얼 엔진에서는 폴더에 따라 파일을 정리해주는 기능이 있다
Classes
모든 게임플레이 클래스 헤더파일이 들어감
Private
모든 cpp 파일이 들어감
Public
모든 모듈 헤더 파일이 들어감
우리는 이번에 규모가 크지 않음으로 Private
폴더와 Public
폴더로 나누도록 하겠다
프로젝트 폴더의 Source 폴더로 들어가 Private
에는 cpp, Public
에는 h 파일을 넣어준다
완성하면 다음과 같다
다음 uproject 파일을 우 클릭후 Generate Visual Studio Project files 를 눌러준다
이후 비주얼 스튜디오를 켜보면 폴더가 나뉜채로 존재하게 된다
언리얼 엔진에서는 추가로 모듈을 생성하는 기능은 제공하지 않는다
따라서 우리가 직접 모듈을 추가해야 하는데 필요한 요소는 다음과 같다
이번에는 예제에 있는 ArenaBattleSetting
폴더 안에 모두 존재하기 때문에,
ArenaBattleSetting
폴더를 Source 폴더에 넣고 다시 한번 재생성을 해준다
이제 비주얼 스튜디오에서 추가한 모듈을 빌드해주도록 설정해주어야한다
각각 게임을 빌드하는 ArenaBattle.Target.cs 와 에디터를 빌드하는 ArenaBattleEditor.Target.cs 를 수정해주자
ArenaBattle.Target.cs
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class ArenaBattleTarget : TargetRules
{
public ArenaBattleTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V2;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_1;
ExtraModuleNames.AddRange(new string[]{ "ArenaBattle", "ArenaBattleSetting" });
}
}
ArenaBattleEditor.Target.cs
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class ArenaBattleEditorTarget : TargetRules
{
public ArenaBattleEditorTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Editor;
DefaultBuildSettings = BuildSettingsVersion.V2;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_1;
ExtraModuleNames.AddRange(new string[] { "ArenaBattle", "ArenaBattleSetting" });
}
}
모듈을 추가 후 컴파일을 진행하게 되면, Binaries
폴더에 새로운 파일이 생성된다
이렇게 dll 파일이 생성되었다면 에디터가 dll 파일을 로딩하도록 수정해주어야 한다
이때 ArenaBattleSetting
모듈을 먼저 로딩하고, ArenaBattle
모듈이 ArenaBattleSetting
에 의존성을 가지게 설정할 것이다
ArenaBattle.uproject
{
"FileVersion": 3,
"EngineAssociation": "5.1",
"Category": "",
"Description": "",
"Modules": [
{
"Name": "ArenaBattleSetting",
"Type": "Runtime",
"LoadingPhase": "PreDefault",
"AdditionalDependencies": [
"CoreUObject"
]
},
{
"Name": "ArenaBattle",
"Type": "Runtime",
"LoadingPhase": "Default",
"AdditionalDependencies": [
"Engine",
"UMG",
"AIModule",
"ArenaBattleSetting"
]
}
],
"Plugins": [
{
"Name": "ModelingToolsEditorMode",
"Enabled": true,
"TargetAllowList": [
"Editor"
]
}
]
}
이후 C++ 클래스를 생성하면 ArenaBattleSetting
모듈로 생성할수 있게 되어 있는 것을 볼 수 있다
또한 앞으로 생성하는 C++ 클래스는 헤더와 소스 파일이 나뉘어 저장된다
이번에는 ArenaBattleSetting
모듈에 속하고 Object를 부모로 삼는 ABCharacterSetting 클래스를 만들어준다
이 클래스를 통해 INI 파일의 정보를 받고, 그것을 불러와 설정을 바꿔줄 것이다
INI 파일을 받기 위해서는 UCLASS 매크로에 config 키워드를 추가해주어야 한다
ABCharacterSetting.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#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
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABCharacterSetting.h"
UABCharacterSetting::UABCharacterSetting()
{
}
이후 config 폴더에 DefaultArenaBattleSetting
파일을 넣어준다
다음은 ArenaBattle.Build.cs 파일에 ArenaBattleSetting 모듈을 추가해준다
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class ArenaBattle : ModuleRules
{
public ArenaBattle(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay",
"EnhancedInput", "UMG", "NavigationSystem", "AIModule", "GameplayTasks" });
PrivateDependencyModuleNames.AddRange(new string[] { "ArenaBattleSetting" });
}
}
마지막으로 ABCharacter 클래스에서 불러온 모듈을 사용해 적의 에셋을 랜덤하게 불러와준다
ABCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Character.h"
#include "ABCharacter.generated.h"
DECLARE_MULTICAST_DELEGATE(FOnAttackEndDelegate);
UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
GENERATED_BODY()
...
private:
...
void OnAssetLoadCompleted();
FSoftObjectPath CharacterAssetToLoad = FSoftObjectPath(nullptr);
TSharedPtr<struct FStreamableHandle> AssetStreamingHandle;
};
ABCharacter.cpp
#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "DrawDebugHelpers.h"
#include "ABWeapon.h"
#include "ABCharacterStatComponent.h"
#include "Components/WidgetComponent.h"
#include "ABCharacterWidget.h"
#include "ABAIController.h"
#include "ABGameInstance.h"
#include "ABCharacterSetting.h"
#include "Engine/AssetManager.h"
...
void AABCharacter::BeginPlay()
{
Super::BeginPlay();
auto CharacterWidget = Cast<UABCharacterWidget>(HPBarWidget->GetUserWidgetObject());
if (nullptr != CharacterWidget)
CharacterWidget->BindCharacterStat(CharacterStat);
if (!IsPlayerControlled())
{
auto DefaultSetting = GetDefault<UABCharacterSetting>();
int32 RandIndex = FMath::RandRange(0, DefaultSetting->CharacterAssets.Num() - 1);
CharacterAssetToLoad = DefaultSetting->CharacterAssets[RandIndex];
AssetStreamingHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad
(CharacterAssetToLoad, FStreamableDelegate::CreateUObject(this, &AABCharacter::OnAssetLoadCompleted));
}
}
...
void AABCharacter::OnAssetLoadCompleted()
{
USkeletalMesh* AssetLoaded = Cast<USkeletalMesh>(AssetStreamingHandle->GetLoadedAsset());
AssetStreamingHandle.Reset();
if (nullptr != AssetLoaded)
GetMesh()->SetSkeletalMesh(AssetLoaded);
}
...
NPC 상대는 에셋이 랜덤하게 적용되는 것을 볼 수 있다