Unreal Engine - 데디케이티드 서버 10 (콤보 공격)

2025. 5. 30. 02:40·Unreal Engine

1. 플레이어 쪽 함수 세팅


새로운 인풋 액션인 IA_ComboAttack을 추가한 후 Input Binding 진행

  • Section을 사용해야 하기 때문에, SectionName에 사용할 변수를 추가
//헤더
FName ComboAttackSectionName;
FName DefaultComboAttackSectionName;

//cpp
void ADXPlayerCharacter::HandleComboAttackInput(const FInputActionValue& InValue)
{
	if (!HasAuthority() && IsLocallyControlled())
	{
		PlayAnimMontage(ComboAttackMontage, 1.f, ComboAttackSectionName);
		//ServerRPCComboAttack();
	}
}

 

 

2. 애니메이션 노티파이 스테이트


애니메이션이 진행되는 동안 애니메이션 노티파이 스테이트를 통해 로직 수행

  • 다음 section을 나타내는 변수 존재
  • 해당 변수를 통해 다음 섹션으로 넘어갈 수 있도록 Player의 함수와 연동하기
//헤더
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")
FName SectionName;

//cpp
void UDXANSMeleeCombo::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration)
{
	Super::NotifyBegin(MeshComp, Animation, TotalDuration);

	ADXPlayerCharacter* PlayerCharacter = Cast<ADXPlayerCharacter>(MeshComp->GetOwner());
	if (IsValid(PlayerCharacter))
	{
		PlayerCharacter->SetNextComboAttack(SectionName);
	}
}

void UDXANSMeleeCombo::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
	Super::NotifyEnd(MeshComp, Animation);
	
	ADXPlayerCharacter* PlayerCharacter = Cast<ADXPlayerCharacter>(MeshComp->GetOwner());
	if (IsValid(PlayerCharacter))
	{
		PlayerCharacter->ResetComboAttack();
	}
}

 

void ADXPlayerCharacter::SetNextComboAttack(FName InSectionName)
{
	ComboAttackSectionName = InSectionName;
}

void ADXPlayerCharacter::ResetComboAttack()
{
	ComboAttackSectionName = DefaultComboAttackSectionName;
}

 

 

3. 애니메이션 몽타주


애니메이션 몽타주에서 Section을 이용하여 콤보 공격을 구현할 것

  • 먼저 애니메이션 몽타주를 만든 후 아래와 같이 사용하고자 하는 애니메이션을 넣기
  • 각 애니메이션이 시작하는 곳에 section을 만든 다음 사용하고자 하는 이름으로 변경
  • 전에 만들어둔 "애님 노티파이 스테이트"를 추가 (ANS는 다음 섹션으로 넘어가도 되는 부분부터 시작하도록)

 

각 애니메이션 노티파이 스테이트에 있어서 C++로 만들어 둔 변수에 아래와 같이 다음으로 재생할 section의 이름을 넣어준다.

  • 아래 사진은 Combo1 섹션에 존재하는 ANS의 변수 세팅 모습

 

다음으로 각 section이 연결된 링크를 제거하여 모두 따로 분리해 준다.

분리하하면 아래와 같은 모습이 된다.

모두 분리된 섹션

 

지금까지 실행 결과

  • 아직 서버 관련 로직을 넣지 않았기 때문에 아래와 같이 자신의 화면에서만 보인다
  • combo 공격은 잘 이루어지는 모습

 

 

4. 트러블 슈팅 (연속 공격 방지)


현재 문제는 연속된 input (연속 클릭) 시 첫 몽타주가 계속해서 플레이되는 문제가 존재한다.

 

문제 해결을 위해서 현재 몽타주가 재생중인지에 대한 변수를 추가해 줄 것이다.

또한 NotifyBegin 함수와 바인딩을 통해 해당 노티파이에서 실행할 함수를 만들어 주었다.

  • bCanCombo 변수를 추가
  • input 바인딩 된 함수에서 실행하면서 bCanCombo를 false로 변경
  • OnMontageNotifyBegin이라는 함수를 OnPlayMontageNotifyBegin에 바인딩
    • 위 애님 몽타주 사진에서 본 것처럼 "None" 몽타주 노티파이에서 바인딩 된 함수를 실행
    • 바인딩 된 함수에서는 다시 bCanCombo를 true로 만들어주기
void ADXPlayerCharacter::BeginPlay()
{
	Super::BeginPlay();

    ...

	UDXAnimInstanceBase* Anim = Cast<UDXAnimInstanceBase>(GetMesh()->GetAnimInstance());
	if (IsValid(Anim))
	{
		Anim->OnPlayMontageNotifyBegin.AddDynamic(this, &ThisClass::OnMontageNotifyBegin);
	}
}

void ADXPlayerCharacter::HandleComboAttackInput(const FInputActionValue& InValue)
{
	if (!HasAuthority() && IsLocallyControlled())
	{
		if (bCanCombo)
		{
			PlayAnimMontage(ComboAttackMontage, 1.f, ComboAttackSectionName);
			bCanCombo = false;
		}
	}
}

void ADXPlayerCharacter::OnMontageNotifyBegin(FName NotifyName, const FBranchingPointNotifyPayload& PayLoad)
{
	if (NotifyName == "None")
	{
		bCanCombo = true;
	}
}

 

결과 모습

 

 

5. 서버 로직 추가 (몽타주 재생)


우선 서버에서 해당 애니메이션을 볼 수 있도록 처리를 할 것이다.

 

콤보 공격 몽타주를 출력해주는 코드를 함수로 따로 빼주고

void ADXPlayerCharacter::PlayComboAttackMontage()
{
	PlayAnimMontage(ComboAttackMontage, 1.f, ComboAttackSectionName);
}

 

아래와 같이 인풋 바인딩 시 실행되는 함수를 수정해 주었다.

  • 몽타주 함수 실행
  • ServerRPC 함수 추가
void ADXPlayerCharacter::HandleComboAttackInput(const FInputActionValue& InValue)
{
	if (!HasAuthority() && IsLocallyControlled())
	{
		if (bCanCombo)
		{
			PlayComboAttackMontage();
			bCanCombo = false;
			ServerRPCComboAttack();
		}
	}
}

 

ServerRPC 함수에서는 아래와 같이 서버와 자기 자신을 제외한 플레이어에게 Multicast 함수를 통해 몽타주를 재생하도록 한다.

//헤더
UFUNCTION(Server, Reliable)
void ServerRPCComboAttack();
    
UFUNCTION(NetMulticast, Unreliable)
void MulticastRPCComboAttack();
    
//cpp
void ADXPlayerCharacter::ServerRPCComboAttack_Implementation()
{
	MulticastRPCComboAttack();
}

void ADXPlayerCharacter::MulticastRPCComboAttack_Implementation()
{
	//except server and owner player
	if (!HasAuthority() && !IsLocallyControlled())
	{
		PlayComboAttackMontage();
	}
}

 

결과 

  • 몽타주가 다른 클라이언트에게도 보이는 것을 확인할 수 있다.

 

 

6. 서버 로직 추가 (데미지 처리)


생각할 점) 데미지 처리 로직은 중요한 로직이기 때문에 서버에서 처리를 해야한다.

 

데미지를 입히는 함수를 아래와 같이 서버에서 돌아가는 로직으로 아래와 같이 작성해 볼 수 있다.

void ADXPlayerCharacter::CheckMeleeAttackHit()
{
	//server
	if (HasAuthority())
	{
		TArray<FHitResult> OutHitResults;
		TSet<ACharacter*> DamagedCharacters;
		FCollisionQueryParams Params(NAME_None, false, this);

		const float MeleeAttackRange = 50.f;
		const float MeleeAttackRadius = 50.f;
		const float MeleeAttackDamage = 10.f;

		const FVector Forward = GetActorForwardVector();
		const FVector Start = GetActorLocation() + Forward * GetCapsuleComponent()->GetScaledCapsuleRadius();
		const FVector End = Start + GetActorForwardVector() * MeleeAttackRange;

		bool bIsHitDetected = GetWorld()->SweepMultiByChannel(OutHitResults, Start, End, FQuat::Identity, ECC_Camera, FCollisionShape::MakeSphere(MeleeAttackRadius), Params);
		if (bIsHitDetected)
		{
			for (auto const& OutHitResult : OutHitResults)
			{
				ACharacter* DamagedCharacter = Cast<ACharacter>(OutHitResult.GetActor());
				if (IsValid(DamagedCharacter))
				{
					DamagedCharacters.Add(DamagedCharacter);
				}
			}

			FDamageEvent DamageEvent;
			for (auto const& DamagedCharacter : DamagedCharacters)
			{				
				DamagedCharacter->TakeDamage(MeleeAttackDamage, DamageEvent, GetController(), this);
			}
		}
		FColor DrawColor = bIsHitDetected ? FColor::Green : FColor::Red;
		MulticastDrawDebugMeleeAttack(DrawColor, Start, End, Forward);
	}
}

 

참고) TakeDamage 함수는 기존의  TakeDamage 함수를 override한 함수이다.

virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) override;

 

위에서 만든 로직은 애니메이션 몽타주에서 재생할 수 있도록 애님 인스턴스 클래스에 Notify 함수를 만들어서 실행시켰다.

//헤더
UFUNCTION()
void AnimNotify_CheckMeleeAttackHit();

//cpp
void UDXAnimInstanceBase::AnimNotify_CheckMeleeAttackHit()
{
	ADXPlayerCharacter* OwnerPlayerCharacter = Cast<ADXPlayerCharacter>(OwnerCharacter);
	if (IsValid(OwnerPlayerCharacter))
	{
		OwnerPlayerCharacter->CheckMeleeAttackHit();
	}
}
  • 새로 만든 노티파이라면,
    • 노티파이 칸 우클릭 -> 노티파이 추가 -> 새 노티파이 -> 이름 추가(위에서 만든 함수에서 언더바 이후의 이름 사용)
  • 이전에 만들었던 노티파이라면
    • 아래와 같이 선택

  • 추가) 몽타주 틱 타입을 Branching Point로 수정

  • 완성된 모습

 

 

7. 트러블 슈팅 (데미지 처리)


그러나 위의 설정만 진행한 다음 실행하면, 공격 처리가 안된다. (아래 영상 참고)

 

원인을 찾아보기 위해 현재 로직을 살펴보면

  • 로컬 플레이어만 인풋 바인딩 함수 실행
    • 몽타주 실행
    • 서버 RPC 실행
  • 로컬 플레이어의 몽타주 실행의 경우
    • 서버에서 실행되는 로직이 없으므로 CheckMeleeAttackHit 함수가 실행될 수 없다.
  • 서버 RPC를 실행한 경우
    • 멀티캐스트를 하지만, 멀티캐스트 함수에서 !HasAuthority() 일때 실행되기 때문에
    • 마찬가지로 서버의 로직이 없다.

 

해결하기 위해서는

  • CheckMeleeAttackHit 함수를 ServerRPC 함수로 만들거나
  • 멀티캐스트를 서버에서도 진행하게 해주는 방법이 있다.

 

이번에는 멀티캐스트를 서버에서 진행할 수 있도록 하는 방법을 사용해서 해결해 볼 것이다.

  • 아래와 같이 멀티캐스트 함수를 수정
void ADXPlayerCharacter::MulticastRPCComboAttack_Implementation()
{
	if (!IsLocallyControlled())
	{
		PlayComboAttackMontage();
	}
}

 

결과 모습

 

'Unreal Engine' 카테고리의 다른 글

UE5 Issues : 데디케이트 서버 UI에 관해  (0) 2025.06.12
Unreal Engine - 데디케이티드 서버 11 (콤보 공격 최적화)  (0) 2025.06.02
UE5 Issues - Dash Skill (데디케이티드 서버)  (0) 2025.05.27
Unreal Engine - 데디케이티드 서버 9 (게임 종료)  (0) 2025.05.04
Unreal Engine - 데디케이티드 서버 8 (게임 흐름)  (0) 2025.05.02
'Unreal Engine' 카테고리의 다른 글
  • UE5 Issues : 데디케이트 서버 UI에 관해
  • Unreal Engine - 데디케이티드 서버 11 (콤보 공격 최적화)
  • UE5 Issues - Dash Skill (데디케이티드 서버)
  • Unreal Engine - 데디케이티드 서버 9 (게임 종료)
gbleem
gbleem
gbleem 님의 블로그 입니다.
  • gbleem
    gbleem 님의 블로그
    gbleem
  • 전체
    오늘
    어제
    • 분류 전체보기 (184)
      • Unreal Engine (73)
      • C++ (19)
      • 알고리즘(코딩테스트) (27)
      • TIL (60)
      • CS (4)
      • 툴 (1)
  • 블로그 메뉴

    • 홈
    • 카테고리
  • 링크

    • 과제용 깃허브
    • 깃허브
    • velog
  • 공지사항

  • 인기 글

  • 태그

    상속
    gamestate
    BFS
    applydamage
    actor 클래스
    additive animation
    enhanced input system
    motion matching
    싱글턴
    addonscreendebugmessage
    map을 vector로 복사
    C++
    blend pose
    Vector
    템플릿
    const
    character animation
    DP
    매크로 지정자
    cin함수
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
gbleem
Unreal Engine - 데디케이티드 서버 10 (콤보 공격)
상단으로

티스토리툴바