AttachComponent

엔진에서 자주 사용되는 함수 중에는 AttachComponent 란 것이 있다.

이 함수는 Actor 와 SkeletalMeshComponent 에 각각 존재하는데 이 두 쓰임새가 약간 틀리다.

그 차이점에 대해 알아본다.

Actor::AttachComponent

엔진에서 Actor 라 함은 Location 과 Rotation 을 가지고 이동 가능한 녀석을 의미한다. 그리고 Actor 는 처음엔 아무런 외형도 가지고 있지 않다. 우리는 이 Actor 에 외형을 입히고 Light 관련 컴포넌트 등을 추가할 수 있다. Actor 의 외형은 보통 SkeletalMeshComponent 라는 녀석이 담당하는데1) 이름에서 직감하듯이 본(bone) 에니메이션이 가능한 컴포넌트이다. 그리고 외형을 입혔으면 Lighting 관련 컴포넌트를 추가하여 빛에 반응하게 할 수 있다. 또한 엔진에서 충돌처리를 하는데 사용되는 (간략화된, 보통 실린더 형태의) 컴포넌트도 추가될 수 있다.

이와 같이 Actor 의 Location 과 Rotation 을 사용하고 Actor 레벨에서 서로가 동등하게 작동하는 컴포넌트들은 Actor::AttachComponent 를 통해 추가되어야 한다. 심지어 (일반적으로) 외형을 결정하는 SkeletalMeshComponent 가 동시에 2개 이상 추가될 수도 있다.

Actor::AttachComponent 로 추가된 컴포넌트들은 결과적으로 Actor 의 Components 배열AllComponents 배열에 추가된다.

Actor.uc
/** The actor components which are attached directly to the actor's location/rotation. */
var private const array<ActorComponent>    Components;
 
/** All actor components which are directly or indirectly attached to the actor. */
var private transient const array<ActorComponent>    AllComponents;

Components 의 경우 주석에서 보는 바와 같이 Location/Rotation 을 공유할 컴포넌트들이 관리되는 배열이고,

AllComponents 는 직/간접적으로 이 Actor 에 attach 된 컴포넌트들이 관리되는 배열이다. 여기서 직접적으로 attach 된 경우는 Actor::AttachComponent 를 통해 추가된 것이고 간접적으로 attach 된 것은 SkeletalMeshComponent (이는 물론 Components 배열 안에 들어있는 것이다.) 에 attach 된 것을 뜻한다. 후자는 다음 section 에 소개되니 그쪽을 보면 되겠다.

오직 Actor::AttachComponent 를 통해서만 Actor.Components 배열에 추가될 수 있는데, 위에서 언급했듯이 Actor.Components 배열에 있는 컴포넌트들은 Actor 레벨에서 업데이트가 진행될 경우 영향을 받게 된다. 그 경우는 아래와 같다. (대략적으로 나열한 것이고 실제로는 더 많은 경우가 있다.)

  • Actor 가 Location 복제를 받은 경우 모든 Components 중 rigid body 의 위치를 갱신 ( AActor::PostNetReceiveLocation )
  • Actor 이동 후 Components 에 다이나믹 Decal 의 영향을 받는 PrimitiveComponent 나 LightComponent 가 있다면 엔진에게 Decal 업데이트를 요청 ( AActor::PostEditMove )
  • Actor 의 CollisionType 에 따라 Components 들의 collision 관련 속성들을 적절히 세팅한다. ( AActor::SetCollisionFromCollisionType )
  • Actor 의 bCollideActors 플래그가 바뀌면 PrimitiveComponent 들을 reattach 한다. ( AActor::SetCollision )
  • Actor 의 PrimitiveComponent 들의 바운딩 박스를 계산한다. ( AActor::GetComponentsBoundingBox )
  • 두 Actor 간 충돌처리 관련 PrimitiveComponent (CylinderComponent 포함) 끼리 intersection 체크를 한다. ( AActor::IsOverlapping )

종합해보면, Actor 의 외형이나 충돌처리 기능을 하는 컴포넌트들이 Actor.Components 배열에 추가된다고 볼 수 있다.

SkeletalMeshComponent::AttachComponent

SkeletalMeshComponent 는 Actor 의 외형을 담당한다. 그리고 (위에서 언급했듯이) Actor::AttachComponent 를 통해 Actor.Components 에 추가된다. Actor 가 가질 수 있는 많은 컴포넌트들 중 하나라는 말이다.

이러한 SkeletalMeshComponent 는 자체적으로 충분히 외형의 기능을 할 수 있지만 (게임 디자인적인) 필요에 의해서 특정 본(bone)에 다른 컴포넌트가 추가될 필요가 있다. 어깨에 걸쳐진 망토나 손에 잡혀있는 칼, 방패들은 특정 뼈대에 (또는 뼈대로부터 상대적인 위치에) 귀속되어 에니메이션이 되더라도 그곳을 따라다닐 필요가 있다. 이 컴포넌트들이 필요한 것은 Actor 의 Location/Rotation 이 아니라 (에니메이션 된) SkeletalMeshComponent 의 특정 뼈대 위치이다.

이렇게 SkeletalMeshComponent 의 특정 뼈대에 붙어다니는 컴포넌트는 SkeletalMeshComponent::AttachComponent 를 통해 추가되어야 한다.

SkeletalMeshComponent::AttachComponent 로 추가된 컴포넌트들은 결과적으로 SkeletalMeshComponent 의 Attachments 배열Actor 의 AllComponents 배열에 추가된다.

오직 SkeletalMeshComponent::AttachComponent 를 통해서만 SkeletalMeshComponent.Attachments 배열에 추가될 수 있는데 이는 다음과 같은 경우에 영향을 받는다.

  • SkeletalMeshComponent 의 physics 가 초기화 되거나 파기될 때 ( USkeletalMeshComponent::InitComponentRBPhys & TermComponentRBPhys )
  • SkeletalMeshComponent 의 Transform 이 업데이트될 때 ( USkeletalMeshComponent::UpdateTransform )
  • 명시적으로 child 의 Transform 을 업데이트하고자 할 때 ( USkeletalMeshComponent::UpdateChildComponents )
  • AnimNotify 에서 Trail 을 on/off 할 때 Attachments 에 있는 ParticleSystemComponent 를 찾는다. ( UAnimNotify_Trails::GetPSysComponent )

종합해보면, 추가적인 외형 기능을 하는 컴포넌트들이 SkeletalMeshComponent.Attachments 배열에 추가된다고 볼 수 있다.

Actor.AllComponents

위에 소개한 Actor::AttachComponent 와 SkeletalMeshComponent::AttachComponent 함수들을 통해 추가된 컴포넌트들은 모두 Actor.AllComponents 배열에 추가된다.

이 배열은 위 Actor::AttachComponent section 에 소개했듯이 직/간접적으로 Actor 에 attach 된 컴포넌트들이 있다.

다음과 같은 상황에 영향을 받는다.

  • Lighting 관련
    • Terrain 에서 deterministic lighting 을 위한 컴포넌트 재 정렬 ( ATerrain::OrderComponentsForDeterministicLighting )
    • Terrain 의 Update Rectangle 에서 오래된 decoration 을 제거 ( ATerrain::CacheDecorations )
    • InstancedLightmap 을 StaticMeshComponent 에 대입 ( UInstancedStaticMeshComponent::ResolveInstancedLightmapsForActor )
    • LightingBuild
  • Destructible 관련
    • StaticMeshComponent 들 중 파괴 가능한 것들 쿠킹 ( AActor::BuildPhysStaticMeshCache )
  • 기타
    • Actor 내 CollisionComponent 가 단 1개 뿐인지 체크 ( AActor::HasSingleCollidingComponent )
    • Actor 내 모든 컴포넌트의 텍스쳐를 미리 스트리밍 ( AActor::PrestreamTextures )
    • Actor 내 모든 컴포넌트들을 PendingKill ( AActor::MarkComponentsAsPendingKill )
    • Actor 내 모든 컴포넌트들이 다음 Tick 에서 업데이트되는 것을 보장하기 위해 dirty 처리 ( AActor::MarkComponentsAsDirty )
    • Actor 내 모든 컴포넌트들의 LightingCache 를 무효화 ( AActor::InvalidateLightingCache )
    • FStreamingManagerTexture 에서 Actor 가 Spawn 되거나 Destroy 되면 이벤트 발생

어디 붙나 상관없이 일반적 처리를 해주기 위해 Actor.AllComponents 배열에 추가된다고 볼 수 있다.

1) 물론 StaticMeshComponent 가 될 수도 있다.