기말 공지
다음 주 마지막 수업(이론 많을 예정), 17주차 기말 과제 발표
- 6월 24일까지 제출, 6월 26일 발표.
- 발표 5~7분. 설명서 꼭 낼 것.
- 수업 내용을 반영하여 제작하면 가산점.
- 진행하면서 새롭게 알게 된 것 스터디 파일 내면 가산점.
- 유니티나 언리얼 등 게임 엔진과 관련된 너무 많은 기능을 추가하기보다는 네트워크와 데이터베이스 활용에 집중할 것.
- LAN이라고 명시한 이유. AWS 서버를 이용하면 레이턴시가 생기게 된다. 이에 대해 깊게 고민하지는 말 것.
13주차 내용(데드 레커닝.~)부터는 플러스 요소. 이렇게까지 기말 내용을 제출 할 필요는 없음.
방학 때 공부 할 내용을 미리 드리는 것.
네트워크 게임의 기능 추가
동기화 3가지
- 동기 (Sync) & 비동기 (Async)
- 동기 (Synchronous) 입출력, 비동기 (Asynchronous) 입출력, Overlapped
- 동기 & 비동기 (이름이 같다, 추후 학습 예정.)
기획자(화면을 동일하게 맞춘다.), 아티스트와는 개념이 다르다.
완전 동기화 게임 : 두 화면이 완전하게 똑같은 게임, 몹시 힘들다. 레이턴시로 인한 시간 차이로 문제가 생기기 때문.
전송 할 데이터가 몹시 많아진다. 게임에서 중요한 부분들을 고려해서 나눠야 한다.
회사마다 구현 방법이 다르며, 정답이 없다. 매니저를 따로 두는 것이 일반적인 방식.
동기화 할 기능과 그렇지 않은 기능의 고민.
- 중요하지 않은 정보는 서버를 경유하지 않아도 된다.
- 중요한 정보는 완전 동기화.
- 캐릭터의 착장과 같은 경우 애니메이션과 동작까지 영향을 준다.
현실적인 문제로 미래에도 네트워크 게임은 완전 동기화가 불가능 할 것.
캐릭터의 이동과 관해서는 완전히 동기화 할 수 없다.
통신 대역폭에 무언가를 올린다는 것... 통신에 관한 비용은 유저들이 낸다. (통신비)
완전히 동기화하기 위해서 부하를 걸어도
1억을 들여도 시원치 않고,
10억을 들여도 시원치 않으면...
> 돈을 안 들이고 시원치 않게 만드는 게 현실적으로 낫다.
완전 동기화가 불가능하다면 눈속임으로 처리하는 것. 이를 위해 사용하는 것이 Interpolation (보간)
네트워크에서 가장 문제인 것은 인간.
사람의 플레이는 예측이 되지 않는다. 완전 동기화가 사실상 불가능함.
FPS나 MMORPG에서 오브젝트를 배치 할 때는 동기화가 중요하다. 기획자와 타협을 해야 함.
네트워크의 부하가 적다면 데이터를 좀 더 보내도 된다.
하지만 언제나 네트워크 환경을 고려해야 한다.
대한민국에서 보내는 것과 캄보디아에서 보내는 것.
- 대한민국 : 데이터를 많이 송수신하고 보간을 적게 한다.
- 캄보디아 : 데이터를 적게 송수신하고 보간을 많이 한다.
스플라인 보간
- 프레임을 정해서 프레임 수마다 좌표를 송신.
- 수신한 점과 그 전에 수신한 점 사이를 프레임으로 등분하면 지난 프레임의 위치를 구할 수 있음
- 보간으로 구한 좌표는 각 단말에서 일치하지 않지만 큰 차이는 없음.
아이템
발생중, 획득중, 획득 완료, 폐기중, 폐기 완료 등 상태를 정해서 여러 단말이 동시에 조작했을 때를 대비한다.
네트워크의 차이를 커버하기 위한 이펙트나 애니메이션이 필요하다.
패킷 헤더
코딩 자체가 어렵지는 않지만, 복잡해지는 경향이 있다.
설계만 잘 해놓고 챗GPT에게 시켜도 어떻게 완성은 된다.
정작 게임을 만들 때 힘든 것은 자료구조. Dictionary, Queue, Stack. 코딩을 많이 해 봐야 한다.
- Generic (Template)
- Data Structure
- Dictionary
그 외 고려해 두면 좋을 내용
모듈의 결합도
Serializer, Deserializer
서버는 24시간 구동하기 때문에 메모리 관리를 잘 해 주어야 함.
Thread pool을 쓰는 방식으로 최적화 할 수 있다.
멀티 플레이어 게임의 완성
매칭 서버의 개념이 추가.
던전에서 통신이 필요한 정보
- 캐릭터 이동
- HP 동기화
- 몬스터 이동/ 발생 동기화
- 제너레이터가 입은 손상 동기화
- 아이템 획득/ 사용
- 방 이동 동기화
- 소환수 등장 동기화
- 채팅 메세지 송수신
보스전에서 통신이 필요한 정보
- 캐릭터 이동/ 보스 이동/ 공격
- 아이템 사용
- 보상 게임 동기화
- 채팅 메세지 송수신
여러 사람이서 보스를 처치하는 과정에는 다양한 상황이 있다.
보스의 조작이 조금만 달라져도 충돌과 공격의 차이가 발생하는 등... 고려해야 할 부분이 많다.
매칭 서버 (로비 서버)
플레이하고 싶은 사람끼리 연결 해 준다.
게임을 함께 플레이 할 단말의 IP 주소를 몰라도 플레이어끼리 접속할 수 있게 한다.
유니티나 언리얼에서 기본으로 제공한다. (굳이 올인 할 필요는 없다.)
코딩은 복잡하나, 원리는 간단하다.
매칭 서버는 클라이언트가 접속했을 때, 클라이언트의 IP 주소를 알 수 있으므로 방의 참가 상황과 함께 저장 해 두면 게임을 시작할 때 모두에게 IP 주소를 알려줄 수 있다.
매칭서버는 왜 필요한가? (질문)
데이터를 주고 받을 때 조차도 도와줄 기능이 필요하다.
나머지 친구들의 IP를 주고받는 기능을 꼭 넣어주어야 한다.
- 대부분의 게임에서는 네트워크를 하나만 쓰는 것이 아니라, 두 개 이상 쓴다.
- 서버 뿐만 아니라 우리끼리 정보를 주고 받기도 해야 함.
네트워크 토폴로지
컴퓨터나 통신 기기의 접속 형태
- 스타형 (통상적인 형태, 회사가 돈을 씀.) : 아이템 획득/ 사용, 방 이동, 채팅, 보상 획득
- 메시형
- 풀 메시형 (P2P 게임, 군대, 비용이 많이 들음, 유저가 돈을 씀.) : 캐릭터 이동, 몬스터 이동
매칭 완료부터 게임 시작까지
대기 시간을 조정해 플레이어가 불쾌해지지 않게 각 단계를 조정하는 것이 중요하다.
제3의 존재인 Host(방장) 개념의 등장. 자신들끼리 알아서 플레이되게끔 하는 것. 복잡한 구조로 진행이 됨.
방장이 죽으면 게임이 사라져 버리는 문제. 방장이 죽으면 부방장, 다른 플레이어에게 옮기는 작업의 고려 필요.
세션
OSI 7-layer에서도 이야기 했던 개념.
많은 회사에서 세션의 개념을 따로 잡는다.
- 세션 관리 ID : 접속해온 클라이언트에 처음부터 부여된 연속 번호
- 플레이어 ID : 매칭 서버 or 게임 호스트가 될 단말에서 할당
- 플레이어 ID != 세션 관리 ID
난수 생성과 인스턴스화
배경을 똑같이 하는 것은 어려운 일이 아님에도 큰 차이가 난다.
그래픽 디자이너나 기획자가 보기에는 배경이 똑같은 것이 더 눈에 띈다.
각 단말에서 난수의 시드를 일치시키면 발생하는 수치도 일치시킬 수 있다.
게임 시작 전에 송신할 정보
- 각 단말에서 무기를 선택하면 장착할 무기 정보를 던전에 들어가기 전에 통신한다.
- 장착할 무기 정보, 난수 시드 값.
게임을 시작하는 시점에 시드 값을 뿌리면 동일하게 만들 수 있다.
방을 만들어서 동시에 시작하는 게임과 들락날락 할 수 있는 게임이 있을 때,
시드 값을 언제 전달 할 것인지의 시점 고려 필요.
캐릭터가 일제히 이동할 때 등장 위치가 같다면 충돌하지 않게 캐릭터마다 시작 좌표를 조금씩 이동해야 한다.
몬스터의 동기화 방법 또한 게임 디자인에 맞춰서 미리 결정 해 둬야 한다.
- 몬스터의 수가 적고, 위치가 일치하다면 동기화 시킬 것.
- 몬스터의 수가 많고, 위치가 일치하지 않다면 동기화를 과감히 포기할 것.
격투 게임의 예시
격투 게임의 캐릭터는 2개가 아니다.
- 나, 상대 (내 시점)
- 상대, 나 (상대 시점)
총 4개의 캐릭터가 사용된다. 이를 프로그램적으로 리플리(리플리케이션, 복제인간/클론)라고 부른다.
캐릭터의 행동을 캐릭터 소유주가 아닌 관찰자 또한 서버에 요청하게 되면 n배의 요청이 가게 된다.
로컬 캐릭터가 공격 받은 경우만 판정한다. (항상 행동을 실행한 캐릭터의 행동만 서버에 요청하게끔 해야 한다.)
대미지를 주면, 대미지를 받았다는 사실을 호스트에서 모두에게 전달하는 시점에서 동기화 한다.
연타 공격, FPS의 연사와 같은 부분은 과감하게 날려도 된다. 쓸데 없는 낭비이다.
몬스터 제네레이터가 있고, 파괴되었을 때 동기화가 되지 않으면 몬스터가 또 나오는 문제가 생길 수 있음.
보스와 싸우기
보스는 캐릭터로 보는 것이 좋다.
많은 정보량을 사용하지만, 개체 수가 적으므로 신경 써 줘야 한다.
프로그램 작성하기
매칭 서버, 로비 서버를 만드는 것은 소스가 많다.
구현을 포트폴리오로 내는 것은 추천하지 않는다. 들어가는 비용 대비 얻는 효과가 크지 않다.
동기화를 언제, 얼마만큼, 어디까지 할 것인지의 고려가 항상 필요하다.
첨부된 소스파일을 확인하고 방학 동안 공부 해 볼 것.
동기, 비동기 입출력과 중첩
하드디스크에 들어있는 데이터를 읽을 때, 읽는 작업은 운영체제가 진행한다. (권한을 잃는다.)
응용 프로그램 코드와 운영체제 코드는 별개의 동작.
- 동기 (Synchronous) 입출력 : 운영체제의 입출력 작업 대기
- 비동기 (Asynchronous) 입출력 : 입출력 작업 완료를 운영체제가 알려줌 - Notification
- 중첩 (Overlap)
while문이 계속 돌면서 주기적으로 체크하는 것은 낭비이다.
이를 보완하기 위해 Callback, 델리게이트(delegate)를 사용한다.
동기(Synchronous) 입출력
- 응용 프로그램은 입출력 함수를 호출한 후 입출력 작업이 끝날 때 까지 대기한다.
- 입출력 작업이 끝나면 입출력 함수가 리턴하고, 응용 프로그램은 입출력 결과를 처리하거나 다른 작업을 진행한다.
비동기(Asynchronous) 입출력 or 중첩(Overlapped) 입출력
- 응용 프로그램은 입출력 함수를 호출한 후 입출력 작업의 완료 여부와 무관하게 다른 작업을 진행한다.
- 입출력 작업이 끝나면 운영체제는 작업 완료를 응용 프로그램에 알려줌. 응용 프로그램은 하던 작업을 중단하고 작업을 처리한다.
이상적인 게임 서버의 구현
- 가능한 많은 클라이언트가 접속 가능
- 서버는 각 클라이언트의 서비스 요청에 빠르게 반응하며 고속으로 데이터를 전송
- 시스템 자원 사용량을 최소화
이상적인 소켓 입출력 모델의 특징
- 소켓 함수 호출 시 블로킹을 최소화
- 스레드 개수를 일정 수준으로 유지
- CPU 명령 수행과 입출력 작업을 병행
- 유저 모드와 커널 모드 전환 횟수를 최소화
Thread는 굉장히 낭비가 심하고, 의도와 다르게 동작할 수 있다.
싱글스레드 게임 서버
- CPU 개수만큼 프로세스를 띄우는 것이 일반적이다.
멀티스레드 게임 서버
- 프로세스당 로딩해야 하는 게임 정보 (맵 데이터 등)의 용량이 커서 서버 프로세스를 많이 띄우기 곤란할 때 (특히 MMO 서버)
- 서버 한 대의 프로세스가 여러 CPU의 연산량을 동원할 만큼 많은 연산 시
- 코루틴이나 비동기 함수를 쓸 수 없고 디바이스 타임이 발생할 때
- 서버 인스턴스를 서버 기기당 하나만 두어야 할 때
- 서버 다른 방(room 또는 multiplayer session)이 같은 메모리 공간을 엑세스 해야 할 때.
Interative Server (반복 서버) : 여러 클라이언트를 한 번에 하나씩 처리.
- 장점 : 스레드 한 개만으로 구현하므로 시스템 자원의 소모가 적다.
- 단점 : 한 클라이언트의 처리 시간이 길어지면 다른 클라이언트의 대기 시간이 길어진다.
Concurrent Server (병행 서버) : 여러 클라이언트를 동시에 처리.
- 장점 : 한 크라이언트의 처리 시간이 길어지더라도 다른 클라이언트에 영향을 주지 않음.
- 단점 : 스레드를 여러 개 생성하여 구현하므로 시스템 자원의 소모가 많다.
스레드 풀링 (Thread pooling)
- 스레드 개수의 적정화를 달성하기 위한 방안이다.
- 클라이언트마다 각각 스레드를 배정해 줄 때 발생하는 스택 메모리 낭비와 잦은 컨텍스트 스위치 현상을 최소화한다.
스레드는 동접수만큼 만들지 않는다.
인간이 아무리 빠르게 눌러도 CPU의 속도를 따라잡을 수 없다.
적정한 개수의 공중화장실과 이를 필요로 하는 사람들을 연상 해 볼 것.
하나만 지으면 굉장히 대기시간이 길어진다.
여러개 지으면 굉장히 제작 비용이 커진다.
스레드 풀링은 화장실을 5개 만든 이후, 적절히 사용을 분배하는 기법이다.
이벤트 (Event)
멀티스레드 프로그래밍을 위한 또다른 도구
일을 하는 동안 재워두었다가 일을 다 하면 깨우는 형태.
세마포어 (Semaphore)
원하는 개수의 스레드가 자원을 액세스 할 수 있게 지원 (뮤텍스와 임계 영역은 오로지 스레드 1개만 자원을 액세스)
원자 조작 (Atomic operation)
- 뮤텍스나 임계 영역 잠금 없이도 여러 스레드가 안전하게 접근할 수 있는 것을 의미한다.
뮤텍스나 임계 영역은 운영체제에서 제공하는 기능으로, 내부에서는 원자 조작을 활용한다.
파일 입출력과 프로그래밍
무작정 대기는 낭비이다.
반면, 게임 서버는 다루어야 하는 소켓의 개수가 압도적이다.
비용이 많이 드는 스레드를 무작정 사용 할 수는 없기 때문에 새로운 해결책이 필요하다.
블로킹 소켓 (Blocking Socket)
논블로킹 소켓 (Non-Blocking Socket)
블로킹 소켓과 스레드 실행
- accept()
- connect()
- send(), send to()
- recv(), recvfrom()
논블로킹 소켓과 스레드 실행
논블록 소켓의 운영 사례
- 에러가 언제 생기는가.
- 송신 함수에서 would block 리턴 시 : 아무 것도 하지 않았응믈 의미. 송신 함수 호출을 나중에 다시 해 주어야 함.
- 수신 함수에서 would block 리턴 시 : 수신할 데이터가 없음을 의미. 잠시 후 다시 논블록 수신 함수 호출 필요.
- TCP 접속 함수인 connect() 함수에서 would block 리턴 시 : 소켓이 '연결 과정 진행 중인 상태'임을 의미.
실습 진행
using System;
using System.Text;
using System.IO;
namespace Week13
{
// 파일의 상태를 추가
class FileState
{
public byte[] Buffer;
public FileStream File;
}
class Program
{
static void Main(string[] args)
{
//using (FileStream fs
// = new FileStream(@"C:\Windows\System32\Drivers\Etc\HOSTS", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
//{
// // 파일 스트림의 크기만큼 설정
// byte[] buf = new byte[fs.Length];
// //fs.Read(buf, 0, buf.Length);
// fs.BeginRead(buf, 0, buf.Length);
// string txt = Encoding.UTF8.GetString(buf);
// Console.WriteLine(txt);
// Console.ReadLine();
//}
AsyncRead();
}
private static void AsyncRead()
{
FileStream fs
= new FileStream(@"C:\Windows\System32\Drivers\Etc\HOSTS", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
FileState state = new FileState();
state.Buffer = new byte[fs.Length];
state.File = fs;
fs.BeginRead(state.Buffer, 0, state.Buffer.Length, ReadCompleted, state);
Console.ReadLine();
fs.Close();
}
// 콜백함수
static void ReadCompleted(IAsyncResult ar)
{
FileState state = ar.AsyncState as FileState;
state.File.EndRead(ar);
string txt = Encoding.UTF8.GetString(state.Buffer);
Console.WriteLine(txt);
}
}
}
using System;
using System.Text;
using System.IO;
namespace Week13
{
class Program
{
static void Main(string[] args)
{
AwaitRead();
Console.ReadLine();
}
private static async void AwaitRead()
{
using (FileStream fs
= new FileStream(@"C:\Windows\System32\Drivers\Etc\HOSTS", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byte[] buf = new byte[fs.Length];
await fs.ReadAsync(buf, 0, buf.Length);
string txt = Encoding.UTF8.GetString(buf);
Console.WriteLine(txt);
}
}
}
}
TCP와 UDP 상의 수신 버퍼 오버플로우
논블록 소켓
논블로킹 소켓과 스레드 실행
논블록 소켓의 운영 사례
CPU 사용량 폭주 문제의 해결
논블록 소켓의 장점과 단점
장점 : 교착 상태가 생기지 않고, 통제가 가능하다.
단점 : 굉장히 여러가지 복잡한 문제들이 생긴다. 일반적인 코딩 방식과 다른.
단점이 있다고 쓰지 않으면 안 된다. 장점을 활용하고 단점을 조심해야한다.
프로그래밍 트랜드는 계속 바뀌고, 기술 또한 계속 발전한다.
이 바닥에 온 이상 계속해서 공부해야 한다.
동기, 비동기, 인공지능에 대한 공부는 지속해야 한다.
배운 것을 계속 활용하고, 공부해야 한다. 특히 네트워크는 도전해야 하는 것이 많은 분야.
현재는 포톤이라는 것을 가장 많이 사용하며, 유니티 기본 제공 기능도 있다.
유니티 네트워크 기본 기능을 제공했다가 사라지고 다시 재서비스 되었다.
따라서 소스를 받아서 볼 때 버전의 정보가 매우 중요하다.
블로그, 유튜브에 나와있는 것의 상당수가 불필요한 내용일 수 있다.
최대한 최신 버전, 최신 내용을 위주로 살펴보아야 함.
수업을 통해 반다이 남코 현역 프로그래머가 알려주는 유니티 네트워크 프로그래밍을 완독했다.
'대학생활 > 수업' 카테고리의 다른 글
게임기획크리틱 13주차 - 레벨디자인과 그레이박싱 (3) | 2023.06.13 |
---|---|
게임그래픽프로그래밍 13주차 - 게임엔진과 구성요소에 대한 이해 (0) | 2023.06.12 |
게임레벨디자인 13주차 - 기말 시험 공지 (0) | 2023.06.08 |
게임음악작곡법 12주차 - Scales, 리듬의 유지, 음정의 변환, Repetition, Sequence, Inversion, Shorten Motive, Length the Motive (0) | 2023.06.08 |
게임데이터설계 13주차 - 게임데이터의 형식과 활용 (0) | 2023.06.07 |