직전 글에서 C++로 Enhanced Input을 사용해 Move 함수를 호출하는 것 까지 성공하였다.
이제 실제로 탱크를 움직여 보도록 하자.
나중에 이동 속도를 변경할 수 있는 기능을 추가하기 위해서 이동 속도 변수를 만들어서 이동량에 곱해 주기로 한다.
private 구역에 Velocity 변수를 생성했고, 개발 과정에서 편하게 수정하기 위해 EditAnywhere 속성을 UPROPERTY에 추가해 주었다.
Tank.h
UPROPERTY(EditAnywhere, Category = "Movement")
float Velocity = 200;
탱크의 이동을 구현하는 것은 다양한 함수를 이용한 여러 가지 방법이 있지만 그 중에서 AddActorLocalOffset
함수를 사용하려고 한다.
공식 문서 링크
이 함수의 특징은 local reference에다가 벡터를 더할 수 있다는 점이다.
즉 벡터의 축이 월드가 아니라 액터이기 때문에 현재 액터가 바라보는 방향으로 쉽게 움직일 수 있다.
MoveVector 값에다 이동 속도와 DeltaTime을 곱한 값을 AddActorLocalOffset 함수에다 인자로 전달하면 될 것이다.
여기서 주의할 것은 전방 이동의 'W' 입력은 (0, 1)의 벡터 값을 가진다는 것. 즉 Y, X 좌표를 바꿔서 넣어줘야 한다.
void ATank::Move(const FInputActionValue& Value)
{
const FVector2D MoveVector = Value.Get<FVector2D>();
float DeltaTime = UGameplayStatics::GetWorldDeltaSeconds(this);
AddActorLocalOffset(FVector(MoveVector.Y, MoveVector.X, 0) * Velocity * DeltaTime, true);
}
UGameplayStatics 클래스에서는 게임플레이와 관련된 다양한 데이터를 제공할 수 있다. 이 클래스를 통해 Tick 함수 외부에서도 DeltaTime을 사용할 수 있다. ("Kismet/GameplayStatics.h"
include 필요)
잘 움직이긴 하는데 뭔가 이상하다.
사실 탱크의 움직임은 이렇지 않았다...
카트라이더 하는 것처럼 W, S를 누르면 앞, 뒤로만 움직이고, A, D를 누르면 좌우 방향전환이 되어야 탱크답게 움직일 것이다.
이제 수정을 해보도록 하자.
이제 X축 이동이 필요없으므로, IA_Move 이벤트 타입을 float로 수정해 준다.
그리고 IMC도 다음과 같이 수정한다.
먼저 A, D 키는 아예 빼버렸고, swizzle modifier도 필요 없게 되었다.
그리고 Move 함수도 아래와 같이 간단하게 수정해 주었다.
void ATank::Move(const FInputActionValue& Value)
{
float DeltaTime = UGameplayStatics::GetWorldDeltaSeconds(this);
const FVector DeltaVector = FVector(Value.Get<float>(), 0, 0) * Velocity * DeltaTime;
AddActorLocalOffset(DeltaVector, true);
}
이제 IA_Turn을 만들어 주었다.
방향 전환 같은 경우에는 Move와 마찬가지로 1차원 데이터를 써도 되겠으나, 탱크가 제자리에서 도는 것은 불가능하니 앞뒤로 조금이라도 이동하고 있어야만 돌 수 있게 구현해보려고 한다.
따라서 2차원 데이터를 사용하기로 한다.
W, S 키는 부호 상관 없이 눌렸는지 (값이 있는지) 여부만 있으면 되므로 S키에 Negate는 필요없을 것 같다.
그리고 헤더 파일을 수정해 주고 인풋액션을 가져오는 부분과 BindAction 부분은 Move와 똑같이 구성해준다.
Tank.h
이제 Turn 함수를 구현할 차례이다.
AddActorLocalOffset
과 비슷하게 AddActorLocalRotation
이라는 함수가 존재한다. (공식 문서 링크)
이 함수는 FVector가 아닌 FRotator
형 데이터를 파라미터로 갖는다는 것 외에는 AddActorLocalOffset
함수와 비슷하다.
void ATank::Turn(const FInputActionValue& Value)
{
const FVector2D VectorValue = Value.Get<FVector2D>();
if (VectorValue.Y == 0)
return;
float DeltaTime = UGameplayStatics::GetWorldDeltaSeconds(this);
const FRotator DeltaRotation = FRotator(0, VectorValue.X, 0) * TurnVelocity * DeltaTime;
AddActorLocalRotation(DeltaRotation, true);
}
위와 같이 함수를 구성했다. 앞뒤로 움직이지 않는 상태로 방향을 전환하는 것은 이상하므로 VectorValue.Y 즉 W 또는 S 움직임이 없으면 이동하지 않게 하기 위해 바로 리턴시키고 있다.
이후 FRotator의 세 가지 축 (Pitch, Yaw, Roll) 중에서 바닥 방향의 회전인 Yaw 값에 입력받은 VectorValue.X의 방향을 할당해서 속도와 델타타임과 함께 곱해준다.
컴파일하고 실행해서 결과를 살펴보자.
회전은 잘 하는 것을 볼 수 있는데, 후진할 때 방향이 반대가 되어야 한다.
W, S가 입력 여부만 볼 것이 아니라 방향까지 고려해 주어야 하므로, S 키에다가 Negate Modifier를 추가해 주고, DeltaRotation
을 계산할 때 Y 값을 같이 곱해주면 될 것 같다.
void ATank::Turn(const FInputActionValue& Value)
{
const FVector2D VectorValue = Value.Get<FVector2D>();
if (VectorValue.Y == 0)
return;
float DeltaTime = UGameplayStatics::GetWorldDeltaSeconds(this);
const FRotator DeltaRotation = FRotator(0, VectorValue.X * VectorValue.Y, 0) * TurnVelocity * DeltaTime;
AddActorLocalRotation(DeltaRotation, true);
}
이제 잘 움직이는 것을 확인할 수 있다!
나중에는 관성 같은 것도 추가해 볼까 ...