액터는 언리얼 엔진 레벨에 배치할 수 있는 모든 오브젝트를 뜻한다.
여러가지 컴포넌트를 적용시켜 다양한 액터를 만들 수 있다.
틱은 게임이 플레이 되는 동안 매 프레임 호출되는 함수이다.
계속해서 업데이트 되어야 하는 데이터가 있을 때 틱 함수에 적용시킨다.
리플렉션 시스템은 엔진 및 에디터 함수 기능을 제공하는 다양한 매크로로 클래스를 캡슐화 시켜주는 시스템이다.
표준 cpp 클래스, 함수, 변수 등을 모두 코드에 적용시키면 에디터 상에서 편리하게 조작할 수 있게 해준다.

좌측 상단 Tools 탭에서 New C++ Class... 를 눌러준다.


이후 Actor를 선택해준 후, Class Type 을 Public 으로 설정하고 액터의 이름을 정해준다.
나는 이동하는 액터와 회전하는 액터를 만들 예정이기에 각각 MovingActor, RotatingActor 라고 이름 지어주었다.

그러면 이제 Content Browser 에 C++ Classes 폴더와 그 아래에 C++ 클래스 액터 두 개가 생겨난다.
// MovingActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MovingActor.generated.h"
UCLASS()
class HW0607_API AMovingActor : public AActor
{
GENERATED_BODY()
public:
AMovingActor();
protected:
USceneComponent* SceneRoot;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Component")
UStaticMeshComponent* StaticMeshComp;
FVector StartLocation;
FVector MoveDirection;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement")
float MoveSpeed;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement")
float MoveDistance;
float TraveledDistance;
bool IsMovingForward;
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
};
우선 헤더 쪽을 먼저 보자면 기본적으로 액터의 뿌리를 구성하기 위해 USceneComponent 를 이용해 씬 컴포넌트를 만들었고, UStaticComponent 를 이용해 스태틱 메시를 적용 시켰다.
또한 스태틱 메시는 에디터에서 수정하는 편이 좋다고 생각했기에 리플렉션 시스템을 적용시키기 위해 변수 앞에 UPROPERTY() 함수를 추가했다.
뒤에 있는 인수들은 각각 어디서든 수정 가능하고, 블루프린트에서는 읽기만 가능하며, 카테고리는 Component 에 들어가도록 설정해주었다.
그리고 액터가 움직이는 위치와 기준들을 정하기 위해 3차원인 FVector 를 통해 StartLocation 을 정해주고 움직이는 방향 또한 FVector 를 통해 MoveDirection 으로 변수를 선언해주었다.
거기에 액터가 움직이는 속도, 거리를 표현하기 위해 float 값으로 MoveSpeed 와 MoveDistance 를 선언해주었고, 리플렉션 시스템을 적용시켰다.
그 다음으로는 액터가 일정 거리만큼 이동하면 다시 돌아오며 행동을 반복하게 하기 위해서 이동한 거리인 TraveledDistance 와 현재 방향을 나타내는 IsMovingForward 라는 bool 값 변수를 추가해주었다.
// MovingActor.cpp
#include "MovingActor.h"
AMovingActor::AMovingActor()
{
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
PrimaryActorTick.bCanEverTick = true;
MoveSpeed = 100.0f;
MoveDistance = 400.0f;
IsMovingForward = true;
}
void AMovingActor::BeginPlay()
{
Super::BeginPlay();
StartLocation = GetActorLocation();
MoveDirection = FVector(0, 0, 1);
}
void AMovingActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
float MoveStep = MoveSpeed * DeltaTime;
if (!IsMovingForward) MoveStep *= -1;
FVector EndLocation = GetActorLocation() + MoveDirection * MoveStep;
SetActorLocation(EndLocation);
TraveledDistance += FMath::Abs(MoveStep);
if (TraveledDistance >= MoveDistance)
{
IsMovingForward = !IsMovingForward;
TraveledDistance = 0.0f;
}
}
이번엔 소스 파일 쪽이다.
생성자에서 씬 컴포넌트를 만들어 적용시켜주었고, 스태틱 메시 또한 생성 후 액터의 뿌리인 SceneRoot 에 붙여주었다.
그리고 액터의 속도, 이동 거리 등을 생성자에서 초기화 시켜주었고, BeginPlay 에서 시작 위치와 이동 방향을 설정해주었다.
마지막으로 Tick 함수이다.
먼저 액터의 이동 속도와 델타 타임을 곱하여 float 값으로 MoveStep 에 넣어두었고, 앞으로 가고 있다면 이 MoveStep 에 -1을 곱하여 움직이는 방향이 반대가 되도록 설정해두었다.
그리고 도착 지점을 이동 방향과 움직일 거리를 곱한 값을 현재 위치에 더해준 값을 새로운 FVector 인 EndLocation 을 생성해 대입시켜주었고, 현재 위치를 EndLocation으로 설정해주었다.
또한 액터가 움직인 거리는 절대값으로 표현해야하기에 FMath::Abs 를 이용하여 MoveStep 의 값을 절대값으로 바꾼 뒤 TraveledDistance 에 넣어줬다.
그리고 만약 이 값이 미리 설정해둔 이동 거리를 초과하면 방향을 바꾸고, 현재 총 이동한 거리를 초기화 시켜준다.
// RotatingActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "RotatingActor.generated.h"
UCLASS()
class HW0607_API ARotatingActor : public AActor
{
GENERATED_BODY()
public:
ARotatingActor();
protected:
USceneComponent* SceneRoot;
UPROPERTY(EditAnywhere,BlueprintReadOnly, Category="Component")
UStaticMeshComponent* StaticMeshComp;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Movement")
float RotatingSpeed;
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
};
우선 헤더 파일.
똑같이 씬, 스태틱 메시 컴포넌트를 선언하고, 액터의 회전 속도를 float 값을 통해 RotatingSpeed 라고 선언했다.
// RotatingActor.cpp
#include "RotatingActor.h"
ARotatingActor::ARotatingActor()
{
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
PrimaryActorTick.bCanEverTick = true;
RotatingSpeed = 90.0f;
}
void ARotatingActor::BeginPlay()
{
Super::BeginPlay();
}
void ARotatingActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!FMath::IsNearlyZero(RotatingSpeed))
{
AddActorLocalRotation(FRotator(0.0f, RotatingSpeed * DeltaTime, 0.0f));
}
}
다음은 소스 파일.
아까와 같이 생성자에서 컴포넌트들을 적용시켜주고, 회전 속도를 초기화 시켜준다.
이후에는 틱 함수에서 회전을 구현한다. float 값이면 회전 속도가 0.00001 이어도 0이 아니기 때문에 이를 막기 위해서 FMath::IsNeearlyZero 함수를 사용해 보완해주고, 만약 회전 속도가 0이 아니라면 AddActorLocalRotation 함수를 통해 회전시켜준다.
FRotator 를 통해 Yaw, Pitch, Roll 값 중 Pitch 만을 돌릴 것이기 때문에 회전 속도와 델타 타임을 곱해 적용시켜준다.\


C++ 클래스 액터를 블루프린트로 상속시켜서 생성해준다.

그리고 각각 메시와 머티리얼들을 적용시켜주면 된다.

이후엔 대략 액터들을 활용하여 맵을 만들어주면 된다.
생각보다 미리 구현되어 있는 함수들이 있어 편하긴 했지만 이 외우기 어려워 보이는 함수들을 매번 기억해내서 써야한다고 생각하니 조금 막막하기는 하지만, 직접 구현하는 것보다는 낫다고 생각이 들었다.
이전에는 블루프린트만을 사용했지만 이번엔 코드를 작성하여 구현했기에 조금 더 많은 성취감과 만족감을 얻을 수 있었다.