Unity로 실시간 서버 동기화 구현하기: 롤백 네트코드의 기본 개념과 코드 예제
멀티플레이어 게임에서 네트워크 지연과 동기화 문제는 유저 경험을 좌우하는 중요한 요소입니다. 특히 실시간 반응이 중요한 PvP 게임에서 지연이 발생하면 플레이어의 움직임이 부정확하게 표현될 수 있습니다. Unity에서는 이러한 문제를 해결하기 위해 **롤백 네트코드(Rollback Netcode)**를 사용하여 클라이언트 예측과 서버 검증을 결합한 동기화 방식을 구현할 수 있습니다. 여기서는 롤백 네트코드의 개념과 더불어 여러 실용적인 예제 코드를 통해 기본적인 동작 방식을 알아보겠습니다.
목차
1. 롤백 네트코드의 핵심 개념
롤백 네트코드는 클라이언트의 예측을 기반으로 입력을 처리하되, 서버 검증을 통해 최종 동기화 상태를 유지하는 방식입니다. 이 방식의 주요 요소는 다음과 같습니다.
- 예측(Prediction): 클라이언트에서 플레이어 입력을 즉시 반영하여 렌더링합니다.
- 롤백(Rollback): 서버로부터 검증된 데이터가 도착했을 때, 예측이 틀린 경우 캐릭터 위치나 상태를 되돌려 수정합니다.
- 동기화(Synchronization): 모든 클라이언트가 같은 게임 상태를 유지하도록 주기적으로 서버 데이터를 받아 업데이트합니다.
이제 기본적인 코드 예제를 통해 이 개념들을 구체적으로 살펴보겠습니다.
2. 캐릭터 위치 동기화: 기본 롤백 네트코드 구조
이 예제는 클라이언트가 캐릭터의 위치를 예측한 후, 서버로부터 위치 정보를 받아 필요한 경우 위치를 수정하는 간단한 구조입니다. 위치를 예측하고, 서버와 비교해 필요한 경우 롤백합니다.
using UnityEngine;
using System.Collections.Generic;
public class RollbackNetcode : MonoBehaviour
{
public Transform player; // 플레이어 캐릭터 위치
private Queue<Vector3> positionHistory = new Queue<Vector3>(); // 위치 기록 큐
private Vector3 serverPosition; // 서버에서 받은 위치
private float lagCompensation = 0.1f; // 지연 보정 값
void Update()
{
Vector3 inputDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// 클라이언트 예측
Vector3 predictedPosition = player.position + inputDirection * Time.deltaTime * 5f;
player.position = predictedPosition;
// 예측 위치 기록
positionHistory.Enqueue(predictedPosition);
if (positionHistory.Count > 10) positionHistory.Dequeue();
// 서버 위치 업데이트
if (IsServerUpdateAvailable())
{
serverPosition = GetServerPosition();
if (Vector3.Distance(serverPosition, player.position) > lagCompensation)
{
RollbackToServerPosition();
}
}
}
private void RollbackToServerPosition()
{
player.position = serverPosition;
Debug.Log("롤백 실행: 서버 위치로 이동");
}
private Vector3 GetServerPosition()
{
return player.position + new Vector3(0.1f, 0, 0); // 서버 위치 예제
}
private bool IsServerUpdateAvailable()
{
return Time.frameCount % 5 == 0; // 5프레임마다 서버 업데이트
}
}
코드 설명
- positionHistory: 캐릭터의 예측된 위치를 큐에 저장하여 이후 롤백 시 이전 위치를 참조할 수 있게 합니다.
- predictedPosition: 클라이언트 측에서 예측된 캐릭터의 위치입니다. 사용자가 입력한 방향에 따라 위치를 이동시키며, 실제로 게임에 보여지는 위치입니다.
- RollbackToServerPosition(): 서버로부터 검증된 위치(serverPosition)와 클라이언트 위치가 크게 차이 날 경우, 서버 위치로 되돌리는 함수입니다.
Debug.Log
를 통해 롤백이 실행되는 시점을 확인할 수 있습니다.
3. 캐릭터 속도 조정 및 예측 예제
이 예제는 캐릭터가 걷기나 달리기와 같은 다양한 속도로 움직일 때, 클라이언트에서 예측하고 서버와 동기화하는 예제입니다.
using UnityEngine;
public class SpeedAdjustedRollback : MonoBehaviour
{
public Transform character;
private float speed = 5f;
private Vector3 serverPosition;
private float lagCompensation = 0.1f;
void Update()
{
if (Input.GetKey(KeyCode.LeftShift)) speed = 8f; // 달리기
else speed = 5f; // 걷기
Vector3 inputDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 predictedPosition = character.position + inputDirection * Time.deltaTime * speed;
character.position = predictedPosition;
if (IsServerUpdateAvailable())
{
serverPosition = GetServerPosition();
if (Vector3.Distance(serverPosition, character.position) > lagCompensation)
{
character.position = serverPosition;
Debug.Log("롤백: 서버 위치에 맞춤");
}
}
}
private Vector3 GetServerPosition()
{
return character.position + new Vector3(0.05f, 0, 0); // 서버 위치 예제
}
private bool IsServerUpdateAvailable()
{
return Time.frameCount % 10 == 0; // 주기적으로 서버 위치 업데이트
}
}
코드 설명
- speed 조절: 달리기와 걷기를 구분해 서로 다른 속도로 캐릭터를 움직입니다.
Input.GetKey(KeyCode.LeftShift)
를 통해 속도를 변경하고, 이를 바탕으로 예측된 위치(predictedPosition)를 업데이트합니다. - lagCompensation: 서버 위치와 클라이언트 위치의 차이가 일정 범위를 넘을 경우 서버 위치로 롤백하는 기준을 설정합니다. 이 값이 높을수록 롤백이 덜 빈번히 발생합니다.
4. 투사체 발사 및 롤백
이 예제는 슈팅 게임에서 투사체의 위치와 타격 판정을 서버와 동기화하는 방식입니다. 투사체의 이동 경로를 클라이언트가 예측하여 시각적으로 보여주고, 서버와의 차이가 발생할 경우 롤백하여 일관성을 유지합니다.
using UnityEngine;
public class ProjectileRollback : MonoBehaviour
{
public Transform projectile;
public Transform target;
private Vector3 predictedPosition;
private Vector3 serverPosition;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
predictedPosition = projectile.position + transform.forward * 10f * Time.deltaTime;
projectile.position = predictedPosition;
if (IsServerHitConfirmed())
{
Debug.Log("서버에서 타격 확인");
}
else
{
RollbackProjectile();
}
}
}
private bool IsServerHitConfirmed()
{
return Vector3.Distance(projectile.position, target.position) < 0.5f; // 타격 판정
}
private void RollbackProjectile()
{
projectile.position = serverPosition;
Debug.Log("롤백: 투사체 위치를 서버 상태로 맞춤");
}
}
코드 설명
- predictedPosition: 클라이언트에서 투사체가 움직이는 경로를 예측합니다. 예측된 위치를 미리 클라이언트에 보여주기 위해 사용됩니다.
- IsServerHitConfirmed(): 서버에서 타격 여부를 확인하는 함수로, 실제 타겟과의 거리로 타격을 판정합니다.
- RollbackProjectile(): 타격이 확인되지 않으면 투사체 위치를 서버 위치로 되돌립니다. 이는 서버 검증을 통해 동기화가 어긋나는 경우를 수정하기 위함입니다.
5. 실시간 동기화 시 주의할 점
- 프레임 유지: 서버와 클라이언트의 프레임 속도를 동일하게 유지하여 동기화 오류를 줄이는 것이 중요합니다.
- 데이터 압축: 최소한의 데이터(속도, 위치, 방향)만 전송하여 네트워크 대역폭을 절약하세요.
- 오류 보정: 지속적으로 클라이언트와 서버 상태를 비교하고, 동기화 오류가 발생할 때만 롤백하도록 하세요.
- 지연 보정:
lagCompensation
과 같은 보정 값을 설정하여 지연 문제를 해결하세요.
결론
롤백 네트코드를 활용하면 Unity에서 실시간 멀티플레이어 게임의 서버 동기화 문제를 효과적으로 해결할 수 있습니다. 캐릭터의 움직임, 투사체의 발사 등 다양한 상황에 적용 가능한 예제를 통해 실전에서 구현할 수 있는 기본적인 방법을 설명했습니다. 이러한 방식을 적절히 응용하여 빠르고 일관성 있는 멀티플레이어 환경을 구축해보시기 바랍니다.