나같은경우 5.3.2를 써서 개발하는 중인데, 5.3도 되겠지? 하는 안일한 마인드로 처음부터 다시 했다.
굉장히 오래 걸리는 과정이기 때문에 버전 잘못되서 기존 프로젝트 못열면 기분이 상당히 안좋다..
왼쪽 상단 릴리즈 대신 우측의 릴리즈 페이지로 들어가서 정확한 버전을 체크하자.
zip파일을 다운받고 압축 해제 -> setup.bat 파일 실행 (좀 오래걸림. 씻고 저녁 만들어서 먹고 설거지 하고 오면 대충 다 된다.) -> generateprojectfiles.bat 실행 (이건 금방함) 까지 하면 UE5.sln이라고 프로젝트 파일이 생긴다. 이거 열자.
솔루션 Development Editor, Win64 맞는지 확인
UE5를 빌드 해보자. (진짜진짜 오래걸림)
나는 거의 8시간 걸렸다.
빌드가 다되면 UE5를 시작프로젝트로 설정하고 ctrl+f5로 실행해준다.
셰이더 컴파일도 오래걸린다. 체감상 한 30분?
에디터가 잘 열리면 이제부터 이걸로 개발해야한다.
아 에픽게임즈 런처에서 다운받은 언리얼 엔진으로는 데디케이트 서버 구축이 안된다고 한다.
깃헙에서 다운받은 엔진으로만 한다고 한다.
에픽게임즈 런처에서 다운받은 언리얼 엔진과 깃허브에서 다운받은 언리얼 엔진은 버전 숫자는 같아도 내부 API같은게 조금씩 다를수 있어 서로 호환이 안될수도 있다고 한다.
어이가 없네...
그래서 런처에서 다운받은 엔진으로 개발했던 프로젝트가 깃헙에서 다운받은 엔진으로는 안열렸던것.
요새 날이 좀 시원해졌는데 머리가 뜨거워진다.
대충 해결한것 같다.
에픽게임즈 런처에서 다운받았던 에디터로 개발한 프로젝트는 살린것 같다.
먼저 지형을 그려주는 브러시를 플러그인으로 다운받아 썻었는데 그 플러그인을 지우고 그 플러그인으로 생성했던 더미맵도 지웠다.
그리고 프로젝트 폴더의 Binary 폴더와 immediate 폴더를 지웠다.
그다음 일반적인 데디케이트 서버 구축의 과정을 진행했다.
1. source 폴더의 Target.cs 파일을 복붙해 프로젝트명Server.Target.cs로 고쳐주고 vs를 킨다.
2. 프로젝트에 복붙한 파일 추가해주고 내부 프로젝트 이름으로 된 클래스 이름 같은것들을 프로젝트ServerTarget으로 수정해준다.
요런느낌
3. 저장해주고 uproject 우클릭해서 언리얼 엔진 버전을 깃헙에서 받은 엔진으로 설정해준다.
4. uprojecct 파일 우클릭해서 generate visual studio project files 실행해준다.
멀티플레이 게임에 필요한 서버 환경 구축에 대해 아는게 없기 때문에 언리얼 공식문서를 읽어보면서 어떻게 구현하면 될지 공부해보자.
1. 멀티플레이어 기능을 절대로 추가하지 않는다는 확신이 있는게 아닌 이상, 처음부터 멀티플레이어용으로 게임을 개발해라
프로젝트에 멀티플레이어 기능이 필요하게 될 수 있다면, 프로젝트 시작부터 멀티플레이어를 염두에 두고 모든 플레이를 빌드해라.
생각은 했지만, 크게 중요하게 생각하지 않았던 부분을 시작부터 지적당한 기분이다. 당연하게도 싱글 환경에서 구현한 기능들을 멀티플레이어로 확장하려면 훨씬 많은 시간과 비용이 발생한다. 아직 많은 기능을 구현한 상태가 아니므로 다행이라 생각하고 지금이라도 고려하면서 개발해보자.
2. 클라이언트-서버 모델
네트워크 멀티플레이어 게임의 경우 언리얼 엔진은 클라이언트-서버 모델을 사용한다.
네트워크에 존재하는 하나의 컴퓨터가 서버가 되어 멀티플레이어 게임 세션을 호스팅하고, 서버에 접속한 다른 모든 플레이어의 컴퓨터는 클라이언트가 된다.
서버는 각 클라이언트와 게임 스테이트 정보를 공유하고 서로 통신하는 수단을 제공한다.
서버는 하나의 진정한 Authority 게임 스테이트를 보유한다.
클라이언트는 각각 서버가 소유한 Pawn을 원격 제어하며, 게임 내 액션을 수행하도록 Pawn에 프로시저 호출을 전송한다.
서버는 클라이언트 모니터에 직접 영상을 스트리밍 하지 않으며, 대신 게임 스테이트에 대한 정보를 각 클라이언트에 Replicate하여 존재하는 액터와 액터의 행동방식, 보유해야하는 다양한 변수값을 알려준다.
클라이언트는 이 정보를 사용해 서버에서 플레이되는 게임을 매우 비슷하게 시뮬레이션 한다.
3. 클라이언트-서버 모델 예시
로컬 게임플레이
네트워크 게임플레이
플레이어 1이 입력을 눌러 무기를 발사한다.
플레이어 1의 폰이 여기에 반응해 현재 무기를 발사
플레이어 1의 무기가 발사체를 스폰하고 그에 따른 사운드와 비주얼 이펙트를 재생한다.
플레이어 1이 로컬 머신에서 입력을 눌러 무기를 발사한다.
플레이어 1의 로컬 폰이 대응되는 서버의 폰에 무기 발사 명령 전달
서버에 있는 플레이어 1의 무기가 발사체 스폰
서버가 연결된 각 클라이언트에 플레이어 1의 발사체 사본을 생성하라고 지시
서버에 있는 플레이어 1의 무기가 각 클라이언트에 무기 발사와 관련된 사운드외 비주얼 이펙트를 재생하라고 지시
플레이어 1의 발사체가 무기에서 앞으로 이동한다
서버에 있는 플레이어 1의 발사체가 무기에서 앞으로 이동한다
서버가 각 클라이언트에 플레이어 1의 발사체가 이동하면 그 무브먼트를 Replicate 하라고 지시하고, 그에 따라 각 클라이언트 버전의 플레이어 1 발사체도 이동한다
플레이어 1의 발사체가 플레이어 2의 폰과 충돌한다
이 콜리전이 플레이어 1의 발사체를 소멸하는 함수를 트리거하여 플레이어 2의 폰에 데미지를 주고 그에 따른 사운드와 비주얼 이펙트를 재생한다.
플레이어 2가 받은 데미지에 대한 반응으로 화면 이펙트 재생한다.
서버에 있는 플레이어 1의 발사체가 플레이어 2의 폰과 충돌한다.
이 콜리전이 서버에 있는 플레이어 1의 발사체를 소멸하는 함수를 트리거한다.
서버가 자동으로 각 클라이언트에 플레이어 1의 발사체 사본을 소멸하라고 지시
이 콜리전이 모든 클라이언트에 콜리전에 따른 사운드와 비주얼 이펙트 재생하라고 지시하는 함수 트리거
서버에 있는 플레이어 2의 폰이 발사체 콜리전으로부터 데미지를 받는다
서버에 있는 플레이어 2의 폰이 플레이어 2의 클라이언트에 받은 데미지에 대한 반응으로 화면 이펙트 재생하라고 지시
플레이어 1의 폰이 플레이어 2의 폰에 무기를 발사하여 데미지를 입히는 단순한 과정이 독립형 게임에서는 컴퓨터의 같은 월드에서 벌어지지만, 네트워크 게임에서는 상당히 복잡해보이는 절차가 발생한다.
좀 정리해보자.
서버에 존재하는 월드, 각 플레이어에게 존재하는 월드가 따로 있고 상호작용은 여러개의 서로 다른 월드에서 일어난다.
실제 게임은 서버에서 플레이되지만, 서버는 클라이언트의 월드에서 똑같은 이벤트가 발생하도록 보이기 위해 클라이언트에 선별적으로 정보를 전송하여 서버 월드와 동기화 한다.
이 동기화를 위해 Replicate - 이벤트 복제를 서버가 실행하고, 어떤 정보를 replicate할지 선별하여 유저에게 일관된 경험을 제공하는 동시에 replicate하는 정보량을 최소화하여 가능한 네트워크 대역폭을 적게 사용해야한다.
학교에서 진행했던 모든 프로젝트에서 서버는 클라이언트와 통신해서 데이터를 뿌려주는 느낌이 강했었는데, 게임에서의 서버는 확실히 역할이 확대된? 느낌이다.
4. 네트워크 모드 및 서버타입
네트워크 모드
설명
Standalone(독립형)
게임이 원격 클라이언트 접속 허용하지 않는 서버로 돌아감 서버측과 클라이언트측 모두 로컬플레이어에 맞게 실행됨.
Client(클라이언트)
게임이 네트워크 멀티플레이어 세션으로 서버에 접속된 클라이언트로 실행된다. 어떠한 서버측 로직도 실행하지 않는다.
Listen Server(리슨 서버)
게임이 네트워크 멀티플레이어 세션을 호스팅하는 서버로 실행된다. 원격 클라이언트의 접속을 허용. 서버에 바로 로컬 플레이어가 있다. 가벼운 협동 및 경쟁 멀티플레이어에 자주 사용된다.
Dedicated Server(데디케이티드 서버)
게임이 네트워크 멀티플레이어 세션을 호스팅하는 서버로 실행됨. 원격 클라이언트 접속을 허용하지만, 로컬 플레이어는 없다. 더 효율적인 운영을 위해 그래픽, 사운드, 입력 및 기타 플레이어 중심 기능 제외 높은 지속성이나 보안, 대규모 멀티플레이어가 필요한 게임에 자주 사용됨.
우선 Standalone, Client는 고려하지 않고 Listen Server와 Dedicated Server 중에 하나를 선택해야 할 것 같다.
Listen Server는
게임 사본이 있는 어떤 유저라도 리슨 서버를 시작하고 같은 컴퓨터에서 플레이 할 수 있으므로 사용자가 자발적으로 쉽게 구성할 수 있다.
서버를 시작하거나 참여할 서버를 검색하기 위한 게임 내 UI 제공 필요
리슨 서버를 호스팅하는 플레이어가 서버에서 직접 플레이하기 때문에 네트워크 접속을 통해 플레이하는 다른 유저보다 유리하다. 서버에 직접 있는게 응답성이 훨씬 좋고.. 보안쪽으로도 서버측 플레이어가 마음먹으면 훨씬 취약해지니 핵 문제에도 취약해진다.
네트워크 부하가 높거나(서버측 플레이어에게 높은 부하가 걸린다) 경쟁이 치열한 게임엔 부적합.
소규모 플레이어의 협동게임, 경쟁 멀티플레이어 게임엔 매우 편하다.
Dedicated Server는
더 비싸고 환경설정이 어렵다.
게임에 참여하는 모든 플레이어가 개별 컴퓨터를 사용해 자체적으로 네트워크에 접속해야함
서버에 참여하는 모든 플레이어가 같은 타입의 접속으로 게임을 경험하므로 공정성 굿
이 서버 유형은 그래픽을 렌더링하지도 않고, 로컬 플레이어와 관련된 로직을 수행하지도 않는다 -> 게임 플레이 이벤트나 네트워크 퍼포먼스가 더 효율적이다.
5. 액터 레플리케이션
Replication - 레플리케이션이란?
서버가 네트워크 세션에 있는 클라이언트들에 게임의 데이터를 보내 클라이언트와 서버의 상태를 동기화하는 프로세스.
서버에 실제 게임이 돌아가고 있고, 연결된 클라이언트들이 서버의 상태(그래픽이나 오디오 등)를 replicate 해서 게임에 참여중인 다른 클라이언트들과 통신한다.
대부분 액터는 기본적으로 레플리케이션x, 로컬에서 함수 수행.
C++ 액터 클래스에서 bRplicates 변수 설정하거나, 액터 BP의 Replicates 세팅 T로 설정해 특정 클래스 액터에 대한 레플리케이션 활성화.
[가장 많이 사용하는 레플리케이션 기능]
레플리케이션 기능
설명
Creation and Destruction (생성 및 소멸)
서버에서 Authority 있는 액터가 스폰되면 접속된 모든 클라이언트에 자동으로 원격 프록시 생성
이후 정보가 원격 프록시에 레플리케이트 된다.
해당 액터를 소멸시키면 접속된 모든 클라이언트에 있는 원격 프록시 자동 소멸
Movement Replication
Authority 있는 액터에 대해 Replicate Movement가 활성화 되어 있거나 C++에서 bReplicateMovement가 true로 설정되어 있으면, 자동으로 Location, Rotation, Velocity가 replicate 된다.
Properties Replication
Replicate 대상으로 지정된 속성은 값이 변경될 때마다 Authority 있는 액터에서 원격 프록시로 자동으로 복제된다.
Component Replication
액터 컴포넌트가 해당 컴포넌트를 소유한 액터의 일부로 Replicate 된다.
이것으로 지정한 컴포넌트 내 모든 변수가 Replicate 된다.
컴포넌트 내 모든 RPC가 액터 클래스에서 호출된 RPC와 일관되게 행동함.
Subobjects Replication
모든 UObject기반 클래스는 액터에 붙어 서브 오브젝트로써 replicated 될 수 있다.
Remote Procedure Calls (원격 프로시저 호출)
RPC -> 네트워크 게임에서 특정 컴퓨터로 전송되는 특수한 함수
RPC를 처음 호출한 컴퓨터가 어떤 컴퓨터든 간에 지정된 컴퓨터에서만 RPC가 실행된다.
서버(서버에서만 실행)나 클라이언트(액터 소유 클라이언트에서만 실행) 또는 NetMulticast(서버를 포함, 세션에 접속된 모든 컴퓨터에서 실행)로 지정할 수 있다.
생성 및 소멸, 무브먼트 같은 일반적인 레플리케이션에서는 자동으로 처리되지만, 레플리케이션을 활성화해도 다른 모든 게임플레이 기능 자체는 기본적으로 복제되지 않기 때문에, 수동으로 처리해야하는 항목도 있다.
replicate할 속성과 사용자 설정
replicate할 함수와 코드에서 수동으로 호출하는 함수
replicate할 컴포넌트와 서브 오브젝트 및 그에 관련된 속성과 함수
액터, 폰, 캐릭터의 몇가지 기능(Skeletal mesh component, static mesh component, Materials, animation bp, particle system component, Sound emitters, physics object)은 복제되지 않는다.
이러한 기능들은 모든 클라이언트에서 개별적으로 실행되지만, 시작적 요소를 구동하는 변수를 복제하면 모든 클라이언트가 동일한 정보를 가지고 유사하게 시뮬레이션이 가능하다.
6. Unreal이 제공하는 Replication System
언리얼에서는 네트워크 연결을 통해 이러한 데이터를 복제하는 세가지 시스템을 제공한다.
1. Generic Replication System (일반 레플리케이션 시스템)
현재 UE에서 기본적으로 사용하는 복제 시스템.
액터, 프로퍼티, RPC를 복제하는 기능을 지원하며, 세가지 주요 특성이 있다
Dormancy: 네트워크 replicate시 액터가 복제 대상 연결 리스트에 추가될지 여부를 제어
Priority: 네트워크 대역폭이 제한된 상태에서, 액터가 각 프레임마다 얼마나 중요한지 제어하여 replicate 우선순위를 결정하는 기능
Relevancy: 특정 연결에 대해 액터가 관련성이 있는지 제어하는 기능. 네트워크에서 복제할 액터 선택하는데 사용되며 관련성 없다면 복제되지 않는다.
2. Replication Graph
많은 수의 복제된 액터를 효율적으로 처리하도록 설계된 네트워크 복제 시스템
3. Iris Replication System
UE 네트워킹 시스템에 새로 추가된 기능.
기존의 Generic Replication System과 함께 작동하며 일부 기능 대체하고 개선함.
7. 네트워킹 팁
RPC와 복제된 BP 함수는 가능한 적게 사용. RepNotify 프로퍼티를 대신 쓸수있다면 쓰는것 권장
멀티캐스트 함수는 트래픽이 증가하니 신중하게 사용
서버 전용 로직이 꼭 서버 RPC에 포함되진 않아도 괜찮다. 복제되지 않은 함수가 서버에서만 실행되도록 할 수 있다면 그것으로 충분하다.
신뢰할수 있는 RPC를 플레이어 입력에 바인딩할때 주의해라. RPC queue가 넘칠 수 있다. 플레이어 입력 기반 RPC를 호출할 땐 플레이어가 RPC 활성화 가능한 빈도를 제한하는 메커니즘 만들어라
매우 자주 호출되는 RPC는 신뢰할 수 없는 RPC로 처리해라. 예시로, 액터의 tick 함수 내에서 자주 호출되는 RPC 같은것.
함수를 최대한 재사용해라. 클라이언트와 서버가 병렬 실행되도록 보장하기 위한 RepNotify로 호출? RepNotify가 뭐지?
액터의 네트워크 역할 확인. 서버와 클라이언트 모두에서 실행되는 함수에서 실행을 필터링할때 좋다.
IsLocallyControlled 사용해 폰이 로컬에서 제어되는지 확인. 폰이 소유 클라이언트와 관련 있는지에 따라 실행 필터링할때 좋다.
네트워크 게임플레이에서 가장 중요한 최적화 중 하나인 네트워크 휴면상태를 활용해라.
8. 부록 - RepNotify가 무엇인가..
RepNotify는 Replication 시스템의 하나.
특정 프로퍼티가 네트워크를 통해 복제될 때, 그 속성값이 변경되면 자동으로 호출되는 알림 함수를 지정하는 기능.
이를 통해 클라이언트가 속성 값의 변경에 따라 추가적인 로직 실행.
자동으로 업데이트 처리
프로퍼티 값의 변경에 따른 로직을 프로퍼티 자체와 분리해서 코드 관리
필요한 경우에만 추가 로직을 실행함으로써 네트워크 성능 최적화
UI 업데이트, 애니메이션 트리거, 게임플레이 로직, 이벤트 발생 등이 프로퍼티 변경시 일어난다면 RepNotify를 써서 로직을 작동시키면 아주 맛있다고 한다.
이렇게만 보면 여기저기 난사해서 사용해도 될 것 같지만, 주의사항이 있다.
1. 알림 함수 내에서 속성값 변경 금지
복제된 속성 값의 변경을 반영하는 용도로만 사용.
알림 함수 내에서 속성값을 다시 변경하면 무한루프 발생
2. 네트워크 성능 최적화
속성이 변경될 때 마다 알림 함수 호출해서, 빈번한 속성 변경이 있으면 네트워크 성능에 영향.