피격 처리를 위한 새로운 패킷 생성
전에 말했듯이 피격을 처리한느 방식을 변경했다.
그래서 기존의 콜리전을 생성해주는 코드를 비롯한 필요없는 부분들을 지워줬다.
그리고 공격할 때 마다 공격 정보를 담은 패킷을 보내서 동기화를 해주었다.
여기 까지는 큰문제 없이 잘 동작했는데 문제는 피격이었다.
아래와 같이 무기 콜리전이 Overlap 되었을 때 피격 패킷을 보내서 서로의 체력을 동기화 해주려고 했는데,
A플레이어 화면에서는 B 플레이어의 체력이 두 번 깍이고,
B플레이어 화면에서는 아무런 일도 일어나지 않는다.
void AMyKallari::OnWeaponOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (IsNotify)
{
UE_LOG(LogTemp, Warning, TEXT("MyKallari_OnWeaponOverlapBegin"));
FDamageEvent DamageEvent;
if (OtherActor->TakeDamage(Damage, DamageEvent, GetController(), this))
{
Protocol::C_HIT pkt;
Protocol::AttackInfo* AttackInfo = new Protocol::AttackInfo();
AttackInfo->set_damage(Damage);
AttackInfo->set_ishit(true);
AttackInfo->set_attacker_id(Object_Id);
AttackInfo->set_object_id(Cast<ABoPlayer>(OtherActor)->Object_Id);
pkt.set_allocated_info(AttackInfo);
SEND_PACKET(pkt);
LeftWeaponCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
RightWeaponCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
}
void ABoPlayer::Other_Hit(const Protocol::AttackInfo& Info)
{
UE_LOG(LogTemp, Warning, TEXT("Other_Hit"));
FDamageEvent DamageEvent;
Protocol::AttackInfo* HitInfo = new Protocol::AttackInfo();
HitInfo->CopyFrom(Info);
if (GameInstance)
{
if(GameInstance->Players.Contains(HitInfo->object_id()))
GameInstance->Players[HitInfo->object_id()]->TakeDamage(HitInfo->damage(), DamageEvent, GetController(), this);
}
}
원인은 서버와 클라이언트의 HandleHit 함수 때문이었다.
A플레어가 B플레이어를 공격했을 때
1. A플레이어 화면에서 B플레이어의 체력이 두 번 깍임.
2. B플레이어 화면에서는 아무일도 일어나지 않음.
1. A플레이어가 B플레이어를 공격했을 때,
서버의 HandleHit 함수의 BroadCast를 통해 패킷이 A플레이어와 B플레이어 모두에게 전달 됨.
그리고 A플레이어 기준에서는 B플레이어가 MyPlayer가 아니기 때문에 Other_Hit 함수가 호출 됨.
그래서 콜리전이 overlap 되었을 때 한 번, Other_Hit을 통해 두 번 피가 까이게 된다.
2. B플레이어는 S_HIT 패킷을 받아도 B플레이어가 MyPlayer이기 때문에 Other_Hit 함수를 호출하지 못하고 리턴당한다.
그래서 B플레이어 화면에서는 아무일도 일어나지 않는다.
해결법 : 패킷에 공격자 아이디를 추가해서 공격자를 제외하고 브로드캐스팅 해주기 및
클라이언트의 HandleHit 함수에서 IsMyPlayer 체크하는 부분 빼기
그래서 아래와 같이 코드를 수정해주었다.
Room 코드 수정
void Room::HandleHit(Protocol::C_HIT pkt)
{
const uint64 attackerId = pkt.info().attacker_id();
//TODO : 플레이어의 체력 정보도 서버에 저장?
//PlayerRef player = dynamic_pointer_cast<Player>(_objects[objectId]);
//player->체력 정보->CopyFrom(pkt.info());
{
Protocol::S_HIT hitPkt;
{
Protocol::AttackInfo* info = hitPkt.mutable_info();
info->CopyFrom(pkt.info());
}
SendBufferRef sendBuffer = ClientPacketHandler::MakeSendBuffer(hitPkt);
Broadcast(sendBuffer, attackerId);
}
}
- Broadcast 함수를 호출할 때 공격자의 아이디는 제외하고 보내도록 수정.
UMyGameInstance 코드 수정
void UMyGameInstance::HandleHit(const Protocol::S_HIT& HitPkt)
{
if (Socket == nullptr || GameServerSession == nullptr)
return;
auto* World = GetWorld();
if (World == nullptr)
return;
const uint64 ObjectId = HitPkt.info().object_id();
ABoPlayer** FindActor = Players.Find(ObjectId);
if (FindActor == nullptr)
return;
ABoPlayer* Player = (*FindActor);
const Protocol::AttackInfo& Info = HitPkt.info();
Player->Other_Hit(Info);
}
- 내가 조종하는 플레이어도 체력이 깍여야 하므로 아래 조건 없어야 함.
(브로드캐스팅으로 피격당한 액터한테만 패킷 보냈기 때문에 아래 처럼 조건문 해 줄 필요 없음.)
if (Player->IsMyPlayer(Player->Type))
return;
이렇게 코드를 수정해주니 체력도 정상적으로 동기화 되는 모습을 볼 수 있따.
'언리얼 엔진 서버 연동' 카테고리의 다른 글
IOCP 서버 구조 + 서버 스트레스 테스트 (0) | 2024.04.23 |
---|---|
플레이어 사망 처리, 채팅 (0) | 2024.04.21 |
플레이어의 공격과 피격(3) (0) | 2024.04.20 |
플레이어의 공격과 피격(2) (0) | 2024.04.19 |
플레이어의 공격과 피격(1) (0) | 2024.04.18 |