본문 바로가기

언리얼 + cpp

언리얼C++ 2일차(트랜스폼)

트랜스폼

위치(Location), 회전(Rotation), 크기(Scale)을 합쳐서 트랜스폼(Transform)이라고 한다.

이 트랜스폼에는 x(빨강), y(초록), z(파랑) 값이 있는데 이를 조정하여 위치, 회전, 크기 값을 조절할 수 있다.

좌표는 월드 좌표와 상대 좌표가 있다.

월드 좌표는 레벨 전체에서의 절대적인 위치, 회전, 크기 이며 월드의 원점(레벨 좌표 0,0,0)이 기준점이 된다.

상대 좌표는 컴포넌트 사이에서 사용되며 부모 컴포넌트가 기준점이 된다.

 

void AItem::BeginPlay()
{
	Super::BeginPlay();

	SetActorLocation(FVector(300.0f, 200.0f, 100.0f));
	SetActorRotation(FRotator(0.0f, 90.0f, 0.0f));
	SetActorScale3D(FVector(2.0f));
}

SetActorLocation(FVector(x, y, z)) : 월드의 x, y, z좌표에 위치하도록 한다.

SetActorRotation(FRotator(pitch, yaw, roll)) : pitch(X축 회전), yaw(Z축 회전), roll(Y축 회전) 값으로 회전을 한다.

setActorScale3D(FVector(x, y, z)) : 크기를 x, y, z 배율로 키운다.(예시처럼 하나만 쓰면 x y z에 동일한 값으로 들어간다)

컴파일 후 실행을 해보면 설정한 값대로 트랜스폼 값이 변경이 된다.

위 사진처럼 FTransform(회전, 위치, 크기)를 사용하여 값을 한 번에 변경하는 방법도 있다.

 

이제 액터를 매 프레임마다 회전을 시켜줄것이다.

변수 RotationSpeed를 선언한 후 생성자에서 90.0f로 초기화했다. 

매 프레임마다 회전을 하기 위해 Tick 함수에서 구현을 한다.

void AItem::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	AddActorLocalRotation(FRotator(0.0f, RotationSpeed, 0.0f));
}

위처럼 구현하면 이론상 맞지만 컴퓨터마다 성능이 다르고 뽑을 수 있는 프레임이 다르므로 컴퓨터마다 1초에 회전하는 양이 다르게 되는데 이를 해결하기 위해 RotationSpeed에 DeltaTime을 곱해주면 된다.

언리얼에서 Tick(float DeltaTime)은 매 프레임마다 호출되는데, DeltaTime은 그 프레임이 걸린 시간(초)을 의미한다.
120 FPS → Tick 함수가 초당 120번 호출, DeltaTime ≈ 1/120초
60 FPS → Tick 함수가 초당 60번 호출, DeltaTime ≈ 1/60초
1초에 RotationSpeed만큼 회전하는 액터를 만들고 싶으면 RotationSpeed에 DeltaTime을 곱해서
120(Tick함수 1초에 120번 호출) * RotationSpeed * DeltaTime(1/120) = 1초에 RotationSpeed 만큼 회전
60(Tick함수 1초에 60번 호출) * RotationSpeed * DeltaTime(1/60) = 1초에 RotationSpeed 만큼 회전
따라서, 어떤 속도 값(RotationSpeed)에 DeltaTime을 곱해주면 프레임 수와 상관없이 “1초 동안 RotationSpeed만큼” 움직이게 된다.

그래서 RotationSpeed에 DeltaTime을 곱해주고 한가지 더 해줘야할것이 RotationSpeed가 0이 아닐때 회전이 되도록 해야한다.

if (RotationSpeed != 0.0f)
{
	AddActorLocalRotation(FRotator(0.0f, RotationSpeed * DeltaTime, 0.0f));
}

위처럼 해도 로직은 맞지만 부동소수점은 값이 0.000000000001이여도 0이 아닌것으로 취급되어 회전을 하게 되므로

언리얼에서 제공하는 FMath::IsNearlyZero()를 사용하여 0에 가까워지면 true를 반환하도록 할 수 있다.

if (!FMath::IsNearlyZero(RotationSpeed))
{
	AddActorLocalRotation(FRotator(0.0f, RotationSpeed * DeltaTime, 0.0f));
}

그리고 Tick 함수를 사용하면 생성자에서 PrimaryActorTick.bCanEverTick = true;를 선언해줘야 하고 만약 Tick을 사용하지 않는다면 false로 해줘야 성능상 문제가 없다.

1초에 90도 회전하는 의자다.