[UE] StaticMesh
플러그인 시스템을 통해 개발자는 Unreal Editor 내에서 StaticMesh에 적용할 수 있는 새로운 도구, 수정자 및 렌더링 옵션을 생성할 수 있으며 이러한 새로운 기능을 다른 개발자 또는 더 넓은 커뮤니티와 쉽게 공유할 수 있다. StaticMesh 플러그인을 만들려면 개발자는 먼저 Unreal의 Asset Editor를 사용하여 수행할 수 있는 새 자산 유형을 정의해야 한다. 이 새로운 자산 유형은 새로운 재료 또는 충돌 모양과 같은 사용자 정의 속성 및 기능으로 확장될 수 있다. 플러그인이 생성되면 다른 프로젝트에서 사용할 수 있도록 패키징 및 배포할 수 있으며, 개발자는 언리얼 에디터의 사용자 인터페이스를 통해 자신의 StaticMesh에 새 기능을 쉽게 추가할 수 있다. 전반적으로 Unreal Engine의 StaticMesh 플러그인 시스템은 개발자가 엔진의 핵심 기능을 커스터마이즈하여 특정 요구 사항을 충족하고 사용자를 위해 보다 매력적이고 몰입감 있는 경험을 생성할 수 있는 강력하고 유연한 방법을 제공한다.
목차
Plugins | ||
Example | ||
Example.Build.cs ExampleConsoleCommand.h .cpp ExampleDebuggerCategory.h .cpp ExampleModule.h .cpp StaticMesh_Detail.h .cpp 생성 |
||
Source | ||
Utilities | ||
CHelper.h 추가 CLog.h .cpp 추가 |
||
Global.h 추가 CStaticMesh.h .cpp 생성 .Build.cs |
||
.uproject | ||
Source에 Global.h와 Utilities(CHelper, CLog)폴더 추가하기
Global.h 가져오기
이전에 만들었던 Global.h를 가져와서 추가한다.
Global.h
#pragma once
#include "DrawDebugHelpers.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "Utilities/CHelpers.h"
#include "Utilities/CLog.h"
Utilities 폴더 가져오기 - CHelpers.h, CLog.h .cpp
이전에 만들었던 Utilites 폴더를 가져와서 추가한다.
Utilities 폴더 안에는 CHelpers.h, CLog.h .cpp가 있다.
해당 코드들의 변경사항은 없다.
CHelpers.h
#pragma once
#include "CoreMinimal.h"
#define CheckTrue(x) { if(x == true) return; }
#define CheckTrueResult(x, y) { if(x == true) return y; }
#define CheckFalse(x) { if(x == false) return;}
#define CheckFalseResult(x, y) { if(x == false) return y;}
#define CheckNull(x) { if(x == nullptr) return;}
#define CheckNullResult(x, y) { if(x == nullptr) return y;}
#define CreateTextRender()\
{\
CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Tex", Root);\
Text->SetRelativeLocation(FVector(0, 0, 100));\
Text->SetRelativeRotation(FRotator(0, 180, 0));\
Text->SetRelativeScale3D(FVector(2));\
Text->TextRenderColor = FColor::Red;\
Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;\
Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));\
}
class U2212_05_API CHelpers
{
public:
template<typename T>
static void CreateComponent(AActor* InActor, T** OutComponent, FName InName, USceneComponent* InParent = nullptr, FName InSocketName = NAME_None)
{
*OutComponent = InActor->CreateDefaultSubobject<T>(InName);
if (!!InParent)
{
(*OutComponent)->SetupAttachment(InParent, InSocketName); //이렇게 사용하면 Socket Name에 _를 사용하면 안 된다.
return;
}
InActor->SetRootComponent(*OutComponent);
}
//CreateActorComponent 추가
template<typename T>
static void CreateActorComponent(AActor* InActor, T** OutComponent, FName InName)
{
*OutComponent = InActor->CreateDefaultSubobject<T>(InName);
}
template<typename T>
static void GetAsset(T** OutObject, FString InPath)
{
ConstructorHelpers::FObjectFinder<T> asset(*InPath);
*OutObject = asset.Object;
}
template<typename T>
static void GetAssetDynamic(T** OutObject, FString InPath)
{
*OutObject = Cast<T>(StaticLoadObject(T::StaticClass(), nullptr, *InPath));
}
template<typename T>
static void GetClass(TSubclassOf<T>* OutClass, FString InPath)
{
ConstructorHelpers::FClassFinder<T> asset(*InPath);
*OutClass = asset.Class;
}
template<typename T>
static T* FindActor(UWorld* InWorld)
{
for (AActor* actor : InWorld->GetCurrentLevel()->Actors)
{
if (!!actor && actor->IsA<T>())
return Cast<T>(actor);
}
return nullptr;
}
template<typename T>
static void FindActors(UWorld* InWorld, TArray<T*>& OutActors)
{
for (AActor* actor : InWorld->GetCurrentLevel()->Actors)
{
if (!!actor && actor->IsA<T>())
OutActors.Add(Cast<T>(actor));
}
}
template<typename T>
static T* GetComponent(AActor* InActor)
{
return Cast<T>(InActor->GetComponentByClass(T::StaticClass()));
}
template<typename T>
static T* GetComponent(AActor* InActor, const FString& InName)
{
TArray<T*> components;
InActor->GetComponents<T>(components);
for (T* component : components)
{
if (component->GetName() == InName)
return component;
}
return nullptr;
}
static void AttachTo(AActor* InActor, USceneComponent* InParent, FName InSocketName)
{
InActor->AttachToComponent(InParent, FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSocketName);
}
};
CLog.h
#pragma once
#include "CoreMinimal.h"
#define LogLine(){ CLog::Log(__FILE__, __FUNCTION__, __LINE__); }
#define PrintLine(){ CLog::Print(__FILE__, __FUNCTION__, __LINE__); }
class U2212_05_API CLog
{
public:
static void Log(int32 InValue);
static void Log(float InValue);
static void Log(const FString& InValue);
static void Log(const FVector& InValue);
static void Log(const FRotator& InValue);
static void Log(const UObject* InValue);
static void Log(const FString& InFileName, const FString& InFuncName, int32 InLineNumber);
static void Print(int32 InValue, int32 InKey = -1, float InDuration = 10, FColor InColor = FColor::Blue);
static void Print(float InValue, int32 InKey = -1, float InDuration = 10, FColor InColor = FColor::Blue);
static void Print(const FString& InValue, int32 InKey = -1, float InDuration = 10, FColor InColor = FColor::Blue);
static void Print(const FVector& InValue, int32 InKey = -1, float InDuration = 10, FColor InColor = FColor::Blue);
static void Print(const FRotator& InValue, int32 InKey = -1, float InDuration = 10, FColor InColor = FColor::Blue);
static void Print(const UObject* InValue, int32 InKey = -1, float InDuration = 10, FColor InColor = FColor::Blue);
static void Print(const FString& InFileName, const FString& InFuncName, int32 InLineNumber);
};
CLog.cpp
#include "Utilities/CLog.h"
#include "Engine.h"
DEFINE_LOG_CATEGORY_STATIC(GP, Display, All)
void CLog::Log(int32 InValue)
{
//GLog->Log("GP", ELogVerbosity::Display, FString::FromInt(InValue));
UE_LOG(GP, Display, L"%d", InValue);
}
void CLog::Log(float InValue)
{
UE_LOG(GP, Display, L"%f", InValue);
}
void CLog::Log(const FString & InValue)
{
UE_LOG(GP, Display, L"%s", *InValue);
}
void CLog::Log(const FVector & InValue)
{
UE_LOG(GP, Display, L"%s", *InValue.ToString());
}
void CLog::Log(const FRotator & InValue)
{
UE_LOG(GP, Display, L"%s", *InValue.ToString());
}
void CLog::Log(const UObject * InValue)
{
FString str;
if (!!InValue)
str.Append(InValue->GetName());
str.Append(!!InValue ? " Not Null" : "Null");
UE_LOG(GP, Display, L"%s", *str);
}
void CLog::Log(const FString & InFileName, const FString & InFuncName, int32 InLineNumber)
{
//C:\\Test\\Test.cpp
int32 index = 0, length = 0;
InFileName.FindLastChar(L'\\', index);
length = InFileName.Len() - 1;
FString fileName = InFileName.Right(length - index);
UE_LOG(GP, Display, L"%s, %s, %d", *fileName, *InFuncName, InLineNumber);
}
void CLog::Print(int32 InValue, int32 InKey, float InDuration, FColor InColor)
{
GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, FString::FromInt(InValue));
}
void CLog::Print(float InValue, int32 InKey, float InDuration, FColor InColor)
{
GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, FString::SanitizeFloat(InValue));
}
void CLog::Print(const FString & InValue, int32 InKey, float InDuration, FColor InColor)
{
GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, InValue);
}
void CLog::Print(const FVector & InValue, int32 InKey, float InDuration, FColor InColor)
{
GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, InValue.ToString());
}
void CLog::Print(const FRotator & InValue, int32 InKey, float InDuration, FColor InColor)
{
GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, InValue.ToString());
}
void CLog::Print(const UObject * InValue, int32 InKey, float InDuration, FColor InColor)
{
FString str;
if (!!InValue)
str.Append(InValue->GetName());
str.Append(!!InValue ? " Not Null" : "Null");
GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, str);
}
void CLog::Print(const FString& InFileName, const FString& InFuncName, int32 InLineNumber)
{
int32 index = 0, length = 0;
InFileName.FindLastChar(L'\\', index);
length = InFileName.Len() - 1;
FString fileName = InFileName.Right(length - index);
FString str = FString::Printf(L"%s, %s, %d", *fileName, *InFuncName, InLineNumber);
GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Blue, str);
}
U2212_05 수정하기
2212_05.uproject
U2212_05.Build.cs
U2212_05.Build.cs
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class U2212_05 : ModuleRules
{
public U2212_05(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PrivateIncludePaths.Add(ModuleDirectory);
PublicDependencyModuleNames.Add("Core");
PrivateDependencyModuleNames.Add("CoreUObject");
PrivateDependencyModuleNames.Add("Engine");
PrivateDependencyModuleNames.Add("InputCore");
PrivateDependencyModuleNames.Add("HeadMountedDisplay");
PrivateDependencyModuleNames.Add("NavigationSystem");
PrivateDependencyModuleNames.Add("AIModule");
}
}
Plugin -> Game의 private Module은 인식이 안 된다.
U22212_05/CstaticMesh
Source/U2212_05는 ModuleDirectory
보통 private으로 만들고 필요한것만 public으로 연다.
Plugins - Example - Source - Example - Example.Build.cs
플러그인쪽에 있는 아래의 코드와 U2212_05쪽에 있는 위의 코드의 차이를 비교해보자.
Example.Build.cs
Example.Build.cs
using UnrealBuildTool;
public class Example : ModuleRules
{
public Example(ReadOnlyTargetRules Target) : base(Target) //public은 외부에 공개
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.Add(ModuleDirectory);
PublicDependencyModuleNames.Add("Core");
PrivateDependencyModuleNames.Add("U2212_05"); //본인 모델은 private
PrivateDependencyModuleNames.Add("CoreUObject");
PrivateDependencyModuleNames.Add("Engine");
PrivateDependencyModuleNames.Add("Slate");
PrivateDependencyModuleNames.Add("SlateCore");
PrivateDependencyModuleNames.Add("GameplayDebugger");
}
}
CStaticMesh 생성
새 C++ 클래스 - Actor - CStaticMes 생성 (직렬화 될 것이기 때문에 게임쪽에서 만든다)
CStaticMesh.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CStaticMesh.generated.h"
UCLASS()
class U2212_05_API ACStaticMesh : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleAnywhere, Category = "Material")
class UMaterialInstanceConstant* Material;
private:
UPROPERTY(VisibleAnywhere)
class UStaticMeshComponent* Mesh;
public:
ACStaticMesh();
protected:
virtual void BeginPlay() override;
};
변수 생성
- class UMaterialInstanceConstant* Material;
- class UStaticMeshComponent* Mesh;
CStaticMesh.cpp
#include "CStaticMesh.h"
#include "Global.h"
#include "Components/StaticMeshComponent.h"
#include "Materials/MaterialInstanceConstant.h"
ACStaticMesh::ACStaticMesh()
{
CHelpers::CreateComponent<UStaticMeshComponent>(this, &Mesh, "Mesh");
UStaticMesh* mesh;
CHelpers::GetAsset<UStaticMesh>(&mesh, "StaticMesh'/Game/MatineeCam_SM.MatineeCam_SM'");
Mesh->SetStaticMesh(mesh);
CHelpers::GetAsset<UMaterialInstanceConstant>(&Material, "MaterialInstanceConstant'/Game/M_StaticMesh_Inst.M_StaticMesh_Inst'");
Mesh->SetMaterial(0, Material);
}
void ACStaticMesh::BeginPlay()
{
Super::BeginPlay();
}
Matinee Camera 생성
- CHelpers::GetAsset<UStaticMesh>(&mesh, "StaticMesh'/Game/MatineeCam_SM.MatineeCam_SM'");
Matinee Camera 머테리얼 지정
- CHelpers::GetAsset<UMaterialInstanceConstant>(&Material, "MaterialInstanceConstant'/Game /M_StaticMesh_Inst.M_StaticMesh_Inst'");
Matinee Camera로 사용할 StaticMesh를 복사하여 생성 후 콘텐츠 폴더로 이동
Matinee Camera에 사용할 머테리얼 생성
CStaticMesh 기반 블루프린트 생성 - BP_CStaticMesh 생성
Plugin 부분
StaticMesh_Detail 생성
새 C++ 클래스 - 없음 - StaticMesh_Detail 생성
StaticMesh_Detail.h
#pragma once
#include "CoreMinimal.h"
#include "IDetailCustomization.h"
class EXAMPLE_API FStaticMesh_Detail : public IDetailCustomization //IDetailCustomization로부터 상속받는다.
{
public:
static TSharedRef<IDetailCustomization> MakeInstance();
public:
void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
private:
FReply OnClicked_Paint();
};
StaticMesh_Detail.cpp
#include "StaticMesh_Detail.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
TSharedRef<IDetailCustomization> FStaticMesh_Detail::MakeInstance()
{
return MakeShareable(new FStaticMesh_Detail());
}
void FStaticMesh_Detail::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
IDetailCategoryBuilder& actor = DetailBuilder.EditCategory("Actor");
actor.SetCategoryVisibility(false); //actor 카테고리를 숨긴다.
IDetailCategoryBuilder& lighting = DetailBuilder.EditCategory("Lighting");
IDetailCategoryBuilder& mesh = DetailBuilder.EditCategory("Mesh");
mesh.AddCustomRow(FText::FromString("Mesh"))
.NameContent()
[
SNew(STextBlock) //SNew는 Slate UI들을 동적할당 한다.
.Text(FText::FromString("Color"))
];
}
"AddCustomRow"는 개발자가 주어진 위젯에 커스텀 행을 추가할 수 있게 해주는 언리얼 엔진의 기능이다. 이 기능은 엔진의 기본 위젯에서 제공하는 것 이상의 추가 기능이 필요한 사용자 정의 편집기 패널 또는 사용자 인터페이스 요소를 생성하는 데 자주 사용된다.
"AddCustomRow"를 사용하려면 먼저 행을 추가할 위젯 컨테이너의 새 인스턴스를 생성해야 한다. 컨테이너가 있으면 "AddCustomRow" 함수를 호출하고 컨테이너에 대한 참조와 새 행을 생성하는 데 사용할 위임 함수를 전달할 수 있다.
대리자 함수는 행의 실제 콘텐츠를 생성하는 역할을 담당하며 여기에는 필요한 위젯 또는 사용자 지정 논리의 조합이 포함될 수 있다. 행이 생성되면 컨테이너에 추가되고 화면에 표시된다.
StaticMesh_Detail.cpp
#include "StaticMesh_Detail.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
TSharedRef<IDetailCustomization> FStaticMesh_Detail::MakeInstance()
{
return MakeShareable(new FStaticMesh_Detail());
}
void FStaticMesh_Detail::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
IDetailCategoryBuilder& actor = DetailBuilder.EditCategory("Actor");
//actor.SetCategoryVisibility(false); //actor 카테고리를 숨긴다.
TArray<TSharedRef<IPropertyHandle>> handles;
actor.GetDefaultProperties(handles);
for(TSharedRef<IPropertyHandle> handle : handles)
{
// GLog->Log(handle->GetProperty()->GetName());
// GLog->Log(handle->GetProperty()->GetPathName());
// GLog->Log(handle->GetProperty()->GetFullName());
if (handle->GetProperty()->GetName().Compare("bCanBeDamaged") == 0)
DetailBuilder.HideProperty(handle); //해당 조건이면 handle를 숨겨준다.(=actor에 있는 bCanBeDamaged를 숨겨준다.
}
IDetailCategoryBuilder& mesh = DetailBuilder.EditCategory("Mesh");
mesh.AddCustomRow(FText::FromString("Mesh"))
.NameContent()
[
SNew(STextBlock) //SNew는 Slate UI들을 동적할당 한다.
.Text(FText::FromString("Color"))
]
.ValueContent()
[
SNew(SButton)
.VAlign(VAlign_Center)
.HAlign(HAlign_Fill)
.OnClicked(this, &FStaticMesh_Detail::OnClicked_Paint)
//.Content()
[
SNew(STextBlock)
.Text(FText::FromString("Paint"))
]
];
}
FReply FStaticMesh_Detail::OnClicked_Paint()
{
return FReply::Handled();
}
'⭐ Unreal Engine > UE Plugin - Basic' 카테고리의 다른 글
[UE] Plugin (Save StaticMesh & RenderData, LOD) (0) | 2023.04.12 |
---|---|
[UE] Plugin (StaticMesh Detail) (0) | 2023.04.07 |
[UE] Console Command (0) | 2023.04.05 |
[UE] Console, DrawDebugLine (0) | 2023.04.04 |
[UE] Plugin, Slate UI (0) | 2023.04.03 |
댓글
이 글 공유하기
다른 글
-
[UE] Plugin (Save StaticMesh & RenderData, LOD)
[UE] Plugin (Save StaticMesh & RenderData, LOD)
2023.04.12 -
[UE] Plugin (StaticMesh Detail)
[UE] Plugin (StaticMesh Detail)
2023.04.07 -
[UE] Console Command
[UE] Console Command
2023.04.05 -
[UE] Console, DrawDebugLine
[UE] Console, DrawDebugLine
2023.04.04