멀티플레이어 네트워크 동기화에서 동작 처리와 시각 처리의 분리는 필수입니다.
Fusion2는 이 목적을 위해 두 개의 핵심 메서드 FixedUpdateNetwork()와 Render()를 제공합니다.
항목 | FixedUpdateNetwork() | Render() |
기준 시간 단위 | ✅ Fusion Tick (고정 간격) | ❌ 프레임 기반 (가변 간격) |
역할 | ✅ 상태 계산 및 동기화 (위치, 입력, 체력 등) | ✅ 클라이언트 시각화 전용 (보간, 애니메이션 등) |
서버와 클라이언트 공유 여부 | ✅ 서버/클라이언트 공통 동작 | ❌ 클라이언트 전용 처리 |
동작 주기 | ✅ 고정 주기 실행 (Tick 단위) | ✅ 매 프레임 실행 (Unity Update()와 유사) |
보간 사용 여부 | ❌ 계산 대상 (원본 데이터) | ✅ 보간된 결과 반영 |
수정 가능 데이터 | ✅ NetworkObject의 상태 | ❌ 읽기 전용 (변형 금지) |
주 용도 | 입력 처리, 이동 계산, 동기화 로직 | 위치 보간, 애니메이션 연출, 이펙트 표시 등 |
보너스, 그렇다면 Update문을 쓰면 되지 왜 굳이 Render함수를 이용하는 걸까요?
항목 | Update() | Render() |
실행 시점 | Unity 프레임마다 | Fusion이 보간/예측한 Tick 기준 |
Fusion 네트워크 시간 인식 | ❌ 전혀 모름 | ✅ 알고 있음 (보간된 시간 포함) |
NetworkObject 동기화 상태 | ❌ 불완전하거나 최신이 아님 | ✅ 보간 또는 예측된 최신 상태 |
위치/상태 접근 안전성 | ❌ 오차 발생 가능 | ✅ 부드러운 위치 보장 |
목적 | 일반적인 Unity 동작 처리 | Fusion 전용 시각화/보간 처리 |
Fusion의 NetworkObject는 Tick 기반으로 움직이는데, Update()는 그걸 고려하지 않으므로,
Update() 안에서 위치나 상태를 참조하면 보간이 안 돼서 끊기거나, 아직 업데이트되지 않은 상태를 보게 되거나,딜레이 있는 위치를 기반으로 애니메이션을 재생하게 되기 때문입니다.
반면, Fusion이 제공하는 Render()는 내부적으로 현재 Tick에 맞는 보간된 위치, 회전 등 제공해주며, 예측(Prediction)된 결과도 반영해주어 NetworkObject가 이 시점에 어떻게 보일지를 정확히 계산한 결과를 줍니다.
❌ Update() 안에서 캐릭터 회전 애니메이션
void Update() { transform.LookAt(target.position); // ← 보간 안 됨 → 뚝뚝 끊김 }
✅ Render() 안에서 처리
public override void Render() {
transform.LookAt(interpolatedTarget.position); // ← 부드럽게 회전
}