사용자 정의 데이터 인터페이스
C++ 데이터 인터페이스의 클래스·함수·GPU 지원 구조를 이해하고 게임 고유 데이터를 나이아가라 입력으로 노출합니다.
앞서 우리는 나이아가라 시스템이 스태틱 메시, 스켈레탈 메시, 스플라인 등 언리얼 엔진의 기존 데이터를 활용할 수 있도록 돕는 데이터 인터페이스의 기본 개념과 활용법을 살펴보았습니다.
이러한 기본 데이터 인터페이스만으로도 다양한 시각 효과를 만들 수 있지만, 때로는 게임의 특정 로직이나 외부 라이브러리에서 오는 고유한 데이터를 나이아가라 시스템에서 사용하고 싶을 때가 있습니다.
이럴 때 필요한 것이 바로 사용자 정의 데이터 인터페이스(Custom Data Interface)입니다.
사용자 정의 데이터 인터페이스는 나이아가라를 외부 데이터와 연결하는 확장 지점입니다.
C++로 필요한 입력을 노출하면 시스템이 게임 상태나 별도 데이터 소스를 직접 참조할 수 있습니다.
사용자 정의 데이터 인터페이스란?
사용자 정의 데이터 인터페이스는 개발자가 C++ 코드로 직접 구현하여, 나이아가라 시스템이 기본적으로 지원하지 않는 형태의 데이터를 읽거나 쓰도록 확장하는 기능입니다.
이는 마치 나이아가라에 새로운 감각 기관을 추가하여, 게임 세계의 더 복잡하고 특수한 정보를 인식하고 반응할 수 있게 만드는 것과 같습니다.
예를 들어, 다음과 같은 시나리오에서 사용자 정의 데이터 인터페이스가 필요할 수 있습니다.
- 외부 물리 시뮬레이션 데이터: 게임 내에 커스텀 물리 엔진이나 유체 시뮬레이션이 존재하고, 그 결과 데이터를 나이아가라 파티클에 적용하고 싶을 때.
- 특정 AI 행동 데이터: AI 캐릭터의 현재 의사결정이나 심리 상태에 따라 파티클 효과를 변화시키고 싶을 때.
- 게임 특정 상태 데이터: 게임의 고유한 시스템(예: 날씨 시스템, 자원 시스템)에서 오는 복잡한 수치 데이터를 파티클에 반영하고 싶을 때.
- 파일에서 데이터 읽기: CSV, JSON 등 특정 파일 형식으로 저장된 데이터를 파티클에 적용하고 싶을 때.
사용자 정의 데이터 인터페이스를 구현하려면 기본적인 C++ 프로그래밍 지식과 언리얼 엔진의 모듈 및 클래스 생성에 대한 이해가 필요합니다.
이는 나이아가라의 가장 고급 기능 중 하나이므로, 앞서 배운 기본 개념들을 충분히 숙달한 후에 시도하는 것이 좋습니다.
사용자 정의 데이터 인터페이스의 작동 원리 (개념적 설명)
사용자 정의 데이터 인터페이스는 크게 두 가지 영역에서 작동합니다.
UNiagaraDataInterface를 상속받는 C++ 클래스를 생성합니다. 이 클래스에서 나이아가라 시스템이 외부 데이터를 어떻게 읽고(Get), 쓸지(Set)에 대한 로직을 정의합니다.- 데이터 인터페이스가 노출할 함수나 변수(예:
GetMyCustomValue(),SetMyCustomState())를 정의하고, 이들을 나이아가라 스크립트에서 호출할 수 있도록 등록합니다. - 필요에 따라 복잡한 데이터 구조를 처리하거나, 외부 라이브러리와 연동하는 코드를 작성합니다.
- 등록한 DI 타입을 모듈 그래프의 입력으로 사용하고, 그 입력에서 노출 함수를 호출합니다. DI 클래스가 자동으로 Emitter Update의 모듈 목록에 들어오는 것은 아닙니다.
- 스크립트 에디터에서 DI 함수의 입출력을 계산에 연결하고, 결과를 Parameter Map을 통해 파티클 속성에 씁니다.
CPU는 인스턴스 데이터 수명과 접근을 관리합니다. GPU 경로는 렌더 스레드 프록시·리소스 전달과 HLSL 구현이 별도로 필요합니다.
사용자 정의 데이터 인터페이스 구현 단계 (개요)
사용자 정의 데이터 인터페이스를 만들고 사용하는 일반적인 단계를 간략히 설명합니다. (실제 코딩은 별도의 심화 학습이 필요합니다.)
C++ 프로젝트 설정: 언리얼 엔진 프로젝트가 C++ 프로젝트인지 확인하고, 필요한 경우 새 C++ 클래스를 추가하여 프로젝트를 C++ 프로젝트로 변환합니다.
- 프로젝트나 플러그인의 C++ 모듈에서
UNiagaraDataInterface파생 클래스를 만듭니다. 클래스 생성 UI에 이 부모가 표시된다고 가정하지 말고, 해당 버전의 엔진 DI 구현과 헤더를 참고합니다. - Niagara 모듈 의존성과 헤더·노출 설정을 확인하고, 클래스 이름을
MyCustomDataInterface처럼 역할이 드러나게 지정합니다.
- 생성된
.h(헤더) 파일과.cpp(소스) 파일에 데이터 인터페이스의 로직을 작성합니다. -
헤더 파일 (.h)
- 데이터 인터페이스가 노출할 속성(variables)을
UPROPERTY로 선언합니다. 이 속성들은 나이아가라 에디터의 디테일 패널에 노출됩니다. - Niagara 함수 시그니처를 등록하고 지원할 Sim Target을 명시합니다.
UFUNCTION(CallInNiagaraScript)라는 지정자로 자동 노출하는 방식은 사용하지 않습니다. - CPU 인스턴스 데이터와, GPU 경로가 필요할 때 렌더 스레드용
FNiagaraDataInterfaceProxy의 역할을 구분합니다. 게임 스레드 객체를 임의의 실행 스레드에서 직접 읽지 않습니다.
- 데이터 인터페이스가 노출할 속성(variables)을
-
소스 파일 (.cpp)
- 헤더 파일에 선언한 함수와 속성들에 대한 실제 구현 코드를 작성합니다.
- 데이터를 읽고 쓰는 방식을 정의하고, 필요한 경우 외부 시스템과 통신합니다.
- 현재 API의
GetFunctionsInternal로 함수 시그니처를 제공하고GetVMExternalFunction으로 CPU VM 호출을 연결합니다.CanExecuteOnTarget은 구현한 실행 대상과 일치시킵니다. - GPU 지원에는 HLSL 함수 생성과 셰이더 파라미터·리소스 전달이 별도로 필요합니다. CPU C++ 함수가 자동으로 GPU 코드로 변환되지 않습니다. 구체적인 오버라이드 서명은 사용하는 엔진 버전의 헤더를 따릅니다.
모듈 빌드: 코드를 수정한 후 언리얼 엔진을 닫고 비주얼 스튜디오(Visual Studio)에서 프로젝트를 빌드합니다.
성공적으로 빌드되면 엔진이 새로 만든 데이터 인터페이스를 인식합니다.
- 나이아가라 에디터를 엽니다.
- 해당 DI 타입을 입력으로 받는 Niagara 모듈을 만듭니다.
- 모듈 그래프에서 DI 입력을 읽어 등록한 함수를 호출하고 결과를 필요한 속성에 기록합니다.
- 그 모듈을 지원되는 실행 그룹에 추가한 뒤 DI 인스턴스의 설정·소스·CPU/GPU 대상을 확인합니다.
사용자 정의 데이터 인터페이스의 장점
- 확장성: 프로젝트가 소유한 데이터를 필요한 함수로 노출할 수 있습니다. 객체 수명과 접근 가능한 스레드·실행 대상을 지켜야 합니다.
- 처리 비용 제어: 필요한 값을 미리 계산하거나 묶어 전달할 수 있습니다. C++라는 이유만으로 빠른 것은 아니며, 호출 수·복사·GPU 업로드 비용을 측정해야 합니다.
- 특정 게임 로직 통합: 게임의 고유한 시스템이나 데이터 구조를 파티클 효과에 직접 통합하여, 게임 플레이와 시각 효과 간의 깊은 연동을 가능하게 합니다.
구현 범위와 확인 항목
먼저 User Parameter·배열·기존 DI로 해결할 수 있는지 확인합니다. 파일 데이터는 미리 읽고 가공한 뒤 전달하는 방법을 우선 검토하고, 입자마다 파일 I/O를 수행하지 않습니다.
- 함수 이름·입출력 타입·단위·갱신 주기를 작게 정합니다.
- 빈 데이터, 파일 읽기 실패, 사라진 액터, 비활성화 후 재활성화의 동작을 정의합니다.
- CPU 전용이면 이를 명시하고 GPU 대상의 잘못된 사용을 거부합니다. 두 대상을 지원한다면 값을 각각 비교합니다.
- 프리뷰와 실제 플레이에서 소스 수명·스레드 전달·비용을 확인합니다.
이 절은 구현 구조의 개요입니다. 완성된 C++ 구현이나 빌드 성공 결과를 제공하는 예제는 아닙니다. UNiagaraDataInterface API와 사용 중인 버전의 실제 헤더를 기준으로 확장합니다.