커스텀 에디터 모드
FEdMode/UEdMode를 구현하여 뷰포트 인터랙션과 전용 도구 패널을 가진 커스텀 에디터 모드를 만듭니다
에디터 모드란?
FEdMode와 UEdMode의 개념과 차이점
에디터 모드는 레벨 에디터의 뷰포트에서 특수한 인터랙션을 제공하는 시스템입니다. Landscape 모드, Foliage 모드, Modeling 모드 등이 모두 에디터 모드로 구현되어 있습니다.
| 구분 | FEdMode (레거시) | UEdMode (UE5 권장) |
|---|---|---|
| 상속 | 비-UObject | UObject 기반 |
| GC 관리 | 수동 메모리 관리 | 자동 GC |
| 블루프린트 | 접근 불가 | UPROPERTY/UFUNCTION 사용 가능 |
| 툴킷 | FModeToolkit | FModeToolkit |
| 도구(Tool) | 직접 구현 | UInteractiveTool 지원 |
UE5.5+에서는 UEdMode가 권장됩니다. UObject 기반이므로 UPROPERTY, 리플렉션, GC를 활용할 수 있고, Interactive Tool Framework과 자연스럽게 통합됩니다.
UEdMode 구현
커스텀 에디터 모드의 핵심 클래스 작성
#pragma once
#include "CoreMinimal.h"
#include "Tools/UEdMode.h"
UCLASS()
class UMyEditorMode : public UEdMode
{
GENERATED_BODY()
public:
const static FEditorModeID EM_MyMode;
UMyEditorMode();
// UEdMode interface
virtual void Enter() override;
virtual void Exit() override;
virtual void CreateToolkit() override;
// 뷰포트 이벤트 처리
virtual bool HandleClick(
FEditorViewportClient* ViewportClient,
HHitProxy* HitProxy,
const FViewportClick& Click) override;
virtual bool InputDelta(
FEditorViewportClient* ViewportClient,
FViewport* Viewport,
FVector& Drag,
FRotator& Rotation,
FVector& Scale) override;
virtual void Render(
const FSceneView* View,
FViewport* Viewport,
FPrimitiveDrawInterface* PDI) override;
virtual bool UsesToolkits() const override
{ return true; }
};
#include "UMyEditorMode.h"
#include "EditorModeManager.h"
const FEditorModeID UMyEditorMode::EM_MyMode
= TEXT("EM_MyEditorMode");
UMyEditorMode::UMyEditorMode()
{
Info = FEditorModeInfo(
EM_MyMode,
LOCTEXT("ModeName", "My Editor Mode"),
FSlateIcon(FAppStyle::GetAppStyleSetName(),
"LevelEditor.FoliageMode"),
true); // bVisible in mode toolbar
}
void UMyEditorMode::Enter()
{
UEdMode::Enter();
UE_LOG(LogTemp, Log, TEXT("Entered My Editor Mode"));
}
void UMyEditorMode::Exit()
{
UE_LOG(LogTemp, Log, TEXT("Exited My Editor Mode"));
UEdMode::Exit();
}
void UMyEditorMode::CreateToolkit()
{
Toolkit = MakeShareable(new FMyEditorModeToolkit);
Toolkit->Init(Owner->GetToolkitHost());
}
bool UMyEditorMode::HandleClick(
FEditorViewportClient* ViewportClient,
HHitProxy* HitProxy,
const FViewportClick& Click)
{
if (Click.GetKey() == EKeys::LeftMouseButton)
{
// 클릭 위치에서 라인 트레이스
FVector WorldOrigin, WorldDirection;
FEditorViewportClient::DeprojectScreenToWorld(
Click.GetClickPos(),
WorldOrigin, WorldDirection);
// 커스텀 클릭 처리 로직
return true; // 이벤트 소비
}
return false; // 기본 처리로 넘기기
}
void UMyEditorMode::Render(
const FSceneView* View,
FViewport* Viewport,
FPrimitiveDrawInterface* PDI)
{
UEdMode::Render(View, Viewport, PDI);
// 뷰포트에 디버그 도형 그리기
PDI->DrawLine(
FVector::ZeroVector,
FVector(100, 0, 100),
FLinearColor::Red,
SDPG_Foreground);
DrawWireSphere(PDI,
FVector(0, 0, 50),
FLinearColor::Green,
50.0f, // 반지름
16, // 세그먼트
SDPG_Foreground);
}
FModeToolkit — 도구 패널
에디터 모드 전용 사이드 패널 UI 구축
#pragma once
#include "Toolkits/BaseToolkit.h"
class FMyEditorModeToolkit : public FModeToolkit
{
public:
virtual void Init(
const TSharedPtr<IToolkitHost>& Host) override;
// FModeToolkit
virtual FName GetToolkitFName() const override
{ return FName("MyEditorModeToolkit"); }
virtual FText GetBaseToolkitName() const override
{ return LOCTEXT("Name", "My Mode"); }
// 인라인 콘텐츠 (모드 패널 본체)
virtual TSharedPtr<SWidget>
GetInlineContent() const override
{ return ToolkitWidget; }
private:
TSharedPtr<SWidget> ToolkitWidget;
};
void FMyEditorModeToolkit::Init(
const TSharedPtr<IToolkitHost>& Host)
{
FModeToolkit::Init(Host);
ToolkitWidget =
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(8.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("Hdr", "My Editor Mode"))
.Font(FCoreStyle::GetDefaultFontStyle(
"Bold", 14))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(8.0f)
[
SNew(SButton)
.Text(LOCTEXT("Place", "Place Object"))
.OnClicked_Lambda([]()
{
// 배치 로직
return FReply::Handled();
})
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(8.0f)
[
SNew(SButton)
.Text(LOCTEXT("Clear", "Clear All"))
.OnClicked_Lambda([]()
{
// 초기화 로직
return FReply::Handled();
})
];
}
모드 등록
에디터 모듈에서 커스텀 모드 등록 및 활성화
void FMyGameEditorModule::StartupModule()
{
// 에디터 모드 등록
FEditorModeRegistry::Get().RegisterMode<UMyEditorMode>(
UMyEditorMode::EM_MyMode,
LOCTEXT("ModeName", "My Mode"),
FSlateIcon(FAppStyle::GetAppStyleSetName(),
"LevelEditor.FoliageMode"),
true); // bVisible
}
void FMyGameEditorModule::ShutdownModule()
{
FEditorModeRegistry::Get().UnregisterMode(
UMyEditorMode::EM_MyMode);
}
// 코드에서 모드 활성화
GLevelEditorModeTools().ActivateMode(
UMyEditorMode::EM_MyMode);
// 모드 비활성화
GLevelEditorModeTools().DeactivateMode(
UMyEditorMode::EM_MyMode);
Render()— PDI로 3D 도형(와이어, 구체, 박스) 그리기DrawHUD()— Canvas로 2D HUD 그리기HandleClick()— 뷰포트 클릭 처리InputDelta()— 드래그 델타 처리InputKey()— 키 입력 처리
핵심 요약
- UEdMode는 UE5 권장 에디터 모드 베이스 클래스이며, UObject 기반으로 GC와 리플렉션을 활용합니다
- Enter()/Exit()로 모드 진입/종료를 처리하고, HandleClick/InputDelta/Render로 뷰포트 인터랙션을 구현합니다
- FModeToolkit을 상속하여 모드 활성화 시 표시되는 사이드 패널 UI를 구축합니다
- FEditorModeRegistry로 모드를 등록하면 에디터 모드 선택 바에 자동으로 표시됩니다
- Render()에서
FPrimitiveDrawInterface로 뷰포트에 디버그 도형을 그려 시각적 피드백을 제공합니다
도전 과제
배운 내용을 직접 실습해보세요
FEdMode를 상속받아 커스텀 에디터 모드를 만드세요. Enter/Exit 시 로그를 출력하고, 전용 툴바 패널에 간단한 설정 UI를 추가하세요. Modes 패널에서 커스텀 모드 아이콘이 나타나는지 확인하세요.
FEdMode::InputKey, InputDelta, StartTracking을 오버라이드하여 뷰포트에서 마우스 클릭/드래그로 Actor를 배치하거나 이동하는 기능을 구현하세요. HandleClick에서 Line Trace로 배치 위치를 결정하세요.
Foliage 페인터와 유사한 커스텀 에디터 모드를 구현하세요. 브러시 크기/밀도 설정 UI, 마우스 드래그로 Actor를 월드에 페인팅하는 기능, HUD 오버레이로 브러시 범위 시각화를 포함하는 도구를 만드세요.