Unreal Engine Listen Server _ 03 ( 캐릭터 공격 구현 )

2026. 6. 8. 12:24·Unreal Project/Unreal Study

오늘은 Listen Server 환경에서 캐릭터 공격이 서버와 클라이언트 간에 어떻게 동기화 되는지 구현해보고자 합니다.

 

멀티플레이 환경에서는 각 플레이어의 행동이 다른 플레이어에게도 동일하게 보이도록 동기화 과정이 필요합니다.

특히 공격처럼 애니메이션, 판정, 데미지 처리가 함께 이루어지는 기능은 서버와 클라이언트 간 역할을 명확히 나누는 것이 

중요합니다.

 

 

 

구현 결과

 

캐릭터 공격 처리 순서

Attack Flow

 

 

#1. 클라이언트가 공격을 입력한 경우


    -  클라이언트는 먼저 자신의 공격 애니메이션을 즉시 재생하고, 동시에 Server RPC를 통해 서버에 공격 요청을 전달합니다. 

동기화 과정

 

 

 

 

void AABCharacterPlayer::AttackHitCheck()
{
	// 공격 판정은 중요한 로직이기 때문에 서버에서 처리.
	//if(HasAuthority())
	
	if(IsLocallyControlled())
	{
		// 로그 출력.
		AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));


		FHitResult OutHitResult;
		FCollisionQueryParams Params(SCENE_QUERY_STAT(Attack), false, this);

		const float AttackRange = Stat->GetTotalStat().AttackRange;
		const float AttackRadius = Stat->GetAttackRadius();
		const float AttackDamage = Stat->GetTotalStat().Attack;
		const FVector Forward = GetActorForwardVector();
		const FVector Start = GetActorLocation() + GetActorForwardVector() * GetCapsuleComponent()->GetScaledCapsuleRadius();
		const FVector End = Start + GetActorForwardVector() * AttackRange;

		bool HitDetected = GetWorld()->SweepSingleByChannel(
			OutHitResult,
			Start,
			End,
			FQuat::Identity,
			CCHANNEL_ABACTION,
			FCollisionShape::MakeSphere(AttackRadius), Params);




		// 서버 기준 현재 시간.
		float HitCheckTime = GetWorld()->GetGameState()->GetServerWorldTimeSeconds();


		// 클라이언트. 
		if (!HasAuthority())
		{
			// 클라이언트에서 판정해봣더니 부딪힌 경우
			if(HitDetected)
			{
				ServerRPCNotifyHit(OutHitResult, HitCheckTime);
			}

			// 클라이언트에서 판정해봣더니 부딪히지 않은 경우. 
			else
			{
				ServerRPCNotifyMiss(Start, End, Forward, HitCheckTime);
			}
		}
		
		// 서버
		else
		{
			// 디버그 드로우로 충돌 정보 보여주기.
			FColor DebugColor = HitDetected ? FColor::Green : FColor::Red;
			DrawDebugAttackRange(DebugColor, Start, End, Forward);
			

			// 서버에서는 추가로 판단하지 않고, 공격 판정 수락. 
			if(HitDetected)
			{
				AttackHitConfirm(OutHitResult.GetActor());
			}
		
		}

	}
}

 

 

 

 

 

#2 서버가 공격 요청을 받은 경우 


  
- 서버는 공격 가능 여부를 검증한 뒤 공격 상태를 갱신하고, 다른 클라이언트에게 공격 애니메이션이 재생되도록 동기화 합니다. 
    

MulticastRPC 사용 개선

멀티플레이 환경에서 모든 상황에 Muticast RPC를 사용하는 것은 네트워크 데이터 전송량을 불필요하게 증가시킬 수 있습니다.

 

예를 들어 클라이언트가 2명 이상 존재하는 상황을 생각해보겠습니다.

 

Client1이 공격 입력을 수행하면, 먼저 Server RPC를 통해 서버에게 자신의 행동을 전달합니다.

이후 서버는 해당 요청을 처리한 뒤 Multicast RPC를 통해 모든 클라이언트에게 공격 애니메이션 또는 상태 정보를 전달합니다. 

 

이때 Client2 입장에서는 Client1 의 행동을 처음 전달받는 것이므로 필요한 데이터입니다.

하지만 Client1은 이미 Local에서 공격 애니메이션을 재생하고 상태를 처리한 상태이기 때문에, 다시 같은 정보를 전달받으면 중복 처리가 발생할 수 있습니다.

 

또한 Listen Server 환경에서는 서버 측에서도 이미 공격 요청을 처리했기 때문에, Multicast를 통해 서버와 요청 클라이언트까지 다시 동일한 정보를 받게 되면 불필요한 네트워크 전송과 중복 로직이 발생할 수 있습니다. 

 

따라서 모든 대상에게 무조건 Multicast RPC를 보내기보다는, 이미 처리가 완료된 서버와 요청 클라이언트는 제외하고, 해당 정보를 처음 받아야 하는 다른 클라이언트들에게만 전달하는 방식으로 개선할 수 있습니다.

 

이를 통해 중복된 데이터 전송을 줄이고, 네트워크 사용량을 보다 효율적으로 관리할 수 있습니다. 

 

MulticastRPC 사용 개선

 

// 이 함수는 서버에서 실행됨. 
void AABCharacterPlayer::ServerRPCAttack_Implementation(float AttackStartTime)
{
	// 공격 시작 처리.
	bCanAttack = false; 
	OnRep_CanAttack();

	// 서버에 전달된 시간 차를 확인.
	// 서버 기준 현재 시간에서 클라이언트가 전달한 시간을 뺌.
	AttackTimeDifference = GetWorld()->GetRealTimeSeconds() - AttackStartTime;

	// 시간 차가 어느정도인지 로그 출력.
	AB_LOG(LogABNetwork, Log, TEXT("LagTime: %f"), AttackTimeDifference);

	// 타이머설정할때 0또는 음수로 설정하면 동작 안함. 
	// 타이머가 동작은 하도록 값 보정.
	AttackTimeDifference = FMath::Clamp(AttackTimeDifference, 0.0f, AttackTime - 0.01f);

	// 타이머 설정. 
	// 공격 종료 시간을 계산할 때,
	// 애니메이션 재생시간에서 클라에서 서버까지 메시지가 전달되는데까지
	// 걸린시간을 고려해서 설정.

	FTimerHandle Handle; 
	GetWorld()->GetTimerManager().SetTimer(
		Handle,
		FTimerDelegate::CreateLambda(
			[&]()
			{
				// 공격 종료 처리.
				bCanAttack = true;
				OnRep_CanAttack(); 
			}
		), AttackTime - AttackTimeDifference, false
	);

	// 클라이언트가 공격을 시작한 시간을 저장 (기록)
	LastAttackStartTime = AttackStartTime;

	// 서버에서도 애니메이션 재생.
	PlayAttackAnimation();

	// 멀티캐스트 RPC 호출 
	// MulticastRPCAttack();

	// 위에 멀티캐스트 RPC 대신 필요한 클라이언트에만 ClientRPC를 호출. 
	for(APlayerController* PlayerController : TActorRange<APlayerController>(GetWorld()))
	{
		// 2개 필터링.
		// 필터링 대상#1: 요청한 클라이언트. 
		// 필터링 대상#2: 서버에 있는 PlayerController.

		// #1: 요청한 클라이언트의 PlayerController 필터링. 
		// GetController()는 클라이언트가 공격 입력을 눌러서 서버 RPC를 호출했을 때
		// 서버에서 실행되는 함수.
		// 서버에 있는 클라이언트 A의 캐릭터 복제 원본에서
		// ServerRPCAttack_Implementation() 실행
		if (PlayerController && PlayerController != GetController()) 
		{
			// #2: 추가로 필터링 서버에서 제어하는 PlayerController 필터링
			if(!PlayerController->IsLocalController())
			{
				// 해당 클라이언트한테 애니메이션 재생 전달.
				AABCharacterPlayer* OtherPlayer = Cast<AABCharacterPlayer>(PlayerController->GetPawn());

				if(OtherPlayer)
				{
					// ClientRpc를 통해서 아래 로직을 수행 요청.
					// OtherPlayer->PlayerAttackAnimation()
					OtherPlayer->ClientRPCPlayAnimation(this);
				}
			}
		}
	}

}

 

 

 

#3. 공격 판정 처리

  - 공격 판정은 최종적으로 서버에서 검증하며, 유효한 공격으로 판단되면 데미지 처리를 수행합니다. 

 

 

 

 

 

 

 

 

'Unreal Project > Unreal Study' 카테고리의 다른 글

Unreal Engine Listen Server _ 04 ( 커스텀 이동 구현(Teleport) )  (0) 2026.06.10
Unreal StateTree로 NPC 대기와 순찰 AI 구현하기  (0) 2026.06.08
언리얼 팀프로젝트 회고  (0) 2026.06.07
Data_Asset 및 Quater View, Shoulder View 구현  (0) 2026.02.26
Unreal Collision에 관하여  (0) 2025.10.31
'Unreal Project/Unreal Study' 카테고리의 다른 글
  • Unreal Engine Listen Server _ 04 ( 커스텀 이동 구현(Teleport) )
  • Unreal StateTree로 NPC 대기와 순찰 AI 구현하기
  • 언리얼 팀프로젝트 회고
  • Data_Asset 및 Quater View, Shoulder View 구현
seonhwan2547
seonhwan2547
seonhwan2547 님의 블로그 입니다.
  • seonhwan2547
    seonhwan2547 님의 블로그
    seonhwan2547
  • 전체
    오늘
    어제
    • 분류 전체보기 (102) N
      • Unreal Project (37) N
        • Khazan 모작 프로젝트 (4)
        • Unreal Study (14) N
        • Blueprint (6)
        • C++ (6)
        • Shader (0)
        • GAS (2)
        • 이환 모작 포토폴리오 개발일지 (4)
        • Listen Server (1)
      • Directx11 Project (11)
        • Thymesia 팀 프로젝트 (8)
        • Kaku Ancient Seal 개인 프로젝트 (2)
        • Thymesia Animation Tool 개발 (0)
      • Algorithm (6)
        • Binary_Search (2)
        • Greedy (1)
        • Dynamic Programming (1)
        • A-star (1)
      • Coding Test (31)
        • Brutal Force (2)
        • Sort (5)
        • DFS (3)
        • Binary_Search (4)
        • BFS (6)
        • Hash (2)
        • Dynamic Programming (6)
        • Greedy (1)
        • BackTracking (1)
        • Binary_Tree (1)
      • STL Container (1)
        • unorded_set (0)
        • priority_queue (1)
      • C++ 공부 및 몰랐던점 (8)
        • Smart pointer (1)
      • Visual Studio 설정관련 공부 (1)
      • Console Project (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 블로그 소개
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    blueprint
    UI
    hash
    CodingTest
    Game Programming
    server
    Unreal
    GameProgramming
    C++
    UnrealEnigne
    Unreal Engine
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
seonhwan2547
Unreal Engine Listen Server _ 03 ( 캐릭터 공격 구현 )