복제
서버-클라이언트의 구조를 가진 게임 플레이 환경에서, 실질적인 게임의 실행은 서버에서만 일어나고 나머지 클라이언트에서는 게임 상태들을 서버로부터 복제받아 미러링하여 사용자의 기기에서 서버 환경을 동기화한다.
따라서 복제는 항상 서버 -> 클라이언트의 방향으로만 발생한다.
변수가 복제될 때 자동으로 호출되는 함수이다.
ReplicatedUsing
UPROPERTY를 통해 서로 바인딩할 수 있다.
사용자의 입력 등과 같이, 클라이언트 -> 서버의 방향으로 정보가 전달되어야 할 때가 있다. 다른 기기, 예컨대 서버에서 함수를 실행할 수 있도록 하는 함수를 RPC라고 한다.
예를 들어 총을 발사하는 인풋을 클라이언트가 보냈다고 가정할 때 다른 모든 클라이언트에서도 이와 관련한 애니메이션을 비롯한 여러 기능들을 실행해야 한다. 이 때 서버 측에서 실행되는 Server
RPC를 호출하고, 그 서버RPC 안에서 전체 클라이언트에 멀티캐스트 되는 Multicast
RPC를 재호출할 수 있다.
재장전 도중 재차 재장전 기능을 실행한다거나 총을 발사할 수 없도록 하기 위해 현재 캐릭터의 state를 나타낼 수 있는 enum class를 만든다.
UENUM(BlueprintType)
enum class ECombatState : uint8
{
ECS_Unoccupied UMETA(DisplayName = "Unoccupied"),
ECS_Reloading UMETA(DisplayName = "Reloading"),
ECS_MAX UMETA(DisplayName = "DefaultMAX")
};
그리고 인풋과 연결된 Reload()
함수는 갖고 있는 총알 수와 ECombatState
를 통해 유효성을 검증한 후 server RPC를 호출한다.
void UCombatComponent::Reload()
{
if (CarriedAmmo > 0 && CombatState != ECombatState::ECS_Reloading)
{
ServerReload();
}
}
재장전이 시작되면 곧장 ECombatState
를 Reloading
으로 수정하고 애니메이션 몽타주를 실행한다. 그리고 앞으로의 확장성을 위해 멀티캐스트 RPC를 호출하기보다는 ECombatState
를 rep 변수로 만들고, 이에 대한 rep notify 함수를 통해 멀티캐스트로 동작하게끔 하자.
이 rep notify 함수에서는 switch 문을 통해 각 state별로 실행할 동작들을 정의하며, 지금은 간단히 애니메이션 몽타주만 실행하도록 한다.
void UCombatComponent::ServerReload_Implementation()
{
if (Character == nullptr)
{
return;
}
CombatState = ECombatState::ECS_Reloading;
HandleReload();
}
void UCombatComponent::HandleReload()
{
Character->PlayReloadMontage();
}
void UCombatComponent::OnRep_CombatState()
{
switch (CombatState)
{
case ECombatState::ECS_Reloading:
HandleReload();
break;
}
}
마지막으로 재장전 동작이 끝났을 때 다시 재장전 기능을 호출할 수 있게 하고, 탄약을 갱신하는 등의 기능을 하기 위해 FinishReloading()
함수를 만들고 이를 애니메이션 몽타주에서 notify 할 수 있게 하기 위해 UFUNCTION(BlueprintCallable)
을 명시한다.
void UCombatComponent::FinishReloading()
{
if (Character == nullptr)
{
return;
}
if (Character->HasAuthority())
{
CombatState = ECombatState::ECS_Unoccupied;
}
}
재장전이 완료되는 시점, 예컨대 탄창을 다시 총기에 끼워 넣는 애니메이션이 진행되는 시점에 재장전이 완료될 수 있게끔 한다.
해당하는 프레임에 노티파이를 달고, 애니메이션 이벤트 그래프에서 해당 노티파이가 발생했을 때 FinishReloading()
을 실행하도록 블루프린트를 구성한다.