히트스캔 형식의 무기에서 조준선을 기준으로 일정 수치만큼 떨어진 범위를 공격하게 함으로써 산탄 효과를 만들었는데 큰 문제점이 발생한다.
일정 범위 내에서 '무작위' 로 공격 지점을 선택하는데, 이 발사 기능은 서버에서부터 멀티캐스트되어 모든 클라이언트로 복제된다. 이 때 복제된 기능 각각에서 무작위 공격 지점을 선택하는 과정을 거치게 되면서 각각의 클라이언트마다 공격 방향이 제각각이 된다.
물론 '진짜' 발사의 방향은 서버에서 실행된 공격에 의해 결정되지만 클라이언트가 보기에는 내가 상대방을 맞췄는데 맞추지 않은 것으로 판정되거나, 상대방의 공격을 피했는데 맞은 것으로 판정되는 등, 게임 플레이가 보이는 것과 다르게 진행될 수 있다.
따라서 구조를 일부 수정함으로써 서버에서 발사 기능을 수행하기 전에 공격 방향을 먼저 설정하고, 그 방향 벡터를 인자로 전달함으로써 모든 클라이언트로 하여금 일관된 방향으로 발사할 수 있도록 해야 한다.
HitTarget = EquippedWeapon->bUseScatter ? EquippedWeapon->TraceEndWithScatter(HitTarget) : HitTarget;
ServerFire(HitTarget);
HitTarget
은 TickComponent()
에서 매 틱마다 조준선의 중심으로 트레이스를 실시한 결과를 가지고 있다. 무기가 산탄 기능을 사용하지 않을 경우에는 이 값을 그대로 사용하고 그렇지 않다면 TraceEndWithScatter()
함수를 통해 방향을 지정한다.
FVector AWeapon::TraceEndWithScatter(const FVector& HitTarget)
{
const USkeletalMeshSocket* MuzzleFlashSocket = GetWeaponMesh()->GetSocketByName("MuzzleFlash");
if (MuzzleFlashSocket == nullptr)
{
return FVector();
}
FTransform SocketTransform = MuzzleFlashSocket->GetSocketTransform(GetWeaponMesh());
FVector TraceStart = SocketTransform.GetLocation();
FVector ToTargetNormalized = (HitTarget - TraceStart).GetSafeNormal();
FVector SphereCenter = TraceStart + ToTargetNormalized * DistanceToSphere;
FVector RandomVector = UKismetMathLibrary::RandomUnitVector() * FMath::FRandRange(0.f, SphereRadius);
FVector EndLocation = SphereCenter + RandomVector;
FVector ToEndLocation = EndLocation - TraceStart;
return FVector(TraceStart + ToEndLocation * TRACE_LENGTH / ToEndLocation.Size());
}
이 HitTarget
을 인자로 하여 멀티캐스트되는 Fire()
함수에서는 방향 그대로 트레이스를 실시하여 얻은 FHitResult
에 ApplyDamage
함수를 호출한다.
그 결과 두 유저 간의 탄 발사 정보가 일관되게 유지되는 것을 확인할 수 있다.