<해야할 것>
- 서로 다른 Actor 클래스 2개 이상 구현
C++ Actor 클래스 최소 2개 생성각 StaticMeshComponent 포함맵에 배치 가능한 형태여야함회전용 동작 로직 생성이동용 동작 로직 생성
- Tick 함수 기반 동적 Transform 변경
회전 기능 (Rotating Actor)Tick(float DeltaTime) 에서 AddActorLocalRotation()로 매 프레임 회전 적용
Tick(float DeltaTime)에서 위치를 변경하여 왕복 이동 구현StartLocation(시작 위치) 저장/기준점으로 사용MoveSpeed(이동 속도) 적용MaxRange(왕복 범위) 기준으로 이동 제한
이동/회전 시 반드시 DeltaTime 을 활용
- 리플렉션 적용
주요 변수 (회전 속도, 이동 속도, 이동 범위 등)를 UPROPERTY로 선언하여 에디터에서 조정EditAnywhere, BlueprintReadWrite, Category 등을 활용하여 Details 패널에서 편집 가능하게 만들기플레이 중 Details 패널에서 값 변경 시 즉시 반영되는지 확인- C++ 클래스의 자식 블루프린트를 만들어 시각적 효과 등의 추가 로직을 Blueprint에서 구현
<구현>
1. tools -> new c++ class
2. 팝업 창이 뜨면 common class 탭에서 actor를 선택
3. public / private 설정한 후 이름 지정하고 create -> 자동 빌드 (라이브 코딩됨)
4. static mesh comp, scene comp 연결하는 로직 작성
// 헤더에 추가
USceneComponent* SceneRoot;
UStaticMeshComponent* StaticMeshComp;
//cpp 파일에 추가
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
5. 메시 및 머티리얼 할당하기
//cpp 파일에 추가
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Shapes/Shape_Sphere.Shape_Sphere"));
if (MeshAsset.Succeeded())
{
StaticMeshComp->SetStaticMesh(MeshAsset.Object);
}
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialAsset(TEXT("/Game/Resources/Materials/M_Metal_Burnished_Steel.M_Metal_Burnished_Steel"));
if (MaterialAsset.Succeeded())
{
StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
}
6. BeginPlay() 함수에서 Transform 변경
void AMyActor02::BeginPlay()
{
Super::BeginPlay();
FVector NewLocation(0.0f, 0.0f, 6576.0f);
SetActorLocation(NewLocation);
FRotator NewRotation(0.0f);
SetActorRotation(NewRotation);
SetActorScale3D(FVector(30.0f));
}
7. Second Actor에서 사용할 tick 함수 사용을 위한 생성자 설정 / 회전 기능 안 쓰는 액터에선 false로 두기
PrimaryActorTick.bCanEverTick = true;
8. 헤더에 회전을 위한 변수 선언 (RotationSpeed)
// 헤더 파일에 작성
float RotationSpeed;
9. cpp 파일에 회전 코드 작성
//cpp 파일에 작성
AMyActor02::AMyActor02()
{
.
.
.
RotationSpeed = 90.0f;
}
void AMyActor02::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!FMath::IsNearlyZero(RotationSpeed))
{
AddActorLocalRotation(FRotator(0.0f, RotationSpeed * DeltaTime, 0.0f));
}
}
10. 이동 관련 변수 추가 ( MoveVelocity, MaxRange )
11. 왕복 기준점 저장용 변수 추가 ( StartLocation )
// 헤더 파일에 작성
public:
// 이동 관련 변수들 추가
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement|Translation")
FVector MoveVelocity;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement|Translation")
float MaxRange;
private:
// 왕복 기준점 저장용 변수
FVector StartLocation;
12. 이동 기본값 설정
// cpp 파일에 작성
AMyActor::AMyActor()
{
.
.
.
MoveVelocity = FVector(200.0f, 0.0f, 0.0f);
MaxRange = 500.0f;
}
13. 게임 시작 시점의 현재 위치를 이동 기준점으로 저장
void AMyActor::BeginPlay()
{
.
.
.
// 게임 시작 시점의 현재 위치를 이동 기준점으로 저장
StartLocation = GetActorLocation();
}
14. 매 프레임 이동 로직 적용
15. 시작 위치로부터 얼마나 멀어졌는지 거리 계산
16. 설정한 한계치(MaxRange)를 넘어가면 방향 반전
17. 한계치를 넘었을 때(Overshoot), 액터를 정확한 경계선 위치에 맞춰줌 (오차 누적 방지)
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 매 프레임 이동 로직 적용
FVector CurrentLocation = GetActorLocation();
CurrentLocation = CurrentLocation + (MoveVelocity * DeltaTime);
SetActorLocation(CurrentLocation);
// 시작 위치로부터 얼마나 멀어졌는지 계산
float DistanceMoved = FVector::Distance(StartLocation, CurrentLocation);
// 설정한 한계치 넘어가면 방향을 반전시킴
if (DistanceMoved >= MaxRange)
{
// 정확한 왕복을 위해 시작 위치 갱신
FVector MoveDirection = MoveVelocity.GetSafeNormal();
FVector ExactBoundary = StartLocation + (MoveDirection * MaxRange);
SetActorLocation(ExactBoundary);
MoveVelocity = -MoveVelocity;
StartLocation = ExactBoundary;
}
}
18. 리플랙션 적용 및 불필요한 코드 삭제
// 컴포넌트들은 볼 수만 있고 수정은 불가능하게 리플랙션 적용
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
// 이동이나 회전 관련 값들은 수정할 수 있도록 리플랙션 적용
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement|Translation")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement|Rotation")
// 에디터 상에서 수정 가능하게 적용해뒀으니 기존에 작성해둔 BeginPlay() 함수에서 Transform 변경값들 삭제
void AMyActor::BeginPlay()
{
Super::BeginPlay();
SetActorScale3D(FVector(50.0f));
// 게임 시작 시점의 현재 위치를 이동 기준점으로 저장
StartLocation = GetActorLocation();
}
void AMyActor02::BeginPlay()
{
Super::BeginPlay();
SetActorScale3D(FVector(30.0f));
}

First Actor (이동 기능 구현)
<MyActor.h>
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class UE501_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USceneComponent* SceneRoot;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UStaticMeshComponent* StaticMeshComp;
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
public:
// 이동 관련 변수들 추가
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement|Translation")
FVector MoveVelocity;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement|Translation")
float MaxRange;
private:
// 왕복 기준점 저장용 변수
FVector StartLocation;
};
<MyActor.cpp>
#include "MyActor.h"
AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Shapes/Shape_Sphere.Shape_Sphere"));
if (MeshAsset.Succeeded())
{
StaticMeshComp->SetStaticMesh(MeshAsset.Object);
}
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialAsset(TEXT("/Game/Resources/Materials/M_Metal_Burnished_Steel.M_Metal_Burnished_Steel"));
if (MaterialAsset.Succeeded())
{
StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
}
// 이동 기본값 설정
MoveVelocity = FVector(200.0f, 0.0f, 0.0f);
MaxRange = 500.0f;
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
SetActorScale3D(FVector(50.0f));
// 게임 시작 시점의 현재 위치를 이동 기준점으로 저장
StartLocation = GetActorLocation();
}
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 매 프레임 이동 로직 적용
FVector CurrentLocation = GetActorLocation();
CurrentLocation = CurrentLocation + (MoveVelocity * DeltaTime);
SetActorLocation(CurrentLocation);
// 시작 위치로부터 얼마나 멀어졌는지 계산
float DistanceMoved = FVector::Distance(StartLocation, CurrentLocation);
// 설정한 한계치 넘어가면 방향을 반전시킴
if (DistanceMoved >= MaxRange)
{
// 정확한 왕복을 위해 시작 위치 갱신
FVector MoveDirection = MoveVelocity.GetSafeNormal();
FVector ExactBoundary = StartLocation + (MoveDirection * MaxRange);
SetActorLocation(ExactBoundary);
MoveVelocity = -MoveVelocity;
StartLocation = ExactBoundary;
}
}
Second Actor (회전 기능 구현)
<MyActor02.h>
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor02.generated.h"
UCLASS()
class UE501_API AMyActor02 : public AActor
{
GENERATED_BODY()
public:
AMyActor02();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Components")
USceneComponent* SceneRoot;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Components")
UStaticMeshComponent* StaticMeshComp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement|Rotation")
float RotationSpeed;
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
};
<MyActor02.cpp>
#include "MyActor02.h"
AMyActor02::AMyActor02()
{
PrimaryActorTick.bCanEverTick = true;
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Star_C.SM_Star_C"));
if (MeshAsset.Succeeded())
{
StaticMeshComp->SetStaticMesh(MeshAsset.Object);
}
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialAsset(TEXT("/Game/Resources/Materials/M_Gem_D.M_Gem_D"));
if (MaterialAsset.Succeeded())
{
StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
}
RotationSpeed = 90.0f;
}
void AMyActor02::BeginPlay()
{
Super::BeginPlay();
SetActorScale3D(FVector(30.0f));
}
void AMyActor02::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!FMath::IsNearlyZero(RotationSpeed))
{
AddActorLocalRotation(FRotator(0.0f, RotationSpeed * DeltaTime, 0.0f));
}
}
<트러블 슈팅>
두번째 액터의 material이 적용이 계속 안 돼서 구글링 해본 결과
적용하려는 M_Gem_D가 기본 머티리얼이 아니라 머티리얼 인스턴스였음
기존 <UMaterial>을 <UMaterialInterface>로 변경하니 적용됨!
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialAsset(TEXT("/Game/Resources/Materials/M_Gem_D.M_Gem_D"));
if (MaterialAsset.Succeeded())
{
StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
}
<구현 영상>
'학습 > Unreal' 카테고리의 다른 글
| PlayerController 알아보기 / Enhanced Input System을 활용한 입력 매핑 구현하기 (0) | 2026.04.15 |
|---|---|
| Character 클래스를 활용한 캐릭터 구현하기 (0) | 2026.04.15 |
| C++ 클래스와 리플렉션 시스템 활용하기 (0) | 2026.04.10 |
| Tick 함수로 Actor의 Transform 조정하기 (1) | 2026.04.10 |
| Actor의 라이프 사이클, 로그 카테고리 정의 및 추가 (0) | 2026.04.09 |
