플러그인 개발
플러그인은 엔진과 프로젝트 코드 외부에 있으며, UE에서 프로그램 기능을 확장하는 주요 방법입니다.
플러그인은 본질적으로 모듈화된 코드 설계로, 이 설계를 따르면 코드 간의 의존성을 줄이고 향후 반복 및 유지보수가 쉬워집니다.
UE에서 플러그인의 표준 파일 구조는 다음과 같습니다:
- Binaries: 컴파일된 생성 파일 저장
- Content: 플러그인의 에셋 콘텐츠 저장
- Intermediate: 컴파일 과정에서 생성된 중간 파일 저장
- Resources: 플러그인의 리소스 파일(아이콘, UE 외 에셋 등) 저장
- Source: 소스 코드 파일 저장, 다수의 모듈 포함 가능
- .uplugin: 플러그인의 구조 정의 파일
*.uplugin 파일의 기본 구성은 다음과 같습니다:
{
"FileVersion": 3, //파일 버전
"Version": 1, //버전 번호
"VersionName": "1.0", //버전 이름
"FriendlyName": "CustomPlugin", //별칭
"Description": "", //설명
"Category": "Other", //카테고리
"CreatedBy": "", //제작자
"CreatedByURL": "", //제작자 URL
"DocsURL": "", //문서 URL
"MarketplaceURL": "", //마켓플레이스 URL
"SupportURL": "", //지원 URL
"CanContainContent": false, //콘텐츠 디렉토리 포함 여부
"IsBetaVersion": false, //베타 버전 여부
"IsExperimentalVersion": false, //실험적 버전 여부
"Installed": false, //내장 여부
"Modules": [ //모듈 정의
{
"Name": "CustomPlugin", //모듈 이름
"Type": "Editor", //모듈 타입
"LoadingPhase": "Default" //로딩 시점
}
//, //기타 모듈
//{
// ...
//}
]
}
이러한 매개변수의 구성에 대한 자세한 내용은 다음을 참조하십시오:
UE에서 플러그인을 개발할 때의 일반적인 워크플로우는 다음과 같습니다:
- 먼저 편집 - 플러그인을 클릭하여 플러그인 패널을 엽니다
새 플러그인 추가
빈 플러그인 생성
- 프로젝트 구조가 변경되었으므로 IDE 프로젝트 파일을 다시 생성해야 합니다
빈 플러그인은 좋은 시작점입니다. 다른 플러그인들은 단지 추가적인 코드 템플릿이 포함된 것뿐입니다. 익숙하지 않다면 추가해보면서 어떤 코드가 추가되었는지 파악하고, 그 기능을 이해한 후 빈 플러그인에 필요한 기능을 추가하면 됩니다
다음은 C++ 플러그인에 반드시 포함되어야 하는 파일입니다:
// MyPlugin.h
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FMyPluginModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
// MyPlugin.cpp
#include "MyPlugin.h"
#define LOCTEXT_NAMESPACE "FMyPluginModule"
void FMyPluginModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FMyPluginModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FMyPluginModule, MyPlugin) //플러그인 항목 정의
UE는 플러그인 로드 시 StartupModule()을 호출하고, 언로드 시 ShutdownModule()을 호출합니다.
플러그인 개발의 주요 코드 흐름은 다음과 같습니다:
- StartupModule()에서는 코어 모듈의 함수 인터페이스를 호출하거나 코어 모듈의 전역 싱글톤을 사용하여 작업을 수행하거나, 코어 모듈의 진입점에 로직을 연결하여 기능을 확장합니다.
- ShutdownModule()에서는 StartupModule()에서 수행한 작업들을 정리합니다
자주 사용되는 함수 모듈은 다음과 같습니다:
- FFileHelper: 파일 작업
- FPlatformProcess: 플랫폼 관련 작업
- FBlueprintEditorUtils: 블루프린트 작업
- FKismetEditorUtilities: 에디터 작업
- FEdGraphUtilities: 그래프 작업
- FComponentEditorUtils: 컴포넌트 에디터 작업
- FEnumEditorUtils: 열거형 작업
- FStructureEditorUtils: 구조체 작업
- FEditorFolderUtils: 에디터 폴더 작업
- FMaterialEditorUtilities: 머티리얼 작업
- FPluginUtils: 플러그인 작업
- UGameplayStatics: 게임플레이 관련 작업
- ...
전역 싱글톤은 다음과 같습니다:
- GEngine, GEditor, GWorld: 엔진에서 자주 사용되는 전역 싱글톤
- FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get(): 에셋의 생성, 삭제, 이름 변경, 가져오기/내보내기...
- FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get(): 에셋의 검색, 열거...
- FModuleManager::LoadModuleChecked<ISettingsModule>("Settings"): 설정 파일 관련 관리
- FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor"): 프로퍼티 에디터 관련 작업
- FModuleManager::LoadModuleChecked<FLevelEditorModule>(TEXT("LevelEditor")): 레벨 에디터 관련 작업
- FModuleManager::LoadModuleChecked<FBlueprintEditorModule>("Kismet"): 블루프린트 에디터 관련 작업
- ...
코어 모듈의 진입점은 일반적으로 다음을 의미합니다:
- Callback 함수 콜백
- Delegate 델리게이트
일반적인 델리게이트 정의 모음은 다음과 같습니다:
- FCoreUObjectDelegates: UObject 관련 델리게이트
- FCoreDelegates: 코어 모듈 관련 델리게이트
- FEditorDelegates: 에디터 모듈 관련 델리게이트
- FWorldDelegates: 게임 월드 관련 델리게이트
- FGameDelegates
- ...
플러그인 작성을 완료하고 컴파일을 실행하면 플러그인의 Binaries 디렉토리에 다음 파일들이 생성됩니다:
- .module: 모듈 라이브러리의 기본 정보를 정의
- .dll: 동적 라이브러리
- .pdb: 프로그램 디버깅 데이터베이스, 소스 코드를 통한 브레이크포인트 디버깅에 사용됨. 자세한 내용은 여기 참조
*.module 파일의 내용은 다음과 같습니다
{ "BuildId": "a36b278a-b317-44c3-8743-b82361b74343", "Modules": { "MyPlugin": "UnrealEditor-MyPlugin.dll" } }
여기서 주의해야 할 매개변수는 BuildID입니다. 이는 엔진에서 생성되며, 플러그인의 BuildID가 엔진의 BuildID와 일치하지 않을 경우 에디터에서 프로젝트 실행 시 오류가 발생합니다:
엔진의 빌드 ID를 볼 수 있습니다:
기본
메뉴바 및 툴바 확장
엔진 개발 과정에서 편집기의 메뉴바와 툴바를 확장하여 버튼을 추가하고 편리한 기능을 실행하는 등의 요구사항이 자주 발생합니다. 다음과 같은 방식으로 구현할 수 있습니다:
UE4에서는 메뉴 확장의 주요 방식이 FExtender를 통해 이루어졌으나, UE5에서는 보다 사용하기 쉬운 새로운 방식인 UToolMenus가 도입되었습니다.
- UToolMenus는 싱글톤 클래스로, 다음 코드를 통해 싱글톤 인스턴스를 얻을 수 있습니다:
UToolMenus* ToolMenus = UToolMenus::Get();
UE5에서는 모든 확장 가능한 메뉴바와 툴바가 초기화 시 UToolMenus::RegisterMenu 함수를 호출하여 등록됩니다:
UToolMenu* UToolMenus::RegisterMenu(const FName InName,
const FName InParent = NAME_None,
EMultiBoxType InType = EMultiBoxType::Menu,
bool bWarnIfAlreadyRegistered = true)
이 함수는 확장 가능한 ToolMenu를 등록하는 데 사용되며, 함수의 각 매개변수는 다음과 같습니다:
- InName: 등록에 사용되는 체인 필드로, LevelEditor.LevelEditorToolBar.User와 같은 형식입니다. 이후 이 필드를 키로 사용하여 UToolMenu를 얻을 수 있습니다
- InParent: 도구 메뉴의 상위 항목을 지정
- InType: 해당 ToolMenu의 유형을 지정하며, 이 유형에 따라 메뉴를 확장하는 방법이 결정됩니다. 다음과 같은 값을 사용할 수 있습니다:
- MenuBar: 메뉴 바, 메뉴 확장 지원
- ToolBar: 도구 모음, 도구 버튼 추가 가능
- VerticalToolBar: 수직 도구 모음
- SlimHorizontalToolBar: 수평 도구 모음, 도구 모음의 간소화 버전으로 아이콘과 텍스트 요소를 수평 정렬할 수 있음
- UniformToolBar:
- Menu: 메뉴, 메뉴 항목 추가 가능
- ButtonRow: 행으로 배열된 버튼, 행당 최대 버튼 수가 있으며 도구 모음과 유사하지만 여러 행을 가질 수 있음
- bWarnIfAlreadyRegistered: Bool 값, 해당 필드가 이미 등록되어 있을 경우 경고를 발생시킴
메뉴 미리보기가 필요한 시점에는 UToolMenus::GenerateWidget 함수를 호출하여 ToolMenu 위젯을 생성합니다:
TSharedRef<SWidget> UToolMenus::GenerateWidget(const FName Name, const FToolMenuContext& InMenuContext)
ToolMenu가 Register된 후, 미리보기 전 어느 시점에서든 UToolMenus::ExtendMenu 함수를 통해 메뉴바의 내용을 확장할 수 있습니다.
위 코드에는 많은 확장자 참조가 있으며, 다음은 간단한 플러그인 확장자의 예입니다:
#pragma once
#include "Modules/ModuleManager.h"
class FCustomPluginModule : public IModuleInterface
{
public:
virtual void StartupModule() override {
//启动插件时给UToolMenus的启动增加一个回调
UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FCustomPluginModule::RegisterMenus));
}
virtual void ShutdownModule() override {
//去除UToolMenus的启动回调,重置一些状态
UToolMenus::UnRegisterStartupCallback(this);
UToolMenus::UnregisterOwner(this);
}
private:
void RegisterMenus() {
FToolMenuOwnerScoped OwnerScoped(this);
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
FToolMenuSection& Section = Menu->FindOrAddSection("PluginTools");
FToolMenuEntry& MenuEntry = Section.AddEntry(
FToolMenuEntry::InitToolBarButton( //도구 모음 버튼 추가
"CustomToolbarButton",
FUIAction
(
FExecuteAction::CreateLambda([]() {
FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("CustomNs", "HelloWorld", "Hello World!"));
})
)
)
);
MenuEntry.InsertPosition = FToolMenuInsert(NAME_None, EToolMenuInsertType::First);
}
};
이 코드는 레벨 에디터 메인 패널의 툴바에 버튼을 추가하며, 클릭 시 Hello World 대화상자가 표시됩니다:
UToolMenus::Get()에 브레이크포인트를 추가하여 VS 디버그 창을 통해 모든 확장 가능한 필드 목록을 확인할 수 있습니다:
에셋 확장
뷰포트 확장
뷰포트를 확장하기 위해서는 다음과 같은 구조체들이 필요합니다
- SEditorViewport
- FEditorViewportClient
디테일 패널 관련
DetailView는 UE 리플렉션이 제공하는 또 다른 강력한 기능입니다:
블루프린트 확장
사용자 경험
실행 취소/다시 실행
UObject는 트랜잭션 메커니즘을 가지고 있으며, UObject가 RF_Transactional 플래그를 가지고 있다면 다음과 같은 코드를 사용할 수 있습니다:
GEditor->BeginTransaction(NSLOCTEXT("NS", "Transaction", "Transaction")); //트랜잭션 시작
Object->Modify(); //이 작업은 Object의 현재 상태를 트랜잭션에 저장
SomeChange(Object); //Object의 일부 속성 수정
GEditor->EndTransaction(); //트랜잭션 종료, Object的新状态
위 작업을 통해 SomeChanged 작업을 실행 취소/다시 실행할 수 있습니다.
자세한 내용은 다음을 참조하세요: https://blog.csdn.net/qq_29523119/article/details/96778797
에디터 개발 시 특정 객체나 창이 실행 취소/다시 실행될 때 변경 사항에 응답해야 하는 경우가 자주 있습니다(예: 일부 재구성 수행). UE는 실행 취소/다시 실행 이벤트를 모니터링하기 위한 FEditorUndoClient 인터페이스를 제공하며, 다음은 간단한 코드 예시입니다:
class FUndoRedoListener: public FEditorUndoClient{
public:
FUndoRedoListener(){
if (GEditor)
GEditor->RegisterForUndo(this); //적절한 시점에 리스너 등록, 반드시 생성자에서 할 필요는 없음
}
~FUndoRedoListener(){
if (GEditor)
GEditor->UnregisterForUndo(this); //등록 해제
}
protected:
virtual void PostUndo(bool bSuccess) override{ //에디터에서 실행 취소 수행 후 호출되는 함수
RebuildSomething();
}
virtual void PostRedo(bool bSuccess) override{ //에디터에서 다시 실행 수행 후 호출되는 함수
RebuildSomething(); }
};
사용자 피드백
알림
FNotificationInfo Info(FText::FromString(TEXT("This is notification")));
Info.FadeInDuration = 2.0f;
Info.ExpireDuration = 2.0f;
Info.FadeOutDuration = 2.0f;
FSlateNotificationManager::Get().AddNotification(Info);
static TWeakPtr<SNotificationItem> NotificationPtr;
FNotificationInfo Info(FText::FromString(TEXT("This is notification")));
Info.FadeInDuration = 2.0f;
Info.FadeOutDuration = 2.0f;
Info.bFireAndForget = false;
Info.bUseThrobber = false;
FNotificationButtonInfo BtYesInfo = FNotificationButtonInfo(
NSLOCTEXT("NotificationNamespace","Yes", "Yes"),
NSLOCTEXT("NotificationNamespace","Yes", "Yes"),
FSimpleDelegate::CreateLambda([]() {
TSharedPtr<SNotificationItem> Notification = NotificationPtr.Pin();
if (Notification.IsValid())
{
Notification->SetEnabled(false);
Notification->SetExpireDuration(0.0f);
Notification->ExpireAndFadeout();
NotificationPtr.Reset();
}
}),
SNotificationItem::ECompletionState::CS_None
);
Info.ButtonDetails.Add(BtYesInfo);
NotificationPtr = FSlateNotificationManager::Get().AddNotification(Info);
진행률 표시기
float AmountOfWork = 50;
FScopedSlowTask RootTask(AmountOfWork, (NSLOCTEXT("SlowTaskNamespace", "This is slow root task", "This is slow root task")));
RootTask.MakeDialog();
for (int i = 0; i < 50; i++) {
FScopedSlowTask SubTask(1, (NSLOCTEXT("SlowTaskNamespace", "This is slow sub task", "This is slow sub task ")));
SubTask.MakeDialog();
for (int j = 0; j < 2; j++) {
FPlatformProcess::Sleep(0.5);
SubTask.EnterProgressFrame(0.5, FText::FromString("Sub Task"));
}
RootTask.EnterProgressFrame(1, FText::FromString("Root Task"));
}
다이얼로그
EAppReturnType::Type Ret = FMessageDialog::Open(EAppMsgType::YesNo, NSLOCTEXT("NS", "message dialog", "message dialog"));
if (Ret == EAppReturnType::Yes) {
}
기본 조작
문자열
TCHAR
일반적으로 코드의 문자열은 ANSI로 인코딩되지만, ANSI는 지원하는 문자 수가 적기 때문에 문자열 변수를 설정할 때 TEXT() 매크로를 사용하여 ANSI 문자를 TCHAR(네이티브 유니코드 인코딩)로 변환해야 합니다.
TCHAR* ThisIsTChars = TEXT("This is Raw String");
TCHAR와 네이티브 문자열 인코딩 형식을 변환하려면 다음 매크로를 사용할 수 있습니다:
- TCHAR_TO_ANSI (str)
- TCHAR_TO_UTF8 (str)
- TCHAR_TO_UTF16 (str)
- TCHAR_TO_UTF32 (str)
- TCHAR_TO_WCHAR (str)
- ANSI_TO_TCHAR (str)
- UTF8_TO_TCHAR (str)
- ...
게임은 성능에 대한 요구사항이 매우 높으며, 일반 String으로는 UE 게임 개발의 다양한 사용 사례에서 요구되는 성능을 만족시킬 수 없습니다. 따라서 UE4는 세 가지 주요 문자열 클래스를 제공합니다:
FString
일반 문자열로, 수정 가능하며 문자열을 조작하는 다양한 메서드를 제공합니다.
- 생성
- static FString Chr ( TCHAR Ch )
- static FString ChrN ( int32 NumCharacters, TCHAR Char )
- static FString FromInt ( int32 Num )
- static FString SanitizeFloat ( double InFloat, const int32 InMinFractionalDigits = 1 )
- static FString FromBlob (const uint8* SrcBuffer,const uint32 SrcSize);
- 포맷
- static FString Printf (const FmtType& Fmt, Types... Args)
- FString& Appendf (const FmtType& Fmt, Types... Args)
- static FString Format (const TCHAR* InFormatString, const FStringFormatOrderedArguments& InOrderedArguments)
- static FString Format (const TCHAR* InFormatString, const FStringFormatNamedArguments& InNamedArguments)
- 변환
- void ToUpperInline ()
- FString ToUpper ()
- void ToLowerInline ()
- FString ToLower ()
- 일치
- bool Equals (const FString& Other, ESearchCase::Type SearchCase = ESearchCase::CaseSensitive)
- int32 Compare ( const FString& Other, ESearchCase::Type SearchCase = ESearchCase::CaseSensitive ) const
- bool IsNumeric ()
- bool StartsWith (const FString& InPrefix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase)
- bool EndsWith (const FString& InSuffix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase )
- bool MatchesWildcard (const FString& Wildcard, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase)
- bool Contains (const TCHAR* SubStr, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase, ESearchDir::Type SearchDir = ESearchDir::FromStart )
- 트림
- void TrimStartAndEndInline ()
- FString TrimStartAndEnd ()
- void TrimStartInline ()
- FString TrimStart ()
- void TrimEndInline ()
- FString TrimEnd ()
- void TrimQuotesInline (bool* bQuotesRemoved = nullptr);
- FString TrimQuotes (bool* bQuotesRemoved = nullptr)
- void TrimCharInline (const TCHAR CharacterToTrim, bool* bCharRemoved)
- FString TrimChar (const TCHAR CharacterToTrim, bool* bCharRemoved = nullptr)
- 찾기
- int32 Find ( const TCHAR* SubStr, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase, ESearchDir::Type SearchDir = ESearchDir::FromStart, int32 StartPosition=INDEX_NONE )
- bool FindChar ( TCHAR InChar, int32& Index )
- bool FindLastChar ( TCHAR InChar, int32& Index )
- int32 FindLastCharByPredicate (Predicate Pred, int32 Count)
- int32 FindLastCharByPredicate (Predicate Pred)
- 파싱
- int32 ParseIntoArray ( TArray& OutArray, const TCHAR* pchDelim, bool InCullEmpty = true )
- bool Split (const FString& InS, FString* LeftS, FString* RightS, ESearchCase::Type SearchCase, ESearchDir::Type SearchDir = ESearchDir::FromStart)
- 분할
- void LeftInline (int32 Count, bool bAllowShrinking = true)
- FString Left ( int32 Count )
- void LeftChopInline (int32 Count, bool bAllowShrinking = true)
- FString LeftChop ( int32 Count )
- FString Right ( int32 Count )
- void RightInline (int32 Count, bool bAllowShrinking = true)
- FString RightChop ( int32 Count )
- void RightChopInline (int32 Count, bool bAllowShrinking = true)
- FString Mid (int32 Start, int32 Count)
- void MidInline (int32 Start, int32 Count = MAX_int32, bool bAllowShrinking = true)
- 결합
- static FString Join (const RangeType& Range, const TCHAR* Separator)
- static FString JoinBy (const RangeType& Range, const TCHAR* Separator, ProjectionType Proj)
- 치환
- FString Replace (const TCHAR* From, const TCHAR* To, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase)
- int32 ReplaceInline ( const TCHAR* SearchText, const TCHAR* ReplacementText, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase )
- FString ReplaceQuotesWithEscapedQuotes ()
- void ReplaceCharWithEscapedCharInline ( const TArray* Chars = nullptr )
- FString ReplaceCharWithEscapedChar ( const TArray* Chars = nullptr )
- void ReplaceEscapedCharWithCharInline ( const TArray* Chars = nullptr )
- void ConvertTabsToSpacesInline (const int32 InSpacesPerTab);
- FString ConvertTabsToSpaces (const int32 InSpacesPerTab)
- 뒤집기
- void ReverseString ();
- FString Reverse ()
FText
텍스트 현지화(다국어)를 지원하므로, 사용자에게 표시되는 모든 텍스트는 이것을 사용해야 합니다
텍스트 현지화(국제화)
FName
이름을 지정하는 데 사용되며 대소문자를 구분하지 않습니다. 리소스를 빠르게 찾기 위해 FName은 생성 시 문자열 내용을 기반으로 해시 값을 계산합니다. 이를 통해 비교 로직 수행 시 문자열 내용을 비교하지 않고 해시 값의 일치 여부만 확인하면 됩니다.
상호 변환
TestString = TestName.ToString();
TestString = TestText.ToString();
TestName = FName(*TestString);
TestName = TestText // FText에서 FName으로의 직접 변환은 없으나, FString을 거쳐 FName으로 변환할 수 있습니다. 단, FName이 대소문자를 구분하지 않아 신뢰성이 떨어집니다.
TestText = FText::FromName(TestName)
TestText = FText::FromString(TestString)
경로
FPaths
FPackagePath
정규식 (Math)
Math
UE의 Math 함수는 다음 두 네임스페이스에 있습니다:
- FMath: UE의 기본 수학 라이브러리
- UkismetMathLibrary: 블루프린트 라이브러리로, FMath를 기반으로 블루프린트에 노출되는 수많은 함수를 캡슐화합니다.
시간 및 날짜
타이머
파일 작업
입출력
FArchive
파일
UE에서는 싱글톤 클래스 IFileManager를 통해 다양한 파일 작업을 제공하며, 다음과 같은 기능들을 포함합니다:
- 파일의 읽기/쓰기, 이동, 삭제, 존재 여부 확인, 읽기 가능 여부 확인, 검색(재귀)
- 디렉토리 생성, 삭제, 존재 여부 확인, 순회
파일 IO의 경우, FFileHelper에서 일회성 파일 읽기/쓰기를 위한 편리한 정적 메서드들을 제공합니다.
바이트 스트림
이미지
직렬화
Json
XML
멀티스레딩
FThread, FRunnable
FStreamManager
AsyncTask
TaskGraph
디버깅
로그
- 사용자 정의 로그 카테고리// 헤더 파일에서 선언 DECLARE_LOG_CATEGORY_EXTERN(CustomLogCategory, Log, All);// 소스 파일에서 정의 DEFINE_LOG_CATEGORY(CustomLogCategory);
- 로그 출력UE_LOG(CustomLogCategory,Log,TEXT("This is log %d"),123);
- Log
- Warning
- Error
ASSERTION
코드는 다음 위치에 있습니다.:Engine\Source\Runtime\Core\Public\Misc\AssertionMacros.h
- checkCode
- check
- checkf
- checkNoEntry
- checkNoReentry
- checkNoRecursion
- verify
- verifyf
- unimplemented
- ensure
- ensureMsgf
- ensureAlways
- ensureAlwaysMsgf
성능 분석
콘솔 명령어
모델링 도구
void UBPLibrary::SetStaticMeshPivot(UStaticMesh* InStaticMesh, EStaticMeshPivotType PivotType)
{
if(InStaticMesh == nullptr)
return;
auto InteractiveToolsContext = NewObject<UEditorInteractiveToolsContext>();
InteractiveToolsContext->InitializeContextWithEditorModeManager(&GLevelEditorModeTools(), nullptr);
UE::TransformGizmoUtil::RegisterTransformGizmoContextObject(InteractiveToolsContext);
InteractiveToolsContext->ToolManager->RegisterToolType("EditPivotTool", NewObject<UEditPivotToolBuilder>());
UStaticMeshComponent* StaticMeshComp = NewObject<UStaticMeshComponent>();
StaticMeshComp->SetStaticMesh(InStaticMesh);
InteractiveToolsContext->TargetManager->AddTargetFactory(NewObject<UStaticMeshComponentToolTargetFactory>());
UToolTarget* Target = InteractiveToolsContext->TargetManager->BuildTarget(StaticMeshComp, FToolTargetTypeRequirements());
InteractiveToolsContext->ToolManager->SelectActiveToolType(EToolSide::Left, "EditPivotTool");
GLevelEditorModeTools().SelectNone();
GLevelEditorModeTools().GetSelectedComponents()->Select(StaticMeshComp);
InteractiveToolsContext->ToolManager->ActivateTool(EToolSide::Left);
if (auto EditTool = Cast<UEditPivotTool>(InteractiveToolsContext->ToolManager->ActiveLeftTool))
{
EditTool->SetTargets({ Target });
EditTool->RequestAction((EEditPivotToolActions)PivotType);
EditTool->Tick(0.1);
EditTool->Shutdown(EToolShutdownType::Accept);
}
}
'TECH.ART.FLOW.IO' 카테고리의 다른 글
앙상블 스타즈 IP 구축 전략은? (7) | 2025.06.10 |
---|---|
Message part 1 (0) | 2025.06.09 |
[번역][Unity 스타일라이즈드 렌더링] 그랑블루 판타지 리링크 캐릭터 렌더링의 URP 파이프라인 구현 - 신체 부위 스타일라이즈드 PBR (4) | 2025.06.04 |
[번역][Unity 스타일라이즈드 렌더링] 그랑블루 판타지 리링크 캐릭터 렌더링의 URP 파이프라인 구현-안구 렌더링 (0) | 2025.06.04 |
Khronos PBR 뉴트럴 톤 매퍼, 3D 제품의 실감형 색상 렌더링을 위해 출시 (0) | 2025.05.28 |