본문 바로가기

개발/언리얼

언리얼 c++로 움직이는 액터 구현하기

핵심 함수

GetActorLocation() : 현재 엑터의 위치 FVector를 가져오는 함수

SetActorLocation() : 현재 액터의 위치를 "변경"하는 함수핵심 함수

 

목표

c++클래스를 생성

시작지점과 이동거리, 이동속도를 변수로 지정

두 지점을 왕복하는 로직 작성

 

 

Actor를 상속받는 C++클래스 생성

 

DefaultMovingActor.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "DefaultMovingActor.generated.h"

UCLASS()
class HOMEWORK6_API ADefaultMovingActor : public AActor
{
	GENERATED_BODY()
	
public:	
	ADefaultMovingActor();
	UPROPERTY(EditAnywhere, Category = "Components")
	USceneComponent* SceneRoot;
	UPROPERTY(EditAnywhere, Category = "Components")
	UStaticMeshComponent* StaticMeshComp;

	UPROPERTY(VisibleInstanceOnly, Category="Movement")
	FVector StartLocation;
	UPROPERTY(EditInstanceOnly, Category = "Movement")
	float MoveSpeed;
	UPROPERTY(EditInstanceOnly, Category = "Movement")
	FVector MaxRange;
private:
	int direction = 1;
	FVector TargetLocation;

protected:
	virtual void BeginPlay() override;
	virtual void OnConstruction(const FTransform& Transform) override;
	virtual void Tick(float DeltaTime) override;
};

SceneComponent를 루트로 두고

StaticMeshComponent를 추가

 

시작점, 이동위치, 이동속도 변수 3개를 선언하고, 각각 매크로를 이용해 리플렉션에 등록.

 

DefaultMovingActor.cpp

#include "DefaultMovingActor.h"

// Sets default values
ADefaultMovingActor::ADefaultMovingActor()
{
 	PrimaryActorTick.bCanEverTick = true;

	MoveSpeed = 300.f;
	MaxRange = FVector(100.f, 100.f, 0);

	SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
	SetRootComponent(SceneRoot);

	StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
	StaticMeshComp->SetupAttachment(RootComponent);
}

// Called when the game starts or when spawned
void ADefaultMovingActor::BeginPlay()
{
	Super::BeginPlay();
	TargetLocation = StartLocation + MaxRange;
	UE_LOG(LogTemp, Error, TEXT("TargetLocation :  %f, %f, %f"), TargetLocation.X, TargetLocation.Y, TargetLocation.Z);
}

void ADefaultMovingActor::OnConstruction(const FTransform& Transform)
{
	//에디터 상의 값이 갱신될 때 마다 호출되는 함수임.
	//디테일 패널 값을 건드려도 갱신이 된다.
	Super::OnConstruction(Transform);
	StartLocation = GetActorLocation(); 
}

// Called every frame
void ADefaultMovingActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	if (FVector::Dist(GetActorLocation(), StartLocation) <= 20) {
		direction = 1;
		UE_LOG(LogTemp, Error, TEXT("straight"));
	}

	if (FVector::Dist(GetActorLocation(), TargetLocation) <= 20) {
		direction = -1;
		UE_LOG(LogTemp, Error, TEXT("reverse"), TargetLocation.X, TargetLocation.Y, TargetLocation.Z);
	}

	FVector moveDirection = (TargetLocation - StartLocation).GetSafeNormal();
	float stepMove = MoveSpeed * DeltaTime;

	FVector stepLocation = GetActorLocation() + moveDirection * stepMove * direction;
	SetActorLocation(stepLocation, true);
}

생성자에서는 속도, 이동위치의 기본값을 설정하고

루트컴포넌트와 스태틱 메시 컴포넌트를 초기화.

 

BeginPlay()

시작지점 StartLocation과 이동거리 MaxRange를 더해 목표지점을 초기화

 

OnConstruction()

BeginPlay와 비슷한데, 생성될때와 좌표가 이동되는 등 값이 갱신될 때 마다 호출됨.

해당 이벤트에서 값을 갱신하여 시작지점이 항상 액터가 위치한 지점이 되도록 해두었음.

 

Tick()

실행되는 매 프레임마다 호출되는 이벤트,

실제 움직임을 구현할 위치.

 

액터의 현재 위치를 통해 이동할 방향을 확인한 후에

두 지점 벡터를 통해 방향 정규 벡터(크기가 1이고 방향을 가진 벡터)를 구하고,

이동속도 X DeltaTime X 방향값을 모두 곱하여 이번 프레임에 이동할 값을 구해 SetActorLocation로 위치를 이동시킨다.

 

 

현재 코드에서 예상되는문제 : 

만약 랙이 걸리는 등의 이유로 크게 움직여, 시작지점과 목표지점 두 지점을 넘어가버리게 되면 영영 돌아오도록 하지 못한다.

이를 해결하기 위해 두 지점에 도달하거나 넘어가는 조건을 더 확실하게 개선해야 할 것이다.

 

개선방안.

목표지점을 명확하게 관리하고, 목표지점을 향해 정확히 도달 한 후 다음 지점을 찾도록 변경.

추가 개선목표 : 두 지점이 아닌, 3개 이상의 여러 지점을 거쳐 이동하도록 개선 및 왕복을 할것인지 말것인지 여부를 선택 가능하도록 변경