콘텐츠로 이동

UE5 Modular Gameplay Plugin — 게임플레이 기능 분리

Modular Gameplay Plugin은 UE5(Lyra 프로젝트에서 채용)의 게임플레이 기능을 독립 플러그인(GameFeature Plugin)으로 분리하는 아키텍처입니다. 각 기능(무기 시스템, 스킬, 게임 모드)을 독립적으로 활성화/비활성화할 수 있어 DLC, 라이브 서비스, 멀티플레이어 모드 전환에 적합합니다.


Edit → Plugins → Gameplay → ModularGameplay → Enable
Edit → Plugins → Gameplay → GameFeatures → Enable

Tools → New Plugin → Game Feature
Plugin Name: WeaponSystem

생성 구조:

Plugins/GameFeatures/WeaponSystem/
├── WeaponSystem.uplugin
├── Source/WeaponSystem/
└── Content/

.uplugin 설정:

{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "WeaponSystem",
"GameFeatureData": "WeaponSystem/WeaponSystemData.uasset"
}

에디터에서 GameFeatureData 에셋 생성 후 Action 추가:

// C++로 커스텀 GameFeature Action 정의
UCLASS()
class UGameFeatureAction_AddAbilities : public UGameFeatureAction
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
TArray<FGameplayAbilitySpec> AbilitiesToAdd;
virtual void OnGameFeatureActivating(
FGameFeatureActivatingContext& Context) override
{
// 플레이어에게 어빌리티 추가
for (auto* Player : Context.GetPlayers())
{
auto* ASC = Player->GetAbilitySystemComponent();
for (auto& Spec : AbilitiesToAdd)
ASC->GiveAbility(Spec);
}
}
virtual void OnGameFeatureDeactivating(
FGameFeatureDeactivatingContext& Context) override
{
// 어빌리티 제거
for (auto* Player : Context.GetPlayers())
{
auto* ASC = Player->GetAbilitySystemComponent();
ASC->ClearAllAbilities();
}
}
};

4. 런타임 GameFeature 활성화/비활성화

섹션 제목: “4. 런타임 GameFeature 활성화/비활성화”
#include "GameFeaturesSubsystem.h"
// GameFeature 활성화
void AMyGameMode::ActivateWeaponSystem()
{
UGameFeaturesSubsystem& Subsystem =
UGameFeaturesSubsystem::Get();
FString PluginURL = TEXT("/WeaponSystem/WeaponSystem.uplugin");
Subsystem.LoadAndActivateGameFeaturePlugin(
PluginURL,
FGameFeaturePluginChangeStateComplete::CreateLambda(
[](const UE::GameFeatures::FResult& Result)
{
if (Result.HasValue())
UE_LOG(LogTemp, Log, TEXT("WeaponSystem 활성화 성공"));
else
UE_LOG(LogTemp, Error, TEXT("활성화 실패: %s"),
*Result.GetError());
}));
}
// GameFeature 비활성화
void AMyGameMode::DeactivateWeaponSystem()
{
FString PluginURL = TEXT("/WeaponSystem/WeaponSystem.uplugin");
UGameFeaturesSubsystem::Get().DeactivateGameFeaturePlugin(
PluginURL, FGameFeaturePluginChangeStateComplete());
}

5. Component 주입 — AddComponents Action

섹션 제목: “5. Component 주입 — AddComponents Action”

에디터의 GameFeatureData에 AddComponents Action 추가:

// GameFeature 활성화 시 지정 Actor에 Component 자동 추가
UCLASS()
class UGameFeatureAction_AddComponents : public UGameFeatureAction
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
TSoftClassPtr<UActorComponent> ComponentClass;
UPROPERTY(EditAnywhere)
TSoftClassPtr<AActor> ActorClass; // 대상 Actor 타입
virtual void OnGameFeatureActivating(
FGameFeatureActivatingContext& Context) override
{
// 현재 월드의 해당 Actor 타입에 Component 추가
for (TActorIterator<AActor> It(Context.GetWorld(),
ActorClass.LoadSynchronous()); It; ++It)
{
(*It)->AddComponentByClass(
ComponentClass.LoadSynchronous(), false, FTransform(), false);
}
}
};

Unregistered → Registered → Loaded → Active
↑ |
└──────────── Deactivated ─────────┘
// 현재 상태 확인
EGameFeaturePluginState State =
UGameFeaturesSubsystem::Get().GetPluginState(PluginURL);

Plugins/GameFeatures/
├── ShooterCore/ ← 핵심 FPS 기능
├── ShooterMaps/ ← 맵별 설정
├── TopDownArena/ ← 탑다운 모드
└── PerfTests/ ← 성능 테스트 모드

각 GameFeature는 독립 플러그인으로 필요할 때만 로드됩니다.


8. UGameFrameworkComponentManager — Extension Point 패턴

섹션 제목: “8. UGameFrameworkComponentManager — Extension Point 패턴”

Lyra가 사용하는 Extension Point 패턴으로 GameFeature가 코어 Actor를 직접 참조하지 않고도 컴포넌트를 주입합니다.

#include "Components/GameFrameworkComponentManager.h"
// 코어 Actor에서: Extension Point 등록
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
// 이 액터가 "MyCharacter" 확장 포인트를 노출함을 선언
UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
}
void AMyCharacter::EndPlay(const EEndPlayReason::Type Reason)
{
UGameFrameworkComponentManager::RemoveGameFrameworkComponentReceiver(this);
Super::EndPlay(Reason);
}
// GameFeature Action에서: 코어 Actor에 컴포넌트 주입
UCLASS()
class UGameFeatureAction_AddWeaponSlot : public UGameFeatureAction
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
TSoftClassPtr<UActorComponent> WeaponSlotComponentClass;
virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
{
UGameFrameworkComponentManager* Manager =
UGameInstance::GetSubsystem<UGameFrameworkComponentManager>(
Context.GetGameInstance());
// "MyCharacter" 수신자 액터에 WeaponSlotComponent를 자동 추가
Manager->AddComponentRequest(
AMyCharacter::StaticClass(),
WeaponSlotComponentClass);
}
virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override
{
UGameFrameworkComponentManager* Manager =
UGameInstance::GetSubsystem<UGameFrameworkComponentManager>(
Context.GetGameInstance());
Manager->RemoveComponentRequest(
AMyCharacter::StaticClass(),
WeaponSlotComponentClass);
}
};

Extension Point 흐름:

  1. AMyCharacter::BeginPlayAddGameFrameworkComponentReceiver 호출
  2. 이미 활성화된 GameFeature가 있으면 WeaponSlotComponent 즉시 주입
  3. 이후 GameFeature 활성화 → 월드의 모든 수신자 액터에 자동 주입
  4. GameFeature 비활성화 → 주입된 컴포넌트 자동 제거

Modular Gameplay는 게임플레이 기능을 플러그인 단위로 캡슐화해 코어 게임과 완전히 분리합니다. DLC 기능, 시즌 콘텐츠, 게임 모드 전환을 런타임에 플러그인 활성화/비활성화로 처리할 수 있어 라이브 서비스 게임에 특히 효과적입니다. Lyra 프로젝트를 레퍼런스로 삼아 구조를 파악하면 빠르게 적용할 수 있습니다.