[UE5 C++] 기본 공격 판정

LeeTaes·2024년 4월 28일
0

[UE_Project] MysticMaze

목록 보기
6/17

언리얼 엔진을 사용한 RPG 프로젝트 만들기

  • 플레이어 공격 판정
    - 플레이어의 기본 공격 시 SweepTrace를 통해 공격 성공/실패 체크하기

BasicComboAttack 몽타주에 애님 노티파이 추가하기

Anim Notify

  • 애니메이션 재생 중 특정한 이벤트를 발생시키는데 사용되는 기능
  • 애니메이션 시퀀스 내에서 발생하는 특정 지점에서 호출되는 사용자 정의 이벤트라고 볼 수 있다.
  • "AnimNotify" 클래스를 상속받는 "AnimNotify_MMBaseAttackCheck" 클래스를 추가해주도록 하겠습니다.

  • BasicComboAttack 몽타주에서 생성한 노티파이를 추가해주도록 합니다.

  • AnimNotify_MMBaseAttackCheck 클래스를 작성해주도록 하겠습니다.

    AnimNotify_MMBaseAttackCheck Class

    • 노티파이 이벤트가 들어온 경우 실행할 로직을 추가해주도록 합니다.
    • 호출되어야 하는 함수를 오버라이드 해준 후 구현해주도록 하겠습니다.
// AnimNotify_MMBaseAttackCheck Header
UCLASS()
class MYSTICMAZE_API UAnimNotify_MMBaseAttackCheck : public UAnimNotify
{
	GENERATED_BODY()
	
protected:
	virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference);
};


// AnimNotify_MMBaseAttackCheck Cpp
void UAnimNotify_MMBaseAttackCheck::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
	Super::Notify(MeshComp, Animation, EventReference);

	if (MeshComp)
	{
		// TODO : 플레이어 클래스의 AttackCheck 실행
	}
}
  • TODO 부분에서 MeshComp를 가지고 있는 Owner를 구해 실제 충돌 탐지 로직을 호출해주면 됩니다.

공격 성공/실패 여부 확인 로직

  • 언리얼에서 제공하는 Trace 함수를 통해 체크하도록 하겠습니다.
  • Sphere 모형으로 체크할 예정이므로 Sweep을 사용합니다.
  • 범위 내에 있는 모든 물체를 탐지할 예정이므로 Multi를 사용합니다.
  • 즉, MMAction채널을 사용한 SweepMultiByChannel를 진행하도록 하겠습니다.
  • Owner는 "MMPlayerCharacter" 클래스가 되며, 충돌을 탐지하는 로직을 구현해주도록 하겠습니다.
// MMPlayerCharacter Header

// Attack Section
public:
	void BaseAttackCheck();
    
// MMPlayerCharacter CPP
void AMMPlayerCharacter::BaseAttackCheck()
{
	// 충돌 결과를 반환하기 위한 배열
	TArray<FHitResult> OutHitResults;

	// 공격 반경
	float AttackRange = 100.0f;
	// 공격 체크를 위한 구체의 반지름
	float AttackRadius = 50.0f;

	// 충돌 탐지를 위한 시작 지점 (플레이어 현재 위치 + 전방 방향 플레이어의 CapsuleComponent의 반지름 거리)
	FVector Start = GetActorLocation() + (GetActorForwardVector() * GetCapsuleComponent()->GetScaledCapsuleRadius());
	// 충돌 탐지 종료 지점 (시작지점 + 전방 방향의 공격 거리)
	FVector End = Start + (GetActorForwardVector() * AttackRange);
	// 파라미터 설정하기 (트레이스 태그 : Attack, 복잡한 충돌 처리 : false, 무시할 액터 : this) 
	FCollisionQueryParams Params(SCENE_QUERY_STAT(Attack), false, this);

	bool bHasHit = GetWorld()->SweepMultiByChannel(
		OutHitResults,
		Start,
		End,
		FQuat::Identity,
		CHANNEL_MMACTION,
		FCollisionShape::MakeSphere(AttackRadius),
		Params
	);

	if (bHasHit)
	{
		// TODO : 데미지 전달
	}

	// Capsule 모양의 디버깅 체크
	FVector CapsuleOrigin = Start + (End - Start) * 0.5f;
	float CapsuleHalfHeight = AttackRange * 0.5f;
	FColor DrawColor = bHasHit ? FColor::Green : FColor::Red;

	DrawDebugCapsule(GetWorld(), CapsuleOrigin, CapsuleHalfHeight, AttackRadius, FRotationMatrix::MakeFromZ(GetActorForwardVector()).ToQuat(), DrawColor, false, 3.0f);
}
  • 위에서 제작한 함수를 AnimNotify_MMBaseAttackCheck 클래스에서 호출해주도록 하겠습니다.
// AnimNotify_MMBaseAttackCheck Cpp
#include "Animation/AnimNotify_MMBaseAttackCheck.h"
#include "Character/MMPlayerCharacter.h"

void UAnimNotify_MMBaseAttackCheck::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
	Super::Notify(MeshComp, Animation, EventReference);

	if (MeshComp)
	{
		// 플레이어 클래스의 BaseAttackCheck 실행
		AMMPlayerCharacter* Player = Cast<AMMPlayerCharacter>(MeshComp->GetOwner());
		if (Player)
		{
			Player->BaseAttackCheck();
		}
	}
}
  • 맵에 MMCapsule 프리셋을 설정한 물체를 놓고 테스트 결과를 확인해보도록 하겠습니다.

클래스 간의 의존성 줄이기

의존성

  • 한 요소가 다른 요소에 종속되어 있음을 나타냅니다.
  • 클래스 A가 클래스 B를 사용한다면 클래스 A는 클래스 B에 대해 의존하고 있다고 할 수 있습니다.

현재 생성한 MMBaseAttackCheck클래스는 MMPlayerCharacter 클래스의 헤더를 참조하고 있습니다.

  • 인터페이스를 사용해 클래스 간의 의존성을 줄여보도록 하겠습니다.
// MMAnimationAttackInterface Header
class MYSTICMAZE_API IMMAnimationAttackInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	// 기본 공격 성공/실패 여부 체크
	virtual void BaseAttackCheck() = 0;
};
// AMMPlayerCharacter Header

#include "CoreMinimal.h"
#include "Character/MMCharacterBase.h"
#include "InputActionValue.h"
#include "Interface/MMAnimationAttackInterface.h"
#include "MMPlayerCharacter.generated.h"

/**
 * 
 */
UCLASS()
class MYSTICMAZE_API AMMPlayerCharacter : public AMMCharacterBase, public IMMAnimationAttackInterface
{
	...
    // Attack Section
protected:
	virtual void BaseAttackCheck() override;
}
// UAnimNotify_MMBaseAttackCheck Cpp

#include "Animation/AnimNotify_MMBaseAttackCheck.h"
#include "Interface/MMAnimationAttackInterface.h"

void UAnimNotify_MMBaseAttackCheck::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
	Super::Notify(MeshComp, Animation, EventReference);

	if (MeshComp)
	{
		// 공격 성공 여부 체크
		IMMAnimationAttackInterface* AttackPawn = Cast<IMMAnimationAttackInterface>(MeshComp->GetOwner());
		if (AttackPawn)
		{
			AttackPawn->BaseAttackCheck();
		}
	}
}
profile
클라이언트 프로그래머 지망생

0개의 댓글