728x90
반응형
- 정리
- 게임 모드와 로그인
- 게임 모드와 플레이어 컨트롤러를 활용한 로그인 플로우의 이해
- 네트워크 모드의 확인
- 네트웍 멀티플레이어용 로그 매크로 제작
- 게임 플레이 시작 과정의 이해
- 게임 모드와 로그인
- Listen Server의 경우 네트워크 부하가 많은 게임에는 적합하지 않고, 소규모 플레이어 그룹간의 캐주얼한 협동 및 경쟁 멀티플레이어에는 유리하다.
- 게임 사본이 있으면 누구나 같은 컴퓨터에서 Listen Server를 시작하고 플레이할 수 있고, 서버를 시작하거나 참여할 서버를 검색할 수 있다. 별도로 구현해줘야 함
- Dedicated Server의 경우 비주얼이 존재하지않는 서버를 돌리기 위한 서버. 대규모 멀티플레이에 적합하다. 로컬 플레이어가 없음
- 별도의 서버를 돌리기 위한 설비 및 비용이 발생
- 멀티 플레이어 게임으로 만드는 모든 로직은 추가 작업 없이 모두 싱글 플레이어에서 잘 작동한다.
- 게임 모드 기반의 로그인 플로우 (Listen Server)
- 서버를 담당할 어플리케이션은 실행되었지만, 게임은 아직 시작되지 않은 상황
- 네트워크 기능도 아직 활성화 되지 않은 상황
- Stand alone이나 서버에는 게임에서 중요한 심판 역할을 하는 게임 모드 액터가 존재
- 리슨 서버의 경우 클라이언트와 서버의 기능이 함께 공존
- 그렇기 때문에 서버를 돌리는 PC 또한, 게임에 참여해야한다. 게임에 참여하기 위해서는 반드시 플레이어가 있어야 하기 때문에 플레이어를 대표하는 플레이어 컨트롤러가 생성해야한다.
- 게임 모드를 사용해 플레이어 컨트롤러를 생성한다.
- 서버는 클라이언트의 접속 요청을 거부할 수도 있음
- 접속 요청을 허용하면 플레이어 컨트롤러가 생성된다.
- 클라이언트를 대표하는 플레이어 컨트롤러는 클라이언트에 복제된다.
- 클라이언트는 서버가 가지고 있는 컨텐츠의 허상을 복제해서 사용자에게 보여진다. 이때 서버에 있는 모든 정보를 다 전달하지 않고 보여지는 데 문제없을 정도로만 컨텐츠가 효율적으로 복제 된다.
- 네트워크 데이터는 딱 필요한 만큼만 사용하는 것이 좋다.
- 클라이언트는 접속하자마자 게임을 시작하게 된다.
- 네트웍 멀티플레이어용 로그 매크로 제작
// 매크로 지정, 어느 타이밍에 로그가 호출되었는지에 대한 정보를 가져올 수 있다. String TCHAR로 변환해주는 매크로 추가
#define LOG_CALLINFO ANSI_TO_TCHAR(__FUNCTION__)
// 로그 매크로 정의
#define AB_LOG(LogCat, Verbosity, Format, ...) UE_LOG(LogCat, Verbosity, TEXT("%s %s"), LOG_CALLINFO, *FString::Printf(Format, ##__VA_ARGS__))
DECLARE_LOG_CATEGORY_EXTERN(LogABNetwork, Log, All);
- ArenaBattle.h
DEFINE_LOG_CATEGORY(LogABNetwork);
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ArenaBattle, "ArenaBattle" );
- ArenaBattle.cpp
- 어느 타이밍에 로그가 호출되었는지 확인하기 위해 매크로를 만들고, AB_LOG라고 만든 커스텀 로그 매크로를 통해 어느 지점에서 로그가 찍히는지 확인할 수 있다.
- 매크로는 접근하기 용이한 ArenaBattle 클래스에 제작함
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Interface/ABGameInterface.h"
#include "ABGameMode.generated.h"
UCLASS()
class ARENABATTLE_API AABGameMode : public AGameModeBase, public IABGameInterface
{
GENERATED_BODY()
public:
AABGameMode();
virtual void OnPlayerDead() override;
// GameModeBase에 있는 로그인 관련 함수들
virtual void PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) override;
virtual APlayerController* Login(UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) override;
virtual void PostLogin(APlayerController* NewPlayer) override;
virtual void StartPlay() override;
};
- ABGameMode.h
// ...
// GameModeBase에 있는 로그인 관련 함수들
// PreLogin : 클라이언트의 접속 요청을 처리하는 함수
void AABGameMode::PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage)
{
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
}
// Login : 접속을 허용한 클라이언트에 대응하는 플레이어 컨트롤러를 만드는 함수
APlayerController* AABGameMode::Login(UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage)
{
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
APlayerController* NewPlayerController = Super::Login(NewPlayer, InRemoteRole, Portal, Options, UniqueId, ErrorMessage);
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
return NewPlayerController;
}
// PostLogin : 플레이어 입장을 위해 플레이어에 필요한 기본 설정을 모두 마무리하는 함수
void AABGameMode::PostLogin(APlayerController* NewPlayer)
{
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
Super::PostLogin(NewPlayer);
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
}
// StartPlay : 게임의 시작을 지시하는 함수
void AABGameMode::StartPlay()
{
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
Super::StartPlay();
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
}
- ABGameMode.cpp
- 다음과 같이 어느 타이밍에 로그가 호출되는지 파악할 수 있도록 작성해준다.
- 게임 모드의 주요 함수
- PreLogin : 클라이언트의 접속 요청을 처리하는 함수 / 로그인이 된 상태는 아님
- Login : 접속을 허용한 클라이언트에 대응하는 플레이어 컨트롤러를 만드는 함수
- 그래서 return 값이 APlayerController*임을 볼 수 있다.
- PostLogin : 플레이어 입장을 위해 플레이어에 필요한 기본 설정을 모두 마무리하는 함수
- StartPlay : 게임의 시작을 지시하는 함수
- BeginPlay : 게임 모드의 StartPlay를 통해 게임이 시작될 때 모든 액터에서 호출하는 함수
- 서버에서는 PreLogin이라는 과정 자체가 없다. 굳이 로그인을 할 필요가 없기 때문. 그렇기 때문에 Login부터 시작
- 서버와는 다르게 클라이언트는 PreLogin함수가 호출
- 로그인 과정에서 BeginPlay가 호출된다. 클라이언트가 생기기전에 StartPlay라고 게임이 시작됐다라고 선언했기 때문
- 다만, Client의 로그를 보면 BeginPlay가 두번 호출된 것을 볼 수 있는데, 이는 서버의 액터에서 1번, 클라이언트의 액터에서 1번 호출된 것이다.
// GetNetMode를 활용해 Client인지, Standalone인지, Server 인지
#define LOG_NETMODEINFO ((GetNetMode() == ENetMode::NM_Client)? *FString::Printf(TEXT("CLIENT%d"),GPlayInEditorID) : ((GetNetMode()==ENetMode::NM_Standalone) ? TEXT("STANDALONE") : TEXT("SERVER")))
// 매크로 지정, 어느 타이밍에 로그가 호출되었는지에 대한 정보를 가져올 수 있다. String TCHAR로 변환해주는 매크로 추가
#define LOG_CALLINFO ANSI_TO_TCHAR(__FUNCTION__)
// 로그 매크로 정의
#define AB_LOG(LogCat, Verbosity, Format, ...) UE_LOG(LogCat, Verbosity, TEXT("[%s] %s %s"), LOG_NETMODEINFO, LOG_CALLINFO, *FString::Printf(Format, ##__VA_ARGS__))
DECLARE_LOG_CATEGORY_EXTERN(LogABNetwork, Log, All);
- ArenaBattle.h
- 추가로 GetNetMode를 활용해 Client인지, Standalone인지, Server인지를 알 수 있게끔 매크로를 추가해준다.
- PreLogin에 해당 문자열을 추가함으로써 분리시켜준다.
- 해당 지점부터 클라이언트가 새롭게 추가되었다라고 인식할 수 있다.
- 게임의 시작
- 게임 모드의 StartPlay 함수가 호출되면 서버에서는 공식적으로 게임이 시작되는 것을 의미한다. 그래서 이후에 생성된 모든 액터들은 BeginPlay 함수가 자동으로 호출된다.
- 하지만, 클라이언트에는 게임 모드가 없다.
- 서버는 게임 스테이트라고 하는 특별한 액터를 사용한다. 게임 스테이트는 게임 정보를 알려주는 특별한 액터. 게임 모드와 다르게 게임 스테이트 액터는 서버와 클라이언트에 모두 존재
- 게임 모드는 게임을 시작할 때 직접 게임을 시작하지 않고 게임 스테이트에 명령을 내려 간접적으로 지시한다. 게임 모드의 명령을 받은 게임 스테이트는 월드에 속한 모든 액터들에게 BeginPlay를 호출하도록 지시를 하게 된다.
#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "ABGameState.generated.h"
UCLASS()
class ARENABATTLE_API AABGameState : public AGameStateBase
{
GENERATED_BODY()
public:
// GameStateBase에 있는 함수들
// 게임모드에서 StartPlay를 게임스테이트에 지시하여 현재 월드에 있는 모든 액터들에게 BeginPlay를 호출하고 게임을 시작하라고 명령을 내림 / 로컬의 로직이기때문에 서버에서만 실행이 됨
virtual void HandleBeginPlay() override;
// 클라이언트에서 원격으로 호출된 함수
// GameStateBase의 bReplicatedHasBegunPlay라고 하는 프로퍼티가 서버로부터 전송돼서 변경이 감지되면 함수가 호출되며 클라이언트가 신호를 받는 함수
virtual void OnRep_ReplicatedHasBegunPlay() override;
};
- ABGameState.h
#include "Game/ABGameState.h"
#include "ArenaBattle.h"
void AABGameState::HandleBeginPlay()
{
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
Super::HandleBeginPlay();
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
}
void AABGameState::OnRep_ReplicatedHasBegunPlay()
{
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
Super::OnRep_ReplicatedHasBegunPlay();
AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
}
- ABGameState.cpp
- GameStateBase를 상속받은 클래스를 생성해준다.
- HandleBeginPlay()와 OnRep_ReplicatedHasBegunPlay()함수를 오버라이딩해준다.
- HandleBeginPlay()
- 게임모드에서 StartPlay를 게임스테이트에 지시하여 현재 월드에 있는 모든 액터들에게 BeginPlay를 호출하고 게임을 시작하라고 명령을 내림 / 로컬의 로직이기때문에 서버에서만 실행이 됨
- OnRep_ReplicatedHasBegunPlay()
- GameStateBase의 bReplicatedHasBegunPlay라고 하는 프로퍼티가 서버로부터 전송돼서 변경이 감지되면 함수가 호출되며 클라이언트가 신호를 받는 함수
- HandleBeginPlay()
- 서버에서는 StartPlay가 호출되면 HandleBeginPlay가 호출되고 BeginPlay가 호출되는 모습을 볼 수 있다.
- 클라이언트는 복제된 GameState에 의해서 OnRep_ReplicatedHasBegunPlay가 호출되고 그로 인해 BeginPlay가 호출되는 모습을 볼 수 있다.
해당 포스트는 인프런의 <이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이어 프레임웍의 이해>
강의를 수강하고 정리한 내용입니다.
이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이 프레임웍의 이해 강의 | 이득우 - 인프런
이득우 | 또 하나의 언리얼 엔진이라고도 불리는 네트웍 멀티플레이어 프레임웍을 학습합니다. 네트웍 멀티플레이어 게임을 제작할 때 반드시 알아야 하는 주요 개념, 내부 동작 원리, 최적화
www.inflearn.com
728x90
'공부 > 이득우의 언리얼 프로그래밍' 카테고리의 다른 글
[Study] Part 3 - 액터의 역할과 커넥션 핸드셰이킹 (4/15) (0) | 2024.08.14 |
---|---|
[Study] Part 3 - 커넥션과 오너십 (3/15) (0) | 2024.08.12 |
[Study] Part 3 - 언리얼 네트웍 멀티플레이어 프레임웍 개요 (1/15) (0) | 2024.08.09 |
[Study] Part 2 - 게임의 완성 (15/15) (0) | 2024.08.07 |
[Study] Part 2 - 게임플로우 다듬기 (14/15) (0) | 2024.06.24 |