본문 바로가기
Unreal/Manual

Unreal Collider On/Off 충돌체 켜고 끄기

by Dev_카페인 2024. 1. 11.
반응형

[Unreal/C++] Collider On/Off 충돌체 켜고 끄기

 

언리얼 엔진에서 게임을 만들 때 공격 상태에서만 충돌체를 켜는 것이 효율적일때가 많다.

위 이미지 처럼 앞에 충돌체를 두면 공격상태가 아님에도 불구하고 실시간으로 충돌처리가 일어난다.

 

원하는 동작은 박치기를 했을 때 플레이어에게 데미지를 주는 것이다.

 

불필요한 말들을 제거하고 충돌체를 끄고 켜는 방법을 설명하자면 콜리전의 상태를 바꿔주는 것이다.

저 오브젝트 타입은 C++에서 ECollisionEnable에 enum 형식으로 정의되어 있다.

NoCollision = 충돌체를 사용하지 않는다.
QueryOnly = 이벤트 발생을 위해 사용한다.
PhysicsOnly = 물리적 충돌을 위해 사용한다.
QueryAndPhysics = 이벤트와 물리적 충돌 둘다 사용한다.

NoCollision UMETA(DisplayName="No Collision")
QueryOnly UMETA(DisplayName="Query Only (No Physics Collision)"), 
PhysicsOnly UMETA(DisplayName="Physics Only (No Query Collision)"),
QueryAndPhysics UMETA(DisplayName="Collision Enabled (Query and Physics)")

 

이를 바꾸기 위해서 PrimitiveComponent를 상속받은 Sphere, Capsule, Box Component 등에서 SetCollisionEnable(ECollisionEnabled Type)을 사용할 수 있다.

 

물론 충돌체의 Visible을 껐다 켰다 함으로써 충돌을 방지할 수는 있지만 단순히 채널로 구분하는 충돌이기 때문에 추천하지 않는 방법이다.

collider->SetCollisionEnabled(ECollisionEnabled::NoCollision);
collider->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
collider->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
collider->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);

 

어쨌든 충돌체를 설정했다면 델리게이트로 충돌 여부를 받아오면 된다.

void ACharger::InitDelegates()
{
	SphereCollider->OnComponentBeginOverlap.AddDynamic(this, &ACharger::OnComponentBeginOverlap);
}

void ACharger::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OtherActor->ActorHasTag("Enemy")) return;

	UGameplayStatics::ApplyDamage(OtherActor, 5, GetOwner()->GetInstigatorController(), this, UDamageType::StaticClass());
}

 

충돌에서 대상을 제외하고 싶다면 Tag를 이용한 방법을 추천한다.

지금까지는 다른 방법들에 비해 간단하고 효율적인 방법이다.

 

여기까지 이해가 되었다면 공격을 할 때 애니메이션 몽타주에서 충돌체를 껐다켰다 할 필요가 있을 것이다.

애니메이션 몽타주에서 충돌체를 껐다켰다 하기 위해 Animation Notify나 Notify State를 사용한다.

사실상 Tick을 사용하지 않기 때문에 Notify State보다는 단일 Notify가 효율성이 좋지만 내 귀찮음으로 Notify State 를 사용한다. 많은 스크립트는 나를 헷갈리게 하기 때문이다.

 

Animation Notify State를 만들어주고 필요한 NotifyBegin 함수와 NotifyEnd 함수를 오버라이딩 해준다.

위에 보이는 GetNotifyName_Implementation은 애니메이션 몽타주 에디터에 보이는 이름을 재정의 하는 것이다.

저 함수가 없다면 한 눈에 보이지 않는 클래스명이 가독성을 떨어뜨릴 것이다.

public :
	FString GetNotifyName_Implementation() const override;
public :
	virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) override;

	virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override;

그렇게 만든 Animation Notify State를 정의 해준다.

위에서 말한 것처럼 GetNotifyName_Implementation은 에디터에 보이는 이름을 설정해준다.

#include "Enemy/Notifies/EnemyAttackCheck.h"

#include "Enemy/Charger/Charger.h"

FString UEnemyAttackCheck::GetNotifyName_Implementation() const
{
	return "AttackCheck";
}

void UEnemyAttackCheck::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference)
{
	if (MeshComp->GetWorld()->WorldType == EWorldType::Type::EditorPreview)
		return;

	Cast<ACharger>(MeshComp->GetOwner())->SetCollisionActive(true);
}

void UEnemyAttackCheck::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
	if (MeshComp->GetWorld()->WorldType == EWorldType::Type::EditorPreview)
		return;

	Cast<ACharger>(MeshComp->GetOwner())->SetCollisionActive(false);
}

 

또한 필수적으로 if(MeshComp->GetWorld()->WorldType == EWorldType::Type::EditorPreview) return 을 추가해주길 바란다.

이 것이 없다면 에디터상에서도 Notify가 실행되기 때문에 의도하지 않은 Fatal Error를 자주 볼 수 있을 것이다.

위 문장은 에디터 프리뷰로 상태에서는 Notify를 실행하지 않는다는 뜻이다.

 

이렇게 만들어진 notifyState는 아래와 같이 AttackCheck라는 이름으로 보이게 된다.

반응형