충돌 설정과 데미지 전달
‘이득우의 언리얼 c++ 게임 개발의 정석’ 책을 참고하여 작성한 포스트입니다.
물리엔진을 이용한 디버깅 방법과, 충돌한 액터에게 데미지를 가하는 기능을 알아본다.
콜리전 설정
- 언리얼엔진에서 콜리전은 크게 세 가지 방법으로 나뉜다.
- 스태틱메시 에셋:
- 스새틱메시 에셋에 콜리전 영역을 심는 방법이다.
- 스태틱메시를 더블 클릭하면 열리는 스태틱 메시 에디터에서 콜리전 영역 호가인 가능하다.
- 기본 도형(primitive) 컴포넌트
- 구체, 박스, 캡슐의 기본 도형을 이용해 충돌 영역을 지정하는 방법
- 스켈레탈 메시를 움직일 때 주로 사용
- 일반적으로 캐릭터의 이동은 캡슐 컴포넌트 이용해 처리
- 피직스 에셋
- 캐릭터의 각 부위에 기본 도형으로 충돌 영역을 설정하고 이를 연결해 캐릭터의 물리 설정
- 스켈레탈 메시에만 사용 가능
- 스태틱메시 에셋:
- 물리 설정은 크게 다음 세 가지로 구분된다.
- 콜리전 채널과 기본 반응
- 콜리전 채널의 용도
- 다른 콜리전 채널과의 반응
- 충돌체에는 반드시 하나의 콜리전 채널이 설정되어 있어야 한다.
- 기본으로 주어지는 기본 콜리전 채널은 다음과 같다.
- WorldStatic
- 정적 배경 액터에 사용
- 주로 스태틱메시 액터에 있는 스태틱메시 컴포넌트에 사용
- WorldDynamic
- 움직이는 액터에 사용하는 콜리전.
- 블루프린트에 속한 스태틱메시 컴포넌트에 사용
- Pawn
- 플레이어가 조종하는 물체에 주로 사용
- 캐릭터의 충돌을 담당하는 캡슐 컴포넌트에 설정
- Visibility
- 배경 물체가 시각적으로 보이는지 탐지하는 데 확인(폰은 제외)
- 마우스로 물체를 선택하는 Picking 기능 구현할 때 사용
- Camera
- 카메라 설정을 위해 카메라와 목표물 간에 장애물이 있는지 탐지 용도
- PhysicsBody
- 물리 시뮬레이션으로 움직이는 컴포넌트에 설정
- Vehicle
- Destructible
- WorldStatic
- 콜리전 프리셋의 값 Pawn과 Object type의 값 Pawn(콜리전 채널) 은 다른 설정 값이다!
- Collision Enabled 항목에서 물리 기능을 어떻게 사용할지 지정한다.
- Query
- 두 물체의 충돌 영역이 서로 겹치는지 테스트하는 설정
- 겹침을 감지하는 것을 Overlap 이라 부르고, 겹치면 관련 컴포넌트에 ‘BeginOverlap’ 이벤트가 발생한다.
- raycast 나 sweep 기능도 Query 에 속한다.
- 관련 이벤트가 발생하도록 Generates Overlap Events 옵션이 체크된다.
- Physics
- 물리적인 시뮬레이션을 사용할 때 설정
- Query and Physics
- 두 기능 모두 사용
- 물리 엔진의 계산량이 많아져 성능이 떨어질 수 있다.
- Query
- 다른 콜리전 채널과의 반응은
- Ignore
- 콜리전이 있어도 아무 충돌이 일어나지 않음
- Overlap
- 무시와 동일하게 물체를 뚫고 갈수 있지만 이벤트를 발생 시킴
- Block
- 물체가 뚫고 지나가지 못하게 막음
- Hit 이벤트가 발생함
- Generates Overlap Events 옵션이 양쪽에 체크되어 있으면 BeginOverlap 이벤트도 발동 가능
- Ignore
- 프로젝트 설정에서 콜리전 채널 추가가 가능하다.
- 오브젝트 채널
- 콜리전 영역에 지정하는 콜리전 채널
- 기본 콜리전 채널 중 WorldStatic, WorldDynamic, Pawn, PhysicsBody, Vehicle, Destructible
- 트레이스 채널
- 어떤 행동에 설정하는 콜리전 채널
- 기본 콜리전 채널 중 Visibility, Camera
- 오브젝트 채널
- 프로젝트 설정의 콜리전 탭에서 콜리전 채널 추가 후, 프리셋 섹션에서 프리셋을 추가해 충돌 반응을 조정해주어야 한다.
- 폰을 위한 프리셋을 새로 추가한다 했을 때 각 다른 프리셋을 다 클릭해 물리 세팅을 세팅해 주어야 한다.
- OverlapAll, OverlapAllDynamic, OverlapOnlyPawn, Trigger, UI는 겹침으로 해주고
- IgnoreOnlyPawn, Spector, CharacterMesh, RagDoll은 무시로 해주면 된다.
- 그리고 다음과 같이 ABCharacter의 생성자에 캡슐 컴포넌트에 만든 프리셋을 설정해준다.
GetCapsuleComponent()->SetCollisionProfileName(TEXT("ABCharacter"));
트레이스 채널의 활용
- 공격을 가하기 위해서는 공격 타이밍에 공격 범위 안에 액터가 위치한 지 파악 해야 한다.
- 파악하는 방법 중 하나는 물리적 판정을 이용하는 것인데 이 때 물리 엔진을 활용할 수 있다.
- 행동에 대한 물리적 판정을 위해 트레이스 채널이 있다.
- Attack 트레이스 채널을 기본 반응을 무시로 새로 생성하고 Character 프리셋을 열고 Attack 과의 설정을 블록으로 지정한다.
- 그러면 Attack 트레이스 채널을 사용하는 액터의 물리적 행동은 캐릭터의 캡슐 컴포넌트에 설정한 캐릭터 콜리전 프리셋에만 반응한다.
line trace
UWorld::LineTraceSingleByChannel
- Trace a ray against the world using a specific channel and return the first blocking hit
- #include “Engine/World.h”
- hit 하면 true 반환
- 결과는 FHitResult 로 받으면 된다.
- FHitResult의 멤버 변수 Actor를 약포인터로 선언해서 미 참조시 가비지 컬렉터에 의해 자동으로 제거 되게 했다.
- 약 포인터로 지정된 액터에 접근하기 전에 IsValid 함수로 액터가 유효한지 먼저 파악해야 한다.
bool LineTraceSingleByChannel
(
struct FHitResult & OutHit,
const FVector & Start,
const FVector & End,
ECollisionChannel TraceChannel,
const FCollisionQueryParams & Params,
const FCollisionResponseParams & ResponseParam
) const
- OutHit
- First blocking hit found
- Start
- Start location of the ray
- End
- End location of the ray
- TraceChannel
- The ‘channel’ that this ray is in, used to determine which components to hit
- Params
- Additional parameters used for the trace
- ResponseParam
- ResponseContainer to be used for this trace
채널 값
- Attack 채널의 값은 언리얼 엔진에 정의된 ECollisionChannel 열거형으로 가져올 수 있다.
- 기본적으로 총 32개의 콜리전 채널이 제공되는데 그 중 8개는 이미 알아본 기본 채널이고, 6개는 다른 용도로 사용하도록 예약되어 있다.
- 사용자는 나머지 18개만 사용 가능하다.
- 새로 생성하는 오브젝트 채널과 트레이스 채널은 ECC_GameTraceChannel1~18 중 하나를 배정받으며, DefaultEngine.ini 에서 확인 가능하다.
MULTICAST_DELEGATE
- uobject의 멤버함수의 경우 AddUObject, ufuncion 경우 AddUFuntion로 등록한다.
// PostInitilaizeComponents()
ABAnim->OnAttackHitCheck.AddUObject(this, &AABCharacter::AttackCheck);
디버그 드로잉
DrawDebugLine
- #include “DrawDebugHelpers.h”
void DrawDebugLine
(
const UWorld * InWorld,
FVector const & LineStart,
FVector const & LineEnd,
FColor const & Color,
bool bPersistentLines,
float LifeTime,
uint8 DepthPriority,
float Thickness
)
데미지 프레임워크
- AActor에는 TakeDamage 함수가 구현 되어 있는데 이를 이용해 액터에 데미지를 전달할 수 있다.
- 데미지를 가할 때 공격 받은 액터의 TakeDamage를 호출해 전달하면 된다.
AActor::TakeDamageApply
virtual float TakeDamage
(
float DamageAmount,
struct FDamageEvent const & DamageEvent,
class AController * EventInstigator,
AActor * DamageCauser
)
- DamageAmount
- 가하는 데미지량
- DamageEvent
- 데미지 종류
- EventInstigator
- 데미지를 가한 컨트롤러
- DamageCauser
- 직접 데미지를 가한 액터(발사체, 돌 등)
예시 코드
//h
virtual float TakeDamage(
float DamageAmount,
struct FDamageEvent const& DamageEvent,
class AController* EventInstigator,
AActor* DamageCauser) override;
//cpp
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);
//
CharacterStat->SetDamage(FinalDamage);
if (CurrentState == ECharacterState::DEAD)
{
if (EventInstigator->IsPlayerController())
{
auto Ikk = Cast<AABPlayerController>(EventInstigator);
ABCHECK(nullptr != Ikk, 0.0f);
Ikk->NPCKill(this);
}
}
return FinalDamage;
}
Leave a comment