Network Conpendium의 요약정리
자료를 참고하여 정리하는 과정에서 작성자의 오해나 이해가 부족한 점이 있을 수 있음을 알려드립니다
Network Multiplayers 게임관련 주요 클래스
GameInstance
GameMode
GameState
Pawn (and Character, which inherits from Pawn)
PlayerController
PlayerState
게임 데이터를 저장할 때 고려해야 할 사항
- Pawn은 게임 내에서 죽거나 제거되는 경우가 많으며 그때마다 폰 클래스에 저장한 데이터도 사라진다
- PlayerController, PlayerState는 새 레벨이 로드되지 않는 한 폰이 제거되어도 계속 존재한다
GameInstance
- 게임인스턴스는 게임엔진이 시작될 때부터 종료할 때까지 존재한다
- 서버와 클라이언트에 각각 하나의 게임인스턴스가 있고 서로 통신하지는 않는다
- 현재 게임세션의 밖에 존재하며 레벨로드에 영향을 받지 않은 상태로 게임을 구성한다
- 영구적인 정보를 저장할 수 있는 적합한 장소이다
서버에만 존재하는 객체
- GameMode
서버와 모든 클라이언트에 존재하는 객체
- GameState, PlayerState, Pawn
서버와 소유한 클라이언트에 존재하는 객체
- PlayerController 는 클라이언트와 서버에 존재하지만 다른 클라이언트끼리는 공유하지 않는다
- PlayerController는 클라이언트 서버간의 통신에 관련한 작업을 주로 한다
클라이언트에만 존재하는 객체
- HUD, UMG Widgets
GameMode 클래스의 오버라드 가능한 함수
- 게임의 룰을 결정한다(플레이어 수, 최고점수 등)
- Ready to start Match : 클라이언트 수가 채워지면 true를 리턴하고 아니면 false리턴
- Event On Post Login : 클라이언트의 PlayerController를 파라미터를 통해 가져온다. PlayerController를 배열에 저장해 놓으면 나중에 유용하다
- Choose Player Start : 다수개의 Player Start 액터가 레벨상에 존하는 경우에 그 중 아직 점유되지 않은 Player Start를 찾아 리턴한다
- Start Match, Restart Game, End Match, Has Match Started, Abord Match, Has Match Ended, Is Match in Progress, Event OnSetMatchState, Get Match State 등의 함수 및 이벤트를 오버라이드할 수 있고 수동으로 호출도 가능하다
- 이 함수들은 Ready to start Match 함수가 true 를 리턴한 후에 대부분 자동으로 호출되지만 수동으로 호출도 가능하다
GameState
- 클라이언트/서버간의 게임의 현재 상태에 대한 정보교환을 위한 중요한 클래스
- 멀티 플레이어어 게임에서 중요한 정보인 접속된 플레이어 리스트(PlayerState의 리스트)를 포함한다
- GameState는 모든 클라이언트에게 Replicated 되므로 모든 클라이언트가 이 객체에 접근할 수 있다
- 멀티플레이어 게임에서 가장 핵심적인 클래스 중에 하나이다
- GameMode가 승리를 위한 점수를 가지고 있는 반면, GameState는 현재까지 취득한 점수를 가지고 있다
- GameState에는 개발자가 임의의 정보(배열이나 구조체 등)를 저장할 수 있다
- GameMode에 비해 개발자가 다루어야 할 작업은 적은 편이지만 이벤트 그래프에는 모든 클라이언트가 알아야 할 로직을 작성할 수 있다
- PlayerArray MatchState, ElapsedTime은 replicated 설정되어 있으므로 모든 클라이언트에서 접근할 수 있다
- GameState에 선언한 변수를 Replicated 설정하고 Switch has Authority 를 사용하여 서버측에서 값을 변경하면 모든 클라이언트에서 확인할 수 있다
PlayerState
- 모든 접속된 클라이언트는 현재 클라이언트의 정보를 포함하고 있는 한개의 PlayerState 객체를 갖는다
- PlayerState 객체는 모든 클라이언트에게 Replicated 되므로 어떤 클라이언트에서 다른 클라이언트의 정보를 접할 수가 있다
- 현재 클라이언트에서 다른 클라이언트의 PlayerState 객체에 접근하는 쉬운 방법은 GameState::getPlayerArray 를 이용하는 것이다
- PlayerName, Score 등 다른 클라이언트에게 제공해야 하는 다양한 정보(커스텀 변수)를 이 객체에 저장하여 다른 클라이언트에게 전달할 수 있다
- PlayerPawn이 Destroy 되더라도 PlayerState는 유지된다
Pawn
- Actor를 기반으로 파생된 클래스이며 플레이어에 의해 조종될 수 있는 액터이다
- 플레이어는 한번에 한개의 폰을 소유할 수 있으며 쉽게 다른 폰으로 전환할 수 있다(un-possessing, re-possessing)
- Pawn은 대부분 모든 클라이언트에게 Replicated 된다. Actor Replicated 설정된 상태
- 폰은 대부분의 경우 사람 형태이지만 동물이나 자동차, 비행기, 배, 블럭 등도 될 수 있다
- Pawn의 하위 클래스인 Character클래스는 네트워크에서 지원되는 MovementComponent를 포함하고 있어서 위치, 회전 등의 정보가 Replicate 된다
Actor Replication
- 맵에 배치된 액터를 선택하고 [디테일] 뷰 / Replication / 안에 있는 체크박스이다
- Net Load on Client : 클라이언트에 맵이 로드되면서 동시에 이 항목이 체크된 해당 액터도 맵과 함께 클라이언트에게 보여지게 된다
- 서버 측에는 위의 항목과 무관하게 보여진다
- Replicate : 서버에 스폰될 때 클라이언트에게도 Replicate 된다.
- 서버측에 스폰하는 것이 아니라 클라이언트에 스폰될 때는 위의 항목설정과 무관하게 액터가 보여진다
- 클라이언트에서 스폰하는 경우에는 서버측에 Replicate 되지는 않는다
- 게임의 승패에 영향을 주는 중요한 액터를 생성할 때는 반드시 서버에 스폰하고 클라이언트에게 Replicate하는 것이 보안상 최선책이다
- 게임의 승패에 영향이 없는 장식성 액터의 출력도 서버에 스폰하여 클라이언트에 Replicate해도 된다
- 서버에서 Replicate설정된 액터를 삭제하면 모든 Replicate 한 클라이언트에서도 해당 액터가 삭제된다
- 네트워크 상에서는 클라이언트가 Character를 움직이는 것이 아니라 클라이언트의 입력을 받아서 서버에서 Character를 움직이고 클라이언트에 Replicate 된다
Event replication
https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Blueprints/index.html
- 서버측에서 발생하는 이벤트만 유효하게 사용될 수 있도록 이벤트 함수 안에서 Switch Has Authority 노드를 사용하여 현재 인스턴스가 서버측에서 실행되는 것인지 확인 할 수 있다
Switch has Authority
- 현재의 스크립트가 서버측에서 실행되는 클라이언트 로직인지 리모트 클라이언트에서 실행되는 로직인지 확인하여 로직을 분기한다
- 현재 스크립트를 실행하는 머신이 서버인지 클라인지를 식별하여 실행내용을 다르게 작성할 수 있다
- 실행 중인 이벤트 함수가 서버측에서 실행 중인지 확인하여 그 결과를 다른 클라이언트에게 복제할 때도 사용할 수 있다
- 네트워크 게임은 모든 클라이언트의 화면의 내용이 같고 보는 방향만 다르므로 맵은 모두 동일하다고 할 수 있다
- 현재 실행되고 있는 머신이 서버인지 클라이언트인지를 구분하는 이유는 게임의 승패와 관련한 중요한 변수의 변경은 서버측 머신에서만 하려고 하기 때문이다
Function Replication (Remote Procedure Calls or RPCs for short)
https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/Networking/ReplicateFunction/Blueprints/index.html
https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Blueprints/index.html
- 로컬 머신에서 호출되어 다른 머신에서 실행된다
- 클라이언트, 서버간의 데이터 전송방법으로 유용하게 사용된다
- Reliable : 네트워크 상황과 무관하게 데이터가 반드시 전달된다
- Unreliable : 네트워크 상황에 따라 전달하려는 데이터가 손실될 수도 있다
- Multicast : 서버측에서 호출되어 서버에서 실행되고 모든 클라이언트들에게도 포워드된다
- Run on Server : 클라이언트 측에서 호출하고 서버에서만 실행된다
- Run on owning Client : 서버측에서 호출하고 소유한(owning client)클라이언트에서만 실행된다
- PlayerController 에 의해 소유된 폰은 위의 2가지 타입(Run on Server, Run on owning Client)의 함수를 가질 수 있다
- Run on Server, Run on owning Client 함수의 호출과 선언은 클라이언트의 PlayerController, PlayerState, PlayerController가 소유한 Pawn에서만 해야한다
Multicast Function 테스트
- Multicast 로 설정된 함수를 서버 윈도우에서 실행하면 서버 화면 뿐만 아니라 다른 모든 클라이언트 화면에서도 그대로 재현된다. 그러나 클라이언트 화면에서 Multicast 함수를 실행하면 현재 클라이언트에서만 효과가 나타난다
- Multicast 로 설정된 함수는 서버에서 호출되고 서버에서 실행되어 모든 클라이언트에게 포워드되어 실행되기 때문이다
Server Function 테스트
- 클라이언트가 서버로 데이터를 전송하기 위한 주요 방법이다
- Ron on Server 로 설정된 함수를 클라이언트에서 호출하면 서버측 화면에서만 효과가 나타나고 현재 클라이언트 화면에는 효과가 나타나지 않는다
- Run on Server로 설정된 함수는 클라이언트에서 호출되어 서버에서 실행되기 때문이다
- Run on Server로 설정된 함수를 호출하여 서버에서 특정액터의 변수의 값을 변경하고 그 액터가 Replicates 로 설정된 경우에는 클라이언트가 호출한 로직이 서버에서 실행되고 다른 클라이언트에게 Replicated된다
- 클라이언트 측에서 호출할 수 있으므로 Run on Server로 설정된 함수 안에서 다시 Multicast 함수를 호출하면 클라이언트가 호출한 로직은 서버에서 실행되고 모든 클라이언트에게 포워드되어 실행되므로 모든 클라이언트에서 효과가 나타난다
Run On Owning Client Function 테스트
- 서버에서 호출할 수 있고 특정 클라이언트만 선택하여 해당 클라이언트에서만 실행되도록 할 때 사용한다
- 예를 들어 서버측 스크립트 실행 중 특정 클라이언트의 폰이 트리거에 들어가는 경우 해당 폰을 소유한 클라이언트에게만 이벤트 실행결과를 보여주려는 경우에 사용할 수 있다
Replicating Variables
https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/Networking/ReplicateVariable/index.html
액터 클래스에 선언된 변수가 Replicated 로 설정된 경우, 서버에서 해당 변수의 값을 변경하면 모든 클라이언트 머신에도 변경된 값이 복제된다. 이 때 반드시 서버측에서 값이 변경된 경우에만 클라이언트에 복제가 된다
Actor Replication, Event Replication
https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/Networking/ReplicateActor/Blueprints/index.html
https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Blueprints/index.html
https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Actors/index.html
Testing Multiplayers
https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/Networking/TestMultiplayer/index.html
변수복제 VS 함수복제
변수복제와 함수복제를 위해서는 서버측에서 조작되는 객체가 클라이언트 측에도 있어야 한다. 그러한 이유로 변수복제와 함수복제에 사용할 수 있는 객체는 PlayerController 이거나 Pawn 일 경우가 많다
모든 클라이언트에 존재하는 PlayerController 객체는 서버측에도 생성된다. 그러므로 서버측에서 특정 PlayerController의 변수를 변경하고 그 변수의 값이 클라이언트 측에도 변경되도록 하려면 해당 변수를 Replication 설정해야 한다. 이렇게 하면 특정 PlayerController 의 속성을 변경하면 자동으로 해당 PlayerController 가 존재하는 클라이언트 측에서도 그 변수의 값이 변경된다.
서버측에서는 모든 클라이언트의 PlayerController를 가지므로 변수복제를 이용하여 모든 클라이언트의 화면에 다른 값을 표시할 수가 있다.
변수복제는 서버측에서 변경된 변수의 값이 클라이언트에서도 변경되도록 할 때 사용한다. 그 반대는 안된다
서버측에서 모든 클라이언트의 PlayerController를 구할 때는 Get All Actors of Class() 함수노드를 사용하면 된다
레벨에 올려진 캐릭터는 모든 클라이언트에서 서로를 볼 수가 있기 때문에 모든 클라이언트의 컴퓨터에는 모든 캐릭터가 기능을 하고 있다고 할 수 있다. 그런데 게임 중에서 특정 캐릭터가 점프를 한다면 모든 클라이언트 화면에서 해당 캐릭터가 점프하는 동작이 보여져야 한다. 이 때는 서버측에서 점프 동작을 수행하고 그 동작이 모든 클라이언트에 존재하는 해당 캐릭터에 그대로 재현되어야 한다. 이러한 일은 함수복제 중에서도 Multicast 설정으로 할 수 있다
서버측에서 특정 클라이언트의 캐릭터에 접근할 때는 PlayerController:: Get Controlled Pawn() 함수노드를 사용하면 된다