언리얼 엔진 플러그인 개발시 다른 플러그인에 종속성을 갖는 것은 자주 생기는 일입니다. 모듈의 Build.cs에서 다른 모듈을 디펜던시에 추가함으로써 라이브러리를 가져다 사용할 수 있게 되는데요, 만약 종속성이 생겨야 하는 모듈이 존재하지 않을 수도 있다면 어떻게 처리해야할까요?
엔진 단에서 직접적인 기능을 제공하지는 않지만, 간접적으로나마 몇 가지 사항을 체크하여 동적으로 종속성의 판단을 수행할 수 있습니다. 다만 동적이라고 표현되었지만 빌드 스크립트에서 지정해 컴파일 타임에 진행된다는 점을 유의해야 합니다.
플러그인이 설치되었는지 몇 가지 단서가 존재할 수 있는데요.
1. 설치된 엔진에 플러그인이 존재하는지 or 프로젝트에 플러그인이 존재하는지
2. 현재 프로젝트에 플러그인이 활성화 되었는지
3. 적합한 Target, Configuration을 위해 허용된 플러그인 인지 확인
이 정도 과정을 거치면 어느정도 판단이 가능합니다.
모든 케이스를 다루지는 않을텐데요, 간단한 예제를 소개하겠습니다.
string ConstraintPluginDir = Path.GetFullPath(PluginDirectory + "/../" + PLUGIN_NAME);
bool IsDirExist = Directory.Exists(ConstraintPluginDir);
if (IsDirExist)
{
ProjectDescriptor Project = ProjectDescriptor.FromFile(Target.ProjectFile);
var projectPlugins = Plugins.ReadProjectPlugins(Target.ProjectFile.Directory);
foreach (PluginInfo pluginInfo in projectPlugins)
{
if (pluginInfo.Name == PLUGIN_NAME)
{
if (
#if UE_4_24_OR_LATER
Plugins.IsPluginEnabledForTarget
#else
Plugins.IsPluginEnabledForProject
#endif
(pluginInfo, Project, Target.Platform, Target.Configuration,
TargetRules.TargetType.Game))
{
PrivateDependencyModuleNames.Add(MODULE_NAME);
}
break;
}
}
}
먼저, 디렉터리를 확인합니다.
저는 프로젝트폴더/Plugins 경로에 모든 플러그인을 설치해두는 스타일인데요, 그에 기반하여 코드를 작성했습니다.
PluginDirectory는 모듈이 소속된 플러그인의 경로를 리턴하는데요, ChatClient.Build.cs 였기 때문에, ProjectRoot/Plugins/ChatClient 를 리턴할 것입니다. 그래서 상위 폴더에 LiteTCP 폴더가 있는지 확인하는 과정을 코드로 작성했습니다.
만약 게임 프로젝트의 Build.cs 에서 확인해야 한다면, Target.ProjectFile에서 Plugins 폴더를 찾은 다음, 플러그인이 포함되었는지 서치할 수 있습니다.
조금 더 심화 해 볼까요?
LiteTCP 모듈 에서 전처리기를 정의하고, 이를 활용하여 ChatClient에서 로직을 구성해 봅시다.
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class LiteTCP : ModuleRules
{
public LiteTCP(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject", "Engine", "Slate", "SlateCore",
}
);
PublicDefinitions.Add("LITE_TCP=1");
}
}
LITE_TCP=1 이라는 전처리기를 넣어 주었는데요, 이는 C++ 코드에서
#define LITE_TCP 1
이 됩니다.
이제 ChatClient 모듈의 코드에서 이 전처리기를 활용하여 로직을 작성합니다.
// Copyright Epic Games, Inc. All Rights Reserved.
#include "ChatClient.h"
#define LOCTEXT_NAMESPACE "FChatClientModule"
void FChatClientModule::StartupModule()
{
#if LITE_TCP
UE_LOG(LogTemp, Log, TEXT("LITE TCP IS LOADED!"));
#endif
}
void FChatClientModule::ShutdownModule()
{
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FChatClientModule, ChatClient)
런타임에 모듈이 로드될 때, LITE_TCP 모듈이 존재한다면 (이미 Build.cs를 통해 컴파일타임에 결정되었음) 로그가 출력될 것입니다. 그런데 만약, 컴파일 타임에 LiteTCP 플러그인이 없다면 컴파일 에러가 발생할텐데요, LITE_TCP 전처리기가 정의되지 않았기 때문입니다.
ChatClient 플러그인을 개발하는 입장에서는 어떤 외부 플러그인이 선택적으로 적용될 수 있을지 미리 알고 있으므로, 이런 상황을 대비하는 방어코드를 넣어주어야 합니다.
ChatClient.Build.cs에서 외부 모듈의 활성화를 판독한 다음, PublicDefinitions.Add로 LITE_TCP=0 을 넣어주면 됩니다. 이번에는 조금 다른 방법으로 해결해 보았습니다.
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
#ifndef LITE_TCP
#define LITE_TCP 0
#endif
class FChatClientModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
원래는 PreCompiledHeader와 같은 헤더에 포함되는 것이 바람직한데요, LITE_TCP 정의를 모듈의 구현부에서 사용했으므로 모듈 헤더에 넣어도 무방하다고 판단되어 모듈헤더에 직접 정의했습니다.
ChatClient.Build.cs에서 처리하지 않은 이유는 다소 설득력이 떨어질 수 있지만 코드를 처음 파악할 때 전처리기가 정의되지 않는 경우를 C++ 코드에서 확인할 수 있는 쪽이 흐름 파악에 도움이 될 것이라 판단했기 때문입니다. 필요에 따라 Build.cs에서 모두 정의해주는 것도 괜찮을 선택일 수 있을 것입니다.
여담
Build.cs에서 넣어주면 IDE 레벨에서는 predefined라고 뜨며 정의부를 찾아주지 못하는데요,
LITE_TCP_INTERNAL과 같이 실사용 전처리기와 다른이름으로 정의하고, 그 정의 여부에 따라 코드에서 재정의 해 사용해주어도 됩니다.
'Unreal > 작업방식' 카테고리의 다른 글
언리얼: C++ 클래스 추가시 자동 컴파일 해제 (0) | 2022.03.07 |
---|---|
언리얼: 모듈 추가해서 코드 분리 작성하기 (0) | 2021.10.21 |
언리얼: Delegates 블루프린트에서 할당, 바인딩, 호출 가능하게 하기 (0) | 2021.10.21 |