Pawn의 이동, 회전 구현

정혜창·2025년 1월 31일
0

내일배움캠프

목록 보기
25/43
post-thumbnail

근 몇일간 Pawn의 이동구현에 얼마나 많은 시간을 할애했는지 모르겠다. Pawn은 Character와 달리 CharacterMovementComponent도 없을 뿐더러 FloatingPawnMovement가 있더라도 이것을 사용하지 않고 직접 이동을 구현할 생각이였다.

참고로 CharacterMovementComponent는 Character를 상속받는 클래스에서만 사용가능하고 중력, 경사면, 점프 같은 물리 기반 기능을 자동으로 처리해주는 컴포넌트이다. 그래서 이동로직을 작성할때 컴포넌트에 있는 함수만 호출하면 자동으로 처리해준다.

FloatingPawnMovement 같은 경우 CharacterMovementComponent 처럼 중력, 경사면등 물리 기반 기능을 자동으로 처리해주진 않지만 간단한 이동을 처리해주는 멤버함수들이 포함되어 있다.


컴포넌트 구성

우선 캐릭터의 경우 CapsuleComponent가 RootComponent로 설정 되어 있으므로 나는 BoxComponent(이하 Box)를 생성하여 RootComponent로 설정해주었다. 그리고 SkeletalMeshComponent(이하 SkeletalMesh)를 Box에 붙이고 SpringArmComponent(이하 SpringArm)도 마찬가지로 Box에 붙였다. CameraComponent(이하 Camera)는 SpringArm에 붙여주었다.

ADronePawn.cpp

ADronePawn::ADronePawn()
{
	PrimaryActorTick.bCanEverTick = false;

	CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionBoxComponent"));
	SetRootComponent(CollisionBox);
	CollisionBox->SetSimulatePhysics(false);
	CollisionBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	CollisionBox->SetCollisionObjectType(ECC_WorldDynamic);
	CollisionBox->SetCollisionResponseToAllChannels(ECR_Block);
	CollisionBox->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
	CollisionBox->SetGenerateOverlapEvents(true);

	SkeletalMeshComp = CreateDefaultSubobject <USkeletalMeshComponent>(TEXT("SkeletalMeshComponent"));
	SkeletalMeshComp->SetupAttachment(CollisionBox);
	SkeletalMeshComp->SetSimulatePhysics(false);
	SkeletalMeshComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	SkeletalMeshComp->SetCollisionObjectType(ECC_Pawn);
	SkeletalMeshComp->SetCollisionResponseToAllChannels(ECR_Block);
	SkeletalMeshComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);

	bUseControllerRotationYaw = true;
	bUseControllerRotationPitch = true;
	bUseControllerRotationRoll = true;

	SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComponent"));
	SpringArmComp->SetupAttachment(CollisionBox);
	SpringArmComp->TargetArmLength = 700.0f;
	SpringArmComp->bUsePawnControlRotation = true;

	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
	CameraComponent->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);
	CameraComponent->bUsePawnControlRotation = false;
}
  • SetSimulationPhysics는 과제 요건에서 false로 해라고 했기 때문에 false로 설정. 이로 인해 아래 ECollisionEnabled::QueryAndPhysics 로 설정을 하더라도 충돌 감지만 하고(Query) 물리적인 충돌은 일어나지 않으므로 ECollisionEnabled::QueryOnly로 설정.

  • 이후에 물리적인 충돌은 SetActorLocation이나 AddActorLocalOffset 등에서 Sweep옵션을 활성화 하면 충돌을 감지하는 순간 이동제한이 함수 내부에서 이루어져서 이동이 제한된다. 하지만 마찬가지로 SetCollisionResponseToAllChannels(ECR_Ignore)을 하면 마찬가지로 물리적인 충돌은 이루어지지 않는다.


충돌

언리얼 엔진의 충돌 시스템은 단순 충돌 감지(Query), 충돌 반응(Physics), 라인 트레이스(Trace)등 다양한 기능이 있어서 헷갈릴 수 있다.
특히 Sweep, Trace, Physics Simulation이 어떻게 다른지, 각각의 우선순위와 관계를 정확히 이해해보자

1. Sweep

앞서 말했듯이 Sweep은 충돌 감지를 하고 단순 이동 제한이 걸리는 것이다.

  • 이동하면서 충돌을 감지한다.
    • AddActorLocalOffset(CurrentVelocity, true);
    • `SetActorLocation(NewLocation, true);
  • 동작 과정:
    • 이동하기 전에 충돌을 감지
    • 충돌이 감지되면 이동을 조정하거나 멈춤

2. Physics Simulation

이런 식으로 말그대로 충돌 감지를 하고 충돌 반응(중력, 튕김) 등을 모두 처리한다.

  • 오브젝트가 충돌 시 실제 물리 반응을 보이게 한다.
    • SetSimulatePhysics(true);
    • AddForce(), AddImpulse()
    • 중력, 충돌, 마찰 등의 반응이 적용됨
  • 동작 과정:
    • SetSimulatePhysics(true)를 호출하면, 엔진이 물리 연산을 활성화
    • 충돌이 발생하면 반응(튕김, 밀림, 회전, 마찰 등)이 자동으로 적용
    • AddForce(), AddImpulse() 등을 통해 힘을 가할 수 있다.

3. Trace

단순히 충돌감지만 하고 반응이 없으면 다음과 같이 처리된다.

  • 오브젝트를 이동시키지 않고, 특정 위치에서 충돌 감지만 수행
    • LineTraceSingleByChannel()
    • BoxTraceMultiByChannel()
  • 레이를 쏴서(Raycast 라고도 함) 충돌을 감지
  • 충돌이 감지되면, 히트 정보를 반환
  • 충돌이 감지되더라도 오브젝트의 이동에는 영향을 주지 않음

  • (눈에 보이는 충돌 : 충돌 감지 + 충돌 반응)
    중요한 것은 우리 눈에 물리적인 충돌이 이루어지는 것을 보려면 충돌 감지와 함께 충돌 반응이 필수라는 것이다. 충돌채널을 All Block을 하든 특정 Oject에 대해 Block을 하든간에 충돌 감지만 있으면 절대 물리적인 충돌이 일어날 수 없다.

  • 충돌 채널 :
    이러한 충돌시스템이 언제, 어떻게 동작할지 결정하는 것이 충돌채널과 충돌 반응이다.
    채널을 설정하면 같은 채널을 공유하는 오브젝트끼리 충돌할지 말지를 정할 수 있고 사용자가 원하는 커스텀 채널도 만들 수 있다.

  • 충돌 반응 :
    충돌이 감지될 때, 그 충돌에 어떻게 반응할지를 결정하는 설정이다. SetCollisionResponseToChannel()을 사용하여 설정.

    • 충돌 채널이 일지하더라도 "충돌 반응"에 따라 충돌이 실제로 발생할 수도 있고, 무시될 수도 있다.
    • 서로 같은 채널이어도 한쪽이 ECR_Ignore로 설정하면 충돌이 일어나지 않는다.

  • 우리가 보는 카메라같은 경우는 카메라컴포넌트의 상위 컴포넌트, 스프링암컴포넌트에서 충돌을 감지하는 순간TargetArmLength의 길이를 0으로 줄여버린다.

  • 이는 언리얼 엔진에서 "충돌이 일어났다"라고 판단해서 카메라가 플레이어 내부로 들어가 1인칭 처럼 보이게 되는것이다.
    • 언리얼 엔진에서는 SpringArmComponent가 충돌을 감지하면 자동으로 카메라를 조정하는 기능이 있다.
    • 즉, SpringArm의 끝점이 충돌했다고 판단되면, 카메라가 플레이어 몸 안으로 들어감.
    • SpringArm->bDoCollisionTest = true; 면 충돌 감지가 활성화
      이 값이 true이고, SpringArm의 끝이 충돌했다고 판단되면 TargetArmLength를 자동으로 줄임
    • bDoCollisionTest = true;(default 값) 상태에서 CollisionBox와 BeginOverlap이 발생하면서 언리얼이 "카메라가 충돌했다"라고 판단하고 TargetArmLength = 0으로 줄이는 것이다.


  • Character.cpp 에서 bDoCollisionTest를 false로 설정하니 박스콜리전에 Overlap 되더라도 TargetArmLength는 유지가 되는모습이다.
profile
Unreal 1기

0개의 댓글

관련 채용 정보