나만의 작은 도서관
[TIL][C++] 251020 MMO 서버 개발 118일차: [언리얼] 컨트롤러의 시야 방향에서 Yaw 회전값을 방향 벡터로 변환하는 방법, 캐릭터가 직선 궤도에서 벗어났을때 자연스럽게 궤도로 돌려놓는 방법 고민 본문
Today I Learn
[TIL][C++] 251020 MMO 서버 개발 118일차: [언리얼] 컨트롤러의 시야 방향에서 Yaw 회전값을 방향 벡터로 변환하는 방법, 캐릭터가 직선 궤도에서 벗어났을때 자연스럽게 궤도로 돌려놓는 방법 고민
pledge24 2025. 10. 20. 22:44주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다.
[언리얼] 컨트롤러의 시야 방향에서 Yaw 회전값을 방향벡터로 변환하는 방법
- 컨트롤러 시야 방향은 컨트롤하고 있는 유저의 뷰포트 방향을 의미한다. (컨트롤하지 않는 액터의 경우 액터가 바라보는 방향) 상황에 따라 컨트롤러 시야 방향을 벡터로 얻어야 하는 경우가 있는데, 이때 아래와 같이 FRotator::Vector()를 활용하면 손쉽게 구할 수 있다.
- FRotator::Vector()는 저장된 회전값을 단위 벡터로 변환해준다.
const FRotator ControlRotation = GetControlRotation();
// 컨트롤러의 시야 방향에서 Yaw 회전값만 추출
const FRotator YawRotation(0.f, ControlRotation.Yaw, 0.f);
const FVector DirectionVector = YawRotation.Vector();
[언리얼] 캐릭터가 직선 궤도에서 벗어났을 때 자연스럽게 궤도로 돌려놓는 방법 고민
- 서버로부터 다른 캐릭터의 위치와 이동 방향을 넘겨받게 되면 클라이언트는 해당 데이터를 적용하여 캐릭터의 움직임을 바꿔야 한다.
- 문제는 Dead Reckoning 방식을 사용하는 상황 + 위치 불일치가 발생한 경우, 위치 보정을 하기 위해 서버가 준 위치를 목적지로 잡고 이동하는 것은 조금 이상할 수 있다는 것이다. 왜냐하면 서버가 준 위치는 과거의 위치이고, 이 과거의 위치로 복간하며 이동한다면 보간 이동하는 동안에 오차는 더욱 커지기 때문. 그래서 ‘서버가 준 위치를 목적지로 잡지 않고 자연스럽게 보정을 하려면 어떻게 해야 할까?’라는 고민을 하게 되었다.
Solve?) 이동 방향은 바로 갱신, 오차는 이동 방향과 수직 방향으로 조금씩 개선
- Dead Reckoning 방식을 사용하면 자유로워질 수 있는 문제가 하나 있는데, 바로 ‘캐릭터 회전에 대한 불일치’ 문제이다. 캐릭터의 방향을 이용하여 이동하기 때문에 방향이 바뀔 때마다 송신 측 클라이언트가 제때 보내준다면 수신 측 클라이언트에서 캐릭터 회전 불일치 문제는 발생하지 않기 때문.
- 그런데 이건 어디까지나 이동 방향을 곧바로 적용했을 때 이야기인지라, 이 부분을 바꿀 수는 없었다. 따라서 이동 방향에 대해선 곧바로 갱신하기로 했다. 다만, 고개를 휙 하고 돌리는 건 조금 딱딱하다 생각해 CMC를 통해 이동하게 하면서 부드럽게 고개를 돌리도록 해두었다.
방향은 잡았고, 이동 오차는?
- 당연히 이동 데이터가 오자마자 회전값만 넣었다면 방향은 똑같지만, 실제 이동하는 궤도는 다를 것이다. 따라서 이러한 오차를 줄여주기 위한 장치가 필요한데, 이 장치로써 이동 방향과 수직 방향으로 조금씩 이동하는 방법을 채택했다. 코드는 아래와 같다.
// ClientPos에서 Server로 부터 받은 캐릭터 이동 궤도로 수직으로 찍은 수직 벡터를 반환
FVector AP1Player::GetPerpendicular() const
{
FVector Point = FVector(ServerPos->x(), ServerPos->y(), ServerPos->z());
FVector ClosestPoint = UKismetMathLibrary::FindClosestPointOnLine(Point, GetActorLocation(), MoveDirection);
return ClosestPoint - Point;
}
...
// AP1Player::Tick()
if (Dist > CorrectionThreshold) // 보정 거리 초과 시 Reposition
{
SetActorLocation(ServerLocation);
SetActorRotation(FRotator(0, ServerPos->yaw(), 0));
}
else
{
FVector Perpendicular = GetPerpendicular();
FVector PerpendicularDir = Perpendicular.GetSafeNormal();;
FVector CorrectionDist = FMath::Min(PerpendicularDir * CORRECTION_SPEED * DeltaSeconds, Perpendicular);
AddActorWorldOffset(CorrectionDist);
}
