728x90
이 글은 Inflearn - Rookiss : 언리얼 엔진4 입문 강의를 듣고 정리한 글입니다.

 

목차.

  1. 서론
  2. 오브젝트 채널 추가
  3. 프리셋 추가
  4. 프리셋 적용
  5. 트레이스 채널 추가
  6. C++ 클래스 코드 수정(공격 판정)
  7. 결과

 

서론

 

Collision은 게임 개발에서 오브젝트 간의 상호작용을 제어하는 중요한 개념입니다.

언리얼에서는 오브젝트 간의 충돌을 감지하고 그 결과를 다양한 방식으로 활용할 수 있습니다.

 

게임을 시작하면 캐릭터가 낙하하지 않고 바닥 위에 서있는 것을 볼 수 있습니다.

 

기본적으로 캐릭터는 Capsule Component가 붙어있기 때문에 바닥과 충돌하기 때문입니다.

이 글에서는 언리얼에서 Collision 시스템에 대한 이해와

플레이어가 공격을 하면 Collision으로 이벤트를 발생시키도록 해보겠습니다.

 

오브젝트 채널 추가

 

Collsion을 제어하기 위해서는 오브젝트 채널을 추가하고 프리셋을 적용해야 합니다.

 

프로젝트 세팅 -> 콜리전에  Object ChannelsTrace Channels가 있습니다.

 

Object Channels : 오브젝트 유형 목록

Trace Channels : 어떤 행동을 지정

 

이런 차이가 있는데 아직은 크게 어떤 차이인지 감이 오지 않습니다.

오브젝트 채널은 특정 오브젝트를 지정하는 것 같고 트레이스 채널은 공격, 점프 등 특정 행동을 지정하는 것 같습니다.

 

새-오브젝트-채널-추가
새 오브젝트 채널 추가

 

새 오브젝트 채널을 생성하고 이름은 MyCharacter로 설정하고 기본 반응은 Block으로 해주겠습니다.

Block은 Block 오브젝트와 충돌하면 통과하지 못하고 충돌할 수 있게 해 줍니다.

 

 

오브젝트 채널을 새로 만들어줬으니 그에 맞게 프리셋을 새로 만들어주겠습니다.

 

프리셋 추가

 

프리셋은 일종의 규칙이라고 볼 수 있습니다.

프리셋을 통해 MyCharacter가 어떤 오브젝트와는 충돌하고 어떤 오브젝트는 충돌하지 않게 설정할 수 있습니다.

 

하단 Preset 항목에서 새 프로파일을 눌러 프로파일을 새로 추가해 줍니다.

이름은 마찬가지로 MyCharacter로 하고 플레이어는 다른 오브젝트와 충돌해야 하기 때문에 
콜리전 켜짐 : Collision Enabled

오브젝트 유형 : MyCharacter

 

오브젝트 채널을 생성할 때 Block으로 기본반응을 설정해 줬기 때문에

트레이스 유형과 오브젝트 유형들이 모두 블록으로 되어있는 것입니다.

 

블록 : 물체가 뚫고 지나가지 못함.

겹침 : 뚫고 지나가지만 이벤트를 발생시킴(ex 포탈, 퀘스트)

무시 : 둘 다 발생 x

 

 

Pawn이 블록으로 체크되어 있는데 이것은 MyCharacter와 Pawn이 충돌할 수 있다는 뜻입니다.

 

MyCharacter-프로파일-편집
MyCharacter 프로파일 편집

 

그런데 이렇게 MyCharacter 한 방향에서 충돌 설정을 해줬다고 끝이 아니라 반대로 다른 프리셋도 적절하게 설정해줘야 합니다.

 

무슨 뜻이냐면 

 

OverlapAll-프로파일-편집
OverlapAll 프로파일 편집

 

OverlapAll 프리셋을 보면 MyCharacter만 블록으로 되어있다는 것을 볼 수 있습니다.

 

OverlapAll은 모든 오브젝트들이 통과할 수 있고 이벤트만 발생할 수 있다는 규칙을 가지고 있는데,

MyCharacter 혼자 충돌이 가능하면 안 되니 겹침으로 바꿔줘야 합니다.

 

다른 프리셋들도 이렇게 콜리전 반응을 적절하게 수정해 줍니다.

 

프리셋 적용

 

프리셋을 새로 만들었으니 이제 캐릭터에 적용해 주겠습니다.

 

캐릭터 블루프린트 클래스에서 캡슐 콜라이더를 클릭 -> 콜리전 -> 콜리전 프리셋에 보면 Default로 Pawn으로 설정되어 있을 건데

이것을 MyCharacter 프리셋으로 변경해 주겠습니다.

 

블루프린트-클래스에서-콜리전-프리셋-변경
블루프린트 클래스에서 콜리전 프리셋 설정

 

이제 게임을 실행하고 캐릭터의 콜리전 프리셋을 확인해 보면 

 

캡슐 컴포넌트의 콜리전 프리셋

 

MyCharacter로 잘 변경된 것을 볼 수 있습니다.

 

여기까지 캐릭터에게 커스텀으로 콜리전을 추가해주는 작업을 했습니다.

이번에는 캐릭터가 공격하면 콜리전을 생성하고 적에게 충돌 시 이벤트를 발생시키도록 해보겠습니다.

 

트레이스 채널 추가

 

아까 말한 것처럼 어떤 행동을 지정할 때는 트레이스 채널을 사용한다고 했으니 

공격 충돌판정을 위해 트레이스 채널을 추가해 주겠습니다.

 

프로젝트 세팅-콜리전-새-트레이스-채널-추가
새 트레이스 채널 추가

 

기본 반응은 Ignore로 해줍니다.

 

Attack-블록으로-변경
Attack 블록으로 변경

 

MyCharacter의 프리셋에 가보면 Attack이 무시로 되어있는데,

Attack은 MyCharacter와 충돌해야 하기 때문에 겹침 또는 블록으로 변경해 줍니다.

 

C++ 클래스 코드 수정(공격 판정)

 

공격을 하고 피격이 되었을 때 이 정보를 전달하기 위해 델리게이트 타입을 하나 만들겠습니다.

 

MyAnimInstance.h

DECLARE_MULTICAST_DELEGATE(FOnAttackHit);
/**
 * 
 */
UCLASS()
class TESTUNREALENGINE_API UMyAnimInstance : public UAnimInstance
{
....
public:
	FOnAttackHit OnAttackHit;
}

 

MyAnimInstance.cpp

void UMyAnimInstance::AnimNotify_AttackHit()
{
	OnAttackHit.Broadcast();
}

 

전에 만든 노티파이 코드에

이벤트가 발생했을 때 브로드캐스트를 해서 모두에게 알려줄 수 있도록 코드를 작성했습니다.

이와 같은 방법은 리스너 패턴(구독자 패턴)으로 내가 일을 한 것을 구독한 사람에게 알려줄 수 있습니다.

 

이제 OnAttackHit라는 이벤트가 발생했을 때 내가 받아주고 싶은 함수를 연동하면 되는데,

이 구독을 BeginPlay에서 하기보단 그보다 전에 하는 것이 좋습니다.

 

BeginPlay에서 모든 컴포넌트들이 초기화된 다음에 서로 연동시켜 주는 부분을 넣는 것이 맞기 때문입니다.

 

MyCharacter.h

protected:
....
	virtual void PostInitializeComponents() override;
....
public:
....
	void AttackCheck();
....

 

MyCharacter.cpp

void AMyCharacter::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	AnimInstance = Cast<UMyAnimInstance>(GetMesh()->GetAnimInstance());
	if (AnimInstance)
	{
		AnimInstance->OnMontageEnded.AddDynamic(this, &AMyCharacter::OnAttackMontageEnded);
		AnimInstance->OnAttackHit.AddUObject(this, &AMyCharacter::AttackCheck);
	}
}

void AMyCharacter::AttackCheck()
{
	FHitResult HitResult;
	FCollisionQueryParams Params(NAME_None, false, this);

	float AttackRange = 100.f;
	float AttackRadius = 50.f;

	bool bResult = GetWorld()->SweepSingleByChannel(
		OUT HitResult,
		GetActorLocation(),
		GetActorLocation() + GetActorForwardVector() *  AttackRange,
		FQuat::Identity,
		ECollisionChannel::ECC_GameTraceChannel12,
		FCollisionShape::MakeSphere(AttackRadius),
		Params);
	if (bResult && HitResult.Actor.IsValid())
	{
		UE_LOG(LogTemp, Log, TEXT("Hit Actor : %s"), *HitResult.Actor->GetName());
	}
}

 

 

기존에 BeginPlay에 있던 코드도 모두 옮겨주었고 AddUObject라는 것을 추가해서 OnAttackHit라는 이벤트가 발생했을 때 연동하고 싶은 함수를 추가해 주면 됩니다.

 

AttackCheck라고 공격이 적중했을 때 Debug를 출력하는 코드를 새로 만들었습니다.

코드가 엄청 긴데 

 

SweepSingleByChannel매개변수로 

OutHit : 결과물

Start : 판정의 시작 부분

End : 판정의 끝부분

Rot : 피격판정의 도형 회전

TraceChannel : 충돌 탐지에 사용할 채널 정보

CollisionShape : 어떤 형태로 탐지할지

 

정도가 있습니다..

컴파일 후 레벨에 블루프린트 클래스를 미리 하나 배치합니다.

 

그리고 게임을 실행하고 때리면,

로그가 찍히며 상대방의 정보를 잘 출력하는 것을 볼 수 있습니다.

 

하지만 공격이 어느 정도의 범위로 나가는지 확인이 어려운데 시각적으로 디버그를 하는 코드도 추가해 보겠습니다.

 

MyCharacter.cpp

#include "DrawDebugHelpers.h"
....
....
void AMyCharacter::AttackCheck()
{
	FHitResult HitResult;
	FCollisionQueryParams Params(NAME_None, false, this);

	float AttackRange = 100.f;
	float AttackRadius = 50.f;

	bool bResult = GetWorld()->SweepSingleByChannel(
		OUT HitResult,
		GetActorLocation(),
		GetActorLocation() + GetActorForwardVector() *  AttackRange,
		FQuat::Identity,
		ECollisionChannel::ECC_GameTraceChannel12,
		FCollisionShape::MakeSphere(AttackRadius),
		Params);
	FVector Vec = GetActorForwardVector() * AttackRange;
	FVector Center = GetActorLocation() + Vec * 0.5f;
	float HalfHeight = AttackRange * 0.5f + AttackRadius;
	FQuat Rotation = FRotationMatrix::MakeFromZ(Vec).ToQuat();
	FColor DrawColor;
	if (bResult)
		DrawColor = FColor::Green;
	else
		DrawColor = FColor::Red;
	DrawDebugCapsule(GetWorld(), Center, HalfHeight, AttackRadius,
		Rotation, DrawColor, false, 2.f);
	if (bResult && HitResult.Actor.IsValid())
	{
		UE_LOG(LogTemp, Log, TEXT("Hit Actor : %s"), *HitResult.Actor->GetName());
	}
}

 

코드의 형태는 아까 작성한 코드와 비슷합니다.

 

결과

 

적-콜리전-충돌
적 콜리전 충돌

 

이제 공격을 하면 빨간색 캡슐이 그려지고 만약 다른 콜라이더와 충돌했다면 초록색 콜라이더가 생성되는 것을 볼 수 있습니다.

 

이번에 오브젝트 채널, 트레이스 채널 이렇게 두 개의 채널을 추가하고

프리셋을 통해서 규칙을 설정하고

델리게이트를 통해 이벤트를 구독한 뒤

충돌했을 때 함수가 호출되도록 코드를 작성해 봤습니다.

 

728x90

+ Recent posts