

언리얼에서 제공하는 RPC 공식 문서다.
저번에 이걸 이용해 클라이언트(서버 포함)가 조준을 호출한 경우, 다른 클라이언트들에게 해당 사항을 알려줘 똑같이 조준 애니메이션이 실행되는 기능을 구현했다.
그럼 이번에 구현한 총기 격발 기능을 보자.
void UCombatComponent::FireButtonPressed(bool bPressed)
{
bFireButtonPressed = bPressed;
if (bFireButtonPressed)
{
ServerFire();
}
}
void UCombatComponent::ServerFire_Implementation()
{
MulticastFire();
}
void UCombatComponent::MulticastFire_Implementation()
{
if (EquippedWeapon == nullptr) return;
if (Character)
{
Character->PlayFireMontage(bAiming);
EquippedWeapon->Fire();
}
}
위 로직을 천천히 살펴보자.
클라이언트가 FireButtonPressed를 호출하면(좌클릭을 누르면), ServerFire를 호출해 서버에게 격발 기능 실행을 요청한다.
서버는 그 요청을 받아 MulticastFire를 실행한다.
UFUNCTION(NetMulticast, Reliable)
void MulticastFire();
서버에서 실행된 NetMulticast속성의 함수는 서버를 포함한 모든 클라이언트에서 실행된다.
//BlasterCharacter.cpp
void ABlasterCharacter::PlayFireMontage(bool bAiming)
{
if (!IsWeaponEquipped()) return;
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
if (AnimInstance && FireWeaponMontage)
{
AnimInstance->Montage_Play(FireWeaponMontage);
FName SectionName;
SectionName = bAiming ? FName("RifleAim") : FName("RifleHip");
AnimInstance->Montage_JumpToSection(SectionName);
}
}
//Weapon.cpp
void AWeapon::Fire()
{
if (FireAnimation)
{
WeaponMesh->PlayAnimation(FireAnimation, false);
}
}
MulticastFire를 통해 호출되는 함수들의 내용이다.
캐릭터는 FireWeaponMontage라는 애니메이션 몽타주를 재생할 거고, Weapon은 FireAnimation이라는 애니메이션 에셋을 재생할 거다.


애니메이션의 Additive 세팅을 통해 반동으로 인한 떨림을 나타냈다.
캐릭터는 이걸 재생할 거다.

이건 Weapon이 재생할 애니메이션 에셋이다. 사운드도 함께 포함되어있다.
그럼 우선 작동하는 모습부터 보자.

왼쪽 아래가 내가 조작 중인 클라이언트다.
여기까지가 강의에서 알려준 내용이다.
얼핏 보기엔 전혀 문제 없어보인다.
하지만!

이건 콘솔 명령어로 net pktlag=300을 입력해 고의로 핑을 300까지 높인 모습이다.
저번에 구현했던 조준 애니메이션 출력의 경우, '자신은 즉시, 다른 사람들은 패킷을 받는대로' 출력하고 있다.
하지만 격발은? 자신마저 서버에 다녀온 패킷을 받아 격발하고 있다.
그러니까 핑이 300이면, 좌클릭을 누른 후 적어도 0.6초는 지나야 격발이 된다는 거다.
이러면 조작감이 상당히 불쾌해질 수밖에 없다.
마찬가지로 이번에도 클라이언트는 즉시 격발하게끔 바꾸겠다.
그런데 이번엔 Replicate되는 변수가 없다. 모두 함수로 이루어져있다.
이 경우 클라이언트에서 일단 격발하고 Multicast가 실행될 때 서버에서 요청한 클라이언트를 걸러주는 수밖에 없다.
void UCombatComponent::FireButtonPressed(bool bPressed)
{
bFireButtonPressed = bPressed;
if (bFireButtonPressed)
{
LocalFire();
}
}
void UCombatComponent::LocalFire()
{
if (EquippedWeapon == nullptr) return;
if (Character && Character->IsLocallyControlled())
{
Character->PlayFireMontage(bAiming);
EquippedWeapon->Fire();
ServerFire();
}
}
void UCombatComponent::ServerFire_Implementation()
{
MulticastFire();
}
void UCombatComponent::MulticastFire_Implementation()
{
if (EquippedWeapon == nullptr) return;
if (Character && !Character->IsLocallyControlled())
{
Character->PlayFireMontage(bAiming);
EquippedWeapon->Fire();
}
}
이건 변경한 로직이다.
좌클릭 시 LocalFire를 통해 일단 격발하고, Server에 요청한다.
서버는 요청을 받아 MulticastFire를 호출, 해당 내용을 모두에게 뿌려준다.
MulticastFire에선 애니메이션을 재생할 캐릭터가 자신이 아닌 경우에만 격발 애니메이션을 재생한다.

위 작성 내용을 적용한 모습이다.
마찬가지로 핑 300 환경이 되도록 콘솔 명령어를 입력했다.
왼쪽 아래 클라이언트는 좌클릭 즉시 애니메이션들을 재생하고, 위쪽 서버와 오른쪽 아래의 다른 클라이언트는 패킷을 받는대로 애니메이션을 재생하고 있다.
강의 내용을 따라가되, 내 취향대로 코드를 어느 정도 수정하니까 더 재밌게 강의를 들을 수 있어서 좋다.