SetUp
확장도구 설치
-
UnrealMacroGenerator
Unreal 리플렉션 생성 툴 - 나열된 리플렉션들을 선택하면서 자동으로 키워드를 생성해주는 편리한 기능! 다운로드 후 Alt + W 를 통해 사용
-
ReSharper
UHT, 리플렉션 등 intelliSence 구문분석이 너무 오래 걸리는 상활을 도와주는 유료 툴
코드 입력 시 키워드, 리플렉션 등을 추천해주는 똑똑지니어스한 기능을 가지고 있다
(선생님께서 추천해 주셨지만 유료상품이라… 좀 더 복잡한 함수와 키워드를 사용하게 될 때 구매를 고민해 보자…)
모듈 디렉토리 포함
Source폴더의 파일에 추가하는 #include 헤더를 상대경로로 입력해야(예. ../Source/header.h) 인식되기 때문에 모듈경로를 절대경로로 설정하는게 좋다.
Source\프로젝트명폴더\프로젝트명.Build.cs 파일 public class U03_Basic : ModuleRules에 아래 내용 추가
// 헤더 포함 시 상대경로가 아닌 모듈 기준의 경로로 입력가능
PublicIncludePaths.Add(ModuleDirectory);
C++클래스 액터 생성
C++클래스로 생성된 액터에 각 다른 리플렉션으로 선언하는 변수들이 언리얼 에디터에 어떻게 보여지는 지 확인해보자
헤더
C01_Property.h에 아래 변수들 선언하기
private:
// 블프 & 레벨(뷰포트) 어디서든 수정편집가능
UPROPERTY(EditAnywhere)
int32 A = 10;
// 인스턴스화되었을 때만(뷰포트에 스폰되었을 때만) 수정편집가능 (초기화하지 않으면 기본값 = 0)
UPROPERTY(EditInstanceOnly)
int32 B;
// 블프에서만 수정편집 가능
UPROPERTY(EditDefaultsOnly)
int32 C;
// 블프 & 뷰포트에서 보임 (수정불가)
UPROPERTY(VisibleAnywhere, Category = "Property")
float D = 100.0f;
// 상속받는 클래스에서 수정편집가능
protected:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Property")
int Variable = 10;
소스
C01_Property.cpp 생성자에 변수 B,C 정의
AC01_Property::AC01_Property()
:B(20)
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
C = 30;
}
c++클래스 액터 C01_Property기반 블루프린트 클래스 생성(파생) 후 뷰포트 레벨에 올려졌을 때,
변수 B와 C는 각각 뷰포트, 블루프린트에디터에서만 보여지고 수정편집이 가능하다
BeginPlay & Tick
아래와 같이 C01_Property.cpp의 BeginPlay()에 Log출력할 Fstring str 선언 정의하기
// Called when the game starts or when spawned
void AC01_Property::BeginPlay()
{
Super::BeginPlay();
FString str;
str.Append(" / A : ");
str.Append(FString::FromInt(A));
str.Append(" / B : ");
str.Append(FString::FromInt(B));
str.Append(" / C : ");
str.Append(FString::FromInt(C));
// string str --> str.c_str() = *str
GLog->Log(*str);
}
Tick에 변수D를 매 프레임의 DT씩 증가시켜주기
// Called every frame
void AC01_Property::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
D += DeltaTime;
}
컴파일 후 실행 시 Log가 출력되고 월드아웃라이너의 C01_Property의 디테일에 변수D가 증가하고 있는 모습을 확인할 수 있다.
메쉬와 머티리얼 추가
액터에 메쉬와 머티리얼 추가하기 위해 C02_Mesh 클래스 생성, Tick이벤트는 사용하지 않을 예정이라 Tick함수 삭제
Mesh
C02_Mesh.h에 Mesh정보를 담을 컴포넌트 선언
protected:
// 컴포넌트의 경우 속성을 VisibleAnywhere로 한다
UPROPERTY(VisibleAnywhere)
class UStaticMeshComponent* Mesh;
C02_Mesh.cpp에 StaticMesh관련 함수를 사용하기 위해 헤더 추가
#include "Components/StaticMeshComponent.h"
생성자에서 SetRootComponent & SetStaticMesh
// 블프에서는 자동생성되는 DefaultRootSceen이 C++로 만들 땐 Null로 생성되기때문에 RootComponent를 정의해줘야 함
Mesh = CreateDefaultSubobject<UStaticMeshComponent>("Mesh");
RootComponent = Mesh;
// SetStaticMesh
ConstructorHelpers::FObjectFinder<UStaticMesh> mesh(L"StaticMesh'/Game/Meshes/Cube.Cube'");
if (mesh.Succeeded())
Mesh->SetStaticMesh(mesh.Object);
빌드 후 언리얼 에디터에서 Cube Mesh가 적용된 C02_Mesh를 확인 할 수 있다
(C02_Mesh기반 블루프린트클래스 생성을 통해 디테일 확인 가능)
Material
C02_Mesh.h에 Material Instance Class를 담을 변수 선언
private:
// Material Instance는 Dynamic일 때 수정이 가능하다
class UMaterialInstanceDynamic* Material;
C02_Mesh.cpp에 Material관련 헤더 추가
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialInstanceDynamic.h"
BeginPlay에 Material 생성
// Called when the game starts or when spawned
void AC02_Mesh::BeginPlay()
{
Super::BeginPlay();
// DynamicInstance는 Ingame에서만 생성할 수 있다
UObject* obj = StaticLoadObject(UMaterialInstanceConstant::StaticClass(), nullptr, L"MaterialInstanceConstant'/Game/Materials/M_White_Inst.M_White_Inst'");
UMaterialInstanceConstant * material = Cast<UMaterialInstanceConstant>(obj);
Material = UMaterialInstanceDynamic::Create(material, this);
Mesh->SetMaterial(0, Material);
}
블프에서 캐릭터에 Dynamic Material Instance 생성할 때 와 비교해보면 같은 내용인 걸 확인할 수 있다
여기서 BeginPlay가 수정되었기 때문에 빌드&컴파일을 하면 언리얼에디터가 오류나면서 강제종료 될 수도 있다
C02_Mesh기반 블루프린트클래스 생성된 상태에서 BeginPlay의 내용이 변동됐기 때문에 지속해서 오류가 발생하는 경우인듯 하다
당황하지 말고 침착하게 uproject를 다시 실행하고 BP_C02_Mesh 삭제 후 다시 생성한 뒤에,
뷰포트에 올려보면 다음과 같이 Dynamic Material Instance가 실행 중일 때만 적용된 걸 확인할 수 있다
CHelpers.h & Global.h
반복적으로 사용할 함수는 Template으로 활용하면 편리하다
Source 폴더에 Global.h를 생성하고, 새 폴더 Utilities에 CHelpers.h를 생성한다
C02_Mesh.h에 CHelpers.h 헤더 포함하기
#include "Utilities/CHelpers.h"
C02_Mesh.cpp에 Global.h 헤더 포함하기
#include "Global.h"
- CHelpers.h
- SetRootComponent
#pragma once #include "CoreMinimal.h" class U03_BASIC_API CHelpers { public: template<typename T> static void CreateComponent(AActor* InActor, T** OutComponent, FName InName, USceneComponent* InParent = nullptr) { *OutComponent = InActor->CreateDefaultSubobject<T>(InName); if (!!InParent) { (*OutComponent)->SetupAttachment(InParent); return; } InActor->SetRootComponent(*OutComponent); } };// 블프에서 자동생성되는 DefaultRootSceen이 C++로 만들 땐 Null로 생성되기때문에 RootComponent를 정의해줘야 함 //Mesh = CreateDefaultSubobject<UStaticMeshComponent>("Mesh"); //RootComponent = Mesh; CHelpers::CreateComponent<UStaticMeshComponent>(this, &Mesh, "Mesh"); - SetStaticMesh
template<typename T> static void GetAsset(T** OutObject, FString InPath) { ConstructorHelpers::FObjectFinder<T> asset(*InPath); //FString이라서 * *OutObject = asset.Object; }//ConstructorHelpers::FObjectFinder<UStaticMesh> mesh(L"StaticMesh'/Game/Meshes/Cube.Cube'"); //if (mesh.Succeeded()) // Mesh->SetStaticMesh(mesh.Object); UStaticMesh* mesh = nullptr; CHelpers::GetAsset(&mesh, "StaticMesh'/Game/Meshes/Cube.Cube'"); Mesh->SetStaticMesh(mesh); - SetDynamicMaterialInstance
template<typename T> static void GetAssetDynamic(T** OutObject, FString InPath) { *OutObject = Cast<T>(StaticLoadObject(T::StaticClass(), nullptr, *InPath)); }// DynamicInstance는 Ingame에서만 생성할 수 있다 // 예) BP_Enemy BeginPlay -> LoadMaterial() 함수 참조 //UObject* obj = StaticLoadObject(UMaterialInstanceConstant::StaticClass(), nullptr, L"MaterialInstanceConstant'/Game/Materials/M_White_Inst.M_White_Inst'"); //UMaterialInstanceConstant * material = Cast<UMaterialInstanceConstant>(obj); //Material = UMaterialInstanceDynamic::Create(material, this); //Mesh->SetMaterial(0, Material); UMaterialInstanceConstant* material = nullptr; CHelpers::GetAssetDynamic<UMaterialInstanceConstant>(&material, "MaterialInstanceConstant'/Game/Materials/M_White_Inst.M_White_Inst'"); Material = UMaterialInstanceDynamic::Create(material, this); Mesh->SetMaterial(0, Material);
- SetRootComponent
- Global.h
#include "kismet/GameplayStatics.h" #include "kismet/KismetSystemLibrary.h" #include "kismet/KismetMathLibrary.h" #include "Utilities/CHelpers.h"
메쉬 & 머티리얼 활용
랜덤 머티리얼 색변환
C02_Mesh에 SetRandomColor()함수 선언 & 정의
private:
UFUNCTION()
void SetRandomColor();
...BeginPlay...
//Deligate함수선언 -> 1초마다 SetRandomColor
UKismetSystemLibrary::K2_SetTimer(this, "SetRandomColor", 1.0f, true);
void AC02_Mesh::SetRandomColor()
{
Material->SetVectorParameterValue("Color", FLinearColor::MakeRandomColor());
}
다양한 메쉬 적용
C02_Mesh 파생 C++클래스 생성을 통해 자식클래스들을 만들어 다른 Mesh를 적용시켜 보자