사용자 정의 데이터 인터페이스
C++ 데이터 인터페이스의 클래스·함수·GPU 지원 구조를 이해하고 게임 고유 데이터를 나이아가라 입력으로 노출합니다.
앞서 우리는 나이아가라 시스템이 스태틱 메시, 스켈레탈 메시, 스플라인 등 언리얼 엔진의 기존 데이터를 활용할 수 있도록 돕는 데이터 인터페이스의 기본 개념과 활용법을 살펴보았습니다.
이러한 기본 데이터 인터페이스만으로도 다양한 시각 효과를 만들 수 있지만, 때로는 게임의 특정 로직이나 외부 라이브러리에서 오는 고유한 데이터를 나이아가라 시스템에서 사용하고 싶을 때가 있습니다.
이럴 때 필요한 것이 바로 사용자 정의 데이터 인터페이스(Custom Data Interface)입니다.
사용자 정의 데이터 인터페이스는 나이아가라를 외부 데이터와 연결하는 확장 지점입니다.
C++로 필요한 입력을 노출하면 시스템이 게임 상태나 별도 데이터 소스를 직접 참조할 수 있습니다.
기본 데이터 인터페이스가 다루지 못하는 게임 로직, 외부 시뮬레이션, 파일 데이터를 파티클 시스템에 연결합니다.
- 데이터 원천
AI 상태, 날씨 시스템, 외부 물리 계산, CSV/JSON처럼 프로젝트 고유 데이터를 준비합니다. source
- C++ 인터페이스
`UNiagaraDataInterface`를 상속해 읽기/쓰기 함수와 프록시 처리를 구현합니다. backend
- 나이아가라 사용
에디터에서 모듈로 추가하고 출력 핀을 파티클 속성에 연결해 효과를 제어합니다. module
사용자 정의 데이터 인터페이스란?
사용자 정의 데이터 인터페이스는 개발자가 C++ 코드로 직접 구현하여, 나이아가라 시스템이 기본적으로 지원하지 않는 형태의 데이터를 읽거나 쓰도록 확장하는 기능입니다.
이는 마치 나이아가라에 새로운 감각 기관을 추가하여, 게임 세계의 더 복잡하고 특수한 정보를 인식하고 반응할 수 있게 만드는 것과 같습니다.
예를 들어, 다음과 같은 시나리오에서 사용자 정의 데이터 인터페이스가 필요할 수 있습니다.
- 외부 물리 시뮬레이션 데이터: 게임 내에 커스텀 물리 엔진이나 유체 시뮬레이션이 존재하고, 그 결과 데이터를 나이아가라 파티클에 적용하고 싶을 때.
- 특정 AI 행동 데이터: AI 캐릭터의 현재 의사결정이나 심리 상태에 따라 파티클 효과를 변화시키고 싶을 때.
- 게임 특정 상태 데이터: 게임의 고유한 시스템(예: 날씨 시스템, 자원 시스템)에서 오는 복잡한 수치 데이터를 파티클에 반영하고 싶을 때.
- 파일에서 데이터 읽기: CSV, JSON 등 특정 파일 형식으로 저장된 데이터를 파티클에 적용하고 싶을 때.
사용자 정의 데이터 인터페이스를 구현하려면 기본적인 C++ 프로그래밍 지식과 언리얼 엔진의 모듈 및 클래스 생성에 대한 이해가 필요합니다.
이는 나이아가라의 가장 고급 기능 중 하나이므로, 앞서 배운 기본 개념들을 충분히 숙달한 후에 시도하는 것이 좋습니다.
사용자 정의 데이터 인터페이스의 작동 원리 (개념적 설명)
사용자 정의 데이터 인터페이스는 크게 두 가지 영역에서 작동합니다.
UNiagaraDataInterface를 상속받는 C++ 클래스를 생성합니다. 이 클래스에서 나이아가라 시스템이 외부 데이터를 어떻게 읽고(Get), 쓸지(Set)에 대한 로직을 정의합니다.- 데이터 인터페이스가 노출할 함수나 변수(예:
GetMyCustomValue(),SetMyCustomState())를 정의하고, 이들을 나이아가라 스크립트에서 호출할 수 있도록 등록합니다. - 필요에 따라 복잡한 데이터 구조를 처리하거나, 외부 라이브러리와 연동하는 코드를 작성합니다.
- C++에서 정의한 사용자 정의 데이터 인터페이스는 나이아가라 에디터에서 일반적인 데이터 인터페이스 모듈처럼 추가하고 사용할 수 있습니다.
- 스크립트 에디터에서 해당 데이터 인터페이스 모듈의 출력 핀을 파티클 속성에 연결하거나, 입력 핀에 다른 파티클 데이터를 연결하여 파티클의 동작을 제어합니다.
아래 다이어그램은 사용자 정의 데이터 인터페이스에서 C++ 구현, 나이아가라 노드, 시뮬레이션 스레드가 어떤 계약으로 나뉘는지 정리한 것입니다.
사용자 정의 데이터 인터페이스는 외부 데이터를 가져오는 C++ 코드, 나이아가라에서 호출할 함수, 시뮬레이션 스레드용 프록시가 함께 맞물립니다.
- C++ 클래스데이터의 소유와 노출 규칙
C++ 클래스 UNiagaraDataInterface를 상속합니다. UPROPERTY로 에디터 값을 노출합니다. 함수 이름과 타입 계약을 등록합니다.
- Niagara 노드아티스트가 쓰는 호출 표면
Niagara 노드 스크립트 에디터에서는 C++ 내부 구조보다 어떤 값을 읽고 어떤 파티클 속성에 연결할지만 보이게 설계합니다.
- Proxy시뮬레이션 스레드 안전성
Proxy 프레임별 읽기 데이터를 복사해 둡니다. 게임 스레드 객체에 직접 의존하지 않습니다. GPU/CPU 실행 경로를 구분합니다.
사용자 정의 데이터 인터페이스 구현 단계 (개요)
기본 DI로 해결되지 않는 요구만 C++ 인터페이스로 열고, 함수와 CPU/GPU 실행 경로를 작게 유지한다.
| 단계 | 정해야 할 것 | 통과 기준 | 위험 |
|---|---|---|---|
| Class | UNiagaraDataInterface 파생 클래스 | 소유 데이터와 책임이 작음 | 범용 유틸 클래스로 커짐 |
| Functions | 그래프에 노출할 함수와 타입 | 필요한 호출만 명명 | API가 많아 사용 실수 증가 |
| CPU VM | CPU 시뮬레이션 호출 경로 | 프리뷰와 PIE에서 같은 값 | CPU에서만 동작 |
| GPU Path | HLSL과 버퍼 업로드 | GPU 시뮬레이션도 값 일치 | 업로드 주기와 비용 누락 |
| Tests | 빈 값, 비활성화, 경계값 | 실패 상태가 예측 가능 | 데이터 없음이 조용히 0으로 보임 |
사용자 정의 데이터 인터페이스를 만들고 사용하는 일반적인 단계를 간략히 설명합니다. (실제 코딩은 별도의 심화 학습이 필요합니다.)
C++ 프로젝트 설정: 언리얼 엔진 프로젝트가 C++ 프로젝트인지 확인하고, 필요한 경우 새 C++ 클래스를 추가하여 프로젝트를 C++ 프로젝트로 변환합니다.
- 언리얼 에디터에서
파일(File)>새 C++ 클래스 추가(New C++ Class...)를 선택합니다. - 부모 클래스 목록에서
NiagaraDataInterface를 검색하여 선택합니다. - 클래스 이름(예:
MyCustomDataInterface)을 지정하고 클래스를 생성합니다.
- 생성된
.h(헤더) 파일과.cpp(소스) 파일에 데이터 인터페이스의 로직을 작성합니다. -
헤더 파일 (.h)
- 데이터 인터페이스가 노출할 속성(variables)을
UPROPERTY로 선언합니다. 이 속성들은 나이아가라 에디터의 디테일 패널에 노출됩니다. - 나이아가라 스크립트에서 호출할 함수를
UFUNCTION(CallInNiagaraScript)매크로와 함께 선언합니다. FNiagaraDataInterfaceProxy구조체를 정의하여 시뮬레이션 스레드에서 데이터를 처리할 로직을 구현합니다.
- 데이터 인터페이스가 노출할 속성(variables)을
-
소스 파일 (.cpp)
- 헤더 파일에 선언한 함수와 속성들에 대한 실제 구현 코드를 작성합니다.
- 데이터를 읽고 쓰는 방식을 정의하고, 필요한 경우 외부 시스템과 통신합니다.
UNiagaraDataInterface::BindInputs및UNiagaraDataInterface::Compile과 같은 가상 함수를 오버라이드하여 나이아가라 시스템과의 연결을 설정합니다.
모듈 빌드: 코드를 수정한 후 언리얼 엔진을 닫고 비주얼 스튜디오(Visual Studio)에서 프로젝트를 빌드합니다.
성공적으로 빌드되면 엔진이 새로 만든 데이터 인터페이스를 인식합니다.
- 나이아가라 에디터를 엽니다.
- 이미터의
Emitter Update또는Particle Update섹션에+버튼을 클릭합니다. - 여러분이 만든 사용자 정의 데이터 인터페이스 클래스 이름(예:
MyCustomDataInterface)을 검색하여 모듈로 추가합니다. - 추가된 모듈을 선택하면 디테일 패널에 C++에서 정의한 속성들이 나타나며, 스크립트 에디터에서는 정의한 함수들을 노드로 사용할 수 있게 됩니다.
사용자 정의 데이터 인터페이스의 장점
- 궁극적인 유연성: 언리얼 엔진의 모든 데이터와 시스템에 접근하여 나이아가라를 확장할 수 있습니다.
- 성능 최적화: C++로 직접 구현하므로, 복잡한 계산이나 외부 데이터 처리를 매우 효율적으로 수행할 수 있습니다.
- 특정 게임 로직 통합: 게임의 고유한 시스템이나 데이터 구조를 파티클 효과에 직접 통합하여, 게임 플레이와 시각 효과 간의 깊은 연동을 가능하게 합니다.
아래 다이어그램은 사용자 정의 데이터 인터페이스를 실제 프로젝트에 넣기 전에 고정해야 할 계약과 검증 지점을 묶은 것입니다.
사용자 정의 데이터 인터페이스는 외부 데이터를 읽는 통로이므로 이름, 타입, 갱신 주기, 스레드 경계를 먼저 정해야 안정적으로 쓰입니다.
- 게임 데이터
source 게임 데이터 AI 상태, 날씨, 외부 시뮬레이션처럼 나이아가라 밖의 원천을 정합니다.
- UNiagaraDataInterface
class UNiagaraDataInterface 속성과 함수를 등록해 에디터가 읽을 수 있는 표면을 만듭니다.
- 시뮬레이션 프록시
proxy 시뮬레이션 프록시 런타임 스레드에서 필요한 값만 안전하게 복사하고 계산합니다.
- 나이아가라 스크립트
node 나이아가라 스크립트 노출된 핀을 모듈 입력으로 연결해 파티클 속성에 적용합니다.
사용자 정의 데이터 인터페이스는 게임 로직이나 외부 데이터를 나이아가라 시스템으로 전달할 때 쓰는 확장 지점입니다.
필요한 데이터 형식과 갱신 주기를 명확히 정해야 안정적으로 사용할 수 있습니다.
이것으로 4장 데이터와 매개변수의 모든 절을 마무리하겠습니다.
이제는 나이아가라 시스템의 데이터 흐름과 매개변수 제어에 대한 깊이 있는 이해를 갖게 되셨을 겁니다.
사용자 정의 데이터 인터페이스는 필요성, C++ 구현, 함수 노출, CPU/GPU 실행 경로, 테스트 기준을 통과할 때 도입하는 것이 좋습니다.
기본 Data Interface와 User Parameter로 해결되는 일을 C++ 확장으로 만들면 유지보수 비용이 더 커진다.
| 게이트 | 검토 질문 | 통과 기준 | 막히면 |
|---|---|---|---|
| Need | 기본 DI나 User Parameter로 해결되지 않는가 | 대체 불가 이유가 문서화됨 | 기본 기능으로 단순화 |
| Class | C++이 소유할 데이터가 명확한가 | 데이터 경계가 작고 이름이 분명함 | 책임을 나누거나 DI 보류 |
| Functions | 그래프에 열 함수가 최소인가 | 입출력 타입과 이름이 고정됨 | API를 줄이고 기본값 정리 |
| Runtime | CPU/GPU 실행 경로가 모두 필요한가 | 지원 경로와 비용이 분리됨 | 한 경로만 지원한다고 명시 |
| Test | 빈 값과 비활성화 상태가 안전한가 | 프리뷰와 플레이 결과가 같음 | 실패값과 로그 기준 추가 |
사용자 정의 데이터 인터페이스는 구현 비용이 크기 때문에, 만들기 전에 대체 가능성과 검증 기준을 확인합니다.
기본 인터페이스로 표현하기 어려운 게임 고유 데이터를 C++로 나이아가라에 노출하는 고급 확장이다.
- 대체 확인
- 계약 고정
- 실행 검증
| 판단 | 필요 조건 | 구현 범위 | 검증 방법 |
|---|---|---|---|
| Need 고유 데이터 | 외부 물리, AI 상태, 날씨, 파일 데이터처럼 기본 함수로 읽을 수 없는 값. | User Parameter나 기존 DI 조합으로 충분하면 구현하지 않는다. | 필요 데이터와 호출 빈도를 먼저 적는다. |
| C++ Layer 클래스와 함수 | UNiagaraDataInterface 기반 클래스로 읽기 함수를 정의한다. | VM/GPU 지원 범위와 데이터 소유자를 고정한다. | 데이터 구조 변경 때 깨지는 계약이 작은지 본다. |
| Expose 스크립트 호출 | 모듈 그래프가 사용할 입력 핀과 함수 시그니처를 노출한다. | 필요한 입력과 출력만 열어 그래프 사용 실수를 줄인다. | 함수 이름, 타입, 기본값이 에디터에서 읽히는지 확인한다. |
| Test 실행 위치 | CPU/GPU, 에디터 프리뷰, 실제 플레이 값을 따로 본다. | 실패 시 대체값과 성능 비용을 함께 정한다. | GPU 파티클에서 필요한 함수가 실제 지원되는지 확인한다. |