ved_Rony
article thumbnail

UML

프로그램 구조를 나타내기 편하게 해준다. 주로 클래스 다이어 그램으로 프로그램 구조를 생각하려 했는 데, 전제 프로세스를 생각하려면 uml도 고려하는 게 좋다는 것을 다시금 깨닫게 된다.

 

시퀀스 다이어그램 & 액티비티 다이어그램

시퀀스 다이어그램 : 객체 간 메시징 흐름을 일목요연하게 표현

액티비티 다이어그램 : 자세하게 프로그램의 실행 방식을 그림으로 묘사

 

게임 플레이 네트워킹

이 책에서는 3가지로 크게 나타내고 있는 데, 서버에서 모든것을 처리 하기, 클라이언트에서 렌더링하기, 추측항법(prediction) 사용하기 이렇게 있다. 기대했던 부분인 만큼 설명 부족하여 조금 실망인 파트였다..

 

모든 역할을 서버에서 하기

가장 고전적인 방식이다. 클라이언트에선 입/출력만 담당하면 되고, 서버에서 로직/렌더링/송출 등을 담당한다. 

지금은 게임 그래픽 품질이 매우 높기 때문에 렌더링과 게임 로직을 모두 서버에서 담당하기엔 무리가 있다.클라이언트와 서버 상호 작용을 최적화해 주어야 한다.

 

클라이언트에서 렌더링하기

  • 서버는 렌더링을 위한 최소 정보인 게임 월드 상태만 클라이언트에 보낸다.
    • 월드 상태의 연산은 서버에서 한다.
    • 월드 상태는 월드에 있는 모든 캐릭터의 위치와 기타 정보(채팅, 몬스터 등)를 말한다.
    • 메시지에는 캐릭터 이동, 채팅 입력, 아이템 사용 등 씬의 상태에 영구적 변화를 가하는 지속성 이벤트가 있고 반대로 특정 좌표에서 수류탄이 터지는 등 씬의 변화에 영향을 잠깐 주고 사라지는 단발성 이벤트가 있다. 파티클과 같이 오브젝트의 결과가 정해져 있는 경우, 단발성 이벤트로 구현해야 서버에서 클라이언트로 보내는 메시지 양이 작아지므로 더 효과적이다.
  • 클라이언트는 렌더링을 수행하며 이를 위한 그래픽 리소스는 클라이언트에서 보유한다.
  • 서버와 클라이언트의 월드 상태를 동일하게 유지한다. 즉, 동기화한다.

이 방식을 원활하게 작동하려면, 레이턴시가 1/60초보다 훨씬 낮아야 하며 항상 균일해야 한다. 왜냐하면 서버와 클라이언트는 1/60초마다 상태를 업데이트하고 이를 지체 없이 보여줘야 하기 때문이다. 그러나 서버에 접속해 있는 클라이언트 수가 많아질수록 서버에서 해야 되는 일도 늘어나게 된다. 그래서 1/60초마다 보내는 것이 불가능한 경우가 생길 수 있다.

 

그래서 1/30초나 1/10초 단위로 월드 변화를 알리면 된다. 하지만 이 방법은 캐릭터 움직임이 딱딱해지는 문제를 발생시킨다. 이 문제를 해결하려면 값을 강제로 부드럽게 만들어야 한다. 서버에서 받는 상태 변화 메시지를 즉시 반영하는 것이 아닌 일정 시간에 걸쳐 서서히 목적 상태로 변화하게 하는 것 이다. 이를 상태 값 보정이라 한다.

 

예를 들어, 현재 시간과 위치를 보내어 lerp 해주도록 한다. 따라서 클라이언트 입장에서 현재위치에서 목적직까지의 위치에 받은 시간에 특정 프레임 값만큼 더해주고 서버에서 받은 시간을 나눠주어 보간해줄수도 있다.

 

추측항법

상태 값 보정을 사용하면 딱딱해지는 문제는 해결할 수 있지만,  레이싱 게임이나 FPS 게임을 개발하고 있다면 이는 큰 문제가 될 수 있다.

상대방 움직임을 어느 정도 예상해서 그 위치로 갈 수 있게 보정하는 추측항법을 사용하면 된다. 추측항법이 제대로 작동하려면 두 기기 간의 레이턴시 캐릭터의 위치, 속도를 알고 있어야 한다.

 

레이턴시를 측정하는 방법에는 대표적으로 라운드 트립 레이턴시가 있다.

  • 기기 A에서 기기 B에 패킷을 보낸다.
  • 기기 B는 이를 받으면 기기 A에 패킷을 보낸다.
  • 기기 A는 패킷을 보낸 시간과 현재 시간의 차이를 구하여 2로 나눈다.

  • 캐릭터 원본이 있는 쪽(이쪽)을 P0이라 하며 P0(t)는 시간 t에서 원본 위치이다.
  • 캐릭터의 이동 정보를 받는 쪽(사본이 있는 쪽, 저쪽)을 P라 하며 P(t)는 시간 t에서 사본 위치이다.
  • a는 레이턴시이다.
  • V0은 속도 벡터이다.
  • 이쪽에서 P0(t=0), P0(t=1)을 보내고 저쪽에서 P(t=0+a), P(t=1+a), P(t=2+a)를 받는다.
  • 저쪽 기기에서 이동 정보를 받았을 때는 이미 a만큼 시간이 지난 상태이기 때문에 정확한 위치는 a만큼의 미래가 되어야 한다. (P(t+a) = P0(t) + a*V0(t))
  • 계산된 위치대로 이동을 하면, 화면에 캐릭터가 살짝 점프하는 것처럼 보이는 문제가 발생한다. 이를 해결하기 위해 앞서 언급한 상태 값 보정 방법을 사용하면 된다.

레이턴시를 완전히 없앨 수는 없지만 추측항법을 포함해 이러한 레이턴시를 안보이게 하는 방법을 통틀어레이턴시 마스킹이라 한다.

 

레이턴시 마스킹

인터넷 환경이 좋지 않을 때 여전히 답답하게 느낄수 있다.그래서 클라이언트에서 사소한 연산을 처리함으로서 이를 해결할수가 있다. 다른 방법도 있지만, 개인적으로 클라이언트가 바로 연출하고, 메세지를 보내는 방식을 선호한다.플레이어가 어떤 행동을 하면, 행동 명령에 대한 메시지를 서버에 보내는 동시에 행동 연출 일부를 즉시 보여주고 서버에서는 행동 명령을 받아 처리 후 클라이언트로 메시지를 보 클라이언트는 이 메시지를 받으면 나머지 연출을 보여준다.

 

실시간 전략 시뮬레이션 게임에서 네트워크 동기화

전략 시뮬레이션 게임에서는 등장하는 캐릭터 수가 수백 개에 육박한다. 플레이어 수가 적음에도 캐릭터 수는 많기 때문에 지금까지 알아본 방식보단 락스텝 동기화 알고리즘을 사용하는 것이 좋다.

락스텝은 컴퓨터 프로그램의 같은 상태에 같은 압력을 주면 같은 결과가 나온다는 원리를 응용한 것이다.

  • 각 플레이어는 다른 플레이어들에게 입력 명령을 보낸다.
  • 플레이어의 입력 명령에 따라 모든 클라이언트가 동시에 씬 업데이트를 한다.
  • 즉, 각 클라이언트 플레이어의 입력 명령만 주고받으며 씬을 구성하는 캐릭터의 이동 상태를 주고받지는 않는다.

실제 레이턴시 줄이기

지금까지는 주어진 레이턴시에서 어떻게 최대한 원활하게 멀티플레이를 할지 살펴보았지만, 가능하면 실제 레이턴시를 줄이는 것도 중요하다.

  • TCP 대신 UDP를 사용하는 것이 좋다. 왜냐하면 TCP는 패킷 재전송을 위한 지연 시간이 발생하기 때문이다.
  • 똑같은 양의 데이터를 보낸다고 하더라도 가급적 적은 수의 패킷으로 보내는 것이 좋다. 예를 들어 1초에 메시지를 100개 이상 보낼 때는 뭉쳐서 보내면 더욱 효과적이다. 이 뭉치는 시간은 10밀리 초 이하가 적당하다.
  • 클라이언트와 서버 간 통신(C/S)과 클라이언트끼리 통신(P2P)하는 것을 섞어 쓰는 것도 방법이다.
profile

ved_Rony

@Rony_chan

검색 태그