멀티 게임 개발일지 2- Unity
깃허브 링크 : SiwonChoi98/MultiPlayerGame: d (github.com)
이번 일지는 싱글게임과는 다른 멀티게임에서의 서버와 클라이언트가 어떻게 동작하는지 적어보겠다.
먼저 방장은 1번, 들어온 유저는 2번이라고 하겠다.
그러면 서버이자 클라이언트인 방장과 클라이언트인 유저가 있을 것이다.
그러면 이런식으로 1번 방장에게는 1번과 2번이 2번 클라에게도 1번과 2번이 있을 것이다.
그렇다면 스크립트에서 이걸 어떻게 구분할까?
원래 unity에서 LifeSycle 함수 등을 사용하기 위해서는 MonoBehaviour를 상속받는다.
하지만 네트워크 오브젝트라는 것을 서버에서 알기 위해서는 NetworkBehiviour를 상속받아야한다.
NetworkBehiviour를 상속받으면 네트워크 오브젝트가 누구의 권한일때 실행하게 해주는 코드를 작성할 수 있다.
간단하게
isLocalPlayer, isOwned, isServer, isClient 등이 있다.
이게 무엇이냐?
[ isLocalPlayer ]
isLocalPlayer는 1번 Server에서 1번 컴포넌트가 본인 객체이기 때문에 isLocalPlayer는 True이다.
그러면 1번 Server에서 2번 객체는 isLocalPlayer가 False인 것이다.
isLocalPlayer의 대표적인 활용법은 GamePlayerScript에서 Input을 받아서 이동과 회전을 하는 코드를 작성했다면 본인객체만 움직여야 할 것이다. 본인 캐릭터만 움직여야하는데 다 움직이면 이상할 것이니 말이다.
아래와 같이 이렇게 동작하게 하면 본인 캐릭터만 움직이게 된다.
*2번째 if문은 무시하면 된다.
또한 isLocalPlayer와 isOwned는 같다라고 보면 될 것 같다. isOwned가 조금 더 큰 개념이다.
[ isServer ]
isServer는 방장이 서버이기 때문에 방장 화면에서는 1번 객체와 2번 객체는 둘다 isServer가 True이다.
그러면 클라인 2번 화면에서는 1번 객체와 2번 객체 둘다 isServer가 False이다.
isServer는 되게 중요하다.!!!!
멀티 게임을 만들려면 결국 보안이 중요해야 하는데 클라에서 게임에 미치는 어떠한 값이 바뀌게 되면 큰일 난다. 흔히 말하는 핵 이다..... 그렇기 때문에 Server에서 값이 변해야 해킹으로 부터 안전할 수 있다.
보통 게임을 시작할 때 해당 데이터를 넣어준다. 이러한 부분들을 서버에서 동작하여 동기화를 시켜주는 것이 중요하다.
공격하는 부분을 예로 들어보겠다.
클라이언트가 공격을 하면 해당 오브젝트에게 데미지 처리를 해야하고, 이 데미지는 UI에서 나타나야한다.
그러면 여기서 데미지 같이 변조할 수 있는 데이터는 아까 말했듯이 어디서 동작해야 한다?
서버에서 동작해야한다.
그러면 서버에서 데미지를 줄 수 있는지 체크를 해서 데미지를 전달해주고 해당 데미지를 받아서 HP가 깎이면 그것을 클라이언트에게 동기화 시켜주면 될 것 이다. 아래 코드를 살펴보자
여기서부터 처음 접한 사람들은 헷갈릴 수 있어서 집중을 잘... 해야한다.
- 플레이어가 마우스 버튼을 클릭 시 호출하는 함수
- 서버에서 Hit를 체크해 해당 StatusComponent에 있는 체력에 데미지를 주는 코드
- Fire함수를 호출할 수 있는 시간을 계산하는 함수
- Fire 함수 호출 시 Effect를 생성하는 함수
코드가 더 있지만 이정도면 이해하는데 충분할 것 같아서 이정도만 봐보겠다.
먼저 isLocalPlayer인 객체가 마우스를 클릭하여 Fire를 호출하면 공격할 수 있는 상태인지를 체크한다.
(공격할 수 있는 상태는 isServer에서만 동작하여야하고 [SyncVar] attribute 를 통해서 클라에게 동기화 시켜준다.)
* [SyncVar] 는 Server에서만 동작하며 서버가 클라에게 동기화 시켜줄 수 있는 attribute 이다.
그리고 나서 본인 객체는 서버나 클라 상관없이 Effect와 Sound는 빠르게 나와야한다.
(반응성이 중요하기 때문에 예측하여 본인 객체의 공격 시 실행되는 코드를 실행한다.)
그리고 만약 서버가 Fire를 호출했다면 isServer는 True이며 [ ClientRpc ] attribute 로 되어있는 이펙트와 사운드를 실행한다.
* [ClientRPC] 는 Server에서 각 Client에게 호출하는 함수이다. //RPC : Remote Procedure Call
본인이 서버였을 때는 본인 객체를 제외한 객체로 RPC를 보내주고, 서버가 아닌 클라이언트가 Fire를 호출하였을 때는
CmdFire()를 통해서 [Command] attribute가 달려있는 함수를 호출해서 Server를 호출해준다.
사진으로 쉽게 이해를 도와보겠다.
이러면 나름 이해가 가능할 것이다.
*TargetRPC 라는 특정 타겟인 클라이언트에게 보내는 Call도 있다.
다시 본론으로 돌아오면 Server에서는 Hit를 체크하고 데미지를 처리한다.
그러면 서버에서 데미지를 줬는데 클라는 데미지를 받은 걸 어떻게 알까?
아까 말했듯이 서버에서 모든 클라에게 동기화해주는 [SyncVar] attribute가 있고, 해당 체력을 SyncVar로 선언하였다.
그럼 유저는 SyncVar로 동기화 되며, 데미지를 받으면서 변경된 Health는 OnHealthChanged 함수를 호출하여
UI상으로 UpdateStat인 대리자에 등록되어있는 체력이 업데이트 되는 코드가 호출 될 것이다.
이러한 방식으로 동작하며 서버와 클라이언트가 게임에서 어떻게 동작하는지를 적어봤다.
다음 일지는 게임 AI가 어떻게 동작하는지 적어보겠다.