4 minute read

‘이득우의 언리얼 c++ 게임 개발의 정석’ 책을 참고하여 작성한 포스트입니다.


물리엔진을 이용한 디버깅 방법과, 충돌한 액터에게 데미지를 가하는 기능을 알아본다.


콜리전 설정

  • 언리얼엔진에서 콜리전은 크게 세 가지 방법으로 나뉜다.
    • 스태틱메시 에셋:
      • 스새틱메시 에셋에 콜리전 영역을 심는 방법이다.
      • 스태틱메시를 더블 클릭하면 열리는 스태틱 메시 에디터에서 콜리전 영역 호가인 가능하다.
    • 기본 도형(primitive) 컴포넌트
      • 구체, 박스, 캡슐의 기본 도형을 이용해 충돌 영역을 지정하는 방법
      • 스켈레탈 메시를 움직일 때 주로 사용
      • 일반적으로 캐릭터의 이동은 캡슐 컴포넌트 이용해 처리
    • 피직스 에셋
      • 캐릭터의 각 부위에 기본 도형으로 충돌 영역을 설정하고 이를 연결해 캐릭터의 물리 설정
      • 스켈레탈 메시에만 사용 가능
  • 물리 설정은 크게 다음 세 가지로 구분된다.
    • 콜리전 채널과 기본 반응
    • 콜리전 채널의 용도
    • 다른 콜리전 채널과의 반응
  • 충돌체에는 반드시 하나의 콜리전 채널이 설정되어 있어야 한다.
  • 기본으로 주어지는 기본 콜리전 채널은 다음과 같다.
    • WorldStatic
      • 정적 배경 액터에 사용
      • 주로 스태틱메시 액터에 있는 스태틱메시 컴포넌트에 사용
    • WorldDynamic
      • 움직이는 액터에 사용하는 콜리전.
      • 블루프린트에 속한 스태틱메시 컴포넌트에 사용
    • Pawn
      • 플레이어가 조종하는 물체에 주로 사용
      • 캐릭터의 충돌을 담당하는 캡슐 컴포넌트에 설정
    • Visibility
      • 배경 물체가 시각적으로 보이는지 탐지하는 데 확인(폰은 제외)
      • 마우스로 물체를 선택하는 Picking 기능 구현할 때 사용
    • Camera
      • 카메라 설정을 위해 카메라와 목표물 간에 장애물이 있는지 탐지 용도
    • PhysicsBody
      • 물리 시뮬레이션으로 움직이는 컴포넌트에 설정
    • Vehicle
    • Destructible
  • 콜리전 프리셋의 값 Pawn과 Object type의 값 Pawn(콜리전 채널) 은 다른 설정 값이다!
  • Collision Enabled 항목에서 물리 기능을 어떻게 사용할지 지정한다.
    • Query
      • 두 물체의 충돌 영역이 서로 겹치는지 테스트하는 설정
      • 겹침을 감지하는 것을 Overlap 이라 부르고, 겹치면 관련 컴포넌트에 ‘BeginOverlap’ 이벤트가 발생한다.
      • raycast 나 sweep 기능도 Query 에 속한다.
      • 관련 이벤트가 발생하도록 Generates Overlap Events 옵션이 체크된다.
    • Physics
      • 물리적인 시뮬레이션을 사용할 때 설정
    • Query and Physics
      • 두 기능 모두 사용
      • 물리 엔진의 계산량이 많아져 성능이 떨어질 수 있다.
  • 다른 콜리전 채널과의 반응은
    • Ignore
      • 콜리전이 있어도 아무 충돌이 일어나지 않음
    • Overlap
      • 무시와 동일하게 물체를 뚫고 갈수 있지만 이벤트를 발생 시킴
    • Block
      • 물체가 뚫고 지나가지 못하게 막음
      • Hit 이벤트가 발생함
      • Generates Overlap Events 옵션이 양쪽에 체크되어 있으면 BeginOverlap 이벤트도 발동 가능
  • 프로젝트 설정에서 콜리전 채널 추가가 가능하다.
    • 오브젝트 채널
      • 콜리전 영역에 지정하는 콜리전 채널
      • 기본 콜리전 채널 중 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