본문으로 건너뛰기
Unreal Engine 모듈 · 플러그인 개발LESSON 34

실습 1: Runtime 상호작용 플러그인 완성

난이도심화
예상 시간240분
선수지식17~33강의 Runtime Plugin 설계·구현·배포

34강. 실습 1: Runtime 상호작용 플러그인 완성

1. 이번 강의에서 해결할 문제​

기능별 예제가 각각 동작해도 최종 폴더에서 Descriptor, Build.cs, Public API, Config, Content 정책이 일치하지 않으면 배포할 수 없습니다. 이번 실습은 단순히 파일을 모으는 작업이 아니라 프로젝트 고유 의존성을 제거하고 실패 조건을 검증하는 통합 과정입니다.

완료 조건은 “원본 게임에서 문이 열린다”가 아니라 “빈 C++ 프로젝트에서 Plugin만으로 Component를 붙이고 다른 Actor와 상호작용한다”입니다.

2. 학습 목표​

3. 핵심 개념​

최종 Plugin의 책임은 세 가지입니다.

  1. 시야 기준으로 상호작용 후보를 찾는다.
  2. IInteractable 계약을 통해 가능 여부와 행동을 호출한다.
  3. 설정·대상 변화 이벤트를 제공하되 입력·UI·게임 규칙은 소유하지 않는다.

다음 항목은 소비 프로젝트 책임입니다.

  • Enhanced Input Binding과 키 표시
  • 문, NPC, 아이템의 실제 게임 규칙
  • 멀티플레이 Server RPC와 서버 권한 재검증
  • 프로젝트 UI, 사운드, 보상 시스템

4. 단계별 실습​

최종 구조를 정리합니다.

Plugins/ReusableInteraction/
├─ Config/
│ └─ DefaultReusableInteraction.ini
├─ Content/ # 선택적 아이콘만, 없어도 동작
├─ Resources/
│ └─ Icon128.png
├─ Source/
│ ├─ ReusableInteraction/
│ │ ├─ Public/
│ │ │ ├─ Interaction/Interactable.h
│ │ │ ├─ Interaction/InteractionComponent.h
│ │ │ └─ Settings/InteractionSettings.h
│ │ ├─ Private/
│ │ │ ├─ Interaction/Interactable.cpp
│ │ │ ├─ Interaction/InteractionComponent.cpp
│ │ │ ├─ Settings/InteractionSettings.cpp
│ │ │ ├─ ReusableInteractionLog.cpp
│ │ │ └─ ReusableInteractionModule.cpp
│ │ └─ ReusableInteraction.Build.cs
│ └─ ReusableInteractionTests/
│ ├─ Private/InteractionSettingsSpec.cpp
│ └─ ReusableInteractionTests.Build.cs
├─ CHANGELOG.md
├─ README.md
└─ ReusableInteraction.uplugin

Descriptor에는 Runtime과 테스트 Module을 분리합니다. 테스트 Module Type 명칭은 사용하는 UE5 버전의 Descriptor 규칙을 확인합니다.

ReusableInteraction.uplugin
{
"FileVersion": 3,
"Version": 10000,
"VersionName": "1.0.0",
"FriendlyName": "Reusable Interaction",
"Description": "A project-independent runtime interaction component.",
"Category": "Gameplay",
"CanContainContent": true,
"Modules": [
{
"Name": "ReusableInteraction",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "ReusableInteractionTests",
"Type": "DeveloperTool",
"LoadingPhase": "Default"
}
]
}

최종 Build.cs는 최소 Runtime 의존만 가집니다.

ReusableInteraction.Build.cs
using UnrealBuildTool;

public class ReusableInteraction : ModuleRules
{
public ReusableInteraction(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

PublicDependencyModuleNames.AddRange(new[]
{
"Core",
"CoreUObject",
"Engine",
"DeveloperSettings"
});

PrivateDependencyModuleNames.AddRange(new string[] { });
}
}

로그 카테고리도 Public Header와 한 CPP에서만 정의합니다.

Public/ReusableInteractionLog.h
#pragma once

#include "Logging/LogMacros.h"

REUSABLEINTERACTION_API DECLARE_LOG_CATEGORY_EXTERN(
LogReusableInteraction, Log, All);
Private/ReusableInteractionLog.cpp
#include "ReusableInteractionLog.h"

DEFINE_LOG_CATEGORY(LogReusableInteraction);

상호작용 대상의 C++ 예제를 만들어 Blueprint만이 아니라 Native 소비도 확인합니다.

소비 프로젝트의 ReusableDoor.h
#pragma once

#include "GameFramework/Actor.h"
#include "Interaction/Interactable.h"
#include "ReusableDoor.generated.h"

UCLASS()
class EMPTYTEST_API AReusableDoor final
: public AActor,
public IInteractable
{
GENERATED_BODY()

public:
virtual bool CanInteract_Implementation(
AActor* InstigatorActor) const override;
virtual FText GetInteractionPrompt_Implementation(
AActor* InstigatorActor) const override;
virtual void Interact_Implementation(
AActor* InstigatorActor) override;

private:
UPROPERTY(VisibleInstanceOnly, Category="Door")
bool bIsOpen = false;
};
소비 프로젝트의 ReusableDoor.cpp
#include "ReusableDoor.h"

bool AReusableDoor::CanInteract_Implementation(
AActor* InstigatorActor) const
{
return IsValid(InstigatorActor);
}

FText AReusableDoor::GetInteractionPrompt_Implementation(
AActor* InstigatorActor) const
{
return bIsOpen
? NSLOCTEXT("EmptyTest", "CloseDoor", "Close door")
: NSLOCTEXT("EmptyTest", "OpenDoor", "Open door");
}

void AReusableDoor::Interact_Implementation(
AActor* InstigatorActor)
{
if (!CanInteract_Implementation(InstigatorActor))
{
return;
}

bIsOpen = !bIsOpen;
SetActorRotation(FRotator(0.0f, bIsOpen ? 90.0f : 0.0f, 0.0f));
}

멀티플레이 프로젝트에 적용할 때 bIsOpen 같은 지속 상태와 실제 판정은 서버가 소유하고 Replicated 변수 또는 RepNotify로 동기화합니다. Multicast를 지속 상태 저장소로 사용하지 않습니다. Client가 보낸 Target은 서버에서 거리·시야·Ownership을 다시 검증합니다.

통합 검증 순서​

  1. Plugin 폴더에서 원본 프로젝트 Module 이름 검색 결과 0건
  2. Editor Target Clean Build
  3. 설정 CDO 자동 테스트 실행
  4. C++ Door와 Blueprint Door 각각 상호작용
  5. Target 파괴·거리 밖·가림·입력 연속 실행 검증
  6. DataGuard 없이 Development Game 빌드
  7. Development Package 실행
  8. BuildPlugin 산출물 생성

5. 코드가 동작하는 이유​

Public API는 Interface, Component, Settings로 제한되고 구현 세부는 Private에 남습니다. 소비 프로젝트는 Module 의존 하나만 선언하고 자신이 소유한 입력·UI·게임 규칙에 Plugin 명령을 연결합니다. Editor 기능과 프로젝트 클래스가 없으므로 Runtime Target 그래프가 작고 이식 가능합니다.

6. 자주 하는 실수와 해결법​

7. 직접 실습​

8. 이해 점검 질문 3개​

9. 핵심 요약​

MINI QUIZ

실습 1: Runtime 상호작용 플러그인 완성 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“실습 1: Runtime 상호작용 플러그인 완성” 실습 결과를 해석하는 올바른 기준은 무엇인가요?
  2. 문제 2“실습 1: Runtime 상호작용 플러그인 완성” 실습 중 ‘샘플 Door를 Plugin 필수 코드로 포함’ 상황을 발견했습니다. 본문과 일치하는 설명은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

34강. 실습 1: Runtime 상호작용 플러그인 완성 미완료 상태