반응형

 

 

피격 처리를 위한 새로운 패킷 생성


전에 말했듯이 피격을 처리한느 방식을 변경했다.

그래서 기존의 콜리전을 생성해주는 코드를 비롯한 필요없는 부분들을 지워줬다.

그리고 공격할 때 마다 공격 정보를 담은 패킷을 보내서 동기화를 해주었다.

 

 

여기 까지는 큰문제 없이 잘 동작했는데 문제는 피격이었다.

아래와 같이 무기 콜리전이 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;

이렇게 코드를 수정해주니 체력도 정상적으로 동기화 되는 모습을 볼 수 있따.

 

 

반응형