게임플레이 요소와 상호작용
체력·환경 속도·오브젝트 파괴·영역 진입 상태를 데이터 인터페이스와 이벤트로 연결해 반응형 효과를 설계합니다.
앞의 세 글에서 블루프린트로 나이아가라 시스템을 켜고 끄고, 플레이 중에 값을 넘기고, 사건이 일어난 순간에 효과를 만드는 방법을 익혔습니다. 이번에는 이것들을 모아, 효과가 게임의 상태와 물체의 속성, 플레이어의 행동에 반응하도록 연결해 봅니다. 체력이 줄면 붉어지는 효과, 바람에 따라 흐르는 연기, 무너지는 물체의 잔해, 특정 영역에 들어가면 켜지는 안개가 그 예입니다. 이 글에서는 나이아가라가 게임 데이터를 직접 읽는 방법과 효과가 끝났음을 블루프린트에 알리는 방법을 살펴보고, 여러 게임플레이 요소에 효과를 연결하는 사례를 정리한 뒤, 영역의 겹침을 세어 효과를 끄는 시점을 계산해 봅니다.
나이아가라가 게임 데이터를 읽는 방법
나이아가라는 데이터 인터페이스(Data Interface)로 게임 월드의 여러 데이터에 직접 접근할 수 있습니다. 데이터 인터페이스에 원본이 되는 객체나 컴포넌트와 필요한 바인딩을 먼저 지정하면, 지원되는 데이터를 나이아가라가 조회할 수 있습니다. 그렇다고 참조를 설정하는 일이나 게임 상태를 누가 가지고 있는지에 대한 설계가 필요 없어지는 것은 아닙니다.
Actor Component 데이터 인터페이스는 지원되는 버전에서 Actor나 씬 컴포넌트의 트랜스폼을 읽습니다. 아무 게임 속성이나 저절로 읽어 주는 만능 인터페이스로 생각하지 않습니다. 입자가 특정 적을 따라가게 하거나 플레이어의 위치를 중심으로 효과를 낼 때 쓰며, 그 데이터 인터페이스 타입의 입력이나 사용자 파라미터를 준비해 원본 Actor나 컴포넌트의 바인딩을 지정한 뒤, 그래프에서 지원되는 함수를 부릅니다. 위치만 필요하다면 블루프린트가 사용자 Position 값으로 넘기는 방법도 있습니다.
Skeletal Mesh 데이터 인터페이스는 스켈레탈 메시의 본과 소켓, 표면의 정보를 가져옵니다. 캐릭터의 특정 본에 입자를 붙이거나 애니메이션되는 메시의 표면에서 입자를 만들 때 쓰며, Skeletal Mesh Location 같은 모듈의 데이터 인터페이스 입력에서 실제 원본 컴포넌트와 샘플링 조건을 지정합니다. 필요한 CPU 접근과 LOD, 본과 표면 샘플링의 지원도 확인합니다.
이 밖에 Static Mesh 데이터 인터페이스는 스태틱 메시의 표면과 정점, 삼각형 정보를 가져와 물체의 표면에서 먼지나 물방울이 떨어지게 하거나 물체의 모양을 따라 입자를 놓을 때 씁니다. Spline 데이터 인터페이스는 스플라인 컴포넌트의 경로를 가져와 입자가 경로를 따라 움직이게 하거나 도로를 따라 흐르는 연기를 만들 때 씁니다. Collision Query 데이터 인터페이스는 나이아가라 안에서 충돌 조회를 해서 그 결과를 입자에 반영하므로, 입자가 특정 콜리전 채널의 물체와 닿는지 계속 확인하며 반응해야 할 때 씁니다.
효과가 끝났다는 알림 받기
나이아가라 시스템은 재생이 끝났을 때 블루프린트에 알림을 보낼 수 있습니다. 효과가 끝난 뒤에 게임 로직을 실행해야 할 때 쓸모가 있습니다. 블루프린트에서 나이아가라 컴포넌트를 선택하고 Details 패널의 Events 섹션에서 On System Finished 이벤트를 추가하면 됩니다. C++에서는 컴포넌트의 OnSystemFinished 변수이고, 그 델리게이트 타입이 FOnNiagaraSystemFinished입니다.
이 알림은 시스템이 완료 상태에 도달했을 때 옵니다. 그래서 반복 재생 중인 시스템은 어느 순간 입자 수가 0이더라도 완료가 아닐 수 있습니다. 이벤트의 바인딩은 효과를 켜기 전에 준비해 두고, 강제로 파괴되거나 컬링되거나 풀로 돌아가는 경우를 정상적인 완료 알림과 같은 것으로 묶어 생각하지 않습니다.
이 알림은 폭발의 표현이 끝난 뒤 시각용 잔해나 UI 연출을 정리하거나, 마법 연출이 끝난 뒤 다음 시각 상태로 넘어가거나, 특정 효과가 끝났을 때 다음 UI 애니메이션을 재생하는 데 씁니다. 다만 피해나 버프를 적용하는 시점은 효과가 끝나는 시점이 아니라, 별도의 게임플레이 타이밍과 서버의 판정으로 정합니다.
게임플레이 요소에 효과 연결하기
플레이어의 체력
나이아가라 쪽에서는 Float 사용자 파라미터 User.HealthRatio를 노출하고, 이 값에 따라 입자의 색이나 생성률, 크기를 바꾸는 로직을 만듭니다. 예를 들어 Scale Color의 커브를 HealthRatio에 따라 빨간색으로 바뀌게 조절합니다. 블루프린트 쪽에서는 체력이 바뀌는 이벤트에서 최대 체력이 0보다 큰지 검사한 뒤, 지금 체력을 최대 체력으로 나눈 값을 0에서 1 사이로 Clamp해 Set Niagara Variable (Float)로 User.HealthRatio에 넘깁니다. 효과를 처음 켜기 전에 기본값도 넣어 둡니다.
주변의 바람
나이아가라 쪽에서는 Vector 사용자 파라미터 User.WindDirection과 Float User.WindStrength를 노출하고, 고른 힘 모듈에서 이 값들로 입자에 바람의 힘을 줍니다. 블루프린트 쪽에서는 월드에 놓은 Wind Directional Source 액터나 특정 볼륨 안에서 바람의 방향과 세기를 계산하고, 그 값을 Vector와 Float setter로 넘기거나 여러 효과가 함께 쓰는 나이아가라 파라미터 컬렉션으로 넘깁니다. Wind Directional Source를 레벨에 두었다고 아무 나이아가라 모듈이나 같은 바람을 저절로 읽는 것은 아닙니다.
물체가 부서질 때의 잔해
나이아가라 쪽에서는 부서질 물체의 파편과 먼지를 나타내는 NS_Debris를 만듭니다. 이 시스템은 Spawn Burst Instantaneous로 한 번에 많은 입자를 만들고 중력과 충돌을 적용합니다. 블루프린트 쪽에서는 물체가 실제로 부서졌다고 판정된 사건에서 Spawn System at Location으로 NS_Debris를 물체의 위치에 만듭니다. Hit가 곧 파괴를 뜻하지는 않으므로, 파괴 판정을 통과한 한 번의 사건에서만 부르고, 필요한 위치와 세기는 물체가 사라지기 전에 미리 얻어 둡니다. 초기값이 필요하면 Auto Activate를 끄고 만든 뒤 돌려받은 컴포넌트에 사용자 값을 넣고 Activate합니다.
영역에 들어가고 나갈 때
블루프린트 쪽에서는 레벨에 Trigger Box 같은 콜리전 볼륨을 두고, 그 볼륨의 OnComponentBeginOverlap과 OnComponentEndOverlap 이벤트를 씁니다. 그리고 올바른 대상만 걸러 효과를 Activate, Deactivate합니다. 양쪽의 Generate Overlap Events와 콜리전 응답을 확인하고, 여러 컴포넌트나 액터가 함께 겹칠 수 있다면 겹친 대상의 집합이나 개수를 관리해 마지막 대상이 나갈 때 끕니다. 안개 지역에 들어가면 안개 효과를, 독성 지역에 들어가면 독가스 효과를 켜는 것이 그 예입니다.
마지막 겹침이 끝날 때 효과 끄기
겹침을 세는 방법을 따라가 봅니다. 대상 A의 Capsule과 Mesh가 같은 영역에 들어오는 경우입니다. 프로젝트가 유효한 겹침을 (Actor, Component) 쌍으로 구분해 집합에 넣기로 정했다면, Capsule이 들어오고 이어서 Mesh가 들어올 때 집합의 원소는 1에서 2가 됩니다. 효과는 처음으로 0에서 1이 될 때만 켜고, Capsule이 먼저 나가 2에서 1이 될 때는 끄지 않습니다.
| 순서 | 이벤트 | 집합의 원소 수 | 효과 |
|---|---|---|---|
| 1 | Begin(Capsule) | 0 → 1 | Activate |
| 2 | Begin(Mesh) | 1 → 2 | 그대로 |
| 3 | End(Capsule) | 2 → 1 | 그대로 |
| 4 | End(Mesh) | 1 → 0 | Deactivate |
Actor 하나만으로 중복을 지우면, 어느 컴포넌트가 아직 영역에 남아 있는지에 대한 정보를 잃을 수 있습니다. 그래서 이 예에서는 컴포넌트까지 함께 구분합니다.
성능을 생각할 때
게임플레이 요소와 효과를 연결하면 쓸모가 커지지만, 성능도 함께 따져야 합니다. 매 프레임 갱신할 필요가 없는 값이라면 Event Tick 대신 타이머나 OnHealthChanged 같은 게임 이벤트에 반응하도록 만듭니다. 데이터 인터페이스는 편리하지만, 본이 아주 많은 스켈레탈 메시처럼 참조하는 데이터가 복잡하면 비용이 듭니다. 필요한 데이터만 가져오고 필요 없는 계산은 피합니다.
일회성 효과는 Spawn System at Location으로, 부모의 움직임을 계속 따라가야 하는 효과는 Spawn System Attached로 만듭니다. 붙인 효과는 부모의 트랜스폼이 바뀔 때마다 함께 갱신되므로, 필요 없이 붙이지 않습니다.
총알의 충격이나 발자국처럼 자주 만들어지고 사라지는 효과는, 매번 새로 만들고 파괴하는 대신 Spawn 노드에 내장된 Pooling Method를 먼저 검토합니다. 풀로 돌려보낸 컴포넌트를 계속 고치지 않고, 다시 쓸 때마다 사용자 값을 새로 넣으며, 효과가 끝나고 회수되는 시점을 누가 관리할지 분명히 정해 둡니다.
더 깊이: Pooling Method가 고르는 것과 풀 미리 채우기
Spawn 노드의 Pooling Method는 ENCPoolMethod 값 가운데 하나를 고르는 것입니다. API 문서에 따르면 None은 풀을 쓰지 않고 컴포넌트를 새로 만들고, AutoRelease는 풀에서 컴포넌트를 받아 쓰고 엔진이 자동으로 풀에 돌려보냅니다. ManualRelease는 풀에서 받아 쓰지만 자동으로 돌려보내지 않으므로, 돌려보내는 시점을 코드가 직접 정해야 합니다. 그래서 AutoRelease로 받은 컴포넌트의 참조를 오래 쥐고 있으면, 엔진이 이미 풀로 회수해 다른 사건에 다시 내준 컴포넌트를 건드릴 수 있습니다.
풀의 크기와 미리 채우기(priming)는 시스템 속성의 성능 항목에서 시스템마다 정합니다. Scalability and Best Practices 문서는 풀을 미리 채우면 실행 중의 할당을 줄일 수 있지만, 에셋을 동적으로 불러오는 경우에는 그때 끊김이 생길 수 있다고 설명합니다. 그래서 레벨과 함께 미리 불러 두는 효과인지, 실행 중에 불러오는 효과인지에 따라 미리 채울 양을 다르게 정합니다.
정리
- 데이터 인터페이스에 원본 객체나 컴포넌트를 지정하면 나이아가라가 트랜스폼이나 메시, 경로, 충돌 조회 같은 게임 데이터를 직접 읽을 수 있습니다.
- 효과가 끝났다는 알림은 나이아가라 컴포넌트의
On System Finished이벤트로 받으며, 반복 중인 시스템의 입자 수 0이나 강제 파괴는 정상 완료와 다릅니다. - 체력이나 바람 같은 게임의 값은 사용자 파라미터나 파라미터 컬렉션으로 넘기고, 레벨에 액터를 둔 것만으로 모듈이 값을 읽지는 않습니다.
- 잔해 효과는 실제 파괴 판정을 통과한 한 번의 사건에서 만들고, 필요한 위치와 세기는 물체가 사라지기 전에 얻습니다.
- 영역 효과는 겹친 대상의 집합을 관리해 마지막 대상이 나갈 때 끄며, 피해와 같은 게임 판정은 효과가 아니라 게임 코드가 정합니다.
연습 문제
1. 중복된 Begin과 두 번의 End
본문의 겹침 예에서 원소가 2개인 상태로 Begin(Capsule)이 한 번 더 중복으로 전달된 뒤, End(Capsule)와 End(Mesh)가 차례로 왔습니다. 집합의 원소 수와 Deactivate하는 시점을 차례로 구해 보세요. 이 시각 효과의 완료 알림만으로 독 지역의 피해를 서버에서 확정해도 될까요?
해설 보기
이미 원소가 2개인 상태에서 중복된 Begin(Capsule)은 같은 쌍이 이미 집합에 있으므로 2개를 그대로 유지합니다. 그 뒤 두 End가 오면 2에서 1, 1에서 0이 되므로, 마지막인 Mesh가 나갈 때 Deactivate합니다. 대상이 파괴되거나 유효하지 않게 되는 경우도 집합에서 빼는 규칙으로 처리하며, 집합의 개수가 모든 게임에서 정답인 것은 아니므로 여러 대상과 거르는 조건을 정해 둡니다.
피해를 확정해서는 안 됩니다. 피해와 그 시점은 서버의 게임 코드가 정하며, 나이아가라의 완료 알림이나 입자 수, 컬링을 그 판정에 쓰지 않습니다.
2. 끝나지 않는 완료 알림
연기 효과가 끝나면 다음 연출로 넘어가려고 On System Finished를 연결했는데, 화면에서 연기가 모두 사라진 뒤에도 이벤트가 오지 않습니다. 무엇을 확인해야 할까요?
해설 보기
시스템이나 이미터가 반복 재생으로 설정되어 있는지 확인합니다. 반복 중인 시스템은 어느 순간 입자가 모두 사라져 보여도 완료 상태가 아니므로 알림이 오지 않습니다.
끝이 있는 효과로 만들려면 System State와 Emitter State의 루프를 Once처럼 유한하게 두고 Inactive Response를 확인합니다. 또 이벤트 바인딩이 효과를 켜기 전에 되어 있는지, 효과가 강제로 파괴되거나 풀로 돌아가 정상 완료가 아닌 방식으로 끝나지는 않았는지도 확인합니다.
3. 바람 액터를 두었는데
레벨에 Wind Directional Source를 두고 세기를 크게 했는데, 나이아가라의 연기는 전혀 흔들리지 않습니다. 왜 그럴까요?
해설 보기
레벨에 바람 액터를 둔 것만으로 아무 나이아가라 모듈이 그 바람을 저절로 읽지는 않습니다. 연기 시스템의 힘 모듈이 바람의 값을 입력으로 받고 있어야 합니다.
블루프린트에서 바람의 방향과 세기를 계산해 User.WindDirection과 User.WindStrength에 넘기거나 파라미터 컬렉션에 넣고, 연기 시스템의 힘 모듈이 그 값을 읽도록 연결합니다.
다음 글 레벨 시퀀스와 나이아가라 통합에서는 시네마틱의 레벨 시퀀스에서 나이아가라 효과를 재생하고, 사용자 파라미터를 키프레임으로 바꾸는 방법을 살펴봅니다.