유니티에서는 쓰레드를 몇 개까지 할당 받을 수 있을까

당신은 주제를 찾고 있습니까 “유니티 네트워크 – RPC는 진짜 어마어마하게 중요합니다ㅣ포톤PUN2“? 다음 카테고리의 웹사이트 ppa.maxfit.vn 에서 귀하의 모든 질문에 답변해 드립니다: https://ppa.maxfit.vn/blog. 바로 아래에서 답을 찾을 수 있습니다. 작성자 고라니TV – 게임개발 채널 이(가) 작성한 기사에는 조회수 18,715회 및 좋아요 268개 개의 좋아요가 있습니다.

  • 유니티 네트워크 주제에 대한 동영상 보기
  • d여기에서 RPC는 진짜 어마어마하게 중요합니다ㅣ포톤PUN2 – 유니티 네트워크 주제에 대한 세부정보를 참조하세요
  • 유니티 네트워크 주제에 대한 자세한 내용은 여기를 참조하세요.
    • [Unity 5] 네트워크 기초(네트워크 매니저, 플레이어 설정 …
    • 유니티에서 네트워크 설정하기 – MirrorNetwork
    • [Unity] 네트워크 프로그래밍
    • 유니티 네트워크 프로그래밍 – YES24
    • [Unity3D] UNet Tutorial (8) – 네트워크 매니저(Network Manager)
    • [Unity] Physics.Simulate를 이용한 네트워크 동기화
  • 주제와 관련된 이미지 유니티 네트워크
  • 주제에 대한 기사 평가 유니티 네트워크
  • [Unity 5] 네트워크 기초(네트워크 매니저, 플레이어 설정), 키보드입력에 따른 처리(Input.GetKeyDown()), 스크립트에서 프리펩 생성시키기(총알 발사), 명령[Command], ClientRpc 호출[ClientRpc], 적 만들기 , 플레이어 랜덤 리스폰
  • [Unity] 네트워크 프로그래밍
  • 유니티 네트워크 프로그래밍
  • [Unity3D] UNet Tutorial (8) – 네트워크 매니저(Network Manager)
  • [Unity] Physics.Simulate를 이용한 네트워크 동기화
  • 키워드에 대한 정보 유니티 네트워크
  • 사람들이 주제에 대해 자주 검색하는 키워드 RPC는 진짜 어마어마하게 중요합니다ㅣ포톤PUN2

유니티 네트워크 주제에 대한 동영상 보기

여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!

이 영상을 보시면 PhotonView 컴포넌트와
PhotonView 컴포넌트의 독특한 함수 RPC의
개념을 확실하게 잡을 수 있을 것입니다.
또한 Transform (Position, Rotation, Scale) 동기화도 배울 수 있습니다.
# 스크립트 보기
https://goraniunity2d.blogspot.com/2019/08/rpc.html

#RPC #유니티포톤2 #동기화 #PhotonView #PhotonPUN2 #멀티

유니티 네트워크 주제에 대한 자세한 내용은 여기를 참조하세요.

[Unity 5] 네트워크 기초(네트워크 매니저, 플레이어 설정 …

[Unity 5] 네트워크 기초(네트워크 매니저, 플레이어 설정), 키보드입력에 따른 처리(Input.GetKeyDown()), 스크립트에서 프리펩 생성시키기(총알 …

+ 더 읽기

Source: m.blog.naver.com

Date Published: 5/17/2021

View: 7038

유니티에서 네트워크 설정하기 – MirrorNetwork

유니티3D 에디터에서. 에셋 다운로드. 에셋스토어에서 Mirror 를 검색하여 미러네트워크 에셋을 추가합니다. <01. 에셋스토어 ...

+ 자세한 내용은 여기를 클릭하십시오

Source: beatchoi.github.io

Date Published: 12/13/2021

View: 7825

[Unity] 네트워크 프로그래밍

서버를 알기 전에 네트워크 기능을 알고, 데이터 통신도 해보고 이동 동기화 전까지만 해보면 될 것이다. 비동기 논블락킹 서버를 구현만 할 수 …

+ 여기를 클릭

Source: euncero.tistory.com

Date Published: 12/14/2022

View: 6583

유니티 네트워크 프로그래밍 – YES24

유니티 네트워크 프로그래밍. 가와다 마사토시 저 / 김성재 역 | 길벗 | 2015년 08월 04일 저자/출판사 더보기/감추기. 리뷰 총점8.3 리뷰 6건 | 판매지수 300.

+ 여기에 더 보기

Source: www.yes24.com

Date Published: 2/28/2021

View: 8213

[Unity3D] UNet Tutorial (8) – 네트워크 매니저(Network Manager)

네트워크 매니저(Network Manager). 이전 섹션들에서는 유니티 네트워크에서 오브젝트를 스폰하는 법, 원격 액션을 주고 받는 법과 멤버 변수의 값을 …

+ 자세한 내용은 여기를 클릭하십시오

Source: wergia.tistory.com

Date Published: 8/15/2021

View: 9515

[Unity] Physics.Simulate를 이용한 네트워크 동기화

들어가며 네트워크 게임에서 서버와 클라이언트간의 네트워크 전송 지연(transmission delay)은 이 세상에 물리 법칙이 적용되는한 피할 수 없는 사실 …

+ 여기에 자세히 보기

Source: kukuta.tistory.com

Date Published: 10/19/2021

View: 7556

주제와 관련된 이미지 유니티 네트워크

주제와 관련된 더 많은 사진을 참조하십시오 RPC는 진짜 어마어마하게 중요합니다ㅣ포톤PUN2. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.

유니티에서는 쓰레드를 몇 개까지 할당 받을 수 있을까
RPC는 진짜 어마어마하게 중요합니다ㅣ포톤PUN2

주제에 대한 기사 평가 유니티 네트워크

  • Author: 고라니TV – 게임개발 채널
  • Views: 조회수 18,715회
  • Likes: 좋아요 268개
  • Date Published: 2019. 8. 15.
  • Video Url link: https://www.youtube.com/watch?v=8zJRxOU4aCc

[Unity 5] 네트워크 기초(네트워크 매니저, 플레이어 설정), 키보드입력에 따른 처리(Input.GetKeyDown()), 스크립트에서 프리펩 생성시키기(총알 발사), 명령[Command], ClientRpc 호출[ClientRpc], 적 만들기 , 플레이어 랜덤 리스폰

1. 네트워크 기초

▶ 네트워크 매니저 생성하기

1) Create Empty를 선택하여 빈 오브젝트를 생성

2) Add Component를 선택하여 NetWorkManager, Network Manager HUD 추가

3) Network Manager 컴포넌트의 Spawn Info – PlayerPrefab는 네트워킹되고 있는 게임 화면에서 클라이언트나 서버가 사용할 수 있는 player를 지정함

Network Manager HUD

– Network Manager HUD는 게임화면에서 위와 같이 HUD가 생성되게 합니다.

– LAN Host를 클릭하는 플레이어가 서버가 되고 LAN Client(C)를 클릭하는 플레이어가 클라이언트가 됩니다.(현재 localhost로 지정)

▶ 네트워킹 중인 게임 플레이어 설정

1) 서버 – 클라이언트 통신중에 여러 player중 자신을 구별을 하기 위해서 Network Identity 컴퍼넌트를 추가

2) 네트워크 상의 구별된 자신 플레이어를 위치를 지정하기 위해서

▶ 네트워킹 중인 게임의 플레이어 스크립트

1) UsingEngine.Networking 를 통해서 네트워크 관련 API를 사용할 수 있게 하고 네트워크에 이용되는 스크립트는 NetworkBehaviour를 상속받습니다.

2) OnStartLocalPlayer() : 플레이 시 자동 호출되며 자신의 메쉬에 색을 입힘

3) 입력에 따른 플레이어 이동 – (변수 x,y는 게임 상의 x축과 z축 의미), y축은 위쪽(하늘쪽)이므로 Translate() 함수에 0을 줘어서 못움직이게함

4) 스페이스바를 누를 때 동작 – Fire()함수 호출

5) 스크립트에서 프리펩 생성

※ 해당 스크립트는 플레이어로 사용된 프리펩에 포함되어 있습니다.

※ public GameObject 변수 : 유니티 에디터에서 프리펩을 직접 설정하기 위해서 public 선언을 하고 GameObject라는 상위 클래스를 선언함, 해당 변수에 게임 오브젝트를 지정할 수 있다.

[실행 화면]

– 현재 클라이언트쪽에서 발사하는 총알이 서버에서는 안보임(서버쪽에서 클라이언트 총알 안보임)

▶ 네트워킹을 위한 총알 설정하기

1) 컴포넌트로 Network Identity와 Network Transform를 추가합니다.

2) 총알은 한번 발사하면 위치,속도가 변하지 않기 때문에 1 번만 전송하면 되므로 Network Send Rate 속성을 0으로 설정합니다.

※ 내 생각 : Network Send Rate 속성을 높이면 캐릭터의 움직임이 자연스럽다. 그러나 과부하 + 네트워크 상태 좋아야함

▶ 네트워크 매니저에 총알 등록

※ 내 생각 : 환경적 요소(static)같이 변하지 않은 것은 프로그램을 다운받을 때 서버에 접속한 클라이언트들이 모두 통일적으로 가진다. 그러나 캐릭터의 움직임이나 총알, 적을 출현에 따른 처리(죽이기, 캐릭터 다짐 등) 등등 변화가 있는 요소에 대해서는 네트워크를 통해서 통일적으로 처리해주어야 하는데, 이러한 기능을 네트워크 매니저가 일부분 담당하고 있다.

▶ 서버를 통한 총알 발사

– 클라이언트가 사용할 플레이어의 스크립트안에 내용입니다. [Command]를 통해서 서버로 총알 생성을 요청할 수 있습니다.

※ 내 생각 : 플레이어(PlayerCube)은 총알과 같이 요청할 필요가 없다(이유: Network Manager – Spawn Info – Player Prefab에 등록됨). 그러나 총알과 같이 부가적인 요소는 서버에 요청을 통해서 각 클라이언트간의 화면을 통일시켜야 한다.

▶ 총알 충돌과 플레이어 상태 (Non-Networked Health)

– 해당 내용은 총알과 플레이어 충돌로 인하여 총알이 사라지고 플레이어의 체력이 줄어듬

– 아래 스크립트는 Bullet 프리펩에 Bullet이라는 이름의 스크립트를 추가하여 작성한 내용입니다.

ex.

– 충돌 시 OnCollisionEnter() 함수가 호출됩니다. Collision 객체를 통해서 충돌한 객체를 가져옵니다.(현재 플레이어를 타켓함)

– 플레이가는 PlayerMove라는 컴포넌트(스크립트)를 가지고 있으므로 이를 가지고 있다는 뜻은 플레이어라는 의미입니다.

– 충돌로 인해 데미지를 주어 플레이어의 체력을 감소시키기 위해서 플레이어 프리펩에 Combat이라는 스크립트를 추가했습니다.

ex. 플레이어 프리펩의 스크립트 – Combat (총알과 충돌 시 10의 데미지 주기)

ex, 플레이어 프리펩의 스크립트 – HealthBar

using System.Collections;

using System.Collections.Generic;

using UnityEngine; public class HealthBar : MonoBehaviour { GUIStyle healthStyle;

GUIStyle backStyle;

Combat combat;

private void Awake()

{

combat = GetComponent();

}

private void OnGUI()

{

InitStyles(); Vector3 pos = Camera.main.WorldToScreenPoint(transform.position); // 체력바 배경 그리기

GUI.color = Color.gray;

GUI.backgroundColor = Color.gray;

GUI.Box(new Rect(pos.x – 26, Screen.height – pos.y + 20, Combat.maxHealth / 2, 7), “.”, backStyle); // 체력바의 체력양표시 그리기

GUI.color = Color.green;

GUI.backgroundColor = Color.green;

GUI.Box(new Rect(pos.x – 25, Screen.height – pos.y + 21, combat.health / 2, 5), “.”, healthStyle);

} void InitStyles()

{

if(healthStyle == null)

{

healthStyle = new GUIStyle(GUI.skin.box);

healthStyle.normal.background = MakeTex(2, 2, new Color(0f, 1f, 0f, 1.0f));

} if (backStyle == null)

{

backStyle = new GUIStyle(GUI.skin.box);

backStyle.normal.background = MakeTex(2, 2, new Color(0f, 0f, 0f, 1.0f));

} } Texture2D MakeTex(int width, int height, Color col)

{

Color[] pix = new Color[width * height];

for(int i = 0; i < pix.Length; ++i) { pix[i] = col; } Texture2D result = new Texture2D(width, height); result.SetPixels(pix); result.Apply(); return result; } } [실행결과] - 자세히 보면 미세하게 체력이 다름 ▶ 총알 충돌과 플레이어 상태 (Networked Health ) - Combat 스크립트를 변경하여 Combat의 health 변수를 서버에 접속한 모든 클라이어들(서버도 포함)에게 변화를 인식시킴 [실행 결과] - 서버에 접속된 모든 클라이언트가 일치된 변화를 보이고 있다. ▶ 죽음과 리스폰 - 캐릭터가 총을 받고 죽었을 때 다시 리스폰되어야 합니다. 이와같은 설정은 아래와 같이합니다. 명령 [Command] - [Command] 와 같이 쓸 수 있는 이유는 NetworkBehaviour 클래스를 상속하였기 때문입니다. [Command]를 적용할 메소드를 클라이언트에서 호출되어 서버에서 실행하는 것입니다. - 명령은 클라이언트의 프레이어 오브젝트에서 서버의 플레이어 오브젝트로 보내는 것 (ex. 총알 발사) - 보안 상의 이유로 자신의 플레이어 오브젝트에서만 명령을 호출할 수 있다. => 다른 플레이어를 조작할 수 없다.

– 함수를 명령으로 만드는 방법 : [Command]를 추가하고 “Cmd”접두사를 추가함

– 명령 메소드는 특별하게 취급되어 일반적인 함수와 같은 로컬 환경에서 실행되지 않음

– 명령을 보내는 것은 매 프레임마다 실행이 됩니다. 그러므로 남발 시 네트워크 장애를 발생시킬 수 있다.

ClientRpc 호출 [ClientRpc]

– [ClientRpc] 와 같이 쓸 수 있는 이유는 NetworkBehaviour 클래스를 상속하였기 때문입니다. [ClientRpc]를 적용한 메소드는 서버에서 호출되어 클라이언트에서 실행하기 합니다.

– 이 호출은 networkIdentity를 갖는 스폰된 어떠한 서버 오브젝트로부터 전송될 수 있습니다. 서버에는 우선 순위가 있기 때문에, ClientRpc를 전송하는 서버 오브젝트에 보안 문제점은 없습니다.

– 함수를 ClientRpc호출로 만드는 방법 : 메서드에 [ClientRpc] 사용자 정의 속성을 추가하고 “Rpc”접두사를 붙입니다.

– 이런게 만들어진 함수는 서버에서 호출될 경우, 클라이언트 상에서 실행됩니다. 어떠한 인수도 자동으로 전달됨

▶ 적 만들기(Non-Player objects)

– 해당 내용은 적을 화면에 생성 시키고 총알을 통해서 공격을 하면 체력이 깍이고 사라지는 것입니다.

1) 적 프리펩 만들기

– 총알에 맞으면 데미지를 주기 위해서 Combat,HealthBar 스크립트 상속

– Destroy On Death 변수를 true로 하면 체력이 0이 되면 리스폰하지 않고 제거하기 위함입니다.

2) 적 출력 시킬 위치 정하기

– 빈 오브젝트 (Create Empty)를 생성합니다.

– 스크립트에 적을 추가 시키는 코드를 작성합니다..

3) 스폰되는 적을 NetworkManager에 추가합니다.

ex. 적 제거되는 스크립트

//생략

public class Combat : NetworkBehaviour { public const int maxHealth = 100;

public bool destroyOnDeath; [SyncVar]

public int health = maxHealth; public void TakeDamage(int amount)

{

if (!isServer)

return; health -= amount;

if (health <= 0 ) { if (destroyOnDeath) { Destroy(gameObject); } ​// 생략 ​ } ▶ 플레이어 랜던 리스폰 1) 빈 오브젝트의 컴포넌트로 Network Start Position 를 추가합니다. - 이때 Position 값에 따라서 캐릭터가 리스폰 됩니다. 2) Player Spawn Method 를 Round Robin 으로 설정합니다.

[Unity] 네트워크 프로그래밍

서버(Server)

IP공유기 하나가지고 여러 개의 PC가 접속할 때 다른 유저로 구분하는 것이다.

ex) 아이피 터널링, 구글 홀펀칭 – 5G 기능

C, C++에는 유니온이라는 구조체가 있다. 서버에 들어갈 경우 알아야 할 개념이다.

서버를 알기 전에 네트워크 기능을 알고, 데이터 통신도 해보고 이동 동기화 전까지만 해보면 될 것이다.

비동기 논블락킹 서버를 구현만 할 수 있으면 된다.

서버에 몬스터 배치까지만 해보고 동기화는 나중에 하겠다.

서버의 한계가 있기 때문에 스마트폰 6명 정도 동기화만 해도 현업 수준이다.

서버와 클라이언트

클라이언트 서버 모델을 게임에 적용한 것이 온라인 게임이다.

서버

서비스 제공자

클라이언트

서비스 요청자

클라이언트 서버 모델

서비스 요청자인 클라이언트와 서비스 자원의 제공자인 서버 간에 작업을 분리해주는 분산 애플리케이션 구조이자 네트워크 아키텍처를 나타낸다.

아키텍처

컴퓨터를 기능면에서 본 구성 방식 기억장치의 번지 방식, 입출력 장치의 구성 방식 등을 가리킴.

일반 적으로 같은 아키텍처의 컴퓨터에는 소프트웨어의 호환성이 있음.

최적화를 목표로 두고 시스템 구성과 동작원리 그리고 시스템의 구성 환경 등을 설명 및 설계하는 청사진 또는 설계도이다.

https://tuhbm.github.io/2019/04/24/architecture/

네트워크 프로그래밍

TCP/IP, UDP 중심의 프로그래밍이다.

네트워크 모델

네트워크 세부 기능들은 매우 복잡하고 다양하다.

이를 체계적으로 분류한 개념적인 모델이다.

네트워크 대표적인 모델 종류는 OSI 7 Layer와 TCP/IP 모델로 기본적으로 이해하고 있어야 한다.

데이터들은 바로 전송되는 것이 아닌 몇 단계씩 걸쳐서 전송이 된다.

마더보드에 랜카드가 부착되어 있고선 랜선을 타고 데이터가 전달이 된다.

그러면 PC 랜선부터 타고 랜카드를 거쳐서 실제 데이터가 응용 프로그램까지 전달되는 과정을 레이어로 설명한다.

그 과정을 7단계 레이어로 보여준 것이 OSI 7 Layer이다.

ex) PC마다 IP(주소)가 있다.

PC와 PC에 데이터를 전송한다.

PC1에서 “안녕하세요”라고 전송하면

데이터가 RP를 타고 거쳐서 PC2까지 전송이 된다.

OSI 7 Layer (Open System Interconnection)

7개의 Layer로 구성한 네트워크로 각 레이어마다 자신의 고유한 기능이 존재한다.

응용 프로그램 계층(Application Layer)

암호를 푼 데이터가 응용 프로그램에 전달되고 사용자나 응용 프로그램 간의 데이터 교환을 가능하게 하는 계층이다.

ex)카톡에서 카톡으로 전송된다.

HTTP, FTP, 터미널 서비스, 여러 메일 프로그램, 디렉토리 서비스 등을 제공한다.

표현 계층(Pressentation Layer)

데이터를 해석하는 단계로 데이터의 구조를 하나의 통일된 형식으로 표현하고, 데이터의 압축과 암호화 기능을 수행한다. 문자열로 보냈으면 바이트 단위로 전송이 된다.

ex) 어플리케이션에서는 바이트 단위로 전송되는 것이기 때문에 문자열로 해석해야 한다.

그 다음 받은 데이터가 암호화가 되어 있다면 암호를 풀어야 한다.

세션 계층(Session Layer)

두 시스템 간의 통신 중 동기화를 유지하고 데이터 교환을 관리한다.

포트를 찾아간다. 몇 번 포트인지 해당 포트로 데이터가 전달된다.

정보 교환을 효과적으로 할 수 있도록 전송 계층에서 설정된 종단 간의 논리적인 연결에 추가 서비스를 제공한다.

전송 계층(Transform Layer)

IP주소의 이동 경로를 전송한다. 종단 간의 신뢰성 있고, 투명한 데이터 전송을 제공한다.

이를 위해 오류 제어, 통신량 제어, 다중화를 제공하며 응용 프로그램 간 통신을 위해 포트를 사용한다.

네트워크 계층(Network Layer)

데이터는 IP주소를 갖고 있다. 8비트 숫자 4개로 구성된 IP주소 체계로 사용하며, 경로 제어와 통신량 제어 등을 수행한다.

데이터 링크 계층(Data Link Layer)

마더 보드에 있는 랜카드를 거치는 단계로 물리적인 링크를 통하여 동기화, 에러 제어, 흐름 제어 등을 통해 패킷을 전송한다. 16진수 12개로 만들어진 MAC 주소를 객체 간 통신에 사용하며, MAC 주소는 고유하다.

물리 계층(Physical Layer)

데이터 베이스 계층으로 랜선을 타고 온 것을 피지컬 레이어라고 한다.

기계적, 전기적, 기능적, 절차적 특성을 정의하며 비트 스트림을 물리 매체를 통해 전송한다.

프로토콜(Protocol)

네트워크에서 프로토콜은 통신 규약 을 의미한다.

통신을 할 때 어떠한 규약을 갖고 있다.

대표적 프로토콜은 TCP/IP와 UDP가 있다.

서버를 안하건 기본적으로 알고 있자.

TCP/IP

OSI7Layer을 4단계로 구분한 네트워크 모델로 실제 사용하는 것은 TCP/IP모델이다.

TCP통신은 연결형 통신 서비스 로 연결해놓은 상태로 물고있어야 한다.

TCP는 상대방이 받았다는 응답을 받아야만 다음 데이터가 전송 된다.

응답을 받은 다음에 다음 패킷(데이터)을 전송한다.

순차적인 데이터 전송으로 데이터의 전송 순서를 보장한다.

신뢰성있는 데이터 전달과 흐름을 제어한다.

응답을 받았을 때 콜백을 처리해주면 쉽다.

그게 비동기 방식 의 처리이다.

비동기 방식의 콜백을 우리가 받아 낼 수 있다.

만드는 것이 아니라 이미 있으므로 쓴다.

ex) PC 두 대에 TCP통신으로 “안녕하세요” 라고 데이터를 보내게 되면

PC 1에서 데이터를 보내면 PC2가 응답을 받아야 다음 데이터가 간다.

받았다는 응답을 보내고, 실패해도 보낸다.

그러면 응답이 또 온다. 이 개념이 끝이다.

UDP

UDP는 비연결형 서비스 로 연결없이 통신 가능하다.

UDP는 그냥 IP에다가 패킷을 보내버리는 브로드 캐스팅 방식이다.

정보를 주고 받을때 정보를 주고 받는다는 신호절차를 거치지 않는다.

보내기만 하면 되기 때문에 순차적이지 않아 받는 쪽에서 순서가 바뀔 수 있다.

일방적이고 응답이 없어 속도가 빠르다.

그러나 속도는 빠르나 순서가 바뀔수가 있어 순차적인 데이터 전송이 보장되지 않는다.

ex)

PC A가 그냥 보내기만 함.

PC B가 그냥 보내기만함.

A에서 B로 서버를 거쳐서 패킷을 보내는데 “안녕하세요” 라고 데이터가 전달이 되고,

“또 만났네요”라고 두번째 패킷을 보낼 때는 받는 쪽에서는 첫번째 패킷을 받고 두번째 패킷을 받도록

순서대로 받아야 하는데 순서가 보장이 안돼서

A에서 B로 전송될 때 두 번째 패킷을 보낼때 서버 연결이 돼서 B는

“또 만났네요”, “안녕하세요” 순서로 받을 수가 있다.

그래서 게임에서는 보통 TCP를 사용한다.

“안녕하세요” (응답 성공), “또 만났네요” (응답 성공)

이 차이점만 잘 이해하자.

UDP는 TCP에서 소켓 설정만 바꾸면 되는거라 쉽다.

다섯 단계를 거쳐서 A에서 B로 보낼 때 내가 조종하는 것이 아닌, 라우팅이다.

* 라우팅 : 어떤 네트워크 안에서 통신 데이터를 보낼 때 최적의 경로를 선택하는 과정이다.

최적의 경로는 주어진 데이터를 가장 짧은 거리로 또는 가장 적은 시간 안에 전송할 수 있는 경로이다.

ex)전화 통신망, 정보 통신망, 교통망 등 여러 종류의 네트워크에 사용된다.

소켓(Socket)

소켓이란 데이터를 보내고 받게 하는 객체로 소켓을 사용하여 통신할 수 있다.

소켓 프로그래밍

소켓을 이용해 컴퓨터끼리 통신하는 프로그래밍 방식을 말한다.

기계마다 소켓을 갖고 있으며, 소켓이 있어야지만 소켓가지고 네트워크 통신할 수 있다.

소켓은 네트워크 통신을 하기 위한 기본적인 기능을 갖고 있는 주체이다.

실제 통신을 담당하는 건 물리적으로는 랜선을 통해서 데이터가 오고가지만

이것은 하드웨어이고, 프로그램을 통해서 데이터를 통신하는 것은 소켓이다.

소켓 하나가 클라이언트 하나이다.

소켓이 있어야만 통신이 가능하여 클라이언트 통신을 소켓 통신이라고도 한다.

그러므로 코드를 작성할 때 소켓을 이용해 데이터를 보내고 받는 것을 알아야 한다.

데이터를 보내고 받도록 통신하는 객체가 바로 소켓이다.

이처럼 서버 프로그램을 한다는 것은 소켓을 다루는 것이다.

그래서 소켓 클래스가 필요하다.

그런데 C언어에도 소켓이 존재하기 때문에 소켓을 클래스라고 말할 수는 없다.

객체 대신 주체라는 말을 쓸 수도 있다.

C#에서는 소켓 클래스가 있기 때문에 소켓 객체를 만들어서 사용하면 된다.

소켓을 직접 제작하는것이 아닌 소켓이라는 데이터 타입이 존재한다.

소켓 인스턴스를 만들어서 소켓을 가지고 통신하면 된다.

ex)

PC 하나에 소켓 하나가 있다.

PC와 PC끼리 통신하고자 한다.

데이터를 보낼때 받는 PC입장에서 보내는 PC의 소켓을 알고 있어야 한다.

그러면 같은 응용 프로그램이 설치되어 있을 경우 각각의 소켓으로 통신이 가능하다.

그러면 랜카드를 거쳐 랜선을 타고 전달이 된다.

그러므로 소켓은 당연히 양쪽 PC의 IP를 알고 있어야 한다.

ex)

서버와 각각의 클라이언트가 여러 개가 있을 경우

응용 프로그램을 이용해 서비스를 사용하기 위해 클라이언트는 서버에게 서비스를 요청할 것이다.

서버는 우리 서비스를 사용하는 사용자인지 확인하고 응답을 한다.

로그인 성공 후 클라이언트를 접속한다.

서버 입장에서 여러 명의 클라이언트가 연결되어 있는데, 클라이언트 한 개가 소켓이다.

ex) 채팅 서버

채팅 서버에 접속해 있는 유저가 4명이다.

서버에서 각각의 PC는 소켓으로 알 수 있다.

그러므로 서버 입장에서 각각 다른 소켓을 최소 4개를 갖고 있어야 한다.

유저가 접속을 하게 되면 서버와 통신하라고 소켓을 할당 받는다.

각 클라이언트마다 IP주소를 갖고 있고 서버에서는 이 정보를 소켓으로 알아내는 것이다.

IP 주소(IP Address)

인터넷에 연결된 컴퓨터들의 주소이다.

IP주소에는 IPv6주소와 IPv4 주소가 있다.

IPv4가 숫자 4개로 나타내는 과거부터 사용한 IP이다.

IP사용량이 기하급수적으로 늘어나 네자리 숫자가지고는 IP를 감당할 수 없게 되어

IP6이 추가되고 IP 128bit의 주소 체계를 다시 만들어 냈다.

그래서 PC는 2개의 IP주소를 갖고 있고 IPv4로 표현하는가 IPv6로 표현하는가 차이가 있다.

ex)

2016년이후로 구글과 IOS도 마찬가지로 IPv6 체계로 해야할 것이다.

IPv4 방식으로 했던 온라인 게임들 IPv6로 변경한다.

IPv4

32비트 주소 체계로 4개의 숫자로 표현한다.

IPv6

128비트 주소 체계로 6개의 숫자로 표현한다.

IP 주소 검색 방법

커멘드 창에 ipconfig를 치면 내 PC에 IP를 확인할 수 있다.

cmd창 > ipconfig 검색

ex)

고유IP

PC에서 외부로 데이터가 나가려면 고유 IP주소를 타고 데이터가 나가는 것이다.

유동IP

유동IP는 지역IP , 로컬 주소 , 내부 네트워크 주소이다.

외부로 데이터를 전송할 때는 유동 IP를 가지고 나가는 것이 아닌 고정 IP를 알아야 한다.

ex)

네이버에 자료를 갖고 오고 싶으면 고정IP를 타고 전송하는 것이다.

공유기를 보면 내 PC에 할당된 고정IP가 보여진다.

ex) 사담

고정IP가 하나인 공유기 한 대에 PC 두 세대 돌리면 PC두 대 IP가 몰린 공유기가 있을 수 있다.

공유기가 3, 4개 PC를 갖고 있더라도 데이터는 고유IP 하나를 가지고 나가는 것이다.

공유기 한 대의 사용량을 PC 여러대에서 나누어서 사용하는 개념이다.

IP 한 명분을 신청하고, 그 한 명 분의 IP를 벤드윅스라고 하는데 PC 여러대에서 나눠쓰면 속도가 느려질수 밖에 없다.

통신사에서 PC 별로 통신 사용료를 지불해야 한다고 한다.

그런데 이렇게 사용하지말라고 하지만 일반적으로 그렇게 사용함.

일반적으로 PC한데 신청해놓고 PC에다 공유기 올려놓고 공유기에 여러 PC를 할당해서 사용하는데 속도가 느려진다.

호스트(host)

네트워크에 연결된 PC를 호스트라고도 한다.

DNS 프로그램 코드 작성할 때 반드시 등장한다.

DNS(Domain Name System)

DNS는 사람이 읽을 수 있는 도메인 이름을 머신이 읽을 수 있는 IP주소 또는 호스트로 변환할 수 있다.

DSN정보를 닷넷에서 알아오는 클래스가 DNS클래스이다.

DNS클래스는 도메인에 관련된 정보를 알아오거나 그러한 기능을 모아놓은 클래스이다.

ex)

www.naver.com, www.daum.net

ex) 구글의 IP와 호스트 정보를 가져오기

구글에 접속하기 위해서 www.google 닷컴으로 접속하면 그게 도메인이고

네트워크 상 안에는 IP주소가 있다.

도메인이 IP주소를 대신하는 호스트 명이 된다.

도메인은 www.으로 시작한다.

DNS를 이용해 www.google.com을 DB가 읽을 수 있는 IP주소로 변환한다.

그 주소를 알아야 어디로 갈지 알 수 있다.

포트(Port)

포트란 PCP가 가지고 있는 주소이자 통로 번호이다.

MAC주소가 네트워크 카드의 고유 식별자이고, IP가 시스템의 논리적인 주소라면

포트는 시스템에 도착한 후 패킷이 찾아갈 응용 프로그램과 통하는 통로 번호 이다.

통신을 할 때 IP만 가지고 되는 게 아니라 서버에 포트를 할당해야 한다.

그러므로 PCP를 연결할 포트는 무조건 하나가 필요하다.

포트의 타입은 unsingd short로 0~65535(216)번까지 존재하며, IP나 MAC주소처럼 출발지와 목적지에 응용프로그램별로 포트 번호를 가지고 통신한다. 눈에 띄는 포트를 사용해도 되고, 포트 번호를 섞어서 사용해야 한다.

웰노우 포트 포트

0~1023번 포트까지로 대부분 고유의 사용 용도가 있다.

레지스터드 포트

1024번에서 49151번 중 임의의 포트를 응용 프로그램에 할당하여 사용한다.

다이나믹 포트

다이나믹 포트도 사용하지 말라고 한다.

다이나믹 포트는 49152~65535로서 시스템에서 사용한다.

패킷(Packet)

데이터 전송에서 송신측과 수신측에 의하여 하나의 단위로 취급되어 전송되는 집합체를 의미한다.

즉, 많은 양의 전송 데이터를 수신측으로 이동시키기 위해서 일정 크기로 데이터를 분할하게 되는데, 이 단위 크기로 분할된 하나의 데이터 덩어리를 패킷이라고 한다.

패킷의 종류를 #define 또는 열거체(enum)으로 만들어 놓고 분류를 할 수 있다.

ex)이동 패킷, 스킬 패킷, 귓속말 패킷이야 등등

가변 길이 패킷

– 데이터 길이 + 헤더 + 데이터

– 데이터 길이 + 헤더 + 데이터 + 엔드마커

헤더 : 데이터의 종류를 보내며 클라이언트, 서버가 동시에 갖고 있다.

엔드 마커 : 패킷의 끝을 나타내는 것으로 보통 2byte 정도 할당한다.

ex) 패킷을 전송하기 위한 설계

“안녕하세요”라고 보내고 싶다.

“안녕하세요”는 256byte가 안됀다.

패킷의 첫번째 부분의 1바이트는 데이터의 길이를 명시하고,

두번째 부분의 1바이트는 헤더를 붙일 것이고,

세번째 부분에는 “안녕하세요”라는 데이터를 넣을 수 있다.

이런식으로 길이 + 헤더 + 데이터 구조로 패킷 데이터를 구성해서 보낸다.

메모리 저장 방식

컴퓨터 메모리에 저장되는 바이트의 순서로 빅엔디안과 리틀엔디안 방식이 있다.

빅엔디안

빅엔디안은 바이트의 열에서 가장 큰 값부터 저장된다.

리틀엔디안

리틀엔디안은 바이트의 열에서 가장 작은 값부터 저장된다.

ex)

리틀엔디안 방식의 메모리로 “안녕하세요”라는 데이터를 보내면

받은 데로 출력할 경우 “요세하녕안”으로 거꾸로 출력이 될 수 있다.

메모리에 저장된 바이트 순서는 주로 과거의 인텔 계열과 유닉스 계열 차이점인데

인텔 계열에 맞추면 문제가 없다. 이건 CPU가 결정한다.

동기와 비동기

네트워크 프로그래밍 하면서 핵심적인 내용은 동기와 비동기이다.

동기 방식(Synchronous)

동기 방식은 함수가 끝난 다음에 다음 함수가 호출이 된다.

동기화는 클라와 서버를 동시에 돌려야 하지만 데이터 전송하고 받는 건 문제가 안되는데

클라에서 동기화를 하려면 클라이언트 문제가 맞다.

프레임스킵을 구현할 줄만 알면 동기화가 된다.

프레임이 일정 프레임 아래로 내려갔을 때 스킵을 구현할 줄 알면 동기화가 된다.

그러나 유니티에서 프레임 스킵 구현이 쉽지가 않다.

비동기(Asynchronous)

요청을 보낸 후 응답과는 상관없이 다음 방식이 동작한다.

서버는 비동기 방식으로 만들어야 효율이 좋다.

동기 방식 서버도 사용이 되기는 한다.

함수 이름앞에 Asynch란 말이 붙으면 비동기라는 의미가 된다.

ex)

서버에서 유저 로그인 처리시 1000명이 접속할 경우

동기 방식으로 만들면 한명 접속 처리하고 모두 완료한 다음에 두번 접속 처리, 완료한 다음에 세번째 접속처리..

비동기 방식의 로그인을 만들게 되면 비동기 방식은 한명 접속 요청이 오게 되면 처리하라고 던져놓고 두번째 유저를 받는다. 처리가 끝나고 안끝나고는 중요하지 않고 그걸 처리하는 건 따로 있다.

다음 유저를 바로바로 받는다. 대기하고 있는 유저를 또 던져놓고 다음 유저를 또 던져 놓는다.

그래서 동기 방식보다 비동기 방식이 빠르다.

C와 C++기반의 IO CP를 별도로 구성을 해야하는데

C#에서는 다른 방식으로 구현할 수 있어서 편리하다.

스레드

프로세스 안에 스레드라는 개념이 존재한다.

스레드는 어떠한 작업을 하기 위해서 별도로 구동되는 실행 단위 이다.

CPU 하나를 스케줄링해서 실행된다.

실행 파일인 프로세스 안에는 여러 개의 스레드가 존재 한다.

여러 개의 스레드 중에서 메인 역할을 하는 스레드가 메인 스레드이다.

메인 스레드에서 메인 함수를 호출하는 것이다.

멀티 스레드

멀티스레드는 어떠한 작업을 동시 작업 하는 것으로 스레드가 여러 개가 구동되면 멀티 스레드 방식 처리이다.

스레드가 여러 개가 구동된다는 말은 이 작업을 하기 위해서 메인 스레드 하나만 사용하는게 아니라 그 작업을 동시 작업 시키는 것으로 멀티 테스킹이라고 한다.

사실 동시 작업이라는 개념은 PC에 존재하지 않고 CPU를 나눠 쓰는 방식이다.

스레드를 동시에 실행되는 개념이 아니라 시간 할애를 해주는 것이다.

스레드A 한 번, 스레드B 한 번,,, 빠르게 반복되다 보니까 동시 작업하는 것처럼 보인다.

유니티는 C# 닷넷을 주로 사용하고 서버와 클라이언트의 네트워크 프로그래밍이 가장 쉬운 방식이다.

그래서 언어를 같이 맞춰서 사용한다.

서버를 하려면 스레드 개념이 있어야 한다.

유니티에서 스레드를 사용하지 않고도 비동기 방식 서버를 구동시킬 수 있는 것은 닷넷에서 비동기 방식 함수들을 다 만들어 주었기 때문이다. 초창기에 비동기 방식을 만드려고 클래스 매핑도 하고 자구적인 노력으로 다 만들어서 닷넷은 네트워크 통신이 편하게 되어있다. 지금 배우고있는 것이 닷넷 기반의 서버이다.

네트워크 공부는 어렵지는 않지만 테스트 환경이 쉽지 않다.

ex) 서버에 몰려있는 PC가 최대 500~1000대 일반적으로 MMORPG 서버는 3000명 수용이 가능한게 일반적이다.

3000명이 붙었을 때 어떤일이 일어날지 모른다.

디버깅 할 경우 브레이크 포인트를 걸어두면 다른사람들을 멈춘다. 코드가 진행이 안돼니까

클라이언트가 잘못되더라도 PC하나 죽지만 서버가 잘못되면 서버에 붙어있는 모든 클라이언트가 잘못되므로 조심해야 한다.

서버 프로그래밍 공부시 복잡한 쿼리는 아니더라도 데이터베이스에 의해서 로그인부터해서 레벨업을 DB에 반영해서 서버로부터 다시 넘겨받는 연습을 해보자.

유니티 연동해서 한 세트 만들어서, 서버 기반 움직임 지원하기

서버 프로그램

서버 프로그램을 하기 위해서 어떠한 구조와 순서로 서버 프로그래밍을 구성해야 하는지 먼저 이해해야 한다.

네트워크 통신의 순서가 정해져 있으며, 그 순서대로 소켓을 구동시켜야 한다.

네트워크 통신 순서

네트워크 통신 순서로 서버 프로그램을 만들면 된다.

이 순서로 네트워크 함수를 호출하면 된다.

유저 접속

먼저 서버에서 필요한 것은 유저 접속을 처리하는 소켓이다.

유저 접속을 처리하는 소켓은 bind – listen – accept 순서로 호출하면 된다.

먼저 bind()로 서버를 연결하고

Listen()을 호출하여 새로운 클라이언트가 접속했는지 대기하고 있다가

새로운 클라이언트가 connect()하여 접속 요청을 하면 서버는 accept()를 하게 된다.

accept()가 호출되었을 때 접속한 클라이언트와 통신하라고 소켓 하나를 리턴해준다.

그 소켓을 가지고 클라이언트와 통신하면 된다.

그리고 클라이언트에 전송할 패킷이 있다면 그 소켓을 통해서 해당 클라이언트한테 전송을 해주면 된다.

데이터를 보내게 되면 그 소켓을 통해서 전달이 된다.

그래서 서로 데이터를 주고 받으면 클라이언트 입장에서는 게임을 종료하면

소켓을 클라이언트는 닫아주고, 서버에서도 할당했던 소켓을 닫아주고 그 소켓을 리스트에서 지워주면 된다.

C#은 네트워크 프로그래밍시 웬만한 클래스를 만들어서 제공하기 때문에 사용하기가 편함.

닷넷에서 미리 만들어 놓은 서버가 있기 때문에 서버 모듈을 일일이 하나씩 안만들어도 된다.

닷넷에서 이미 C# Listenner 클래스를 만들었으며 서버 기능이 다 가능하며 성능도 똑같다.

이어서 오는 방식으로 Stream방식을 지원한다.

ex)

네트워크에서는 데이터를 전송하게 되면

“안녕하세요” 전송시 “안녕”까지 오고 “하세요”가 나중에 올 수도 있다.

그게 데이터가 연속되게 온다는 보장이 없다.

과거의 서버는 어떻게 작업했는가.

바이트 단위로 전송하는데 바이트를 계속 이어붙이는 코드를 만들어 놓고 구동을 시켜야 한다.

바이트 단위 전송이기 때문에 바이트를 계속 이어서 붙일 수 있게 서비스하는 언어가 C++이다.

그리고 그 부분을 뺄 수 있는게 C#서버이다.

C# Stream 방식은 이어서 온다. 않끈김, 끊기더라도 이어서 온다.

C#서버에서도 바이트 단위로 이어서 패킷을 완성하는 방식으로 사용할 수 있으나

Stream 방식을 할 수있어 그렇게 할 필요가 없다.

서버 프로그래머가 아니더라도 네트워크 통신은 기본이다.

온라인 게임을 만드는데는 반드시 네트워크 개념이 있어야 한다.

비정상적인 처리와 종료 처리 등의 예외 처리를 신경써야 한다.

패킷을 직접 수동으로 이어붙일 필요가 없기 때문이다.

서버가 어려운 이유는 여러 명이 다수의 사용자가 접속할 때는 우리가 모르는 ,경험하지 못한 상황이 많이 발생하며

그래픽컬하지 않아 논리적으로 구동되기 때문에 디버깅이 쉽지 않다.

또한 서버에서 디버그 모드시 코드가 진행되다 중단점에 진입시 다른 클라이언트는 아무것도 못한다.

서버는 한 번 블럭이 되서 코드가 진행이 안돼는 상태면 전체가 서비스를 이용을 못하는 것이다.

코드가 하나 틀리게 되면 그게 어렵기 때문에 경력자를 채용하는 편이다.

디버그 모드를 쉽게 할 수 없다.

그러나 네트워크 통신 순서대로만 하면 된다.

서버의 기술

서버를 어떻게 구분하느냐도 중요하다.

서버는 PC 하나의 단위(워크프로세서 단위)가 아닌 프로세스(실행 파일)단위이다.

또 서버와 서버 간에 통신을 해야 함. 동기화를 해야 다른 프로그램이 구동 되고 있다.

ex) 로그인 서버, NPC 서버, 게임 서버 이렇게 있다고 하면

채팅 서버, 몬스터 서버의 실행 파일 단위로 논리적으로 구분해서 사용할 수 있다.

몬스터 서버는 몬스터만 관리, 게임 서버는 게임 서버만 관리한다고 해보자.

우리 눈에 보이는 게임 공간에는 몬스터가 존재, 게임 공간 존재

그런데 몬스터는 다른 프로그램에서 도는 것, 근데 몬스터는 따로 놀 수 없다.

이 두 개를 어떻게 맞출것인가 이런게 서버 프로그래머가 해야할 일이다.

ex)

또한 서버는 수용량이 중요하다.

서버를 한 번 만들어 놨는데 몇명 수용되는 서버인가, 클라이언트와 서버는 윈도우 기반이 맞는데

그래픽 유저 인터페이스 기반의 서버를 만들어 리소스 시스템 자원을 사용하게 되면, 수용량이 떨어지게 된다.

ex)

윈도우 서버 1000명, 리눅스 서버 3000명

MSSQL 또는 MYSQL이냐에 따라 비용적인 부분도 있다.

ex)

MMORPG 게임 서버 유지 비용은?

한 달에 서버만 유지 하는데 유지 비용만 몇천만원이 나간다.

회사 입장에서 비용적인 측면에서 서버 하나를 만들어 놓으면

물리 적인 서버에 수용할 수 있는 수용량은 늘리고 사용량은 줄여야 한다.

그래서 패킷을 잘 설계해야 함. 패킷을 한번에 처리할 수 있는 것을 두번에 처리하면 사용량이 두 배로 늘어난다.

서버가 클라이언트 보다 오히려 손은 많이 안가지만 한 번 문제가 발생하면 신중히 해결해야 함.

그리고 사람마다 유저 접속을 처리하는 실제 클라이언트 소켓을 accept 함수가 처리한다고 했다.

유저 하나당 소켓을 하나 사용하거나, 소켓을 둘을 사용할 수 있다.

할당을 그렇게 해놓는 것이다.

유저 하나당 소켓 하나를 만들거나 둘로 만드는 것은 게임마다 다름.

ex) 통신시 보내기 소켓 전용, 받기 전용 소켓으로 둘 수도 있다.

서버를 잘하다는 말 = 서버의 구동과 몇명까지 수용이 가능한가

클라이언트는 FPS 몇프레임이 나오고 서버는 몇명까지 수용가능한지

그래서 처음에 유저가 선택을 하게 해서 서버에 들어가게 하고

그래서 유저가 꽉차면 혼잡 원할 이런식으로 보여준다.

이걸 어떻게 판단하는가

사용량, CPU사용량, 메모리 사용량으로 수용량을 정할 수 있다.

어느 서버에 내 계정이 있는데 다른 서버로 옮겨달라.

이건 독립적인 것으로 데이터베이스와 다르다.

유니티 네트워크 프로그래밍

출판사 리뷰

나도 온라인 게임을 만들 수 있을까?

온라인 게임은 누구나 쉽게 만들 수 있다. 단, 오프라인 게임과 온라인 게임의 게임 디자인 차이를 이해해야 한다. ‘오프라인 게임을 만들고 네트워크 기능만 추가하면 온라인 게임’이라는 생각은 망하는 지름길이다. 각 장에서는 ‘만들고자 하는 게임의 게임 디자인을 어떻게 할 것인가’부터 시작한다. 그리고 프로그램을 차례대로 만들어가며 게임 제작 과정을 설명한다. 설명을 따라가면 온라인 게임 고유의 게임 디자인 방법과 게임 디자인을 ‘왜 그렇게 해야만 하는지’를 배울 수 있다.

네트워크 프로그래밍은 처음? 기초부터 활용하는 방법까지!

온라인 게임을 만들 때 데이터 통신의 기초 지식은 필수이므로 피해갈 수 없다. ‘통신=어렵다’는 선입관을 버릴 수 있도록 패킷, IP 주소, TCP, UDP, 지연, 대역 등 기본 사항을 차근차근 단계별로 살펴본다. 데이터를 주고받는 과정을 이해한 다음에는 이와 같은 기초 지식이 게임 디자인에 따라 어떤 방식으로 활용되는지 설명한다.

게임 디자인에 따라 통신 방식이 달라진다.

게임을 제작할 때는 게임 디자인을 가장 먼저 결정해야 한다. 게임 디자인을 결정할 때는 전체 프로세스는 어떻게 되는가? 플레이는 어떻게 진행되는가? 무엇을 언제 통신해야 하는가? 와 같은 큰 그림을 생각한 뒤, 캐릭터는 어떻게 이동하는가? 액션의 규칙은 어떻게 정할 것인가? 아이템은 어떻게 관리할 것인가? 와 같이 작은 그림을 완성해간다. 결정한 게임 디자인에 맞게 통신 방식을 적용한다. 온라인 틱택토(턴 대전 방식), 액션 가위바위보(통신 지연 해결), 블록 깨기(키 입력 동기화), 모형 정원 커뮤니케이션 게임(비동기 통신), 멀티 플레이어 액션 게임(매칭 서버)이라는 5가지 게임 디자인을 통해 다양한 통신 방식을 설명한다.

[이 책의 대상 독자]

신입/초보 서버 개발자, 온라인 게임 개발의 기초를 배우고 싶은 학생

신입/초보 온라인 게임 기획자(게임 디자인 및 게임 운영 방식에 대한 기초 학습 가능)

[예제소스 다운로드]

https://github.com/gilbutITbook/006772

[지은이 서문]

지은이 : 가와다 마사토시

1993년 세가(SEGA) 사에서 ‘버추얼 파이터’가 발매되었습니다. 그때 텔레비전으로 이 게임을 처음 봤을 때, “어떻게 이런 게임을 만들 수 있을까!”라고 감탄했습니다. 그리고 그 기술을 배우기 위해 게임 업계에 발을 들여놓게 되었습니다. 당시에는 3D 폴리곤 게임을 만드는 기술이 기업에만 있었고, 지금처럼 참고할 책이 많은 환경도 아니었기 때문입니다. 그로부터 20년쯤 지난 오늘날 3D 게임은 게임 프로그래머에게 당연한 기술이 되었습니다.

2000년에 DirectX가 버전 8로 올라가면서, 3D 표현을 향상시키는 셰이더(shader)가 등장했습니다. 당시 셰이더 프로그래밍을 할 수 있는 사람은 몇 안 되는 기술자뿐이었습니다. 관련 서적도 외국어 책이 몇 권 있는 정도라서 예제 프로그램으로 학습하는 수밖에 없었습니다. 하지만 지금은 셰이더를 다룰 수 있는 프로그래머가 많아졌고, 셰이더를 사용할 수 없는 프로그래머는 시대에 뒤처지고 있습니다.

요즘은 네트워크 인프라가 갖추어져 게임에서도 온라인을 지원합니다. PC, 스마트폰, 가정용 게임기, 휴대용 게임기 등 거의 모든 게임 환경에 네트워크 기능이 있습니다. 환경이 변하면서 온라인이 지원되는 게임을 이제는 당연하게 여기게 되었습니다. 언제 어디서나 온라인 게임을 즐길 수 있게 된 것입니다. 가정용 게임기에서는 초고속 인터넷을 이용한 대규모 온라인 게임도 가능해졌습니다.

이처럼 최근 게임에서는 온라인이 반드시 지원돼야 하지만, 프로그래머 관점에서 보면 기초적인 네트워크 프로그램을 다룰 수 있는 프로그래머는 있어도 온라인 게임 자체를 제작할 수 있는 프로그래머는 극히 적습니다. 비디오 게임이 3D로 전환하기 시작한 무렵이나 셰이더가 등장하기 시작한 무렵처럼 현재 온라인 게임을 만들 수 있는 프로그래머는 여전히 모자란 상황입니다.

3D 그래픽과 셰이더가 걸어온 역사는 네트워크에서도 다시 반복될 것입니다. 온라인 게임을 제작하는 능력이 당연한 시대가 옵니다. 현재는 아직 온라인 게임을 다룬 책이 많지 않습니다. 이 책이 그 부족함을 채우는 데 도움이 되면 좋겠습니다.

[옮긴이 서문]

옮긴이 : 김성재

지은이가 책에서 지적한 것처럼 저 역시 이 책을 읽기 전까지는 오프라인 게임에 통신 기능을 더

하고 조금만 수정하면 온라인 게임이 만들어지는지 알고 있었습니다. 통신에 관해서는 프로그래

밍을 배울 때 한 번씩 만들어보는 채팅 애플리케이션 정도의 네트워크 지식밖에 없었기에 떠올릴

수 있는 무지한 발상이었지요. 온라인 게임을 만들려면 여러 사람, 아니 단둘이 플레이하는 게임

에서조차 얼마나 많은 것을 고려해야 하는지 이 책을 통해서 이제 조금 알게 된 느낌입니다.

이 책의 큰 특징은 책 제목에서도 알 수 있듯이 네트워크 게임 개발에 꼭 필요한 지식을 기초부터

튼튼하게 쌓아 올릴 수 있다는 점입니다. 당연히 유니티를 기반으로 설명되어 있지만, 꼭 유니티

에만 적용되는 내용이 아니라 온라인 게임을 개발하고자 하는 모든 분에게 도움이 될 만한 내용입

니다.

온라인 게임이 가진 근본적인 문제들을 이야기하면서 왜 온라인 게임은 게임 디자인 단계부터 달

라야 하는지, 어떤 정보를 주고받아야 하는지, 어떻게 주고받아야 하는지를 비교적 통신 사양이

간단한 게임에서 시작하여 꽤 복잡한 게임까지 예를 들어 자세히 설명하고 있습니다. 먼저 학습해

본 학습자의 입장에서 볼 때 내용이 쉽지는 않지만, 반복해서 본다면 분명히 본격적인 온라인 게

임을 개발하는 데 든든한 기반이 되어 줄 것이라고 생각합니다.

드디어 연이어 작업한 세 권의 유니티 게임 프로그래밍 책 중 마지막 권이 끝났습니다. 한동안 이

책과 함께 했던 탓인지 요즘은 게임을 할 때 “여기서는 이런 정보를 주고받고 있겠지?” “어떤 정보

가 동기화되고 있을까?”라고 상상하는 버릇이 생겼습니다. 이 책을 통해 독자 여러분이 하나라도

필요한 지식을 얻으실 수 있다면 옮긴이로서 가장 큰 기쁨이 될 것입니다. 마지막으로 번역 작업

내내 제 길벗이 되어주신 이원휘 편집자님께 깊이 감사드립니다. 함께 일하면서 많이 배울 수 있

었습니다!

[Unity3D] UNet Tutorial (8) – 네트워크 매니저(Network Manager)

네트워크 매니저(Network Manager)

이전 섹션들에서는 유니티 네트워크에서 오브젝트를 스폰하는 법, 원격 액션을 주고 받는 법과 멤버 변수의 값을 동기화하는 SyncVar의 사용법에 대해서 알아보았고, 그 와중에 사용되는 네트워크 매니저는 기본적인 네트워크 매니저를 사용하고, 서버 열기와 클라이언트의 연결을 간단히 하기 위해서 유니티 네트워크에서 기본적으로 제공하는 Network Manager HUD를 사용해왔다.

유넷에서 제공하는 기본 네크워크 매니저와 네트워크 매니저 HUD

이렇게 기본적으로 제공되는 매니저와 HUD는 매우 기본적인 유넷 서버 열기와 클라이언트의 접속 기능을 제공하고 간단한 UI를 통해 서버를 열고 클라이언트로서 서버에 접속하게 할 수 있게 해준다.

Network Manager HUD를 사용하고 게임을 플레이하면 이러한 UI들을 출력해서 손쉽게 서버를 열고 접속할 수 있게 만들어준다.

기본적으로 제공되는 매니저와 HUD는 가볍고 손쉽게 유넷을 테스트할 수 있게 도와주지만, 게임 제작자의 경우, 간단한 테스트를 넘어서 자신이 원하는 기능을 구현하고자 할 것이다. 그렇기 때문에 이번 세션에서는 네트워크 매니저에서 서버와 호스트를 여는 기능과 열린 서버와 호스트에 클라이언트로서 접속하는 기능을 유니티 네트워크가 제공하는 HUD를 사용하지 않고 구현하는 방법을 알아볼 것이다.

네트워크 매니저에서 서버 & 호스트 열기와 클라이언트로 접속하는 기능의 구현

네트워크 매니저를 커스터마이즈하기 위해서는 우선 클래스를 하나 만들어서, Network Manager를 상속받아야 한다.

using UnityEngine.Networking;

public class CustomUNetManager : NetworkManager

{

}

우선은 간단하게 네트워크 매니저 HUD가 제공하는 기능을 직접 구현해보도록 하자.

유니티가 제공하는 기본 Ui를 이용해서 위의 이미지와 같이 버튼 세 개를 만들어 보자. 그리고 다음의 코드를 작성한 이후에 각 버튼과 매칭을 시켜주면 된다.

using UnityEngine.Networking;

public class CustomUNetManager : NetworkManager

{

public void OpenServer()

{

StartServer();

}

public void OpenHost()

{

StartHost();

}

public void ConnectClientToServer()

{

StartClient();

}

}

위의 예시 코드에서 StartServer() 함수는 서버를 시작하는 것, StartHost() 함수는 호스트로서 서버와 클라이언트를 동시에 시작하는 것, StartClient() 함수는 클라이언트를 시작하고 서버에 연결하는 역할을 한다.

서버와 클라이언트, 호스트에 대한 설명은 유넷 튜토리얼 2번 섹션인 UNet Tutorial (2) – 간단한 개념에서 이야기했었다.

그 다음으로는 서버가 제대로 열리고 클라이언트가 제대로 접속되었는지 확인하기 위한 코드들을 작성해보자. 다음의 코드들은 CustomUNetManager 클래스 내에 작성되어야 한다.

public override void OnStartServer()

{

base.OnStartServer();

Debug.Log(“[Server]Start Server”);

}

StartServer() 함수를 호출해서 서버가 정상적으로 시작된 이후에 호출될 콜백 함수이다.

public override void OnStartHost()

{

base.OnStartHost();

Debug.Log(“[Host]Start Host”);

}

StartHost() 함수를 호출해서 호스트가 정상적으로 시작된 이후에 호출될 콜백 함수이다.

public override void OnStartClient(NetworkClient client)

{

base.OnStartClient(client);

Debug.Log(“[Client]Start Client”);

}

StartClient() 함수를 호출해서 클라이언트가 정상적으로 시작된 이후에 호출될 콜백 함수이다.

public override void OnServerConnect(NetworkConnection conn)

{

base.OnServerConnect(conn);

Debug.Log(“[Client]Connect Server Sucess.”);

}

서버에 클라이언트가 연결되었을 때, 서버에서 호출될 콜백 함수이다.

public override void OnClientConnect(NetworkConnection conn)

{

base.OnClientConnect(conn);

Debug.Log(“[Server]Connected Client.”);

}

서버에 클라이언트가 연결되었을 때, 클라이언트에서 호출될 콜백 함수이다.

네트워크 매니저에 포함된 콜백 함수들은 위의 5가지 이 외에도 여러 가지가 있지만, 이것에 대한 설명은 다른 섹션에서 진행할 것이다.

위의 코드를 모두 추가한 뒤에 빌드를 해서 서버나 호스트를 열고 클라이언트를 접속시켜보면 서버와 호스트, 클라이언트가 접속되는지 확인할 수 있을 것이다.

서버를 열고 클라이언트를 접속시켰을 때의 로그

호스트를 열고 클라이언트를 접속시켰을 때의 로그

클라이언트가 서버나 호스트에 연결되었을 때의 로그

서버 주소와 포트 설정하기

위의 예시에서는 같은 컴퓨터에 열린 로컬 서버에 클라이언트가 접속했다. 하지만 실제의 네트워크 게임에서는 서버와 클라이언트가 실행되는 컴퓨터가 다르기 때문에, 접속하고자 하는 서버의 IP와 Port를 지정해주어야 한다. 이번에는 사용자로부터 서버의 IP 주소와 Port를 입력받기 위한 Input Field를 만들어보자.

Input Field를 모두 추가한 이후에 아래와 같이 코드를 수정하고 Input Field들을 넣어주면 된다.

using UnityEngine;

using UnityEngine.Networking;

using UnityEngine.UI;

public class CustomUNetManager : NetworkManager

{

[SerializeField]

InputField ipInputField;

[SerializeField]

InputField portInputField;

public void OpenServer()

{

networkPort = int.Parse(portInputField.text);

StartServer();

}

public void OpenHost()

{

networkPort = int.Parse(portInputField.text);

StartHost();

}

public void ConnectClientToServer()

{

networkAddress = ipInputField.text;

networkPort = int.Parse(portInputField.text);

StartClient();

} }

네트워크 매니저 테스트를 위한 예제는 아래의 첨부파일을 통해 받아볼 수 있다.

NetworkManagerTest.unitypackage 다운로드

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

[투네이션] [Patreon] [디스코드 채널]

반응형

[Unity] Physics.Simulate를 이용한 네트워크 동기화

들어가며

네트워크 게임에서 서버와 클라이언트간의 네트워크 전송 지연(transmission delay)은 이 세상에 물리 법칙이 적용되는한 피할 수 없는 사실이다. 플레이어가 액션을 취한 뒤 서버의 시뮬레이션을 거쳐 다른 플레이어들에게 전파되기 까지는 항상 지연이 수반 된다.

그림1

위 그림에서 클라이언트 A에서 전송한 패킷이 서버에서 처리를 거쳐 클라이언트 B에게 전달 되기 까지 100ms가 필요했다. 따라서 클라이언트 B에서 볼 수 있는 클라이언트 A의 최근 모습은 아무리 빨라도 100ms 이전의 모습 밖에는 볼 수 없다.

다시 한번 말하지만 네트워크 지연은 절대 피할 수 없는 물리적 한계이고, 길다면 길고 짧다면 짦은 온라인 게임의 역사속에 이 문제를 해결하기 위한 많은 방법들이 제시되었다. 이번 포스트에서는 이런 전송 지연을 ‘클라이언트의 예측’으로 해결하는 이론적 방법에 대해서 알아 보고, 이를 유니티의 Physics.Simulate를 이용해 쉽게 구현 해보도록 하겠다.

클라이언트 측 예측

요즘 일반적인 게임은 초당 60프레임을 제공한다. 계산을 편하게 하기위해 서버도 동일한게 초당 60프레임으로 시뮬레이션을 돌릴 수 있다고 가정하고 아래 그림 처럼 초당 60프레임에서 서버가 플레이어의 점프를 시뮬레이션 한다고 상상해보자.

그림2

위 [그림2]에서 사각형 박스 하나는 프레임 하나를 나타내고, 박스 안의 숫자는 점프한 플레이어의 높이 픽셀 좌표다. 서버는 한 프레임마다 플레이어를 1픽셀씩 움직이지만 4프레임마다 한번만 상태 패킷을 보내고 있다. 클라이언트가 상태 패킷을 수신하면 플레이어의 높이를 갱신하는데 한번 갱신한 이후 다음번 상태 패킷이 오는 4프레임 동안 같은 위치의 픽셀에 플레이어를 그리고 있기 때문에 클라이언트의 GPU는 초당 60프레임으로 쉴새 없이 일하고 있지만 유저가 보는 화면은 고작 초당 15프레임으로 돌아가는 셈이다. 비싼 그래픽 카드를 샀지만 겨우 15프레임이라니 허탈하지 않을 수 없다.

이런 문제는 빠른 조작감과 정확한 판정을 요구하는 FPS 게임에서 특히 문제가 된다. 전송 레이턴시 때문에 조작감이 떨어지며 다른 플레이어를 조준하기도 어려워 진다. 정확한 현재 시점의 상대 플레이어 위치가 확보되지 않으면 조준 사격의 의미가 없다. 나는 분명히 정확히 상대 방의 머리에 조준해 사격 했지만 사실 지금 나에게 랜더링 되고 있는 상대의 모습은 100ms 이전의 상태로 서버에서는 이미 이동한 것으로 시뮬레이션 되어 허공에 총을 쏜것 처럼 판정한다.

이런 문제를 해결하기 위해 사용할 수 있는 좋은 방법 중에 하나가 ‘클라이언트 측 예측’이다. 예측이란 서버에서 조금 오래 된 상태를 받아 그것을 기반으로 현재 상태라고 추측한 상태와 가장 가깝게 맞춘 뒤 플레이어게 보여주는 것이다. 이를 다른 개발 서적에서는 외삽(extrapolation)법이라고 하고 외삽을 활용하는 기법을 클라이언트 측 예측이라 한다.

보외법(補外法) 또는 외삽(外揷, extrapolation)은 수학에서 원래의 관찰 범위를 넘어서서 다른 변수와의 관계에 기초하여 변수의 값을 추정하는 과정이다.

출처 : https://ko.wikipedia.org/wiki/%EB%B3%B4%EC%99%B8%EB%B2%95

과거의 상태를 이용해 현재 상태를 예측하려면 아래 두가지 요소가 필수적이다.

클라이언트는 최종 갱신 내역이 얼마나 오래전 것인지 알 수 있어야 한다.(전송 지연이 얼마인지 알아야 한다) 클라이언트와 서버는 동일한 시뮬레이션 코드를 실행할 수 있어야 한다.

전송 지연(transmission delay) 측정

클라이언트 예측을 위해서 가장 먼저 필요한 정보는 전송이 얼마나 지연이 되고 있는가 하는 것이다. 위의 [그림1]에서 클라이언트 A는 자신이 패킷을 보내고 다시 응답을 수신하기 전까지 100ms의 시간이 걸렸다. 이렇게 패킷을 보내고 받을 때 까지 걸리는 시간을 Round Trip Time(이하 RTT)라고 하며, 네트워크에서 한쪽 방향으로 패킷이 전송되는데 걸리는 시간은 왕복 시간의 절반만큼 걸린다고 가정하면 아래와 같이 지연 시간을 구할 수 있다. (물론 왕복 시간의 절반을 취한다는 것은 어디까지나 추정일 뿐이다. 보내고 받는 양쪽 방향의 네트워크 속도가 꼭 같다고 할 수는 없지만 1/2정도면 대부분 실시간 게임 용도로 충분하다 하겠다)

클라이언트는 최종 갱신 내역이 얼마나 오래전 것인지 알 수 있어야 한다.

전송 지연 시간 = 1/2 * RTT

RTT를 측정하기 위해 단순히 서버에서 타임 스탬프를 찍어 클라이언트에게 보내고, 클라이언트는 로컬 타임 스탬프와 비교하여 경과시간을 측정하는 방식은 원격에 있는 장비들의 시계를 동일하게 동기화하는 것은 불가능한 이유 때문에 허용 범위 이상의 오차를 발생 시킬 수 있는 정확하지 못한 방법이다. 보다 정확하게 RTT를 측정하기 위해서는 클라이언트에서 서버로 타임 스탬프를 찍어 전송하고 전송한 타임스탬프 그대로 서버로 부터 돌려 받아 두 시간의 차를 이용해 왕복 시간을 측정해야 한다.

클라이언트 예측

서버의 시뮬레이션이 얼마나 늦게 클라이언트에서 도착하는지 알았다면 이제 본격적으로 클라이언트 예측에 대해 알아 볼 시간이다. 현실적인 전송 지연을 고려 했을 때 플레이어가 서버의 시뮬레이션 결과를 원격에서 랜더링 할 수 있는 시간은 최소한 서버의 진짜 상태 보다 1/2 RTT 시간만큰 뒤쳐지게 된다. 이렇게 클라이언트는 상태 갱신 패킷을 받은 시점에서 그 갱신 내역이 1/2 RTT만큼 오래 되었다는 것을 알고 있다.

이제 이 문제를 어떻게 해결해야 할까? 간단하다. 과거의 상태를 현재 상태로 당기려면 클라이언트가 과거의 상태로 부터 시뮬레이션을 1/2 RTT 시간만큼 더 진행 시킨뒤 화면에 보여주면 된다. 이런 방식으로 서버의 진짜 현재 게임 상태에 훨씬 근접한 상태로 예측하여 플레이어에게 보여 줄 수 있다. 이 때 필요한 것이 우리가 위에서 살펴 봤던 클라이언트 예측의 두번째 조건이다.

클라이언트와 서버는 동일한 시뮬레이션 코드를 실행할 수 있어야 한다.

과거의 상태로 부터 시뮬레이션을 1/2 RTT 시간만큼 더 진행 한다.

게임 시뮬레이션의 구현은 여러 부분에서 결정론적이므로 서버와 클라이언트가 같은 시뮬레이션 로직을 실행한다면 대체로 같은 시뮬레이션 결과를 얻는다. 총알이 발사되어 공기 중에 날아갈 때 서버와 클라이언트에서 같은 방식으로 날아 갈 것이고, 공이 벽이나 바닥에 튕길 때도 같은 물리 법칙으로 동작할 것이다. 클라이언트와 서버가 AI 로직도 같이 공유한다면 AI가 제어하는 객체 또한 서버와 동기화를 맞추어 시뮬레이션 할 수 있다. 심지어 랜덤 조차도 클라이언트와 서버가 같은 난수를 생성 하도록할 수 있다.

데드 레커닝(Dead Reckoning)

앞에서 대부분의 게임 시뮬레이션의 구현은 결정론적이기 때문에 예측이 가능하다고 이야기 했다. 하지만 단 한 종류의 객체는 전적으로 비결정론적이며 완벽한 예측 시뮬레이션이 절대 불가능한것이 있다. 바로 플레이어다. 클라이언트 프로그램이 원격지의 플레이어가 무슨 돌발 행동을 할지 갑자기 어디로 이동할지 예측하는 것은 불가능 하다. 이때문에 플레이어가 조작하는 객체에 대해서의 예측은 항상 오차를 수반한다. 클라이언트가 취할 수 있는 최선책은 기존 정보를 토대로 추정하고 그 추정치를 서버로 부터 받은 신규 정보로 보정해 나가는 것이다.

플레이어의 돌발행동은 예측이 불가능 하다 = 예측의 오차가 발생한다

네트워크 게임에서 데드 레커닝(dead reckoning)이란 대상체가 현재 하는 행동을 지속할 것이란 가정하에 대상체의 다음 행동을 예측하는 기법이다. 플레이어가 지금 뛰고 있다면 계속 같은 방향으로 뛸것을 가정하며, 오른쪽으로 회전하고 있다면 같은 방향으로 계속 회전할 것으로 가정한다.

데드 레커닝 : 현재 행동을 지속할 것이란 가정하에 다음 행동을 예측하는 기법

원격 플레이어가 예상치 못한 동작을 하면 클라이언트 쪽 시뮬레이션이 서버의 상태와 조금 씩 달라지는데, 이렇게 달라진 부분들은 최대한 빠른 시간 내에 수정되어야 한다.

예를 들어 1/2 RTT가 50ms이고 프레임레이트가 초당 60프레임이며 10프레임당 한번씩 상태 패킷을 보낸다고 가정하자. 플레이어가 오른쪽으로 1프레임당 1픽셀씩 이동하다 동기화 패킷을 보내고 15프레임 때 갑자기 위쪽으로 이동 방향을 바꾼다면 이것을 시뮬레이션 하는 다른 플레이어는 새로운 상태 패킷이 도착하기 전 5프레임 동안 추가로 오른쪽으로 이동을 더 하게 되는 오차가 발생하게 된다.

데드 레커닝은 서버에서 모든 정버를 완벽하게 확보한뒤 수행되는 방식이 아니다. 따라서 보수적 알고리즘으로 분류하지 않으며 낙관적 알고리즘(optimistic algorithm)으로 분류한다. 이런 방식은 대부분 비슷한 추정 상태를 얻을 수 있지만, 가끔은 완전히 틀린 결과를 도출하게 되어 수정이 필요하게 된다.

클라이언트가 자신의 시뮬레이션이 부정확하다는 것을 탐지하게 되면 아래 세가지 방법중에 하나를 선택하여 상태를 동기화할 수 있다.

즉시 상태 갱신 : 그냥 새 상태를 즉시 반영해 버린다. 플레이어는 객체들이 갑자기 텔레포트하는 것을 보게 될것이지만 그래도 부정확한것 보다는 낫다고 판단할 때 사용하는 방법이다. 하지만 상태는 1/2 RTT만큼 뒤쳐져 있으므로 클라이언트는 반영한 시점에서 다시 1/2 RTT만큼 시뮬레이션을 진행해야 한다 .

: 그냥 새 상태를 즉시 반영해 버린다. 플레이어는 객체들이 갑자기 텔레포트하는 것을 보게 될것이지만 그래도 부정확한것 보다는 낫다고 판단할 때 사용하는 방법이다. 하지만 상태는 1/2 RTT만큼 뒤쳐져 있으므로 . 보간 : 클라이언트 측 보간 법을 이용하여 잘못 예측한 상태에서 출발해 몇 프레임에 걸쳐 새 상태로 보간해 나간다. 이를 위해 위치, 회전등 부정확한 각 상태 변수에 대한 보정용 델타 값을 계산해 저장해 두었다가 매 프레임마다 점진적으로 적용해 나간다.

: 클라이언트 측 보간 법을 이용하여 잘못 예측한 상태에서 출발해 몇 프레임에 걸쳐 새 상태로 보간해 나간다. 이를 위해 위치, 회전등 부정확한 각 상태 변수에 대한 보정용 델타 값을 계산해 저장해 두었다가 매 프레임마다 점진적으로 적용해 나간다. 상태 변수의 도함수를 유도하여 적용 : 멈추어 있던 상태의 객체가 갑자기 속력을 내는 경우, 보간하더라도 어색해 보일 수 있다. 플레이어가 눈치채지 못하게 하려면 속력에 대해 도함수를 유도하여 가속도를 구하는 식으로 시뮬레이션을 섬세하게 제어해야 한다. 수학적으로 복잡하고 골치 아프지만 자연스럽게 보이게 하는데 가장 좋은 방법이다.

Unity를 이용해 구현해보기

기본적인 이론들을 간단하게나마 살펴 보았으니 이제 실제 유니티 엔진 위에서 구현하는 예제를 살펴 보도록하자. 프로젝트의 전체 소스 코드는 [여기]에 업로드 되어있다.

앞에서 예측을 하기 위해서 필요한 조건은 ‘전송 지연 시간 측정’과 ‘동일 시뮬레이션’이었다. 이제 부터 유니티 엔진에서 제공하는 기능을 이용해 손쉽게 구현하는 방법에 대해 살펴 보도록 하자.

UnityEngine.Ping – 전송 지연 시간

전송 지연 시간을 측정하기 위해 클라이언트에서 타임 스탬프를 찍어 서버로 전송하고 서버로 부터 그대로 돌려 받은 타임 스탬프를 이용해 RTT를 구하는 방법도 있지만 더 간단하게 Ping 클래스를 이용하는 방법도 있다. Ping 객체는 생성자의 인자로 주어진 dot 표현 방식의 ip 주소에 ping 메시지를 보내 응답 시간을 저장한다. 비동기로 동작하며 isDone 변수를 이용해 완료 여부를 판단 할 수 있다.

아래는 Ping 클래스의 사용법을 간단히 표현한 샘플 코드이다. 전체 코드는 [Assets/Gamnet/Script/Client/Session.cs]의 132라인에서 부터 살펴 보면 된다.

using UnityEngine; class Session { private Ping ping; public int ping_time { get; private set; } // 일정 주기마다 지속적으로 호출 void PingTimer() { if (null == ping) { // ping time 측정 시작 ping = new Ping(connector.endpoint.Address.ToString()); } else { // ping time 측정 완료 if (true == ping.isDone) { ping_time = ping.time; ping = null; } } } }

요즘에는 ping 테스트를 할 수 있는 ICMP를 차단하는 방화벽도 늘어나고 있는 추세다. 만일 Ping이 정상적으로 동작하지 않는다면 어쩔 수 없이 타임 스탬프를 찍어서 주고 받는 방식을 이용해야 한다. 위에서 공유된 소스코드에 직접 RTT를 측정하는 코드 샘플도 추가 되어 있으므로 살펴 보도록 하자.

Physics.Simulate – 동일 시뮬레이션

지연 시간 측정 외 나머지 필요 조건은 서버와 클라이언트가 같은 시뮬레이션 로직을 돌릴수 있어야 한다는 것이었다. 이는 클라이언트 뿐만 아니라 서버도 유니티 엔진 기반으로 만드는 것으로 해결하도록 하겠다. 유니티를 이용해 서버 빌드를 하는 방법에 관한 방법은 [여기]에서 찾아 볼 수 있다.

서버와 클라이언트가 동일한 시뮬레이션을 돌리는 것에 추가하여 클라이언트가 받은 서버의 상태는 현재로 부터 1/2 RTT 만큼 이전의 상태라고 했었다. 클라이언트는 그만큼 더 서버와 동일한 시뮬레이션을 진행한 후 플레이어게게 보여주어야 한다. 실제 우리가 이런 시뮬레이션을 코드로 직접 만들게 된다면 방향과 현재 속도, 그리고 가속도, 충돌에 따른 반사, 심지어 중력과 물체의 무게, 탄력에 따른 반사 정도를 모두 만들어 줘야 할 것이다. 하지만 유니티에서는 이런 모든 문제를 Physics.Simulate가 해결해 준다.

public static void Simulate(float step /* 시뮬레이션을 앞으로 진행 시키는 시간 */);

씬(scene)에서 step에 지정된 시간만큼 물리 운동을 시뮬레이션한다. Physics.autoSimulation이 꺼져 있을 때 수동으로 물리 운동을 시뮬레이션하려면 이것을 호출하면 된다. 시뮬레이션에는 충돌 감지, 리지드바디 및 조인트 통합, 물리적 콜백(접촉, 트리거 및 조인트) 처리의 모든것을 포함한다. 참고로 Physics.Simulate를 호출해도 FixedUpdate는 호출 되지 않는다. MonoBehaviour.FixedUpdate 는 자동 시뮬레이션의 활성화 여부, Physics.Simulate를 호출과 관계 없이, Time.fixedDeltaTime 에서 정의한 속도로 계속 호출된다.

NOTE 1 : 만일 프레임 레이트에 종속적인 값(예: Time.deltaTime )을 물리 엔진에 전달하면 프레임 속도 변동으로 인해 예측 할 수 없는 시뮬레이션 결과를 얻게 된다. 결정론적(고정적인) 물리 시뮬레이션 결과를 얻기 위해서는 호출 할 때 마다 고정 값을 Physics.Simulate에 전달해야 한다.

NOTE 2 : 일반적으로 step작은 양수여야 한다. 만일 0.03보다 큰 값을 사용하면 부정확한 결과가 생성될 수 있다.

아래 코드 샘플은 예제 프로젝트에서 사용된 Simulate 코드를 단순하게 편집한 내용이다. 전체 소스코드는 [여기]에서 확인 할 수 있다.

public class Main : MonoBehaviour { // 서버로 부터 상태 메시지를 받음 public void OnRecv_SyncBall_Ntf(Packet.MsgSvrCli_SyncBall_Ntf ntf) { // …코드 생략… Ball ball = go.GetComponent(); if (null == ball) { return; } // 예측 시뮬레이션을 하기 위한 서버의 상태값 동기화 ball.transform.localPosition = ntf.ball.localPosition; ball.transform.rotation = ntf.ball.rotation; ball.rigidBody.velocity = ntf.ball.velocity; // Network.NetworkDelay : 네트워크 지연 시간(초) deltaTime += (float)Network.NetworkDelay / 1000; while (deltaTime >= Time.fixedDeltaTime) { deltaTime -= Time.fixedDeltaTime; physicsScene.Simulate(Time.fixedDeltaTime); } } }

위 코드에서 핵심은 먼저 14라인에서 클라이언트 시뮬레이션 예측을 위해 서버로 부터 받은 상태 값들을 로컬 객체에 저장(동기화)하는 부분이다. 이 예제 프로젝트에서는 예측을 위해 현재 위치, 회전, 이동 방향과 속도를 사용하고 있다. 다음으로 눈여겨 보아야 할 부분은 19라인에서 부터 시작하는 예측 시뮬레이션이다. 클라이언트는 상태 동기화 패킷을 수신 후 네트워크 지연 시간만큼 시뮬레이션을 강제 진행 시킨다.

Tip. Physics.Simulate는 씬에 포함된 모든 객체들에 대해 물리 시뮬레이션을 진행하므로 만일 특정 객체에 대해서만 물리 시뮬레이션을 사용하고 싶다면 별도의 씬을 생성하여 대상 객체를 해당 씬에서 시뮬레이션 하도록 만들어야 한다. 여기에 사용된 예제 프로젝트에서는 편의를 위해 서버와 클라이언트가 하나의 프로세스 상에서 구동되었기 때문에 클라이언트 예측을 위해 시뮬레이션을 강제 진행하면 서버도 그만큼 빨라지는 버그가 있었다. 이를 해결하기 위해 클라이언트와 서버를 별도의 씬에서 구동하도록 만들어야만 했다.

// https://docs.unity3d.com/2019.1/Documentation/ScriptReference/PhysicsScene.html private PhysicsScene physicsScene; // 물리 씬..이라는데 공식 문서에서도 설명이 부족함 void Start() { CreateSceneParameters csp = new CreateSceneParameters(LocalPhysicsMode.Physics3D); Scene scene = SceneManager.CreateScene(“LocalPhysicsScene”, csp); // 동적으로 씬 생성 physicsScene = scene.GetPhysicsScene(); SceneManager.MoveGameObjectToScene(gameObject, scene); // 객체를 신규 씬으로 이동 // 코드 생략…

그리고 별도로 분리했던 물리 씬에서는 자동으로 시뮬레이션이 진행 되지 않았으므로 Update함수 내에서 명시적으로 Simulate를 호출해주어야만 했다(이 부분은 내가 실수를 해서 그런건지 왜 이런건지 확실히 알아 보지 못했다)

결과

결과는 아래 동영상과 같다. 왼쪽의 검은 배경은 클라이언트 측 예측이고 오른쪽 밝은 배경은 서버측 시뮬레이션이다. 서버와 클라이언트는 500ms의 전송 지연 시간을 가지며 동영상의 처음 부분 부터 7초까지는 지연된 화면을 그대로 플레이하고 있다. 동영상의 7초 부터 서버에서 받은 상태 값으로 부터 500ms 더 시뮬레이션을 진행해 보여주고 있다. 서버와 클라이언트의 공이 동일하게 움직이는 것을 볼 수 있을 것이다. 블록이 파괴 이벤트 패킷에는 예측을 진행하지 않으므로 왼쪽 화면에서는 공이 지나가고 500ms 뒤에 블록이 사라지는 것을 확인 할 수 있다.

만일 직접 플레이를 해보고 싶다면 [여기]에서 프로젝트를 다운 받아 유니티로 구동 시켜 보면 된다.

부록 1. 같이 읽으면 좋은 글

키워드에 대한 정보 유니티 네트워크

다음은 Bing에서 유니티 네트워크 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.

이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!

사람들이 주제에 대해 자주 검색하는 키워드 RPC는 진짜 어마어마하게 중요합니다ㅣ포톤PUN2

  • 포톤2
  • RPC

RPC는 #진짜 #어마어마하게 #중요합니다ㅣ포톤PUN2


YouTube에서 유니티 네트워크 주제의 다른 동영상 보기

주제에 대한 기사를 시청해 주셔서 감사합니다 RPC는 진짜 어마어마하게 중요합니다ㅣ포톤PUN2 | 유니티 네트워크, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.