멀티플레이 개발
Numbers = Numbers.FilterByPredicate(
[](int32 Num)
{
return Num > 0;
}
);
FilterByPredicate(...)는 Numbers의 각 원소를 람다 함수에 넣고, true가 나온 원소만 모아서 새로운 TArray를 반환해주는 함수이다.
std::vector에서 반복문과 if를 사용하여 거르던 작업을 FilterByPredicate()에서 대신 해주는 것과 같다.
단순히 Numbers.FilterByPredicate(...); 한다고 Numbers가 바뀌는게 아니라 새 배열을 반환하므로 받아줘야한다.
Numbers = Numbers.FilterByPredicate(...);
FChar::IsDigit(TEXT('3')); // true
FChar::IsDigit(TEXT('8')); // true
FChar::IsDigit(TEXT('A')); // false
FChar::IsDigit(TEXT('#')); // false
IsDigit()은 현재 문자 C가 숫자 모양의 문자인지 확인한다.
for (TCHAR C : InNumberString)
{
if (FChar::IsDigit(C) == false || C == TEXT('0'))
{
return false;
}
}
이런 식으로 사용가능
PlayerState는 플레이어 한 명의 공개 상태를 저장하는 Actor이다.
예를 들어 플레이어 이름, 점수, 킬 수, 팀, 시도 횟수 같은 데이터를 둔다.
플레이어가 A,B 두 명이라면 서버에는 원본 PlayerState가 각각 존재한다.
Server
├─ PlayerState_A 원본
└─ PlayerState_B 원본
그리고 각 클라이언트에는 모든 플레이어의 PlayerState복제본이 생성된다.
Client A
├─ PlayerState_A 복제본
└─ PlayerState_B 복제본
Client B
├─ PlayerState_A 복제본
└─ PlayerState_B 복제본
PlayerState가 하나의 객체를 모든 컴퓨터가 공유하는 것은 아니다.
서버의 PlayerState와 ClientPlayerState는 서로 다른 인스턴스이며, 서버 원본의 값이 클라이언트 복제본으로 동기화되는 구조이다.
만약 서버에서 Client A의 변수를 변경하면, 각 클라이언트에 존재하는 A의 PlayerState복제본도 같은 값으로 갱신된다. 반대로 클라이언트가 자신의 PlayerState 복제본 값을 직접 변경해도 서버에는 전달되지 않습니다.
따라서 클라이언트는 Server RPC로 행동을 요청하고, 서버가 PlayerState원본을 수정해야 한다.
또 각 클라이언트는 자기 PlayerController만 가지지만, PlayerState는 다른 플레이어의 것도 가집니다.
즉, PlayerState는 서버에 플레이어별 원본이 존재하고, 각 원본에 대응하는 복제본이 모든 클라이언트에 생성된다. 서버에서 원본 값을 변경하면 각 클라이언트의 복제본으로 동기화된다.
bReplicates = true;
이건 " 이 Actor 자체를 네트워크 복제 대상으로 지정한다. " 라는 뜻이다.
Actor 자체가 복제되지 않으면, 그 안의 변수를 복제할 대상도 클라이언트에 존재하지 않는다.
다만 APlayerState는 원래 서버에서 플레이어마다 생성되고 모든 클라이언트에 복제되도록 설계된 클래스이다. 따라서 직접 만든 일반 Actor와 달리, PlayerState에서는 보통 bReplicates = true를 다시 적지 않아도 된다.
일반 Actor라면 보통 생성자에서 설정
UPROPERTY(Replicated)
int32 CurrentGuessCount;
이건 " 이 Actor 안에 있는 CurrentGuessCount를 복제 변수로 사용한다. " 라는 선언이다.
서버에서 값이 바뀌면 클라이언트에 존재하는 해당 Actor 복제본의 값도 갱신된다. 복제 프로퍼티는 기본적으로 서버가 변경한 값을 연결된 클라이언트의 로컬 Actor 복제본에 전달하는 구조이다.
하지만 UPROPERTY(Replicated)만 붙인다고 끝나지는 않는다.
virtual void GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps
) const override;
이 함수는 " 이 클래스에서 실제로 어떤 변수들을 네트워크 복제 목록에 등록할 것인가? " 를 엔진에 알려주는 함수이다.
복제 프로퍼티를 가진 Actor 클래스는 이 함수를 오버라이드해서 해당 프로퍼티를 등록해야 한다.
#include "Net/UnrealNetwork.h"
void AMyPlayerState::GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps
) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyPlayerState, PlayerNameString);
DOREPLIFETIME(AMyPlayerState, CurrentGuessCount);
DOREPLIFETIME(AMyPlayerState, MaxGuessCount);
}
DOREPLIFETIME()은 AmyPlayerState클래스의 CurrentGuessCount 변수를 복제 목록에 등록한다는 뜻이다.
여기서 Lifetime은 변수 값이 영구 저장된다는 뜻이 아니다. 해당 Actor가 네트워크에 존재하는 동안 이 프로퍼티를 복제 대상으로 관리한다. 라는 뜻이다.
기본형은 다음과 같다.
DOREPLIFETIME(클래스 이름, 변수 이름);
bReplicates = true
→ Actor 자체를 클라이언트에 복제할 수 있게 함
UPROPERTY(Replicated)
→ 해당 변수를 복제 변수라고 표시함
GetLifetimeReplicatedProps()
+ DOREPLIFETIME()
→ 해당 변수를 실제 복제 목록에 등록함
일반 Acotr에서는 세요소를 한 세트로 볼 수 있지만, PlayerState에서는 Actor 자체가 원래 복제되므로 보통 bReplicates = true를 제외한 두 단계가 핵심이다.