C++ 액터 클래스를 이용하여 복잡한 로직을 작성하고 BP를 파생하여 사용하는 예
레벨 위에 TargetPoint 4개를 큰 사각형이 되도록 배치하고 그 위치를 향하여 계속 이동하는 큐브를 구현하고자 한다. 이러한 로직이 그다지 복잡한 것은 아니지만 블루프린트를 이용하기에는 적합하지 않을 정도로 복잡해지므로 C++ 액터 클래스를 생성하고 그 안에 사각형 운동을 하는 로직을 작성한 다음 완성된 C++액터 클래스를 기반으로 블루프린트 액터를 생성하여 사용하면 C++의 장점과 블루프린트의 장점을 동시에 활용할 수 있다
CppActor.h
#pragma once
#include "Runtime/Engine/Classes/Engine/TargetPoint.h"
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CppActor.generated.h"
UCLASS(Blueprintable, BlueprintType)
class CPP_TEST_API ACppActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ACppActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cpp")
TArray<ATargetPoint *> Arr;
UFUNCTION(BlueprintCallable, Category = "Cpp")
void MoveRect(float DeltaTime);
};
위의 헤더파일에 Arr 배열의 원소가 포인터로 선언된 것은 블루프린트에서 취급되는 모든 UObject와 그 하위 클래스의 변수들은 모두 포인터이다. 언리얼 에디터에서 레퍼런스라고 부르고 있지만 C++의 레퍼런스와는 달리 언리얼의 레퍼런스는 포인터를 의미하는 경우가 많다
https://answers.unrealengine.com/questions/351656/pointer-to-uobject-and-inheritance-in-bpc.html
All UObject (and its descendants) variables are pointers.
Calling it a reference is misleading. It is not a reference (&), it is a pointer.
CppActor.cpp
#include "CppActor.h"
#include "../Public/CppActor.h"
// Sets default values
ACppActor::ACppActor()
{
// 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;
}
// Called when the game starts or when spawned
void ACppActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ACppActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ACppActor::MoveRect(float DeltaTime)
{
static int idx = 0;
static FVector * SpdVec = new FVector(0.0F, 200.0F*DeltaTime, 0.0F);
FVector ALoc, NextLoc;
ALoc = GetActorLocation();
int next = idx + 1 == 4 ? 0 : idx + 1;
NextLoc = Arr[next]->GetActorLocation();
switch (idx) {
case 0:
if (ALoc.Y >= NextLoc.Y) {
ALoc.Y = NextLoc.Y;
idx++;
SpdVec->X = -200.0F * DeltaTime; SpdVec->Y = 0.0F; SpdVec->Z = 0.0F;
}
break;
case 1:
if (ALoc.X <= NextLoc.X) {
ALoc.X = NextLoc.X;
idx++;
SpdVec->X = 0.0F; SpdVec->Y =-200.0F * DeltaTime; SpdVec->Z = 0.0F;
}
break;
case 2:
if (ALoc.Y <= NextLoc.Y) {
ALoc.Y = NextLoc.Y;
idx++;
SpdVec->X = 200.0F * DeltaTime; SpdVec->Y =0.0F; SpdVec->Z = 0.0F;
}
break;
case 3:
if (ALoc.X >= NextLoc.X) {
ALoc.X = NextLoc.X;
idx=0;
SpdVec->X = 0.0F; SpdVec->Y = 200.0F*DeltaTime; SpdVec->Z = 0.0F;
}
break;
}
ALoc += (*SpdVec);
SetActorLocation(ALoc);
}
위에 있는 헤더파일에는 아래와 같은 내용이 있다
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cpp")
TArray<ATargetPoint *> Arr;
ATargetPoint 클래스 객체의 포인터를 원소로 하는 배열이 선언되어 있는데, 이 배열의 원소는 Blueprint에서 초기화하여 Arr 변수에 전달하기 위해 EditAnywhere 를 선언한 것이다. 위의 클래스를 드래그하여 레벨에 배치하거나 위의 클래스를 기반으로 블루프린트 클래스를 생성하여 블루프린트 에디터를 열고 [클래스 디폴트] 를 선택하고 우측 [디테일] 뷰를 보면 Arr변수의 원소를 초기화할 수 있는 항목이 나타난다
블루프린트에서 TargetPoint 배열을 선언할 때 원소의 자료형은 [TargetPoint 오브젝트 레퍼런스]형으로 지정해야 하고 [인스턴스 편집가능]을 선택하면 언리얼 에디터의 레벨 위에서 위의 액터를 선택했을 때 우측 Detail 뷰에서 배열의 원소를 할당할 수가 있으므로 레벨에 배치한 ATargetPoint을 선택하여 할당해 줄 수 있다
TargetPoint 레퍼런스 배열의 선언
위의 블루프린트에서 선언한 TargetPoint 참조배열에 원소를 초기화하는 예
블루프린트에서 생성하고 원소가 초기화된 배열을 C++의 TArray<AtargetPoint *> Arr 배열에 전달하는 블루프린트의 예