동적 액터와의 기본 상호작용
Trace와 Skeletal Mesh 데이터를 사용해 움직이는 액터와 충돌하거나 소켓에 부착되는 캐릭터 먼지 효과를 구현합니다.
정적 메시와의 기본 콜리전에서는 움직이지 않는 바닥에 입자가 부딪히게 했습니다. 실제 게임에서는 입자가 움직이는 대상과도 어울려야 합니다. 캐릭터가 맞을 때 튀는 효과, 달리는 차량에서 뿜어 나오는 연기, 몬스터의 특정 부위에서 피어나는 마법이 모두 그렇습니다. 이런 상호작용은 움직이는 대상과 부딪히는 것뿐 아니라, 대상의 움직임에 반응하거나 대상의 특정 위치에 입자를 붙이는 일까지 포함합니다. 이 글에서는 움직이는 액터와 스켈레탈 메시(Skeletal Mesh)에 입자를 부딪히게 하는 방법과 붙이는 방법을 구분해 살펴보고, 캐릭터의 발밑에 먼지 효과를 붙인 뒤 좌표 공간에 따라 먼지가 어디에 남는지 계산해 봅니다.
움직이는 액터와 부딪히기
애니메이션되는 스켈레탈 메시와 부딪히게 하려면 먼저 어떤 표면 데이터를 쓸지 구분해야 합니다. 일반 메시의 거리 필드는 스킨 애니메이션으로 변형된 표면을 정확히 나타내는 충돌 데이터가 아닙니다. 그래서 품질 설정을 높이는 것으로 해결되는 문제가 아니며, 다른 경로를 써야 합니다.
트레이스로 부딪히기
움직이는 액터와 부딪히는 가장 기본적인 방법은 CPU 충돌의 월드 트레이스입니다. 이미터를 CPU Sim으로 두고 Collision의 월드 트레이스 경로를 씁니다. GPU 거리 필드 설정을 그대로 둔 채 채널만 바꾸는 방식이 아니며, 실제로 얼마나 정확히 감지하는지는 대상의 query collision 형태와 시간 간격에 따라 달라집니다.
트레이스를 쓸 때는 입자가 어떤 채널에 대해 부딪힐지 정해야 합니다. Collision 모듈의 Trace Channel에서 Visibility나 프로젝트에서 만든 트레이스 채널처럼 알맞은 채널을 고릅니다. 그리고 부딪힐 액터의 메시 컴포넌트, 즉 Static Mesh Component나 Skeletal Mesh Component의 Collision Presets에서 query collision이 켜져 있고 그 트레이스 채널에 대해 Block으로 응답하는지 확인합니다. 이때 WorldDynamic이나 PhysicsBody는 Object Type의 분류이므로 트레이스 채널과 헷갈리지 않습니다. 스켈레탈 메시라면 Physics Asset처럼 실제로 조회되는 형태도 확인합니다.
트레이스는 입자마다 조회를 하므로, 많은 입자가 동시에 트레이스 충돌을 하면 비용이 커질 수 있습니다.
스켈레탈 메시의 데이터 쓰기
스켈레탈 메시와 어울리는 효과에는 Skeletal Mesh 데이터 인터페이스를 쓰는 방법이 쓰임새가 가장 넓습니다. 이 데이터 인터페이스는 스켈레탈 메시의 본(bone)과 소켓(socket), 표면처럼 애니메이션되는 형태 정보를 나이아가라에 넘겨줍니다.
이를 이용하면 캐릭터의 손이나 발, 머리 같은 소켓에서 입자를 만들거나 붙여 캐릭터와 함께 움직이게 할 수 있고, 캐릭터의 몸 표면에서 먼지나 물방울, 피격 효과가 뿜어 나오게 할 수 있습니다. 몬스터의 꼬리나 촉수처럼 움직이는 본을 입자가 따라가게 할 수도 있습니다.
다만 이것은 충돌과 다른 기능입니다. Skeletal Mesh 데이터 인터페이스는 본과 소켓, 표면의 표본을 제공할 뿐이고, Physics Asset과의 충돌 조회는 따로 지원되는 데이터 인터페이스나 충돌 경로가 필요합니다. 표면을 샘플링하는 것만으로 충돌 반응이 생기지는 않습니다.
액터에 효과 붙이기
가장 기본적인 상호작용은 나이아가라 시스템 자체를 움직이는 액터에 붙이는(attach) 것입니다. 캐릭터나 차량 같은 액터의 블루프린트 클래스를 열고, Components 패널의 Add 버튼으로 Niagara Particle System 컴포넌트를 추가합니다. 추가한 컴포넌트를 선택하고 Details 패널의 Niagara System Asset 속성에 NS_BasicSpawn 같은 나이아가라 시스템 에셋을 지정합니다.
특정 부위에 붙이려면 이 나이아가라 컴포넌트를 Skeletal Mesh 컴포넌트의 자식으로 옮긴 뒤, Details의 Parent Socket에서 실제 본이나 소켓의 이름을 고릅니다. 컴포넌트 트리에서 소켓 자체를 부모로 만드는 것이 아니라, 메시 컴포넌트를 부모로 두고 그 안의 소켓을 고르는 방식입니다. 캐릭터가 발을 디딜 때 발밑에서 이는 먼지, 총구에서 터지는 화염, 차량의 배기구에서 나오는 가스가 이렇게 만드는 효과의 예입니다.
액터의 데이터를 효과에 넘기기
더 복잡한 상호작용을 만들려면 액터의 데이터를 나이아가라에 직접 넘깁니다. 쓰는 버전에서 지원하는 Actor Component 데이터 인터페이스로 액터의 트랜스폼을 읽거나, 블루프린트에서 필요한 위치와 방향만 사용자 파라미터로 넘길 수 있습니다. 어느 쪽이든 데이터 인터페이스가 어떤 원본에 연결되었는지와 값의 좌표 공간을 맞춰야 합니다. 본과 소켓, 표면의 정보가 필요하다면 앞에서 본 Skeletal Mesh 데이터 인터페이스를 씁니다.
캐릭터 발밑에 먼지 붙이기
이 실습은 블루프린트와의 연결이 꼭 필요하므로, 흐름을 개념 위주로 따라가 봅니다.
먼저 바닥에서 솟아오르는 작은 먼지 효과 NS_FootDust를 만듭니다. Emitter Update의 Spawn Rate는 낮게, Particle Spawn의 Lifetime과 스프라이트 크기는 작게 두고, Add Velocity로 Z축 위쪽으로 살짝 초기 속도를 줍니다. Particle Update에는 Particle State와 Solve Forces and Velocity를 둡니다.
다음으로 언리얼 엔진의 기본 캐릭터 블루프린트(BP_ThirdPersonCharacter 같은 것)를 열고, Components 패널의 Add 버튼으로 Niagara Particle System 컴포넌트를 추가한 뒤 Niagara System Asset 속성에 NS_FootDust를 지정합니다. 그다음 이 컴포넌트를 끌어 스켈레탈 메시 컴포넌트(Mesh) 아래로 옮기고, Details 패널의 Parent Socket에서 발에 해당하는 본, 예를 들어 foot_l이나 foot_r을 고릅니다. Location, Rotation, Scale의 오프셋을 조절해 효과가 발밑에 정확히 오게 합니다.
필요하다면 효과를 켜고 끄는 시점도 정합니다. 이동 상태가 바뀔 때만 Activate와 Deactivate를 부르고, 같은 상태에서 Tick마다 다시 켜지 않습니다. 발이 땅에 닿는 순간의 먼지라면, 애님 노티파이(Anim Notify)에서 지면 위치에 한 번 재생하는 시스템을 만드는 방법도 비교해 봅니다. 또 대상이 파괴되거나 EndPlay될 때는 만든 컴포넌트와 바깥의 참조를 정리하고, 소켓이 없을 때 원점에서 효과가 조용히 재생되지 않도록 소켓 이름을 확인합니다.
좌표 공간도 정해야 합니다. 이미터의 Local Space가 꺼져 있으면, 생긴 입자는 월드에 남아 캐릭터가 떠나도 그 자리에 머뭅니다. 켜면 이미 생긴 먼지도 부모의 움직임을 따라갈 수 있으므로, 땅에 남는 먼지와 캐릭터에 붙어 다니는 오라를 구분해 설정합니다.
붙인 효과의 입자는 어디에 남을까
좌표 공간에 따라 이미 생긴 입자가 어디에 있는지 계산해 봅니다. 회전과 스케일은 바뀌지 않고 입자 자체의 움직임도 없는, X축 이동만 있는 경우를 가정합니다. 부모의 원점이 월드 X=0cm일 때 입자 하나를 X=10cm에 만든 뒤, 부모를 X=100cm로 옮깁니다.
Local Space에서는 입자의 로컬 X=10cm를 새 부모의 변환으로 읽으므로 월드 X는 100 + 10 = 110cm가 됩니다. 월드 공간(World Space)에서는 이미 써 둔 월드 X=10cm가 그대로 남습니다. 두 경우 모두 붙여 둔 컴포넌트에서 새로 생기는 입자의 위치는 부모를 따라가지만, 이미 생긴 입자의 좌표를 읽는 기준이 다른 것입니다.
성능을 생각할 때
움직이는 액터와 연결할 때는 대상을 참조하는 방식과 갱신에 드는 비용을 나누어 판단합니다. 복잡한 환경에서 많은 입자가 트레이스 충돌을 하면 CPU 비용이 커질 수 있으므로, 부딪힐 입자의 수와 수명, 조회 범위를 제한하고 필요할 때만 씁니다. 스켈레탈 메시의 본이나 표면 데이터를 나이아가라로 넘기는 데도 비용이 들어, 복잡한 메시의 모든 본을 따라가거나 많은 입자가 표면을 참조하면 성능에 영향을 줄 수 있습니다.
효과를 액터에 붙이는 것은 비교적 가벼운 방법이지만, 붙인 시스템의 입자가 너무 많거나 계산이 복잡하면 액터 자체의 갱신 비용도 늘어납니다. 움직이는 대상과 어울리는 효과도 LOD와 스케일러빌리티로 다듬어, 멀리 있는 액터의 효과는 단순하게 만들거나 끕니다.
나이아가라의 충돌과 샘플링은 화면의 표현에 쓰고, 명중이나 피해, 서버의 권한 판정은 액터와 게임플레이 코드가 맡게 합니다. 대상이 빠르게 움직이거나 비활성화될 때 참조가 제대로 갱신되고 효과가 끝나는지도 확인합니다.
정리
- 움직이는 액터와 부딪히려면 CPU 충돌의 월드 트레이스를 쓰고, 트레이스 채널과 대상 컴포넌트의 query collision,
Block응답을 맞춥니다. - 일반 메시의 거리 필드는 애니메이션으로 변형된 스켈레탈 메시의 정확한 충돌 데이터가 아닙니다.
- Skeletal Mesh 데이터 인터페이스는 본과 소켓, 표면의 표본을 제공하며, 그것만으로 충돌 반응이 생기지는 않습니다.
- 효과를 붙일 때는 나이아가라 컴포넌트를 메시 컴포넌트의 자식으로 두고
Parent Socket에서 소켓을 고릅니다. - Local Space에서는 이미 생긴 입자가 부모를 따라가고 월드 공간에서는 그 자리에 남으므로, 땅에 남는 먼지와 따라다니는 오라를 구분해 설정합니다.
연습 문제
1. 부모를 X=-40cm로 옮기면
본문과 같은 처음 상태에서 부모를 X=-40cm로 옮깁니다. 이미 생긴 입자의 월드 X를 Local Space와 월드 공간에서 각각 구해 보세요. 땅에 남는 먼지와 캐릭터를 따라다니는 오라에는 각각 어떤 조건을 고르면 좋을까요?
해설 보기
Local Space에서는 로컬 X=10cm를 새 부모의 위치로 읽으므로 -40 + 10 = -30cm이고, 월드 공간에서는 써 둔 월드 X인 10cm가 그대로 남습니다.
땅에 남길 먼지는 월드 공간으로, 이미 생긴 입자도 부모를 따라가야 하는 오라는 Local Space로 검토합니다. 이 계산은 이동만 있는 경우이므로, 회전이나 스케일, 입자에 작용하는 힘이 있다면 그 변환과 시뮬레이션도 따로 적용합니다. 소켓에 붙이는 것 자체를 충돌 반응으로 해석하지 않습니다.
2. 캐릭터를 통과하는 입자
CPU Sim의 입자가 캐릭터에 부딪혀 튕기게 하려고 Collision의 트레이스 채널을 Visibility로 두었는데, 입자가 캐릭터를 그대로 통과합니다. 무엇을 확인해야 할까요?
해설 보기
캐릭터의 메시 컴포넌트가 Visibility 채널에 대해 Block으로 응답하는지 확인합니다. 캐릭터 메시에 쓰는 CharacterMesh 프리셋은 Visibility를 무시하도록 되어 있을 수 있으며, 이 프리셋의 Object Type이 Pawn이라는 것과 트레이스 채널의 응답은 별개입니다.
또 query collision이 켜져 있는지, 스켈레탈 메시라면 Physics Asset 같은 실제 조회 형태가 입자가 지나가는 자리를 덮는지 봅니다. 응답이 맞는데도 통과한다면 입자가 한 틱에 대상의 두께를 건너뛸 만큼 빠른지도 확인합니다.
3. 원점에서 재생되는 먼지
캐릭터의 발밑에 먼지를 붙였는데, 일부 캐릭터에서는 발이 아니라 캐릭터 원점에서 먼지가 피어납니다. 무엇이 문제일 가능성이 클까요?
해설 보기
Parent Socket에 적은 본이나 소켓의 이름이 그 캐릭터의 스켈레톤에 없을 가능성이 큽니다. 소켓을 찾지 못하면 효과는 부모 컴포넌트의 원점에 붙어 재생될 수 있습니다.
캐릭터마다 스켈레톤의 본 이름이 다를 수 있으므로, 그 메시에 실제로 있는 소켓 이름을 확인해 지정합니다. 블루프린트에서 붙인다면 소켓이 있는지 먼저 검사하고, 없을 때는 효과를 재생하지 않도록 처리합니다.
다음 글 기본 물리 기반 파티클 시뮬레이션에서는 중력과 저항, 바람 같은 힘을 모듈로 더하고, 그 힘이 속도와 위치로 계산되는 순서를 따라가 봅니다.