트레이스 채널을 통한 공격 대상 검출

유영준·2023년 1월 11일
0
post-thumbnail

오늘은 물리충돌 SweepSingleByChannel 에 사용될 트레이싱 채널을 만드는 것으로 시작한다

트레이스 채널 생성

TraceChannel은 유니티에 Layer와 비슷하게 검출 시 걸러주는 역할을 한다

이름은 Attack, Response 는 Ignore 로 만들어준다

이 트레이스 채널에 대한 반응을 저번에 만들어둔 ABCharacter 프리셋에서 설정해준다 설정은 Block

SweepSingleToChannel 은 파라미터 설정이 복잡한데 다음과 같다

  • SweeprSingleToChannel은 하나만 검출하게 되고, 여러개를 검출하고 싶다면 SweepMultiByChannel 이 있다
  • HitResult 물리적 충돌이 탐지도니 경우 고나련된 정보를 담을 구조체
  • Start 탐색을 시작하는 위치
  • End 탐색을 끝내는 위치
  • Rot 탐색에 사용할 도형의 회전
  • TraceChannel 물리 충돌 감지에 사용할 트레이스 채널 정보
  • CollisionShape 탐색에 사용할 도형 정보
  • Params 탐색 방법에 대한 설정 값을 모아둔 구조체
  • ResponseParams 탐색 반응을 설정하기 위한 구조체

코드는 다음과 같다

ABCharacter.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "ArenaBattle.h"
#include "GameFramework/Character.h"
#include "ABCharacter.generated.h"

UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
	...
    
	void AttackCheck();

	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
	float AttackRange;

	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
	float AttackRadius;

};

공격을 체크해주는 AttackCheck 함수와 공격 범위를 지정할 변수 AttackRangeAttackRadius를 설정해준다

ABCharacter.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "ABCharacter.h"
#include "ABAnimInstance.h"

// Sets default values
AABCharacter::AABCharacter()
{
 	...
    
	AttackRange      = 200.0f;
	AttackRadius     = 50.0f;

}

void AABCharacter::PostInitializeComponents()
{
	...

	ABAnim->OnAttackHitCheck.AddUObject(this, &AABCharacter::AttackCheck);
}

void AABCharacter::AttackCheck()
{
	FHitResult HitResult;
	FCollisionQueryParams Params(NAME_None, false, this);
	bool bResult = GetWorld()->SweepSingleByChannel
	(
		HitResult,
		GetActorLocation(),
		GetActorLocation() + GetActorForwardVector() * AttackRange,
		FQuat::Identity,
		ECollisionChannel::ECC_GameTraceChannel2,
		FCollisionShape::MakeSphere(AttackRadius),
		Params
	);

	if (bResult)
	{
    	//UE4 에서는 HitResult.Actor.IsValid()
		if (HitResult.GetActor()->IsValidLowLevel())
		{
        	//FDamageEvent DamageEvent;
            //HitResult.GetActor()->TakeDamage(50.0f, DamageEvent, GetController(), this)
        
			UGameplayStatics::ApplyDamage(HitResult.GetActor(), 50.0f, GetController(), this, UDamageType::StaticClass());
		}
	}
}

여기서 Params 은 인자로 Name_None, false, this 를 줬는데, 각각
Name_None TraceTag 의 이름, Name_None은 TraceTag 가 없다고 명시하는 것
false 복잡한 검사가 아닌 간단한 검사를 실행 (true 시 복잡한 검사(complex collision test) 를 하게 되며
검출되는 첫번째 오브젝트, 충돌 지점 등 상세한 정보를 얻게 된다)
this 충돌검사를 하는 객체이며 검사시에 제외된다

데미지를 추가하는 ApplyDamage 함수는 주석 처리된 FDamageEvent 를 활용한 방식과 동일하게 작동한다


디버깅 캡슐 그리기

다만 이렇게 끝내면, 검출이 되었는지 안되었는지 검출하는것이 어렵기 때문에 씬에 디버깅이 나오게 하려 한다
대상을 검출 시 초록색, 아니라면 빨간색으로 검출되게 할 것인다

drawDebugCapsule을 하기 위해서 DrawDebugHelpers.h 를 추가해줘야한다

// Fill out your copyright notice in the Description page of Project Settings.


#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "DrawDebugHelpers.h"

...

void AABCharacter::AttackCheck()
{
	FHitResult HitResult;
	FCollisionQueryParams Params(NAME_None, false, this);
	bool bResult = GetWorld()->SweepSingleByChannel
	(
		HitResult,
		GetActorLocation(),
		GetActorLocation() + GetActorForwardVector() * AttackRange,
		FQuat::Identity,
		ECollisionChannel::ECC_GameTraceChannel2,
		FCollisionShape::MakeSphere(AttackRadius),
		Params
	);

#if ENABLE_DRAW_DEBUG
	
	FVector TraceVec    = GetActorForwardVector() * AttackRange;
	FVector Center      = GetActorLocation() + TraceVec * 0.5f;
	float HalfHeight    = AttackRange * 0.5f + AttackRadius;
	FQuat CapsuleRot    = FRotationMatrix::MakeFromZ(TraceVec).ToQuat();
	FColor DrawColor    = bResult ? FColor::Green : FColor::Red;
	float DebugLifeTime = 3.0f;

	DrawDebugCapsule
	(
		GetWorld(),
		Center,
		HalfHeight,
		AttackRadius,
		CapsuleRot,
		DrawColor,
		false,
		DebugLifeTime
	);

#endif

	if (bResult)
	{
		if (HitResult.GetActor()->IsValidLowLevel())
		{
			UGameplayStatics::ApplyDamage(HitResult.GetActor(), 50.0f, GetController(), this, UDamageType::StaticClass());
		}
	}
}

피격 시 초록색, 아니라면 붉은 색으로 캡슐을 그리게 된다


사망 모션 추가하기

마지막으로 공격 시행 시 피해를 입은 대상이 데미지를 받도록 설정하겠다

먼저 ABAnimInstance.h에 데미지를 받으면 사망할 수 있게 IsDead 변수를 추가해준다

ABAnimInstance.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "ArenaBattle.h"
#include "Animation/AnimInstance.h"
#include "ABAnimInstance.generated.h"

	...

class ARENABATTLE_API UABAnimInstance : public UAnimInstance
{
	GENERATED_BODY()
	
public:

	...

	void SetDeadAnim() { IsDead = true; }

private:

	...

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Pawn, Meta = (AllowPrivateAccess = true))
	bool IsDead;
}

이제 실질적으로 데미지를 입도록 하겠다

이는 TakeDamage 함수를 통해 가능하다

ABCharacter.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "ArenaBattle.h"
#include "GameFramework/Character.h"
#include "ABCharacter.generated.h"

UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
	GENERATED_BODY()

...

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	virtual void PostInitializeComponents() override;
	virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, 
    class AController* EventInstigator, AActor* DamageCauser) override;
    
    ...
    
};

ABCharacter.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "DrawDebugHelpers.h"

...

float AABCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, 
AController* EventInstigator, AActor* DamageCauser)
{
	float FinalDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	if (FinalDamage > 0.0f)
	{
		ABAnim->SetDeadAnim();
		SetActorEnableCollision(false);
	}

	return FinalDamage;
}

IsDead 변수를 블루프린트에서 블랜딩 해줌으로서 마무리하자

(IsDead 변수의 True, False 여부를 통해 블랜딩)

profile
토비폭스가 되고픈 게임 개발자

0개의 댓글