본문 바로가기
포트폴리오 제작/Project_P

Project_P 공격 판정 구현(4) TA 타겟액터 구현

by k99812 2024. 9. 11.

TA 타겟액터 생성


 

GameplayAbilityTagetActor를 상속받아 PPTA_Trace 클래스를 생성

 

PPTA_Trace 헤더파일


#include "CoreMinimal.h"
#include "Abilities/GameplayAbilityTargetActor.h"
#include "PPTA_Trace.generated.h"

/**
 * 
 */
UCLASS()
class PROJECT_P_API APPTA_Trace : public AGameplayAbilityTargetActor
{
	GENERATED_BODY()
	
public:
	APPTA_Trace();

	virtual void StartTargeting(UGameplayAbility* Ability) override;

	virtual void ConfirmTargetingAndContinue() override;

	void SetShowDebug(bool InShowDebug) { bShowDebug = InShowDebug; }

protected:
	virtual FGameplayAbilityTargetDataHandle MakeTargetData() const;

	bool bShowDebug = false;
};

 

  • APPTA_Trace();
    • 생성자
  • virtual void StartTargeting(UGameplayAbility* Ability) override;
    • 부모함수에서 상속받은 함수
    • 타겟 액터를 초기화 및 처음 필요한 로직을 실행하는 함수
  • virtual void ConfirmTargetingAndContinue() override;
    • 부모함수에서 상속받은 함수
    • 부모로직은 사용하지 않음
      그러므로 Cpp에서 Super키워드로 부모 로직을 실행시키면 안됨
  • void SetShowDebug(bool InShowDebug) { bShowDebug = InShowDebug; }
    • TA가 생성하는 콜라이더를 디버그할지 설정하는 함수
  • virtual FGameplayAbilityTargetDataHandle MakeTargetData() const;
    • 타겟 데이터를 만드는 함수
  • bool bShowDebug = false;
    • 디버그할지 안할지 저장할 bool 변수

 

Cpp 파일


생성자

APPTA_Trace::APPTA_Trace()
{
}

 

생성자에서는 설정할게 없음

 

StartTargeting

void APPTA_Trace::StartTargeting(UGameplayAbility* Ability)
{
	Super::StartTargeting(Ability);

	SourceActor = Ability->GetCurrentActorInfo()->AvatarActor.Get();
}

 

  • Super::StartTargeting(Ability);
    • 부모 함수 실행
    • 부모함수에선 OwningAbility 변수를 들어온 변수 Ability로 초기화
  • SourceActor = Ability->GetCurrentActorInfo()->AvatarActor.Get();
    • 추가적으로 SourceActor를 초기화

 

ConfirmTargetingAndContinue

void APPTA_Trace::ConfirmTargetingAndContinue()
{
	//Super::ConfirmTargetingAndContinue();

	if (SourceActor && IsConfirmTargetingAllowed())
	{
		FGameplayAbilityTargetDataHandle DataHandle = MakeTargetData();
		TargetDataReadyDelegate.Broadcast(DataHandle);
	}
}

 

ConfirmTargetingAndContinue 함수는 타겟데이터핸들을 생성해 반환해주는 함수

TargetDataReadyDelegate에 콜백함수를 연결해야 타겟데이터를 반환받을 수 있음

 

  • ConfirmTargeting
    • IsConfirmTargetingAllowed()가 true일시 ConfirmTargetingAndContinue함수를 호출해 데이터핸들을 만듦
      bDestroyOnConfirmation 변수가 true이면 바로 Destroy함수 호출

 

  • CancelTargeting
    • 타겟데이터의 타겟팅을 취소하는 함수
    • CanceledDelegate로 Brodcast를해 빈 데이터핸들을 전달함

 

  •  //Super::ConfirmTargetingAndContinue();
    • ConfirmTargetingAndContinue 부모함수의 로직은
      비어있는 TargetDataHandle을 만들어 Brodcast함
      그래서 부모로직을 실행시킬 필요가 없음
    • 또한 부모함수 check(ShouldProduceTargetData()); 에서
      ShouldProduceTargetData함수가 false를 반환해 check함수에서 에디터가 꺼짐
  • if (SourceActor && IsConfirmTargetingAllowed())
    • 소스액터가 있고 IsConfirmTargetingAllowed가 true면 실행
      • IsConfirmTargetingAllowed 함수는 단순히 return true를 실행
        필요하다면 override를 해 필요한 로직을 작성해야함
    • FGameplayAbilityTargetDataHandle DataHandle = MakeTargetData();
      • MakeTargetData 함수를 이용하여 데이터핸들을 생성
    • TargetDataReadyDelegate.Broadcast(DataHandle);
      • 생성한 데이터핸들을 브로드캐스트
      • TargetDataReadyDelegate 콜백함수에서 EndTask를 호출하여
        TA를 소유한 어빌리티 태스크가 종료되어 AT→OnDestroy함수가 호출
        OnDestroy에서 TA →Destroy를 실행하여 TA를 없앰

 

MakeTargetData

FGameplayAbilityTargetDataHandle APPTA_Trace::MakeTargetData() const
{
	ACharacter* Character = CastChecked<ACharacter>(SourceActor);

	FHitResult OutHitResult;
	const float AttackRadius = 50.0f;
	const float AttackRange = 100.0f;

	//Params(SCENE_QUERY_STAT(태그이름), 복잡한 트레이스 할지, 충돌 검출 안할 액터)
	//SCENE_QUERY_STAT(태그이름) 들어간 인자로 태그를 생성
	FCollisionQueryParams Params(SCENE_QUERY_STAT(PPTA_Trace), false, Character);
	const FVector FowardVector = Character->GetActorForwardVector();
	const FVector Start = Character->GetActorLocation() + FowardVector * Character->GetCapsuleComponent()->GetScaledCapsuleRadius();
	const FVector End = Start + FowardVector * AttackRange;

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

	FGameplayAbilityTargetDataHandle TargetDataHandle;
	if (HitDetected)
	{
		FGameplayAbilityTargetData_SingleTargetHit* TargetData = new FGameplayAbilityTargetData_SingleTargetHit(OutHitResult);
		TargetDataHandle.Add(TargetData);
	}

#if ENABLE_DRAW_DEBUG

	if (bShowDebug)
	{
		FVector CapsuleOrigine = Start + (End - Start) * 0.5;
		float CapsuleHalfHeight = AttackRange * 0.5;
		FColor DrawColor = HitDetected ? FColor::Green : FColor::Red;

		DrawDebugCapsule(GetWorld(), CapsuleOrigine, CapsuleHalfHeight, AttackRadius, FRotationMatrix::MakeFromZ(FowardVector).ToQuat(), DrawColor, false, 3.0f);
	}

#endif

	return TargetDataHandle;
}

 

  • ACharacter* Character = CastChecked<ACharacter>(SourceActor);
    • 소스액터를 캐릭터로 형변환
  • FHitResult OutHitResult;
    • Sweep결과를 저장할 HitResult
  • const float AttackRadius = 50.0f;
    const float AttackRange = 100.0f;
    • 공격 범위, 사거리를 저장하는 변수
      현재는 하드코딩으로 작성했지만 추후 어트리뷰트 추가 후
      어트리뷰트를 이용하여 가져올 예정
  • FCollisionQueryParams Params(SCENE_QUERY_STAT(PPTA_Trace), false, Character);
    • Params(SCENE_QUERY_STAT(태그이름), 복잡한 트레이스 할지, 충돌 검출 안할 액터)
      • Parmas의 첫번째 인자는 Parmas에 지정할 이름인데 SCENE_QUERY_STAT매크로를 이용하여 지정
    • 추후 Sweep에 들어갈 Params
  • const FVector FowardVector = Character->GetActorForwardVector();
    • 캐릭터로 부터 FowardVector를 가져옴
  • const FVector Start = Character->GetActorLocation() + 
    FowardVector * Character->GetCapsuleComponent()->GetScaledCapsuleRadius();
    • 시작 지점 = 캐릭터의 로케이션 + (포워드 백터 * 캡슐의 반지름)
      • (포워드 백터 * 캡슐의 반지름)으로 캐릭터의 콜라이더 끝부분으로 시작지점을 지정
  • const FVector End = Start + FowardVector * AttackRange;
    • 끝 지점 = 스타트 지점 + FowardVector * 공격 사거리
      • 스타트 지점에 공격 사거리를 더해줌
  • bool HitDetected = GetWorld()>
    SweepSingleByChannel(OutHitResult, Start, End, FQuat::Identity, CCHANNEL_PPACTION, 
    FCollisionShape::MakeSphere(AttackRadius), Params);
    • SweepSingleByChannel 함수를 이용해 공격판정
      Sweep(도형)Single(한개의 결과)ByChannel(채널을 이용)
    • SweepSingleByChannel(결과를 저장할 구조체, 시작지점, 끝지점, 로테이션, 트레이스채널, 생성할 도형, 쿼리 파람)
    • 생성한 도형이 Start지점부터 End까지 이동하여 첫번째로 부짖히는 액터
      (해당 액터의 콜리전 프리셋에서 설정한 트레이스 채널의 이벤트가 Block이 설정되어야 됨)
      에 Block 이벤트를 발생하여 HitReuslt에 저장됨
  • FGameplayAbilityTargetDataHandle TargetDataHandle;
    • 빈 타겟데이터 핸들을 만듦
  • if (HitDetected)
    • Sweep이 성공했으면
    • FGameplayAbilityTargetData_SingleTargetHit* TargetData = 
      new FGameplayAbilityTargetData_SingleTargetHit(OutHitResult);
      • 싱글히트 결과인 경우 FGameplayAbilityTargetData_SingleTargetHit 를 사용하여 인스턴스를 만들 수 있음
      • new FGameplayAbilityTargetData_SingleTargetHit(OutHitResult)
        • OutHitResult 를 넣어 FGameplayAbilityTargetData를 생성
    • TargetDataHandle.Add(TargetData);
      • 타겟 데이터 핸들에 생성한 타겟 데이터를 넣어줌
  • return TargetDataHandle;
    • 최종적으로 만든 데이터를 리턴

 

  • #if ENABLE_DRAW_DEBUG
    • 디버그 드로우가 활성화 되어있으면
    • if (bShowDebug)
      • FVector CapsuleOrigine = Start + (End - Start) * 0.5;
        float CapsuleHalfHeight = AttackRange * 0.5;
        • 그릴 캡슐의 중심점과 높이의 반절을 구함
      • FColor DrawColor = HitDetected ? FColor::Green : FColor::Red;
        • 색 지정
      • DrawDebugCapsule(GetWorld(), CapsuleOrigine, CapsuleHalfHeight, AttackRadius, FRotationMatrix::MakeFromZ(FowardVector).ToQuat(), DrawColor, false, 3.0f);
        • DrawDebugCapsule 함수를 이용하여 캡슐을 그림

 

 

콜리전 관련 참고할 글

https://frompero.github.io/post/unreal/unreal-10/

 

[Unreal] 언리얼 충돌 처리 방법, 월드 트레이싱 함수

충돌체를 만들자!

frompero.github.io