Biped Movement
- 직립 2족 보행 플러그인은 ALS plugin으로 디테일을 표현할 수 있다
- Unreal에서 공식적으로 지원하는 Motion Matching 플러그인도 있다
- 그렇다면 4족 보행은?
Quadruped Movement
- 빠른 방향 전환 시, 몸을 비틀며 자연스럽게 도는 모션을 만들어 줘야 했다
- 하지만 Unreal에서의 Character는 Character Capsule 1개에 대해 SkeletalMesh 1개가 대응하는 구조
- 다른 게임에서 상반신과 하반신을 나눠서 작업하기에는 너무 많은 코스트
Solution?
- 캐릭터의 Forward 기준으로 Yaw가 일정 각도 이상으로 회전해야 하는 경우, Path point를 만드는 방법
- 이 경우, MoveTo 작동 방식에 착안해 Custom BT Task ndoe를 만들어 작업
- 최종 목적지까지 몇 개의 waypoint를 추가해야 자연스러울지가 이슈
- waypoint에 도착하자마자 다음 waypoint를 향해 방향을 확 틀어서 부자연스러워 보인다
- 충분히 짧은 거리에 충분히 많은 waypoint를 만들어줘야 하는데, 이걸 정의하기 애매했다
- 캐릭터의 Rotation Rate를 조절해 한 프레임에 회전할 수 있는 최대 회전각도를 낮춘다
- 이 경우에는 위의 작업보다 work load가 더 적다
- 생각보다 결과물도 자연스럽다
* 단, 목적지에 도착할 수 없는 경우가 있다
- 이동 속도와 지향 방향은 기존과 동일한데 그만큼 Rotation Rate가 빠르지 않기 때문에, 목적지 주변을 맴돌기만하는 버그가 발생할 수 있다
* 그래서 빠른 회전이 필요한 경우만 한정에 Rotation Rate를 조절해주거나, 전용 애니메이션을 재생해 캐릭터를 빠르게 회전시켜주는 게 더 낫다는 판단
// 특정 방향 MoveInput으로 이동 Request를 받는다
void UCustomCharacterMovementComponent::RequestPathMove(const FVector& MoveInput)
{
if(IsValid(GetOwner()) == false)
{
return;
}
// 4족인 경우에만 진행
if (bQuadrupedMovement == false)
{
Super::RequestPathMove(MoveInput);
return;
}
FVector AdjustedMoveInput(MoveInput);
const FVector Forward = GetOwner()->GetActorForwardVector();
const FVector Right = GetOwner()->GetActorRightVector();
const FVector2d Right2D { Right.X, Right.Y };
const FVector2D Forward2D { Forward.X, Forward.Y };
FVector2D MoveInput2D { MoveInput.X, MoveInput.Y };
MoveInput2D.Normalize();
const double DotDirectionMoveInput = FVector2d::DotProduct(bMoveBackward ? -Forward2D : Forward2D, MoveInput2D);
// 현재 바라보는 방향(혹은 후면 방향)과 이동 입력의 각도 차이가 11.5도(Acos(0.98)) 이내이면 같은 방향으로 간주
constexpr double MINIMUM_DOT = 0.98;
if(DotDirectionMoveInput > MINIMUM_DOT && bMoveBackward
|| DotDirectionMoveInput < MINIMUM_DOT && bMoveBackward == false)
{
verify(RotationRate.Yaw > UE_KINDA_SMALL_NUMBER);
check(GetWorld());
const float DeltaSeconds = GetWorld()->GetDeltaSeconds();
// 한 프레임 동안 회전할 각도(도 단위)를 계산
float AngleDeg = DeltaSeconds * RotationRate.Yaw;
if (bMoveBackward)
{
// 뒷걸음질할 때는 회전을 조금 더 빨리
constexpr float BACKWARD_ROTATION_MULTIPLIER = 1.5f;
AngleDeg *= BACKWARD_ROTATION_MULTIPLIER;
}
// 좌우 방향 구분
const double DotRightMoveInput = FVector2d::DotProduct(Right2D,MoveInput2D);
if (DotRightMoveInput < 0.0f)
{
AngleDeg *= -1.0;
}
// AngleDeg만큼 돌려준다
AdjustedMoveInput = bMoveBackward
? (-Forward).RotateAngleAxis(AngleDeg, FVector::UpVector)
: Forward.RotateAngleAxis(AngleDeg, FVector::UpVector);
}
Super::RequestPathMove(AdjustedMoveInput);
}
기타 참고