Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

공부용 이모저모

UE4 - 충돌 설정과 데미지 전달 본문

UE4 - C++

UE4 - 충돌 설정과 데미지 전달

불타는버스 2021. 9. 30. 14:14

이득우 C++ 9장 내용

 

콜리전 설정(충돌설정)

종류

1.스태틱 메쉬 에셋 : 스태틱 매쉬(가만있는 메쉬)에 충돌설정을 심는 방법.비주얼 적인 표현이 가능하다

2.도형 컴포넌트 : 구체,박스,캡슐(캐릭터) 등의 도형에 충돌을 설정한다. 주로 스켈레탈 메쉬(다이나믹 메쉬)에 쓰인다.

3.피직스 에셋 : 캐릭터의 각 관절 등에 충돌영역을 심어 물리를 설정.스켈레탈 메쉬에서만 쓰인다.

 

콜리전 채널 - 콜리전 할 대상을 정할 수 있다.

 

채널 종류

WorldStatic - 정적 배경액터에 쓰이는 콜리전 채널. 주로 스태틱 매시 컴포넌트 사용

WorldDynamic - 움직이는 액터용 콜리전 채널, 블루 프린터에 속한 스태틱 매시 컴포넌트에 주로 사용.

Pawn - 플레이어의 조작 객체.캡슐 컴포넌트가 기본으로 되어있다.

Visibility - 물체가 보이는지 탐지할때 쓰인다.(폰은 제외) 마우스로 선택하는 Picking에 쓰인다.

Camera - 카메라와 목표물간 장애물이 있는지 검사한다. 벽이 캐릭터를 가릴때 카메라를 유동적으로 움직이는등에 쓰임

PhysicsBody - 물리 시뮬레이션으로 움직이는 컴포넌트에 쓰인다.

 

캐릭터는 기본적으로 캡슐 컴포넌트 상속
콜리전의 종류
실제 적용 되는것은 Object Type인 Pawn이다.
실제 충돌 적용은 Object Type에서 결정된다.

Collision Enabled는 3가지 옵션이 있다. 
Query: 두 물체의 충돌영역이 겹치는지 테스트.겹치는것을 Overlap이라 부르며 BeginOverlap 이벤트가 발생한다. RayCast(마우스 충돌등에 쓰임)도 Query 옵션에 해당된다.

Physics:물리적인 시뮬레이션을 사용할때 설정한다.

Query and Physics : 두 기능을 동시에 사용하나, 부담이 간다.

 

Generate Overlap Events - 충돌이 감지되었을때 BeginOverlap || hitEvent을 실행시킬 것인가?

 

해당 Type이 다른 Type과 충돌할경우 체크

무시: 충돌이 발생하지 않음.

겹침: 서로 뚫고 들어가나, BeginOverlap을 발동 시킬 수 있음

블록: 서로 충돌하지 못하게 막는다.단 HitEvent는 일어난다(BeginOverlap과 별도)

 

프로젝트 세팅에 Collision으로 가면 세팅이 가능해진다.(더블클릭시 표시) 혹은 새로운 충돌을 만들 수 있다.
위에서 수정못하던 Pawn의 수정이 가능

Visibility,Camera만 트레이스 체널이고 나머지는 오브젝트 채널에 속한다.

새 오브젝트 채널 생성
새 프로파일 생성

기본을 블록으로 설정했기 때문에 베이스는 전부 블록으로 되어있다.

트리거는 기본을 겹침으로 변경

트리거는 언리얼에서 영역안에 들어왔을때를 감지해주는 용도로 사용하기 때문에
일반적으론 겹침 체크 해두는것이 좋다고 한다. 트리거와 같이 용도에 맞게 몇몇 옵션을 수정해야한다.

 

OverlapAll : 겹침

OverlapAllDynamic : 겹침

IgnoreOnlyPawn : 무시(폰만 제외)

OverlapOnlyPawn : 겹침(폰 류에만 겹침 설정)

Spectator : 무시(외부 관중과의 충돌여부)

CharacterMesh : 무시(캐릭터 매시에 쓰이는 물리)

RagDoll : 무시(스켈레탈 메시의 피직스 에셋 물리를 가동하기 위한 물리 설정)

UI : 겹침(UI에 사용하는 요소)

 

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

// Sets default values
AABCharacter::AABCharacter()
{
 	...
	//ABCharacter로 콜리전 변경
	GetCapsuleComponent()->SetCollisionProfileName(TEXT("ABCharacter"));
}

ABCHARACTER의 Capsule Component가 바뀌어있다.

트레이스 채널의 활용

지난번 애니메이션에 이어 캡슐 컴포넌트를 이용한 충돌로 연속공격에 판정을 넣기로 한다

Attack Trace 채널 추가. 타입은 Ignore
ABCharacter에서 Attack설정을 Block으로

SweepSingleByChannel 함수 : 트레이스 채널을 사용해 물리적 충돌 여부를 가림. GetWorld에 내장된 함수.

 

이하 해당 함수의 파라메터

HitResult : 물리적 충돌이 탐지 된 경우 관련된 정보를 담을 구조체

Start : 탐색을 시작할 위치

End : 탐색을 끝낼 위치

Rot : 탐색에 사용할 도형의 회전

TraceChannel : 물리충돌 감지에 사용할 채널 정보

CollisionShape : 탐색에 사용할 기본 도형 정보.구체,캡슐,박스 사용

Params : 탐색 방법에 대한 설정값을 모아둔 구조체

ResponseParams : 탐색 반응을 설정하기 위한 구조체

 

언리얼 엔진은 게임에서 사용 할 수 있도록 32개의 콜리전 채널을 제공한다.

여기서 기본 언리얼 사용 콜리전이 8개, 엔진에서 별도로 사용하는게 6개이고,

유저가 사용 할 수 있는 콜리전 채널은 18개이다.

 

엔진 코드의 EngineTpyes.h에 해당 선언된 18개의 채널을 확인 할 수 있다.

UENUM(BlueprintType)
enum ECollisionChannel
{
	...
	ECC_GameTraceChannel1 UMETA(Hidden),
	ECC_GameTraceChannel2 UMETA(Hidden),
	ECC_GameTraceChannel3 UMETA(Hidden),
	ECC_GameTraceChannel4 UMETA(Hidden),
	ECC_GameTraceChannel5 UMETA(Hidden),
	ECC_GameTraceChannel6 UMETA(Hidden),
	ECC_GameTraceChannel7 UMETA(Hidden),
	ECC_GameTraceChannel8 UMETA(Hidden),
	ECC_GameTraceChannel9 UMETA(Hidden),
	ECC_GameTraceChannel10 UMETA(Hidden),
	ECC_GameTraceChannel11 UMETA(Hidden),
	ECC_GameTraceChannel12 UMETA(Hidden),
	ECC_GameTraceChannel13 UMETA(Hidden),
	ECC_GameTraceChannel14 UMETA(Hidden),
	ECC_GameTraceChannel15 UMETA(Hidden),
	ECC_GameTraceChannel16 UMETA(Hidden),
	ECC_GameTraceChannel17 UMETA(Hidden),
	ECC_GameTraceChannel18 UMETA(Hidden),
	...
}

저 채널은 프로젝트 세팅에서 새 트레이스 채널을 만들때마다 배정된다.

이는 DefaultEngine.ini 에서 확인 할 수 있다.

+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="ABCharacter")
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel2,DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False,Name="Attack")

 

FCollisionShape::MakeSphere - 타원 콜리젼 만드는 함수

이걸 사용해서 판정을 추가하기로 한다

 

ABCharacter.h 수정

private:
	....
	void AttackCheck();

ABCharacter.cpp 수정

void AABCharacter::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	...
	//AttackHitCheck 노티파이를 받으면 AttackCheck 실행
	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() * 200.0f,
		FQuat::Identity,
		ECollisionChannel::ECC_GameTraceChannel2,
		FCollisionShape::MakeSphere(50.0f),
		Params);

	if (bResult)
	{
		if (HitResult.Actor.IsValid())
		{
        	//존재한다면 로그 남기기
			UE_LOG(LogTemp, Warning, TEXT("Hit Actor Name : %s"), *HitResult.Actor->GetName());
		}
	}
}

이후 ABCharacter을 공격하면 공격한 캐릭터 명이 뜬다

디버그 드로잉

언리얼 엔진에서 제공하는 기능으로 디버그중 시각적으로 범위를 표기해준다.

DebugDrawHelpers.h 헤더에 들어있다.

 

ABCharacter.h 수정

class ARENABATTLE_API AABCharacter : public ACharacter
{
	...

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

ABCharacter.cpp 수정

#include "DrawDebugHelpers.h"

AABCharacter::AABCharacter()
{
	...
    
	AttackRange = 200.0f;
	AttackRadius = 50.0f;
}

void AABCharacter::AttackCheck()
{
	...
	bool bResult = GetWorld()->SweepSingleByChannel(
		HitResult,
		GetActorLocation(),
		GetActorLocation() + GetActorForwardVector() * 200.0f,
		FQuat::Identity,
		ECollisionChannel::ECC_GameTraceChannel2,
		FCollisionShape::MakeSphere(50.0f),
		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 = 5.0f;


	//캡슐 디버그 메쉬그리기
	DrawDebugCapsule(GetWorld(), Center, HalfHeight, AttackRadius, CapsuleRot, DrawColor, false, DebugLifeTime);
#endif
	...
}

타겟이 없을경우
타겟이 있을경우 녹색으로 표시

데미지 프레임워크

언리얼 엔진 제공기능으로, 데미지를  해당 액터로 전달해주는 기능이다.

액터 내부에 TakeDamage라는 함수가 구현되어 있으며, 4가지 인자로 구성된다.

 

DamageAmount : 전달할 데미지의 세기

DamageEvent : 데미지 종류

EventInstigator : 공격 명령을 내린 가해자

DamageCauser : 데미지 전달을 위해 쓰인 도구

 

여기서 가해자란, Pawn을 의미하는게 아닌 Pawn을 조종하는 컨트롤러를 가리킨다.(즉 다른 플레이어)

언리얼엔진이 FPS PVP기반 게임이기 때문에 이러한 방식으로 보내주는것으로 보인다.

 

 

ABCharacter.h 변경

UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
	...
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;
        ...

TakeDamage 함수 override

 

ABCharacter.cpp 변경

void AABCharacter::AttackCheck()
{
	...
	if (bResult)
	{
		if (HitResult.Actor.IsValid())
		{
			//UE_LOG(LogTemp, Warning, TEXT("Hit Actor Name : %s"), *HitResult.Actor->GetName());
			//로그 대신 데미지
			FDamageEvent DamageEvent;
			//찾은 대상에게 50의 데미지 입히기
			HitResult.Actor->TakeDamage(50.0f, DamageEvent, GetController(), this);
		}
	}
}

float AABCharacter::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser)
{
	float FinalDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
	UE_LOG(LogTemp, Warning, TEXT("Actor Name : %s Damage : %f"), *GetName() , FinalDamage);
	return FinalDamage;
}

데미지 판정이 로그로 나온다.당연히 상대도 AABCharacter이어야만 한다.

기존 액터에 TakeDamage 기능이 있고, AABCharacter은 이를 상속하여 추가 기능을 실행시키는 원리이다.

Can be Damaged 체크를 풀경우, 무적이된다.
데미지는 들어오나 0으로 수정

죽음 옵션 추가

 

ABAnimInstance.h 수정

public:
//함수란
	void SetDeadAnim() { IsDead = true; }
private:
//변수란
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Pawn, Meta = (AllowPrivateAccess = true))
		bool IsDead;

ABAnimInstance.Cpp

UABAnimInstance::UABAnimInstance()
{
	IsDead = false;
}

//애니메이션 초당 확인
void UABAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	//폰의 포인터를 얻어온다
	auto Pawn = TryGetPawnOwner();
	if (!::IsValid(Pawn)) 
		return;

	//IsDead가 아닐때만 값 변경
	if (!IsDead)
	{
		//폰의 속도 데이터를 입력한다(GetVelocity가 속도 데이터이고 Size가 그 수치이다)
		CurrentPawnSpeed = Pawn->GetVelocity().Size();
		//폰을 캐릭터로 캐스팅
		auto Character = Cast<ACharacter>(Pawn);
		if (Character)
		{
			//캐릭터의 떨어짐 여부를 저장한다.
			IsInAir = Character->GetMovementComponent()->IsFalling();
		}
	}
}

컴파일 후 애님그래프로 가면 IsDead가 추가되어있다.

IsDead가 켜져있는가를 기점으로 ,기존 몽타주까지의 포즈를 False 포즈로 넣고, True에 Die 애니메이션.

ABCharacter.cpp 추가

float AABCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	float FinalDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
	UE_LOG(LogTemp, Warning, TEXT("Actor Name : %s Damage : %f"), *GetName() , FinalDamage);

	if (FinalDamage > 0.0f)
	{
		//IsDead = true;
		ABAnim->SetDeadAnim();
		//죽었으니 충돌판정 제거
		SetActorEnableCollision(false);
	}
	return FinalDamage;
}

아직 체력바 개념은 없으니, 데미지가 들어오면 죽게 처리했다.

 

잘 적용된다.