<해야할 것>

더보기
더보기
더보기
더보기
  1. 서로 다른 Actor 클래스 2개 이상 구현
    • C++ Actor 클래스 최소 2개 생성
    • 각 StaticMeshComponent 포함
    • 맵에 배치 가능한 형태여야함
    • 회전용 동작 로직 생성
    • 이동용 동작 로직 생성
  2. Tick 함수 기반 동적 Transform 변경

    회전 기능 (Rotating Actor)
    • Tick(float DeltaTime) 에서 AddActorLocalRotation()로 매 프레임 회전 적용
    이동 기능 (Moving Platform Actor)
    • Tick(float DeltaTime)에서 위치를 변경하여 왕복 이동 구현
    • StartLocation(시작 위치) 저장/기준점으로 사용
    • MoveSpeed(이동 속도) 적용
    • MaxRange(왕복 범위) 기준으로 이동 제한
    프레임 독립성
    • 이동/회전 시 반드시 DeltaTime 을 활용
  3. 리플렉션 적용
    • 주요 변수 (회전 속도, 이동 속도, 이동 범위 등)를 UPROPERTY로 선언하여 에디터에서 조정
    • EditAnywhere, BlueprintReadWrite, Category 등을 활용하여 Details 패널에서 편집 가능하게 만들기
    • 플레이 중 Details 패널에서 값 변경 시 즉시 반영되는지 확인
    • C++ 클래스의 자식 블루프린트를 만들어 시각적 효과 등의 추가 로직을 Blueprint에서 구현
  1.  

<구현>

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);
	}

 

 

<구현 영상>

https://youtu.be/aWDiGCyTz2E

+ Recent posts