언리얼엔진3 에서의 충돌

개요

언리얼엔진3 에는 두가지 방식의 충돌처리가 있다. Unreal collision 과 RigidBody 를 위한 Physics collision1) 가 그것이다.

Unreal collision

Unreal collision 을 위한 property 들은 다음과 같다.

  • Actor
    • bCollideComplex : Actor 의 이동중, 단순화된 collision model 을 사용한 충돌 체크를 무시하고 per-poly 충돌체크를 수행한다.
    • bCollideActor : 다른 액터와 충돌 체크 여부 (default=TRUE)
    • bCollideWorld : world 와의 충돌 여부 (default=TRUE)
    • bBlockActor : 다른 액터의 이동을 막을 수 있는지 여부
    • BlockRigidBody : PHYS_RigidBody 를 사용하는 오브젝트와 충돌할 것인지 여부
    • bNoEncroachCheck : encroachment 를 체크를 결정하는 최적화 플래그이다. TRUE 로 설정하면 게임 속도는 빨라지지만 trigger 의 touch 이벤트, player 를 미는 행위, volume 의 입출 신호를 받지 못한다. (default=FALSE)
    • bAlwaysEncroachCheck : MoveActor 함수에서 실제 움직임이 없더라도 encroachment 체크를 수행한다. 이 플래그가 유용한 경우는 실제 움직임은 없지만 바운딩박스가 변경될 때이다.
    • bPathColliding : 에디터에서 Build Path 수행 시 이 액터가 path 를 막을 수 있는지 여부 (default=FALSE)
    • CollisionComponent : 충돌 체크를 하기 위한 컴포넌트 배열
    • CollisionType : 레벨디자이너가 충돌방식에 관하여 간단하게 설정할 수 있도록 하기위한 변수. 에디터에서 이 값을 변경하면 자동적으로 low-level 플래그들이 세팅된다. (Actor 와 CollisionComponent 에 있는 CollideActors, BlockActors, BlockNonZeroExtent, BlockZeroExtent, BlockRigidBody 관련 변수들)
  • PrimitiveComponent
    • CollideActors : Unreal collision 을 하기 위해선 반드시 true
    • BlockActors : 해당 오브젝트가 타 플레이어의 이동을 차단할지 여부. 예를 들어 trigger 나 PickupFactory 의 경우 CollideActors=true, BlockActors=false
    • BlockZeroExtent : ZeroExtent 트레이스는 흔히 생각하는 line 체크로 무기와 같은 항목에 사용된다.
    • BlockNonZeroExtent : 위와 비슷하나 line 체크가 아닌 AABB 체크라는 것이 다르다. 플레이어 이동과 같은 항목에서 사용된다. 만약 이 값이 FALSE 라면 이런 타입의 체크에서는 제외된다.

Actor 와 PrimitiveComponent 사이에 비슷한 성질의 플래그가 있다. 이럴 경우 Actor 의 것이 기준이 되며, PrimitiveComponent 는 Actor 와 같은 값을 가져야만 효력을 가진다.

Physics collision

작성 중

Encroacher

미리 알고 넘어가야 할 용어가 있다. Encroacher 가 그것인데 encroachment 를 수행하는 주체로써 다른 액터의 영역에 침범해 들어갈 수 있는 녀석을 말한다. Encroacher 는 Actor 의 Physics 가 PHYS_RigidBody 나 PHYS_Interpolating 이거나 bCollideAsEncroacher 를 TRUE 일 경우 해당된다.

UnActor.h
inline UBOOL AActor::IsEncroacher() const
{
    return
        bCollideActors &&
        (
            Physics == PHYS_RigidBody ||
            Physics == PHYS_Interpolating ||
            bCollideAsEncroacher
        );
}

코드 흐름 및 관련 함수 소개

위에서 설명한 내용이 매 프레임 어떤 식으로 계산되어 지는지를 코드흐름을 통해 살펴보자.

먼저 Actor 를 움직이는 함수인 UWorld::MoveActor 로 시작된다.

  • UWorld::MoveActor
    • 액터가 Encroacher 라면 UWorld::CheckEncroachment 를 수행하고 그게 아니라면 MultiLineCheck 를 수행한다.
    • 액터의 Attached 들에 대해 MoveActor 를 수행한다. 이때 MoveActor 가 수행되려면 BaseSkelComponent 가 없어야 하며2) bHardAttach, bIngoreBaseRotation 플래그에 따라 살짝 다르게 수행된다.
    • 액터의 RelativeLocation & Rotation 을 계산한다.
    • Bump 이벤트를 날린다.
    • Touch 이벤트를 날린다. 이것은 (CollisionComponent 외에 별도로 Components 에 추가된) extra Components 들에 대한 체크이며, 이때 조건은 CollideActors 와 AlwaysCheckCollision 이 TRUE 인 UPrimitiveComponent 타입 인스턴스여야 한다. Actor.bCollideComplex 가 TRUE 이면 per-poly 체크를 한다.
  • UWorld::CheckEncroachment
    • 내부적으로 FPrimitiveOctree::ActorEncroachmentCheck 를 수행한다.
    • TargetActor 를 push 하고 PushedBy 이벤트를 호출하거나
    • 주체 Actor 가 TargetActor 를 침범하고 있다면 각각 EncroachingOn 과 EncroachedBy 이벤트를 호출한다.
    • 또는 TargetActor 의 Touch 이벤트를 호출한다.
  • FPrimitiveOctree::ActorEncroachmentCheck
    • Actor.Components 중 유효한3) 컴포넌트들을 대상으로 FOctreeNode::ActorEncroachmentCheck 를 수행한다.
    • 이때 FOctreeNode::ActorEncroachmentCheck 를 수행하기 앞서 FPrimitiveOctree.ChkActor & ChkBox & ChkPrimComp & ChkTraceFlags 에 컴포넌트의 정보를 세팅한다.
  • FOctreeNode::ActorEncroachmentCheck
    • 각 OctreeNode 는 이 안에 속해있는 UPrimitiveComponent 배열 (FOctreeNode.Primitives) 을 가지고 있는데 이를 순회하며 Source Actor (이전 단계에 세팅된 정보) 와 Encroachment 체크를 시작한다.
    • 일단 가볍게 AABB intersection 체크하고 만족못하면 제낀다.
    • 이를 만족한다면 좀 더 디테일한 AActor::IsOverlapping 을 호출하여 체크한다.
  • AActor::IsOverlapping
    • Actor to Actor 오버랩 첵하는 함수
    • 초반 조건 첵을 수행하는데 다음과 같은 조건은 이 함수에서 제껴진다.
      • 나와 대상 Actor 가 모두 Encroacher 다.
      • 나와 대상 Actor 중 하나가 다른하나의 Base 다.
      • 나와 대상 Actor 중 하나라도 bCollideActors 가 FALSE 다.
    • 이제 오버랩 첵을 시작하는데 다음 중 하나의 방식으로 첵한다.
      • Actor 중 하나를 Primitive 로 하고 다른 하나를 Point (AABB) 로 두어 체크한다. 이 둘을 배정하는데 핵심 기준은 하나다. 더 작다고 생각되는 놈이 Point (AABB) 가 되는 것이다. Primitive 는 PointCheck 함수를 호출한다.
      • 두 Actor 에게 모두 PrimitiveComponent 가 있다면 그들간 오버랩 (UPrimitiveComponent::IsOverlapping) 을 수행한다.
      • 두 Actor 의 CylinderComponent 간 첵 시도를 하고 둘중 하나라도 CylinderComponent 가 없다면 PrimitiveComponent 들간 오버랩 (UPrimitiveComponent::IsOverlapping) 을 수행한다. 양 Actor 의 모든 Components 간 수행하는 것이기 때문에 가장 느리다. 가급적 이지경까지 오면 안되겠다.
  • UPrimitiveComponent::IsOverlapping
    • PrimitiveComponent to PrimitiveComponent 오버랩 첵하는 함수. 하지만 Actor 버전과 크게 다르지 않다.
    • 두 PrimitiveComponent 중 더 작다고 판단되는 놈을 Point (AABB) 로 두고 나머지 한놈이 PointCheck 함수를 호출한다.

보면 알겠지만 결국에는 PointCheck 를 수행하는 것으로 귀결된다. 결국 핵심은 양자 중 더 작은 놈이 Point 가 되고 (이 경우가 더 정밀하기 때문) 다른 놈이 Point 를 포함하는지를 확인하는 것이다.

MoveActor 로 시작되는 Encroachment 체크는 정밀하지 못하며 CCD (Continuous Collision Detection) 를 수행하지 못한다.

관련 Console 명령어

  • show collision : 레벨에서 사용 중엔 모든 충돌 모델과 차단 볼륨을 표시.
  • show zeroextent : 레벨에서 자신이 마치 zero-extent 트레이스인 것처럼 충돌을 표시. 보이지 않는 것들은 충돌하지 않는다는 뜻.
  • show nonzeroextent : 레벨에서 자신이 마치 nonzero-extent 트레이스인 것처럼 충돌을 표시. 보이지 않는 것들은 충돌하지 않는다는 뜻.
  • show rigidbody : 레벨에서 자신이 강체라고 가정하고 충돌을 표시. (참고:이것은 RBCC 필터 그룹의 효과를 표시하지 않고 다만 BlockRigidBody 와 UseSimpleRigidBodyCollision 의 효과만 표시함)
  • show missingcollision : 일반적으로 레벨 최적화 중 사용되며 밝은 쉐이더를 충돌 모델이 없는 모든 정적 메쉬에 적용함.
  • nxvis collision : 레벨의 Novodex 충돌 정보를 보여주므로 강체가 충돌하는 대상을 확인할 수 있다.

참조

1) 이 충돌처리는 Novodex PhysX 에 기반한다.
2) Skeletal-based 액터는 USkeletalMeshComponent::UpdateTransform 에서 다뤄진다.
3) 여기서 유효한 조건은 동적 타입이 UPrimitiveComponent 이고 AlwaysCheckCollision & CollideActors 가 TRUE 인 경우이다.