Unity 아바타 동기화: 위치·회전 보간과 네트워크 지터 처리
Unity에서 원격 아바타의 위치를 네트워크로 받아 Transform에 바로 적용하면 움직임이 쉽게 끊깁니다. 화면은 매 프레임 렌더링되지만 위치 패킷은 더 낮은 빈도로 도착하고, 각 패킷의 간격도 일정하지 않기 때문입니다.
아바타를 자연스럽게 보여 주려면 네트워크 상태와 화면에 그리는 상태를 분리해야 합니다. 서버에서 받은 값은 정답에 가까운 Snapshot으로 보관하고, 렌더링 위치는 Snapshot 사이를 보간해 계산합니다.
렌더링 프레임과 네트워크 Tick은 다릅니다
Unity 화면이 갱신되는 주기와 네트워크가 상태를 전송하는 주기는 서로 독립적입니다. 네트워크 갱신이 올 때만 아바타를 이동하면 위치가 계단처럼 바뀝니다. 반대로 매 프레임 위치를 전송하면 대역폭과 서버 처리량이 빠르게 증가합니다.
동기화 데이터는 움직임을 재구성할 수 있는 최소 정보로 구성합니다.
entityId
serverTick
sequence
position
rotation
velocity
movementState모든 프레임의 좌표를 보내는 대신 일정 기준을 넘은 변화만 전송할 수 있습니다. 위치, 회전과 애니메이션 상태마다 필요한 정밀도와 갱신 빈도도 다릅니다. 변하지 않는 Scale까지 계속 보내면 의미 없는 데이터가 늘어납니다.
원본 Snapshot과 렌더링 좌표를 분리합니다
네트워크 콜백에서 transform.position을 즉시 바꾸지 않고 시간순 Snapshot 버퍼에 저장합니다. 렌더링 시점에는 가장 최근 서버 시간보다 약간 이전의 시점을 선택하고, 그 시점을 감싸는 두 Snapshot을 찾습니다.
Snapshot A Snapshot B
position A position B
└──── 렌더링 시점 ────┘위치는 Lerp, 회전은 Slerp처럼 값의 성격에 맞는 방식으로 보간합니다. 이 방법은 최신 위치를 즉시 보여 주지는 못합니다. 버퍼만큼 지연되는 대신 패킷 간격의 흔들림을 흡수해 원격 아바타를 부드럽게 보여 줍니다.
보간 지연은 고정된 마법의 숫자보다 실제 Snapshot 도착 간격에 맞춰 정하는 편이 좋습니다. 너무 짧으면 버퍼가 자주 비고, 너무 길면 다른 사용자의 동작이 늦게 보입니다.
늦게 도착한 패킷은 현재 상태를 되돌리면 안 됩니다
네트워크 패킷은 전송한 순서와 다른 순서로 도착하거나 일부가 누락될 수 있습니다. 수신 순서만 믿고 적용하면 아바타가 과거 위치로 되돌아갑니다.
Snapshot에는 서버 Tick이나 단조 증가하는 Sequence를 포함합니다. 버퍼는 이 값을 기준으로 정렬하고 이미 렌더링한 시점보다 오래된 데이터는 폐기합니다. 같은 Sequence가 다시 도착하면 중복으로 추가하지 않습니다.
시간값도 기준을 통일해야 합니다. 각 클라이언트의 로컬 시각은 서로 다를 수 있으므로 보간 기준에는 서버 Tick이나 동기화된 네트워크 시간을 사용합니다. 로컬 수신 시각은 지연 측정과 진단 정보로 별도 보관할 수 있습니다.
버퍼가 비었을 때 무한히 예측하지 않습니다
다음 Snapshot이 늦어지면 보간할 두 번째 값이 없습니다. 짧은 구간에서는 마지막 속도와 방향으로 위치를 예측해 멈춤을 줄일 수 있습니다. 하지만 예측을 오래 유지하면 실제 위치와 멀어집니다.
외삽에는 최대 시간을 두고 그 이후에는 마지막 확인 위치에서 기다립니다. 새 Snapshot이 도착했을 때 오차가 작으면 짧게 보정하고, 오차가 크면 순간 이동 기준을 적용합니다.
작은 오차 부드럽게 목표 위치로 수렴
큰 오차 버퍼를 비우고 즉시 위치 교체순간 이동, 리스폰과 방 이동은 일반 이동과 구분해야 합니다. 이 이벤트를 평범한 위치 변화로 보간하면 아바타가 벽과 공간을 가로질러 움직이는 장면이 생깁니다. Snapshot에 불연속 이동 여부나 상태 세대를 포함하면 기존 버퍼를 안전하게 초기화할 수 있습니다.
이동과 애니메이션은 같은 상태에서 파생합니다
위치는 도착했는데 걷기 애니메이션이 늦거나, 멈춘 아바타가 계속 달리는 문제가 생길 수 있습니다. 좌표와 애니메이션 Boolean을 무관한 이벤트로만 보내면 도착 순서가 달라질 때 화면이 어긋납니다.
렌더링 속도는 보간된 위치 변화량에서 계산할 수 있습니다. 걷기, 달리기, 앉기처럼 의미가 분명한 상태는 Snapshot의 이동 상태로 전달하고, Animator 파라미터는 렌더링 상태에서 갱신합니다.
Root Motion을 사용하는 아바타도 최종 위치의 권한이 어디에 있는지 정해야 합니다. 원격 아바타에서 애니메이션과 네트워크 위치가 동시에 Transform을 수정하면 서로 밀어내며 떨릴 수 있습니다. 네트워크 좌표는 아바타 루트에 적용하고 시각적 모델에서 애니메이션을 재생하는 방식처럼 책임을 나누는 편이 안정적입니다.
누가 위치를 결정하는지 명확해야 합니다
로컬 사용자의 입력을 즉시 반영하면 조작감은 좋아집니다. 하지만 모든 클라이언트가 자신의 좌표를 최종값으로 결정하도록 두면 비정상적인 이동과 충돌 상태를 검증하기 어렵습니다.
서버 권한형 구조에서는 클라이언트가 입력이나 이동 의도를 보내고 서버가 확정 위치를 배포합니다. 소유자 권한형 구조에서는 소유 클라이언트가 위치를 전송하더라도 서버가 속도, 공간 경계와 상태 전이를 검사할 수 있습니다.
아바타마다 현재 소유 세대와 권한 주체를 함께 관리해야 합니다. 재접속이나 소유권 이전 전에 보낸 패킷이 늦게 도착했을 때 새 사용자의 상태를 덮어쓰지 않도록 하기 위해서입니다.
모든 아바타를 같은 빈도로 보낼 필요는 없습니다
같은 공간의 사용자가 늘어나면 모든 위치를 모든 사용자에게 전송하는 구조는 빠르게 커집니다. 화면에 보이지 않거나 멀리 있는 아바타는 낮은 빈도로 동기화하고, 일정 범위 밖의 사용자는 갱신 대상에서 제외할 수 있습니다.
Interest Management 기준에는 거리뿐 아니라 같은 방, 층, 시야 영역과 상호작용 여부를 사용할 수 있습니다. 대상을 다시 동기화 범위에 넣을 때는 누적 Delta만 보내지 않고 현재 전체 상태를 먼저 보내야 합니다.
LOD와 네트워크 빈도도 연결할 수 있습니다. 멀리 있는 아바타는 단순한 모델과 낮은 애니메이션 갱신 빈도를 사용하되, 참가자 목록과 소셜 상태까지 사라지게 해서는 안 됩니다. 렌더링 최적화와 사용자 존재 정보는 별도 계층입니다.
Spawn과 Despawn도 순서를 가집니다
위치 Snapshot이 아바타 생성 이벤트보다 먼저 도착하거나, 퇴장한 아바타의 패킷이 늦게 도착할 수 있습니다. 존재하지 않는 Entity의 Snapshot은 잠시 대기시키거나 버리고, Spawn에서 받은 초기 전체 상태를 기준으로 시작합니다.
Despawn에는 해당 Entity의 세대를 종료하는 의미가 필요합니다. 같은 식별자가 재접속으로 다시 생성됐다면 이전 세대의 Snapshot을 적용하지 않습니다. 화면 오브젝트를 제거할 때 Snapshot 버퍼와 이벤트 구독도 함께 정리해야 Ghost Avatar가 남지 않습니다.
부드러운 움직임은 지연을 관리한 결과입니다
아바타 동기화는 좌표를 자주 보내는 문제만은 아닙니다. 서버 상태를 시간순으로 보관하고, 렌더링을 조금 늦춰 패킷 간격을 흡수하며, 오래된 데이터와 불연속 이동을 구분해야 합니다.
보간은 지연을 없애지 않습니다. 예측 가능한 짧은 지연으로 네트워크 지터를 감추는 방법입니다. 권한, Snapshot 순서와 관심 영역까지 함께 설계해야 가상공간의 원격 아바타가 안정적으로 움직입니다.
함께 읽기
- 실시간 소셜 룸 상태 관리: 입장·이탈·재접속 처리실시간 소셜 룸은 접속자 목록에 사용자를 추가하고 제거하는 기능만으로 유지되지 않습니다. 모바일 네트워크가 잠시 끊기거나 앱이 백그라운드로 가면 연결 종료 이벤트가 늦게 도착할 수 있고, 재접속한 사용자가 기존 아바타와 별도 참가자로 생성될 수도 있습니다.
- Unity 가상공간 로딩 최적화: Addressables와 Additive Scene 설계Unity 가상공간은 첫 화면에 필요한 UI와 사용자가 입장할 공간의 에셋이 다릅니다. 모든 Scene과 아바타, 텍스처를 시작 시점에 함께 로드하면 진입 시간이 길어지고 사용하지 않는 공간까지 메모리를 차지합니다.
- 차량 상태 동기화: Push, WebSocket과 Polling을 함께 쓰는 이유커넥티드 카 앱의 차량 상태는 한 번 조회하고 끝나는 데이터가 아닙니다. 도어, 공조, 충전과 차량 온라인 상태는 앱 밖에서도 계속 바뀝니다. 사용자가 원격 명령을 실행하는 동안에도 이전 상태 이벤트와 새로운 조회 응답이 다른 순서로 도착할 수 있습니다.
- 커넥티드 카 원격 명령: 요청부터 차량 응답까지의 상태 관리커넥티드 카 앱에서 도어 잠금이나 공조 시작 버튼을 누르면 요청은 여러 시스템을 통과합니다. 앱이 서버 요청에 성공했다고 차량 기능까지 실행된 것은 아닙니다. 명령이 차량에 도착하고 제어 장치가 결과를 반환해야 비로소 실행 여부를 판단할 수 있습니다.
- BLE 차량 제어: GATT 연결과 저전력 통신을 안정화하는 방법BLE는 차량과 휴대폰이 가까이 있을 때 작은 데이터를 주고받는 데 적합합니다. 디지털 키, 근거리 도어 제어와 차량 식별처럼 연결 시간이 짧고 배터리 소비를 줄여야 하는 기능에서 자주 사용됩니다.