* 아래의 책의 내용을 정리한 글입니다! *
이번장은 충돌에 대해서 정리해놓은 단원으로 몇달전에 한 번 설정해본 기능들이 나와 이해하기 편했습니다!
언리얼엔진에서는 콜리전을 크게 3가지 방법으로 제작할수있는데
- 스태틱메시 에셋 : 스태틱메시 에셋에 콜리전 영역을 심는 방법, 스태틱메시 컴포넌트에서 비주얼과 충돌이라는 두 가지 기능을 설정할 수 있어 관리가 편리하다.
- 기본 도형(Primitive) 컴포넌트 : 구체, 박스, 캡슐 등의 기본 도형을 사용해 스태틱메시와 별도로 충돌 영역을 지정한다. 스켈레탈 메시를 움직일 때 주로 사용한다.
- 피직스 애셋 : 일반적으로 캐릭터의 이동은 캡슐 컴포넌트를 사용해 처리한다. 하지만 특정 상황에서 캐릭터의 각 관절이 흐느적거리는 헝겊 인형 효과를 구현할 때 이 피직스 애셋을 사용한다. 캐릭터의 각 부위에 기본 도형으로 충돌 영역을 설정하고 이를 연결해 캐릭터의 물리를 설정한다. 피직스 애셋은 스켈레탈 메시에만 사용할 수 있다.
위 처럼 콜리전을 만들었다면 이제 채널에 따라 설정 해줄수있습니다! (각각의 채널에 따라 어떻게 반응 할건지 설정가능)
각각의 채널도 아래처럼 정해져 있는 채널이 있고
- WorldStatic : 움직이지 않는 정적인 배경 액터에 사용하는 콜리전 채널이다. 주로 스태틱메시 액터에 있는 스태틱메시 컴포넌트에 사용한다.
- WorldDynamic : 움직이는 액터에 사용하는 콜리전 채널이다. 블루프린트에 속한 스태틱메시 컴포넌트에 사용한다.
- Pawn : 플레이어가 조종하는 물체에 주로 사용한다. 캐릭터의 충돌을 담당하는 캡슐 컴포넌트에 설정된다.
- Visibility : 배경 물체가 시각적으로 보이는지 탐지하는데 사용한다. 탐지에서 폰은 제외된다. 마우스로 물체를 선택하는 피킹(Picking) 기능을 구현할 때 사용한다.
- Camera : 카메라 설정을 위해 카메라와 목표물 간에 장애물이 있는지 탐지하는데 사용한다. 이전 GTA 방식으로 캐릭터를 조작할 때 장애물이 시야를 가리면 카메라를 장애물 앞으로 줌인하는 기능(SpringArm->bCollisionTest = true)이 있었다. 이때 사용하는 채널이 Camera 채널이다.
- PhysicsBody : 물리 시뮬레이션으로 움직이는 컴포넌트에 설정한다.
사용자가 직접 만들수도 있습니다.
그리고 물체마다 어떤 물리 기능을 적용시킬지도 정해줄수있습니다
- Query : 두 물체의 충돌 영역이 서로 겹치는지 테스트하는 설정. 충돌 영역의 겹침을 감지하는 것을 오버랩(Overlap) 이라고 부르며, 충돌 영역이 겹치면 관련 컴포넌트에 BeginOverlap 이벤트가 발생한다. 지정한 영역에 물체가 충돌하는지 탐지하는 레이캐스트(Raycast) 나 스윕(Sweep) 기능도 Query 에 속한다.
- Physics : 물리적인 시뮬레이션을 사용할 때 설정한다.
- Query and Physics : 위의 두 기능을 모두 사용하는 설정이다.
여기까지가 기본적인 콜리젼 끝!
이제 콜리젼 채널을 하나 만들어 봅시다. 프로젝트 세팅 >> 콜리전
그다음 새 프로파일도 만들어 봅싣다.
트리거 프로파일 편집
ABCharacter.cpp 에 추가
AABCharacter::AABCharacter()// 안에
{
//~~
GetCapsuleComponent()->SetCollisionProfileName(TEXT("ABCharacter"));
}
아래처럼 컴포넌트콜리전프리셋이 변경됩니다!
트레이스 채널에 아래처럼 하나 추가하고
캐릭터 프리셋을 수정해줍시다.
아래 코드 추가해주면
ABCharacter.h
UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
private:
//~
void AttackCheck();
}
ABCharacter.cpp
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() * 200.0f,
FQuat::Identity,
ECollisionChannel::ECC_GameTraceChannel2,
FColiisionShape::MakeSphere(50.0f),
Params);
if (bResult)
{
if (HitResult.Actor.IsValid())
{
ABLOG(Warning, TEXT("Hit Actor Name : %s"), *HitResult.Actor->GetName());
}
}
}
콜리전에따라 로그가 찍히게 됩니다!
이렇게 로그로도 확인가능하지만!
언리얼에서는 게임을 플레이하면서 콜리전을 확인할수있도록 만들어 놓은 매크로가 있다!
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"
//~~
ABCharacter::ABCharacter()
{
//~~
AttackRadius = 50.0f;
AttackRange = 200.0f;
}
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 = 5.0f;
DrawDebugCapsule(GetWorld(),
Center,
HalfHeight,
AttackRadius,
CapsuleRot,
DrawColor,
false,
DebugLifeTime);
#endif
if (bResult)
{
if (HitResult.Actor.IsValid())
{
ABLOG(Warning, TEXT("Hit Actor Name : %s"), *HitResult.Actor->GetName());
}
}
}
아래와 같이 콜리전을 보여준다!
다음은 데미지!
언리얼 엔진의 액터 클래스 AActor 에는 4개의 인자를 갖고 있는 TakeDamage 라는 함수가 이미 구현되어있는데
- DamageAmount : 전달할 대미지의 세기
- DamageEvent : 대미지 종류
- EventInstigator : 공격 명령을 내린 가해자
- DamageCauser : 대미지 전달을 위해 사용한 도구
다음과 같은 인자를 가집니다!
이제 데미지를 구현해 봅시다
ABCharacter.h
class ARENABATTLE_API AABCharacter : public ACharacter
{
//~~
virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) override;
}
대미지는 피해를 입은 액터에 관련 로직을 설정해 주어야 한다.
ABCharacter.cpp
void AABCharacter::AttackCheck()
{
//~~
if (bResult)
{
if (HitResult.Actor.IsValid())
{
ABLOG(Warning, TEXT("Hit Actor Name : %s"), *HitResult.Actor->GetName());
}
FDamageEvent DamageEvent;
HitResult.Actor->TakeDamage(50.0f, DamageEvent, GetController(), this);
}
}
float AABCharacter::TakeDamage(float DamageAmount, FDamageEvent const & DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
float FinalDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
ABLOG(Warning, TEXT("Actor : %s took Damage : %f"), *GetName(), FinalDamage);
return FinalDamage;
}
이제 데미지에 대한 로그도 생기게됩니다!
이제 죽는 모션도 만들어 봅시다.
ABAnimInstance.h
public:
//~
void SetDeadAnim() { IsDead = true; }
private:
//~
UPROPERTY(EditAnywhere, BlueprintReadOnly, 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;
if (!IsDead)
{
CurrentPawnSpeed = Pawn->GetVelocity().Size();
auto Character = Cast<ACharacter>(Pawn);
if (Character)
{
IsInAir = Character->GetMovementComponent()->IsFalling();
}
}
}
void UABAnimInstance::PlayAttackMontage() // 일반 실행
{
ABCHECK(!IsDead);
//~~
}
void UABAnimInstance::JumpToAttackMontageSection(int32 NewSection) // 모션 나누는 부분
{
ABCHECK(!IsDead);
//~~
}
ABCharacter.cpp
float AABCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
//~~~~
if (FinalDamage > 0.0f) {
ABAnim->SetDeadAnim();
SetActorEnableCollision(false);
}
//~~~~
}
이제 마지막으로 애님 블루프린트를 고쳐주면!
끝!
틀린점이 있다면 댓 달아주세요!
'언리얼 최고 > 언리얼 c++' 카테고리의 다른 글
이득우의 언리얼 C++ 정리 - 10 (아이템 상자와 무기제작) (2) | 2022.09.07 |
---|---|
이득우의 언리얼 C++ 정리 - 8 (애니메이션 시스템 활용) (1) | 2022.08.30 |
이득우의 언리얼 C++ 정리 - 7 (애니메이션 시스템의 설계) (0) | 2022.08.20 |
이득우의 언리얼 C++ 정리 - 6 (캐릭터의 제작과 컨트롤) (0) | 2022.08.20 |
이득우의 언리얼 C++ 정리 - 5 (폰의 제작과 조작) (0) | 2022.08.19 |
댓글