내일배움캠프 78일차 - 언리얼 엔진 C++ 멀티플레이어: 디버그 로깅과 네트워크 함수
전처리기 명령어
#pragma region {블록명} ~ #pragma endregion
: 두 블록 사이의 영역을 접을 수 있게 해준다.
: 이는 언리얼 뿐만 아니라 다른 코드에서도 사용할 수 있는 기능이다.
Assert 매크로
언리얼 엔진 환경에서 사용할 수 있는, 특정 상황에서 실행을 종료하고 문제 발생 지점을 체크할 수 있는 Assert 매크로 관련 코드이다.
매크로 발동 조건에는 DO_GUARD_SLOW / DO_CHECK / DO_ENSURE 가 있으며, 각 빌드모드에 따라 1(True), 0(False)로 갈린다.
check(표현식) (DO_CHECK == 1 일때 작동)
: 언리얼의 Assert매크로. 반환값이 false라면 실행을 중지시킨다.
checkf(표현식, 문자열포맷) (DO_CHECK == 1 일때 작동)
: check와 동일하지만, 실행 중지 시 매개변수에 입력된 메세지를 출력한다.
verify(표현식) (DO_CHECK 무관)
: check와 동일하지만, DO_CHECK에 무관하게 작동한다.
verifyf(표현식, 문자열포맷) (DO_CHECK 무관)
: checkf와 동일하지만, DO_CHECK에 무관하게 작동한다.
언리얼 매크로
FORCEINLINE 함수명
: 함수를 강제적으로 inline화 시킨다.
: inline화란 컴파일 단계에서 컴파일러가 함수 호출 지점에 함수 내용을 갖다 붙이는 것을 의미한다.
: getter, setter 등 간단한 함수를 호출할 때 발생하는 오버헤드 비용이 호출지점에서 함수로 이동하는 것보다 코드내용을 직접 실행하는 것이 더 효율이 좋을 수 있다.
DECLARE_LOG_CATEGORY_EXTERN(Category, DefaultVerbosity, CompileTimeVerbosity)
: UE_LOG에 사용할 로그 카테고리를 자작하겠다는 뜻이다. 선언 후 빌드 시 해당 로그타입을 사용하고 필터링에 사용할 수도 있다.
: 이후 DEFINE_LOG_CATEGORY(Category)를 붙여 해당 자작로그를 사용할 것을 선언할 수 있다.
- Category: 새로 정의할 카테고리명
- DefaultVerbosity: 로그 출력, 표시를 어떻게 할 지
- CompileTimeVerbosity: 최종 빌드에 포함할 로그 수준을 결정
ANSI_TO_TCHAR(__FUNCTION__)
: 현재 호출된 함수(__FUNCTION__)의 이름을 TCHAR로 가져온다.
UE_DISABLE_OPTIMIZATION ~ UE_ENABLE_OPTIMIZATION
: 중단점 디버깅을 위한 매크로. 두 매크로 사이의 영역의 최적화를 막아 중단점이 스킵 되지않게 한다.
: DebugGame Editor에서라면 최적화를 하지 않아 대다수의 중단점 체크가 가능하지만 Development Editor에서는 해당 매크로를 사용해줘야 한다.
언리얼 함수
Cast<클래스>
: 해당 파생클래스로 형변환하는 함수. 캐스팅이 성공하면 변환된 객체를 반환, 실패하면 nullptr을 반환한다.
CastChecked<클래스>
: Cast와 비슷하나 캐스팅 실패 시 checkf()를 호출하여 예외를 발생, 캐스팅이 반드시 성공해야 하고 실패 시 프로그램을 종료해야 하는 경우에 적합하다.
언리얼 네트워크에서 사용되는 데이터 단위
넷커넥션(NetConnection)
- 모든 멀티플레이 관련 데이터들이 드나드는 "통로".
- 서버는 접속한 클라이언트의 개수만큼 넷커넥션을 가진다.
- 클라이언트는 서버와의 통신을 위한 단 하나의 넷커넥션만 가진다.
채널(Channel)
- 넷커넥션 개체가 관리하는 속성.
- ActorChannel, ControlChannel, VoiceChannel들을 관리한다. (ActorChannel이 Actor Replication과 관련된 채널)
패킷(Packet), 번치(Bunch)
- 네트워크에서 사용되는 통상적인 데이터 단위
- 언리얼에서는 번치(Bunch)라는 특별한 패킷 단위를 사용한다.
멀티플레이 디버깅 팁
플레이어의 접속을 막고싶다면?
"AGameModeBase::PreLogin() override" 함수에서 'ErrorMessage' 매개변수 문자열을 추가하면 접속을 거부한다. 비어 있으면 접속을 허용한다.NetConnection의 생성을 살펴보고 싶다면?
- 접속 후 서버에서의 ClientConnection 생성 확인
: "AGameModeBase::PostLogin() override" 함수는 로그인 성공 후 호출되므로 이때 확인하면 된다. 또한, AGameModeBase는 서버에만 존재한다.
- 접속 후 클라이언트에서의 ServerConnection 생성 확인
: APlayerController가 클라이언트를 대변한다.
: "APlayerController::PostNetInit() override" 함수는 액터의 네트워크 관련 속성들이 모두 초기화 된 후에 호출되는 함수이므로 NetConnection 관련 속성도 초기화 되었을 것이므로 이때 확인하면 된다.
AActor::BeginPlay() 함수의 호출 과정
GameMode::StartPlay() 함수가 호출되지 않으면 (Super::StartPlay() 미호출), 액터들의 BeginPlay() 함수도 호출되지 않는다.
GameMode는 서버에만 존재하므로 다른 클라이언트에게도 신호를 보내기 위해 각 PC의 GameState에게 BeginPlay() 함수 호출을 지시한다. (AGameStateBase::HandleBeginPlay)
시작 관련 주요 이벤트 함수
PostInitializeComponents()
- 해당 액터에 부착된 컴포넌트가 모두 준비된 상태에 호출된다.
PostNetInit()
- 서버에서 해당 액터의 속성 등 변경된 내용 세팅이 모두 완료된 뒤 클라이언트에 복제되었을 때 호출되는 함수이다.
- 멀티플레이에서는 BeginPlay()가 호출되려면 해당 액터에 대한 서버에서의 처리가 모두 완료되어야 한다.
GameMode::StartPlay()
- 게임의 시작을 지시하는 GameMode의 함수이다.
BeginPlay()
- GameMode의 StartPlay() 함수를 통해 게임이 시작될 때 모든 액터에서 호출되는 함수이다.Owner설정 관련 함수
APawn::PossessedBy(AController* NewController)
APawn::UnPossessed()
AController::OnPossess(APawn* InPawn)
※ 주의
Possess(), OnPossess()는 클라이언트에서 호출되지 않는다. 대신 AActor::Owner가 ReplicatedUsing 키워드가 달린 속성을 가지므로, Owner가 초기화 되면 아래의 OnRep_Owner() 함수가 클라이언트에서 호출된다.
댓글
댓글 쓰기