Game Feature Plugin
모듈형 게임플레이 콘텐츠의 런타임 활성화/비활성화
Game Feature Plugin이란?
모듈형 게임플레이 콘텐츠를 위한 UE5의 강력한 시스템
핵심 개념
Game Feature Plugin은 런타임에 활성화/비활성화할 수 있는 자체 포함형 게임플레이 콘텐츠 패키지입니다. Epic의 Lyra 프로젝트에서 핵심적으로 사용되는 아키텍처입니다.
모듈형 콘텐츠
캐릭터, 무기, 게임 모드 등을 독립적 단위로 패키징
런타임 제어
게임 실행 중 동적으로 활성화/비활성화
Action 시스템
활성화 시 수행할 작업을 데이터로 정의
DLC 지원
다운로드 콘텐츠를 게임에 원활히 통합
일반 플러그인과의 차이
| 특성 | 일반 Plugin | Game Feature Plugin |
|---|---|---|
| 로딩 시점 | 엔진 시작 시 고정 | 런타임에 동적 로드/언로드 |
| 활성화 제어 | 프로젝트 설정에서만 | 코드/BP에서 실시간 제어 |
| 콘텐츠 주입 | 수동 등록 필요 | Action으로 자동 주입 |
| 사용 사례 | 유틸리티, 시스템 | 게임 모드, DLC, 기능 팩 |
- ShooterCore: 슈터 게임의 기본 메카닉
- ShooterMaps: 슈터 게임용 맵 콘텐츠
- TopDownArena: 탑다운 아레나 게임 모드
- 각 Feature는 독립적으로 활성화/비활성화 가능
Game Feature Plugin 설정
.uplugin 설정과 필수 플러그인 활성화
프로젝트 설정
먼저 프로젝트에서 GameFeatures 플러그인을 활성화해야 합니다.
{
"FileVersion": 3,
"EngineAssociation": "5.6",
"Plugins": [
{
"Name": "GameFeatures",
"Enabled": true
},
{
"Name": "ModularGameplay",
"Enabled": true
}
]
}
Game Feature Plugin 디렉토리
MyProject/
└── Plugins/
└── GameFeatures/ // Game Feature 전용 폴더 (중요!)
├── MyCombatFeature/
│ ├── MyCombatFeature.uplugin
│ ├── Source/
│ │ └── MyCombatFeature/
│ └── Content/
│ ├── Abilities/
│ ├── Characters/
│ └── GameFeatureData.uasset
│
└── MyQuestFeature/
├── MyQuestFeature.uplugin
├── Source/
└── Content/
Game Feature Plugin은 반드시 Plugins/GameFeatures/ 폴더 안에 위치해야 합니다. 다른 위치에서는 Game Feature로 인식되지 않습니다.
Game Feature .uplugin 설정
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0.0",
"FriendlyName": "Combat Feature",
"Description": "전투 시스템 기능 팩",
"Category": "Game Features",
// Game Feature 전용 설정
"ExplicitlyLoaded": true,
"BuiltInInitialFeatureState": "Active",
"Modules": [
{
"Name": "MyCombatFeature",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"Plugins": [
{
"Name": "GameplayAbilities",
"Enabled": true
},
{
"Name": "ModularGameplay",
"Enabled": true
}
]
}
- ExplicitlyLoaded: true - Game Feature로 인식되기 위해 필수
- BuiltInInitialFeatureState: "Active" | "Registered" | "Installed" | "Loaded"
- Active: 시작 시 자동 활성화
- Registered: 등록만 되고 수동 활성화 필요
Game Feature Data와 Actions
활성화 시 수행할 작업을 데이터로 정의
Game Feature Data 에셋
UGameFeatureData는 Feature가 활성화될 때 수행할 Action들을 정의합니다. 에디터에서 Content Browser에서 우클릭 - Miscellaneous - Game Feature Data로 생성합니다.
// UGameFeatureData에 포함된 내용
GameFeatureData
├── Actions // 활성화 시 수행할 Action 목록
│ ├── AddComponents // 액터에 컴포넌트 추가
│ ├── AddDataRegistry // 데이터 레지스트리 추가
│ ├── AddGameplayCuePath // Gameplay Cue 경로 추가
│ └── CustomAction // 커스텀 Action
│
└── Primary Asset Id // 에셋 관리자 ID
기본 제공 Actions
| Action | 설명 | 사용 예 |
|---|---|---|
AddComponents |
특정 액터에 컴포넌트 자동 추가 | 캐릭터에 능력 컴포넌트 추가 |
AddDataRegistry |
Data Registry 소스 추가 | 아이템 데이터베이스 등록 |
AddGameplayCuePath |
Gameplay Cue 경로 등록 | 이펙트 경로 추가 |
AddInputBinding |
입력 바인딩 추가 | 새로운 조작키 등록 |
AddAbilities |
GAS 어빌리티 부여 | 캐릭터에 스킬 추가 |
AddComponents Action 예시
// AddComponents Action 설정
// 에디터에서 GameFeatureData 에셋을 열어 Actions에 추가
Actions:
[0] GameFeatureAction_AddComponents
ActorClass: "AMyPlayerCharacter"
Components to Add:
[0] Component Class: "UCombatComponent"
bClientComponent: true
bServerComponent: true
[1] Component Class: "UComboSystemComponent"
bClientComponent: true
bServerComponent: true
커스텀 Game Feature Action
프로젝트에 맞는 커스텀 Action 구현
커스텀 Action 기본 구조
#pragma once
#include "CoreMinimal.h"
#include "GameFeatureAction.h"
#include "MyGameFeatureAction.generated.h"
/**
* 커스텀 Game Feature Action
* Feature 활성화 시 특정 작업을 수행
*/
UCLASS(meta = (DisplayName = "Register Quest System"))
class MYCOMBATFEATURE_API UMyGameFeatureAction_RegisterQuestSystem
: public UGameFeatureAction
{
GENERATED_BODY()
public:
/** Feature 활성화 시 호출 */
virtual void OnGameFeatureActivating(
FGameFeatureActivatingContext& Context) override;
/** Feature 비활성화 시 호출 */
virtual void OnGameFeatureDeactivating(
FGameFeatureDeactivatingContext& Context) override;
/** 에디터에서 설정할 데이터 */
UPROPERTY(EditAnywhere, Category = "Quest")
TSoftObjectPtr<UDataTable> QuestDataTable;
UPROPERTY(EditAnywhere, Category = "Quest")
TArray<FName> InitialQuests;
};
커스텀 Action 구현
#include "MyGameFeatureAction.h"
#include "Engine/AssetManager.h"
#include "MyQuestSubsystem.h"
void UMyGameFeatureAction_RegisterQuestSystem::OnGameFeatureActivating(
FGameFeatureActivatingContext& Context)
{
Super::OnGameFeatureActivating(Context);
// 데이터 테이블 로드
if (QuestDataTable.IsValid())
{
UDataTable* LoadedTable = QuestDataTable.LoadSynchronous();
// World 반복하며 Subsystem에 등록
for (const FWorldContext& WorldContext : GEngine->GetWorldContexts())
{
if (UWorld* World = WorldContext.World())
{
if (UGameInstance* GI = World->GetGameInstance())
{
if (UMyQuestSubsystem* QuestSS =
GI->GetSubsystem<UMyQuestSubsystem>())
{
// 퀘스트 데이터 등록
QuestSS->RegisterQuestData(LoadedTable);
// 초기 퀘스트 추가
for (const FName& QuestId : InitialQuests)
{
QuestSS->AddQuest(QuestId);
}
UE_LOG(LogTemp, Log, TEXT("Quest system registered"));
}
}
}
}
}
}
void UMyGameFeatureAction_RegisterQuestSystem::OnGameFeatureDeactivating(
FGameFeatureDeactivatingContext& Context)
{
// 정리 작업
for (const FWorldContext& WorldContext : GEngine->GetWorldContexts())
{
if (UWorld* World = WorldContext.World())
{
if (UGameInstance* GI = World->GetGameInstance())
{
if (UMyQuestSubsystem* QuestSS =
GI->GetSubsystem<UMyQuestSubsystem>())
{
// 등록 해제
QuestSS->UnregisterQuestData();
}
}
}
}
Super::OnGameFeatureDeactivating(Context);
}
World Ready Action (월드 준비 후 실행)
#pragma once
#include "GameFeatureAction_WorldActionBase.h"
#include "MyWorldReadyAction.generated.h"
/**
* 월드가 준비된 후 실행되는 Action
* 월드 컨텍스트가 필요한 작업에 적합
*/
UCLASS()
class UMyWorldReadyAction : public UGameFeatureAction_WorldActionBase
{
GENERATED_BODY()
protected:
/** 월드에 Feature 추가 시 호출 */
virtual void AddToWorld(const FWorldContext& WorldContext,
const FGameFeatureStateChangeContext& ChangeContext) override
{
UWorld* World = WorldContext.World();
if (!World)
{
return;
}
// 월드에 특정 Actor 스폰
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
World->SpawnActor<AMyFeatureManager>(
AMyFeatureManager::StaticClass(),
FVector::ZeroVector,
FRotator::ZeroRotator,
SpawnParams
);
}
};
런타임 활성화/비활성화
코드와 Blueprint에서 Game Feature 제어하기
C++에서 Game Feature 제어
#include "GameFeaturesSubsystem.h"
void UMyGameManager::LoadCombatFeature()
{
// Game Features Subsystem 가져오기
UGameFeaturesSubsystem& GFS = UGameFeaturesSubsystem::Get();
// Feature 이름 (플러그인 이름)
FString FeatureName = TEXT("MyCombatFeature");
// URL 형식으로 변환
FString PluginURL;
if (GFS.GetPluginURLByName(FeatureName, PluginURL))
{
// 비동기 로드 및 활성화
GFS.LoadAndActivateGameFeaturePlugin(
PluginURL,
FGameFeaturePluginLoadComplete::CreateUObject(
this, &UMyGameManager::OnFeatureLoaded
)
);
}
}
void UMyGameManager::OnFeatureLoaded(const UE::GameFeatures::FResult& Result)
{
if (Result.HasError())
{
UE_LOG(LogTemp, Error, TEXT("Failed to load feature: %s"),
*Result.GetError());
}
else
{
UE_LOG(LogTemp, Log, TEXT("Feature loaded successfully"));
}
}
void UMyGameManager::UnloadCombatFeature()
{
UGameFeaturesSubsystem& GFS = UGameFeaturesSubsystem::Get();
FString PluginURL;
if (GFS.GetPluginURLByName(TEXT("MyCombatFeature"), PluginURL))
{
// 비활성화 및 언로드
GFS.DeactivateGameFeaturePlugin(PluginURL);
GFS.UnloadGameFeaturePlugin(PluginURL);
}
}
Feature 상태 확인
bool UMyGameManager::IsFeatureActive(const FString& FeatureName)
{
UGameFeaturesSubsystem& GFS = UGameFeaturesSubsystem::Get();
FString PluginURL;
if (!GFS.GetPluginURLByName(FeatureName, PluginURL))
{
return false;
}
// 상태 확인
EGameFeaturePluginState State = GFS.GetPluginState(PluginURL);
switch (State)
{
case EGameFeaturePluginState::Active:
return true;
case EGameFeaturePluginState::Registered:
case EGameFeaturePluginState::Loaded:
case EGameFeaturePluginState::Installed:
return false; // 로드됐지만 활성화 안 됨
default:
return false;
}
}
// Feature 상태 변화 감지
void UMyGameManager::ListenToFeatureStateChanges()
{
UGameFeaturesSubsystem& GFS = UGameFeaturesSubsystem::Get();
// 상태 변화 델리게이트 바인딩
GFS.OnGameFeaturePluginStateChanged().AddUObject(
this,
&UMyGameManager::OnFeatureStateChanged
);
}
void UMyGameManager::OnFeatureStateChanged(
const FString& PluginURL,
EGameFeaturePluginState NewState)
{
UE_LOG(LogTemp, Log, TEXT("Feature %s changed to state %d"),
*PluginURL, (int32)NewState);
}
Blueprint에서 사용
// BlueprintCallable 함수로 노출
UCLASS()
class UGameFeatureBPLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Game Features")
static void ActivateGameFeature(const FString& FeatureName);
UFUNCTION(BlueprintCallable, Category = "Game Features")
static void DeactivateGameFeature(const FString& FeatureName);
UFUNCTION(BlueprintPure, Category = "Game Features")
static bool IsGameFeatureActive(const FString& FeatureName);
};
- 게임 모드 전환: PvP 모드 / PvE 모드 동적 전환
- DLC 관리: 구매한 DLC만 활성화
- 시즌 콘텐츠: 특정 기간에만 활성화되는 이벤트
- 성능 옵션: 저사양 기기에서 일부 기능 비활성화
핵심 요약
- Game Feature Plugin은 런타임에 동적으로 활성화/비활성화 가능한 모듈형 게임플레이 콘텐츠
- 반드시 Plugins/GameFeatures/ 폴더에 위치하고 ExplicitlyLoaded: true 설정 필요
- Game Feature Data와 Actions를 통해 활성화 시 수행할 작업을 데이터로 정의
- 기본 제공 Actions: AddComponents, AddAbilities, AddInputBinding 등
- UGameFeaturesSubsystem을 통해 C++/BP에서 Feature 로드/활성화/비활성화 제어
다음 강의에서는 에디터 확장을 통해 커스텀 에디터 도구, Property Customization, Asset 에디터를 개발하는 방법을 학습합니다.
도전 과제
배운 내용을 직접 실습해보세요
Game Feature Plugin을 생성하여 RPG의 확장 콘텐츠(DLC 던전)를 모듈화하세요. UGameFeatureAction을 사용하여 플러그인 활성화 시 자동으로 컴포넌트를 추가하는 로직을 구현하세요.
UGameFeaturesSubsystem::LoadAndActivateGameFeaturePlugin()으로 런타임에 Game Feature를 로드하세요. RPG의 시즌 이벤트 콘텐츠를 Game Feature로 패키징하여 동적으로 활성화/비활성화하세요.
기본 게임, PvP 모드, 레이드 던전, 시즌 이벤트를 각각 별도의 Game Feature Plugin으로 분리하세요. 플러그인 간 의존성을 최소화하고, 독립적으로 빌드/배포할 수 있는 구조를 설계하세요.