[UNITY] NO PUN, NO UNET. C# 서버로 멀티FPS 게임 제작기

RisingJade의 개발기록·2022년 5월 16일
3

KILLER BEAN 제작기

목록 보기
5/5
post-thumbnail

동기

친구들과 함께 [APEX Legend]를 하면 꼭 한 라운드에 한번씩 하는 말이 있다.

친구 1: "아 또 순간이동하네..."
친구 2: "아니 분명 숨었는데 왜 또 맞아!"
나: "아 서버문제만 아니였으면 이겼다.."

APEX Legend에서 발생하는 문제?

1. 서버가 슬로우 모션으로 돌아감

원래 APEX Legend는 20hz로 구동되지만 서버의 각종 처리가 50ms 이상 걸리면 말그대로 슬로우 서버가 되어 우리가 보는 화면이 느리게 보인다.

참고 영상: https://youtu.be/w_YEyxBgy8Y

2. 지연 보정 문제

요즘엔 많이 줄어들긴 했지만 초기에는 정말 문열고 집에 들어갔는데 죽는경우가 꽤 있었다.
반대로 분명 제대로 조준을 해서 맞추었는데도 안 맞는 경우도 꽤 많았다.

참고 영상: https://www.youtube.com/watch?v=N_nsgTRZZZA


크게 보면 이렇게 2가지가 있는데 사실 대부분의 FPS게임에서 가지는 문제점으로 보인다.
평소에도 FPS,TPS 슈터를 즐겨하는 사람으로서 궁금하지 않을 수가 없었다.

"왜? 저런 문제가 생길까?"

모르면 직접해봐야지...

친구1: "컴공이라는 녀석이 이런것도 모르냐!"

준비

어처피 게임 하나 새로 만들때가 됬는데 겸사겸사 하나 만들어보며 어떤문제가 있는지 알아보기로 했다.
다행히 서버는 바닥부터 새로 만드는것이 아니라 저번에 들었던 인프런 강의 중
_[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버_
[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part7: MMO 컨텐츠 구현 (Unity + C# 서버 연동 기초)
기반으로 조금 수정을 하고 클라이언트는 작년 겨울에 잠깐 본 고박사의 유니티 채널: FPS Prototype을 기반으로 수정을 하면 금방 만들 수 있을거라 생각했다.

추가로 만들어야 하는 것

위에서 말했던 "조금 수정"은 생각보다 많은 것을 들어내고 바꿔야하는데... 다행히 서버쪽에서 소켓 연결 및 각종 자료구조들은 그대로 재사용이 가능하지만 서버 내 모든 처리(packet handler, object state control)등등 따로 만들어야 됬다.
클라이언트(유니티)쪽에서는 예전에 에셋번들로 샀던 Character Movement Fundamental 스크립트 일부(이것도 추가적인 액션 때문에 엄청 고치긴했다..)로 기본적인 부드러운 이동제어를 하고 나머지는 직접 짰다.

참고자료:
ROOKIES님의 강의: https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part4/dashboard
고박사의 유니티 노트 채널: https://www.youtube.com/c/%EA%B3%A0%EB%B0%95%EC%82%AC%EC%9D%98%EC%9C%A0%EB%8B%88%ED%8B%B0%EB%85%B8%ED%8A%B8/videos
character movement fundamental 에셋: https://assetstore.unity.com/packages/tools/physics/character-movement-fundamentals-144966



클라이언트

목표

  • FPS일것 - TPS는 추후 지원여부 판단
  • 캐릭터 액션에는 점프, 달리기, 슬라이딩, 조준, 사격이 존재 - 스킬이 추후 지원여부 판단
  • 무기는 1~4번까지 - 1: 주무기 2: 보조무기 3: 근접 4: 폭탄
  • 오버워치의 힐팩같은 힐팩 스폰, 탄약 스폰이 있을 것 - 콜오브듀티시리즈처럼 벽뒤에 가만히 있으면 체력차는건 너무 게임의 템포를 떨어트림
  • 데스매치 타입의 게임 - 개인적으로 가장 좋아하는 타입. 옛날 서든 어택에서도 웨어하우스만 했었다...
  • 최소 3:3에서 최대 8:8까지 지원
  • APEX Legend 처럼 스킬도 지원했으면 좋겠다. - 위를 먼저 만들고 후에 제작

클라이언트를 만들면서 해보려고 하는 것

  • 여러 디자인 패턴 사용
  • 최대한 코딩 인벤션 지키기

기능구현 상태 1(LastUpdate.22.05.06)

일반적인 구현 코드는 생략하겠습니다.

22.05.06

FPS 화면 구현

  • In GameView
  • 씬에서 보이는 모습

    FPS에서 화면 생성을 위해 2개의 카메라를 사용하였다.

    여기서 CameraRoot 아래 CameraWeapon과 투명객체외 모든 Layer을 랜더링하고 WeaponCamera는 오직 Weapon 렌더링하여 구현한다.

    "이렇게 하는 이유는 하나의 카메라만 사용시 weapon부분이 벽을 통과하여 보이거나 시점을 위 아래로 바꿀때 따로 애니메이션구현이 없기 때문에 weapon Arm이 보이지않는데 총알이 조준점으로 나가는 이상한 모습을 보게된다."

캐릭터 액션구현

  • 슬라이딩
  • 점프
  • 달리기
    위 3개는 딱히 어려운것이 없었다. 그냥 슬라이딩은 중력값과 마찰계수를 잘 조정해서 10'이상 경사가 있으면 Lctrl를 눌러 미끄러지게 하고, 슬라이딩 중에 점프를 하면 슬라이딩 속도의 momentum(힘)을 받아 좀 더 멀리 나가게 되어 스피드한 감각을 살렸다.

무기 구현

  • 1번 주무기: AK-47 로우 폴리가 무료 에셋으로 있어서 사용하였다.

  • 2번 보조무기: 평범한 권총 로우 폴리가 무료 에셋으로 있어서 사용하였다.

  • 3번 근접무기: 딱히 맞는 에셋이 없다... 모델찾아 리깅하고 애니메이션 만드는데 시간이 걸리니 나중에 만들려고 한다.

  • 4번 투척무기: 현재 쓰고있는 무료 로우폴리 에셋에는 투척물이 Hand_Grenade밖에 없지만 수류탄 하나라도 구현하였다.
    (3,4번 무기는 따로 애니메이션이 없다... 따로 1번과 같은 포즈에 총이 안들고있는게 디폴트 모션이되었다.)

아이템 스포너

  • 구급상자
  • 주무기 탄약
  • 추가할것: 수류탄, 보조무기 탄약, 여러종류의 총 랜덤 스폰 등

팀 데스매치

  • 서버 개발기록에서 자세한 설명을 하는걸로..

개발해야 할 추가 컨텐츠

  • 평지에서도 달리기중 슬라이딩 가능
  • 맵에 폭탄 배럴 생성 - 완료
  • 좀 더 정교한 맵 생성
  • 무기 종류 확장
    • 주무기 3~5종류 확장
    • 스나이퍼, 샷건 등 뉴타입 무기 제작
    • 근접무기는 없지만 근접평타 생성
  • Kill카운트, Team UI 생성
  • 로드아웃 커스텀 UI 제작
  • 로드아웃 커스텀 무기 포인트 시스템 제작

서버

목표

  • 지연보정으로 인한 자잘한 렉 줄이기 (어디까지 가능한지가 사실 이번 프로젝트로 얻으려는 것)
  • 데디케이트 서버로 해당 라운드만 관장하는 방식, 따라서 최대 접속자 8:8 = 16인
  • 데드레커닝 적용
  • 위와 비슷한 말이긴하지만 패킷전송양(대역폭) 줄일 수 있을만큼 줄여보기
  • 피격, 충돌, 보간등은 일단 유니티 내부적으로 컨트롤하는 것으로 - 현실적인 목표
  • 서버 내부에서 물리적 판단까지 하여 클라에서 보낸 충돌 및 히트 판정에 대한 진위 판별하기 - 최종 목표...
  • 클린코드를 지향하자

기능 구현 상태 1(LastUpdate.22.05.07)

일반적인 구현 코드는 생략하겠습니다.

Main Program

  • ConfigManager에서 config.json을 불러오고, 불러온 config파일에 기반하여 Data를 불러온다.
    아직 config파일과 Data파일에는 대단한게 들어있지는 않다. config에는 data path정도 들어가 있으며 data파일에는 플레이어들의 초기값 세팅정도와 맵의 사이즈와 연관된 minX,maxX,etcRespawnPoint정도 밖에 없다.

  • 아직 프로토타입이라 보기만해도 눈살을 찌푸리게 만드는 주석들이 많은데 이건 ver.1.0.0이 완성되면 한번에 summary형식으로 정리를 해야한다.
  • 현재 게임룸은 서버가 켜질때 RoomManager의 instance로 딱 하나만 생성하며 50ms당 한번씩(20hz) Job Scheduler가 기동된다.

Connector

  • Program

  • ngrok를 통해서 외부에서 접속할 포트를 설정한다. Listener에서 클라이언트의 접속을 기다린다.
  • Init

  • SicketAsynEventArgs를 사용하여 소켓 연결이 완료됬을때의 함수를 콜백하도록 지정하고 RegisterConnect로 연결한다.
  • RegisterConnect


Ngrok(무료 터널링 앱)

AWS EC2 프리티어 사용이 이미 끝나서 서버를 올릴때가 마땅치 않아 찾은 ngrok라는 앱이 있다. 따로 어떤 포트포워딩이나 방화벽 설정이 없이 ngrok 엔드포인트에 데이터를 전송하면 내가 열어둔 포트로 포워딩이 된다. ngrok에서는 터널링 시스템이라 부른다.


Packet Format - protobuf


서버와 클라이언트 사이의 공통된 패킷규약을 만들기 위해 구글이 만든 Protobuf라는 데이터포맷을 사용하였다.
직접 packet generator를 만들어서 하는 것보다 훨씬 이것저것 지원되는 것들이 많아서 사용하였다.

이때, 가장 많이 전달되는 Keyboard입력은 int32 타입 안에 BitFlag를 이용하여 큰 폭으로 대역폭을 줄였다.
또한, transform 데이터로 position을 계속 보내는 것이 아니라 Input값을 BitFlag로 입력되었을 때만 보내고 버튼에서 손을 뗐을때 보내는 방식으로 패킷2번으로 Movement를 제어한다.
(물론 일정시간마다 실제 position을 보내어 서버와의 오차를 체크하고 싱크를 맞추는 부분도 있다.)


Packet Handler

  • 위와 같은 방식으로 packet이 오면 온 packet에 걸맞는 Action을 자동으로 Invoke 해준다.
  • Handler에서는 unpacket을 하고 현재 클라이언트를 담당하는 room의 JobQueue에 Action을 push해준다.

Job Scheduler

  • JobSerializer


ClientSession에서 들어온 패킷들은 해당 패킷에 맞는 핸들러로 간 뒤에 Job에다가 넣어준다. 보통 일반패킷들은 몆초후에 처리할 일이 없으니 따로 예약시간은 두지 않는다.
위의 Main Program에서도 말했듯이 Job은 50ms(20hz)마다 한번씩 돌아간다.

개발해야 할 추가 컨텐츠

  • 팀에 따른 스폰 위치 및 맵 데이터 추가 저장
  • 3:3 대결용 GameRoom 설정
  • 게임 승리시 GameRoom Reset기능

플레이 영상

링크: https://youtu.be/krIg4MJ12zc

"현재 비디오 편집의 오류로 초반부 이후 음소거가 되어있습니다.
몇몇 수정 사항을 고친 뒤 새로운 플레이 영상을 올리겠습니다."

현재 구현 된 사항 (서버 기준)

TCP기반 서버

C# socket과 Timer.timer로 간단한 TCP기반 서버와 서버에서 돌아가는 Job Scheduler 구현

기초 데드레커닝

  • 유니티에서 positionrotation값을 60hz 로 보내는 것이 아닌 입력 값이 있을 때 보내는 Input패킷을 통해 상대 클라이언트에서 이동방향을 추측하고 이동시키는 방법.

    추가적으로 가장 많이 보내지는 이 KeyboardInput패킷은 BitFlag방식을 통해 7개의 입력을 int하나로 보내어 대역폭을 낮출 수 있었다.
  • 한국에서 패킷을보내고 응답받는데 걸리는 시간은 ngrokJP서버를 통해 접근하는 Ping 33ms~38ms과 포워딩에 걸리는 시간포함 최대 약 70ms가 걸리기 때문에 실제 KeyboardInput값을 받아도 지연차이가 생긴다. 따라서 일정 시간마다 한번씩 position을 동기화 하는 작업이 있다.

수정 사항

  • 마지막 수정: 2022.05.08
  • 플레이어별 오디오 볼륨 크기 및 거리에 따른 오디오 볼륨 조정

추가 사항

  • 개인 KillCount 및 팀 KillCount UI 생성
  • 클라우드 서버에 해당 서버프로그램을 올리고 평균 ping을 산출하여 지연 보정 값 계산하는 로직 작성
  • 사운드 및 이펙트 추가
  • 무기의 종류 및 이미지 추가
  • 무기 구매 UI, Tab창 추가
profile
언제나 감사하며 살자!

2개의 댓글

comment-user-thumbnail
2022년 12월 15일

인상깊게 봤습니다!! input을 비트플래그로 한 번에 보내는 건 정말 좋은 방법인거같아요! 괜찮으시다면 코드가 궁금한데 깃허브 링크같은 것이 있을까요??

1개의 답글