본문 바로가기

공부/이득우의 언리얼 프로그래밍

[Study] Part 2 - 캐릭터 공격 판정 (6/15)

728x90
반응형

 

 

 

  • 정리
    • 트레이스 채널을 활용한 공격 판정
      1. 공격 판정 구현을 위한 물리 트레이스 채널 및 프로필 설정
      2. 디버그 드로잉 기능을 활용한 충돌 디버깅
      3. 데미지 프레임웍을 사용한 데미지 전달
      4. 델리게이트와 람다함수의 간편한 활용

 

 

 

캐릭터 액션의 충돌 판정

  • 캐릭터 액션의 충돌 판정
    • 월드가 제공하는 충돌 판정 서비스 사용
    • 월드는 크게 세 가지의 충돌 판정 서비스를 제공
    • 월드 내 배치된 충돌체와 충돌하는지 파악하고, 충돌한 액터 정보를 얻을 수 있음
      • LineTrace
      • Sweep
      • Overlap
  • 트레이스 채널과 충돌 프로필 생성
    • 액션 판정을 위한 트레이스 채널의 생성 : ABAction. 기본 반응은 무시
    • 캐릭터 캡슐용 프로필 : ABAction 트레이스 채널에 반응. 오브젝트 타입은 Pawn
    • 스켈레탈 메시용 프로필 : 랙돌 구현에 주로 활용
    • 기믹 트리거용 프로필 : 폰 캡슐에만 반응하도록 설정. 오브젝트 타입은 WorldStatic

Edit>Project Settings>Engine>Collision
Preset을 통해 New Profile 할 수 있다.

  • 원하는 트레이스 채널과 프로필을 만들 수 있다.
  • Collision Respo
    • Ignore
      • 통과할 수 있음
    • Overlap
      • 통과하지만 이벤트가 발생
    • Block
      • 통과하지 못함
  • Trace Type 중에 Destructible이 있는데, 이는 파괴한 것에 대한 충돌 처리를 설정이다. 파괴한 조각들이 길을 막으면 불편하기 때문에 보통 Ignore로 설정하는 것이 좋다.

 

 

 

~Config
~Config/DefaultEngine.ini

  • 설정하고 나면 Config 폴더 하단의 DefaultEngine.ini에 저장이 된다.
  • 아까 설정한 이름인 ABAction을 검색해 보면, 다음과 같이 나오게 된다. 이름을 ABAction이라고 설정했지만, 코드내에서는 ECC_GameTraceChannel1 이라고 하는 열거형 값을 사용하고 있으므로, 해당 값을 바로 사용해 주는 것이 조금 더 편리하다.

 

 

 

Add C++ Class>AnimNotify
Add Notify

  • AnimNotify로 사용할 C++ 클래스를 만들어주고, 애니메이션 몽타주에서 히트 체크를 할 프레임에 방금 만든 Notify를 Add Notify 해준다.

 

#include "Animation/AnimNotify_AttackHitCheck.h"
#include "Character/ABCharacterBase.h"		// 다른 폴더의 헤더파일을 추가하는 것은 결국 의존성이 생긴다는 것

void UAnimNotify_AttackHitCheck::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
	// 메쉬 컴포넌트가 있다면
	if (MeshComp)
	{
		MeshComp->GetOwner();
	}
}
  • AnimNotify_AttackHitCheck.cpp

 

  • 다른 폴더의 헤더를 추가하는 것은 결국 의존성이 생기기 마련이다. 이 노티파이 기능의 경우 공용으로 여러 종류의 캐릭터가 있을 때 같이 사용되는 것이 좋은데, 이때마다 이렇게 헤더를 추가하는 것은 별로 좋지 않다.
  • 좀 더 범용성 있게 만들려면 인터페이스를 구현해 주는 것이 좋다.
  • 어쩔 수 없는 의존성이 발생되는 경우에는 가급적이면 인터페이스를 통해서 구현하도록 설계하면 보다 범용적으로 이러한 노티파이 기능들을 사용할 수가 있다.
  • 월드 트레이싱 함수의 선택
    • 세 가지 카테고리로 원하는 함수 이름을 얻을 수 있음
    • 카테고리 1 : 처리방법
      • LineTrace
      • Sweep
      • Overlap

캐릭터 액션의 충돌 판정

  • 카테고리 2 : 대상
    • Test : 무언가 감지되었는지를 테스트
    • Single 또는 AnyTest : 감지된 단일 물체 정보를 반환 (LineTrace, Sweep은 Single / Overlpa은 AnyTest)
    • Multi : 감지된 모든 물체 정보를 배열로 반환
  • 카테고리 3 : 처리 설정
    • ByChannel : 채널 정보를 사용해 감지
    • ByObjectType : 물체에 지정된 물리 타입 정보를 사용해 감지
    • ByProfile : 프로필 정보를 사용해 감지
  • {처리방법}{대상}{처리설정} 을 통해 월드 트레이싱 함수를 구성할 수 있다.
  • 캐릭터 공격 판정의 구현 기획
    • 캐릭터의 위치에서 시선 방향으로 물체를 감지
    • 작은 구체를 제작, 시선 방향으로 특정 거리까지만 투사 (Sweep)
    • 하나의 물체만 감지 (Single)
    • 트레이스 채널을 사용해 감지 (ByChannel)

 

{처리방법}{대상}{처리설정} → {Sweep}{Single}{ByChannel}

 

#pragma once

#include "CoreMinimal.h"

#define CROFILE_ABCAPSULE TEXT("ABCapsule")
#define CROFILE_ABTRIGGER TEXT("ABTrigger")
#define CCHANNEL_ABACTION ECC_GameTraceChannel1
  • ABCollision.h

 

  • 전처리기용 파일 생성하기
    • 탐색기에서 전처리기용 폴더를 만들고, 텍스트 파일로 간단히 생성하기. 이후 코드 제너레이트
  • ini 파일에서 지정된 채널 열거형값으로 바로 변환되는데, 전처리기를 통해 좀 더 편리하게 코딩할 수 있다.
  • 물리 충돌 테스트
    • 디버그 드로잉 함수를 사용해 물리 충돌을 시각적으로 테스트하기
    • 90도로 회전시킨 캡슐 그리기
      • Origin
      • HlafHieght
      • Radius

 

 

 

UINTERFACE(MinimalAPI)
class UABAnimationAttackInterface : public UInterface
{
	GENERATED_BODY()
};

class ARENABATTLE_API IABAnimationAttackInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual void AttackHitCheck() = 0;  // 추상가상함수를 만들어 준다. 구현부에서 무조건 구현하도록
};
  • ABAnimationAttackInterface.h
#include "Interface/ABAnimationAttackInterface.h"  // 인터페이스 인클루드
/...

UCLASS()
class ARENABATTLE_API AABCharacterBase : public ACharacter, public IABAnimationAttackInterface		// 인터페이스 상속
{
	// Attack Hit Section
protected:
	virtual void AttackHitCheck() override;
}
  • ABCharacterBase.h
// 트레이스 채널을 체크해 물체가 서로 충돌되는 지를 검사하는 로직
void AABCharacterBase::AttackHitCheck()
{
	FHitResult OutHitResult;

	// 1. 콜리전을 분석할때 어떤 태그 정보로 분석할 때 식별자 정보로 사용이 되는 인자.
	// 2. 복잡한 형태의 콜리전을 대상으로 감지할 것인가
	// 3. 무시할 인자들
	FCollisionQueryParams Params(SCENE_QUERY_STAT(Attack), false, this);

	const float AttackRange = 40.0f;
	const float AttackRadius = 50.0f;
	const float AttackDamage = 30.0f;
	const FVector Start = GetActorLocation() + GetActorForwardVector() * GetCapsuleComponent()->GetScaledCapsuleRadius();
	const FVector End = Start + GetActorForwardVector() * AttackRange;

	// SweepSingleByChannel과 같은 함수는 월드가 제공하는 서비스이기 때문에 GetWorld를 통해 포인터를 가져와준다.
	bool HitDetected = GetWorld()->SweepSingleByChannel(OutHitResult, Start, End, FQuat::Identity, CCHANNEL_ABACTION, FCollisionShape::MakeSphere(AttackRadius), Params);

	if (HitDetected)
	{

	}

// 디버그 드로우
#if ENABLE_DRAW_DEBUG

	FVector CapsuleOrigin = Start + (End - Start) * 0.5f;
	float CapsuleHalfHeight = AttackRange * 0.5f;

	// 충돌했으면 Green / 충돌하지않았으면 Red
	FColor DrawColor = HitDetected ? FColor::Green : FColor::Red;

	// FRotationMatrix ~ : 시선 방향으로 눕히기
	DrawDebugCapsule(GetWorld(), CapsuleOrigin, CapsuleHalfHeight, AttackRadius, FRotationMatrix::MakeFromZ(GetActorForwardVector()).ToQuat(), DrawColor, false, 5.0f);

#endif
  • ABCharacterBase.cpp

 

충돌시 Green Sphere가 생성, 충돌하지 않으면 Red Sphere가 생성된다.

 

 

 

  • NPC 설정
    • 기존에 만들어 둔 ABCharacterBase를 상속받아 C++Class를생성해준다.
    • 또한, ABCharacterBase를 조금 수정해줘야 하는데 기본 에셋들에 대한 기본값들을 설정해준다.
	// 기본 에셋에 대한 기본값들 설정하기
	static ConstructorHelpers::FObjectFinder<UAnimMontage> ComboActionMontageRef(TEXT("/Script/Engine.AnimMontage'/Game/ArenaBattle/Animation/AM_ComboAttack.AM_ComboAttack'"));
	if (ComboActionMontageRef.Object)
	{
		ComboActionMontage = ComboActionMontageRef.Object;
	}

	static ConstructorHelpers::FObjectFinder<UABComboActionData> ComboActionDataRef(TEXT("/Script/ArenaBattle.ABComboActionData'/Game/ArenaBattle/CharacterAction/ABA_ComboAttack.ABA_ComboAttack'"));
	if (ComboActionDataRef.Object)
	{
		ComboActionData = ComboActionDataRef.Object;
	}
  • ABCharacterBase.cpp

 

기본값이 정상적으로 들어온 것을 확인 할 수 있다.

 

 

 

  • NPC가 공격을 받으면 죽는 모션 설정하기
    • NPC의 죽는 모션을 설정하기 위해 죽는 모션에 대한 애니메이션 몽타주를 같은 방법으로 만들어 준다.
    • 다만, 죽는 모션은 바로 실행되어야 하기 때문에 Enable Auto Blend Out에 대한 설정을 Off로 설정해준다.

Animation Montage>Enable Auto Blend Out

 

 

 

Animation Blueprint>Anim Graph

  • 애니메이션 블루프린트를 열어서 DeadSlot을 설정하고 DefaultSlot 다음에 Output으로 나가도록 설정해준다. 그렇게 되면 DeadSlot이 좀 더 우선순위를 가지게 된다.

 

 

 

  // ...	
	// 죽는 상태를 설정하는 함수
	virtual void SetDead();

	// 죽는 애니메이션 몽타주를 재생하는 함수
	void PlayDeadAnimation();

	float DeadEventDelayTime = 5.0f;
  • ABCharacterBase.h
// 트레이스 채널을 체크해 물체가 서로 충돌되는 지를 검사하는 로직
void AABCharacterBase::AttackHitCheck()
{
}

// 데미지를 주는 로직을 가진 함수
float AABCharacterBase::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	// EventInstigator - 나에게 피해를 입힌 가해자
	// DamageCauser - 피해를 입힌 가해자가 사용한 무기라던지 아니면 가해자가 빙의한 폰, 액터 정보들
	// 만약 피해를 받는 나에게 방어력이 설정되어 있다면, 여기서 들어온 데미지를 경감시켜서 최종값으로 리턴하면 된다.
	Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	SetDead();

	return DamageAmount;
}

// 죽은 상태를 설정하는 함수
void AABCharacterBase::SetDead()
{
	// 죽었으니 움직임 제한시켜주면서 죽는 애니메이션 재생 + 콜리전 기능 제한
	GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
	PlayDeadAnimation();
	SetActorEnableCollision(false);
}

// 죽는 애니메이션 몽타주를 재생하는 함수
void AABCharacterBase::PlayDeadAnimation()
{
	// AnimInstance 가져오기 + 모든 애니메이션 몽타주 중지 시키고 죽는 애니메이션 재생하기
	UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
	AnimInstance->StopAllMontages(0.0f);;
	AnimInstance->Montage_Play(DeadMontage, 1.0f);
}
  • ABCharacterBase.cpp
UCLASS()
class ARENABATTLE_API AABCharacterNonPlayer : public AABCharacterBase
{
	GENERATED_BODY()
	
public:
	AABCharacterNonPlayer();

protected:
	void SetDead() override;
};
  • AABCharacterNonPlayer.h
#include "Character/ABCharacterNonPlayer.h"

AABCharacterNonPlayer::AABCharacterNonPlayer()
{
}

void AABCharacterNonPlayer::SetDead()
{
	Super::SetDead();

	FTimerHandle DeadTimerHandle;
	GetWorld()->GetTimerManager().SetTimer(DeadTimerHandle, FTimerDelegate::CreateLambda(
		[&]()
		{
			Destroy();
		}
	), DeadEventDelayTime, false);
}
  • AABCharacterNonPlayer.cpp

 

  • 데미지를 주는 함수와 죽은 상태를 설정하는 함수, 죽는 애니메이션 몽타주를 재생하는 함수를 만들어 주고, NonPlayer SetDead()함수가 동작하면, 피격 후 약 5초뒤에 사라질 수 있도록 동작하는 로직을 타이머를 통해 구현해주고, 람다를 통해 Destroy()시켜준다.

 

 

 

 

 

 

 

 

해당 포스트는 인프런의 <이득우의 언리얼 프로그래밍 Part2 -언리얼 게임 프레임웍의 이해>
강의를 수강하고 정리한 내용입니다.
 

이득우의 언리얼 프로그래밍 Part2 - 언리얼 게임 프레임웍의 이해 | 이득우 - 인프런

청강문화산업대학교에서 언리얼 엔진, 게임 수학, UEFN 게임제작을 가르치고 있습니다. - 이득우의 언리얼 C++ 프로그래밍, 넥슨 코리아 공식 교육 교재 선정 2023 - 스마일게이트 언리얼 프로그래

www.inflearn.com

 

 

 

 

 

 

728x90