동적 파라미터 갱신
블루프린트의 게임 상태를 사용자 변수에 지속적으로 전달해 캐릭터 속도에 따라 파티클 크기와 움직임이 반응하게 합니다.
체력·속도·목표 위치가 바뀌는 동안 효과도 반응해야 한다면 User Parameter를 갱신합니다. 무엇을 전달하는지뿐 아니라 값의 단위, 갱신 주기, Niagara에서 소비하는 단계를 정해야 합니다.
읽기, 변환, 전달, 소비
Blueprint에서 원본 값을 읽고 시각 표현 범위로 변환한 뒤 해당 Niagara Component의 Set Niagara Variable로 전달합니다. Niagara System에는 같은 이름과 타입의 User Parameter가 있고, 실제 모듈 입력에 연결되어 있어야 합니다.
- 속력은 Float, 방향은 Vector, 월드 위치는 Position 등 의미에 맞는 타입과 setter를 선택합니다.
- Particle Spawn에서 읽는 초기 속도는 새로운 입자에 반영됩니다. 기존 입자의 크기나 색을 계속 바꾸려면 Particle Update에서 읽습니다.
- 목표 추적은 해당 위치를 읽는 Point Attraction Force 등의 좌표 공간을 맞춥니다. 값을 전달하는 것만으로 자동 추적하지 않습니다.
직접 해보기: 속력에 따른 발밑 먼지 크기
이 실습에서는 User.PlayerSpeed의 단위를 끝까지 cm/s로 유지하고, Niagara에서 한 번만 크기 배율로 바꿉니다.
Niagara 시스템
기본 Sprite 시스템을 복제해 NS_SpeedDust로 저장합니다. Emitter State와 Spawn Rate를 구성하고, Particle Spawn의 Initialize Particle에서 Lifetime 0.5~1초, Sprite Size 5 cm, 밝은 갈색 Color를 설정합니다.
Particle Spawn의 Add Velocity에 Z=20 cm/s, X/Y=-10~10 cm/s를 줍니다. 아래 중력은 우선 끄고 Particle Update에 Particle State와 Solve Forces and Velocity를 둡니다.
단일 프레임 먼지 텍스처를 쓰는 Translucent 머티리얼을 Sprite Renderer에 할당합니다. RGB/Alpha에 Particle Color를 반영하고, 필요하면 머티리얼 DepthFade로 교차 경계를 부드럽게 합니다. Renderer의 공통 Soft Particle 옵션을 가정하지 않습니다.
Float User.PlayerSpeed를 만들고 기본값을 0으로 둡니다. Particle Update의 Scale Sprite Size에서 이 값을 Clamp(PlayerSpeed / 600, 0, 1) × 3으로 변환해 Uniform 배율에 연결합니다. 이 모듈은 초기 크기를 기준으로 배율을 적용하며 현재 크기를 매 틱 계속 곱하는 방식과 구분합니다.
Particle State에서 수명 종료 시 제거하고, Scale Color의 Alpha를 NormalizedAge에 따라 1→0으로 줄입니다. 먼지를 월드에 남기려면 Emitter Local Space를 끕니다.
캐릭터 Blueprint
플레이어 캐릭터에 Niagara 컴포넌트를 추가하고 NS_SpeedDust를 할당합니다. Mesh의 자식으로 옮긴 뒤 실제 발 본/소켓 이름을 Parent Socket에 지정하고 상대 위치를 맞춥니다. Auto Activate는 끕니다.
Event Tick에서 Get Velocity의 수평 성분 길이(Vector Length XY)를 구합니다. 점프의 수직 속력까지 먼지 크기에 반영하려는 경우에만 3D Length를 선택합니다. Get/Length 같은 Pure 노드는 값 선으로 연결하고 실행 선은 setter로 보냅니다.
Niagara Component를 Target으로 Set Niagara Variable (Float)를 호출해 User.PlayerSpeed에 원본 수평 속력(cm/s)을 전달합니다. Blueprint에서 다시 0~10으로 매핑하지 않습니다. Max Walk Speed가 600과 다르면 Niagara의 기준 속도도 함께 맞춥니다.
속력이 5 cm/s를 초과하고 캐릭터가 지면에 있는지를 비교해 이동 상태 Bool을 만듭니다. 이전 상태와 달라졌을 때만 Activate 또는 Deactivate를 호출합니다. 값은 Activate보다 먼저 전달하고, 매 Tick Reset으로 재시작하지 않습니다.
EndPlay에서 별도 타이머나 외부 참조가 있다면 정리합니다. 컴파일·저장 후 정지, 걷기, 달리기, 점프에서 생성과 크기 변화를 비교합니다. Deactivate 후 기존 입자의 소멸은 Inactive Response와 수명을 따릅니다.
값과 예상 크기
| 전달한 수평 속력 | Uniform 배율 | 초기 5 cm의 예상 크기 |
|---|---|---|
| 0 cm/s | 0 | 0 cm; 이동 상태도 꺼짐 |
| 100 cm/s | 0.5 | 2.5 cm |
| 300 cm/s | 1.5 | 7.5 cm |
| 600 cm/s 이상 | 3 | 최대 15 cm |
이 값은 수식의 결과이며 실제 화면 점유율은 카메라·텍스처·겹침에 따라 달라집니다. 최대 크기 배율과 기준 속력을 먼저 정하고 필요하면 입력을 보간합니다. 정지 시 기존 먼지까지 크기 0으로 사라지는 것이 부자연스러우면 생성 시점의 배율을 Spawn에 저장하고 수명 페이드는 별도로 유지하는 설계를 비교합니다.
갱신 주기 선택
- Tick: 매 프레임 추적이 필요한 속도·조준점에 사용합니다. 작은 차이를 무시해도 되는지 확인하고 초기값 전달은 생략하지 않습니다.
- Event: 체력·장비·스킬 상태는 변경 이벤트에서 갱신합니다.
- Timer: 느린 바람·온도 등은 일정 간격으로 갱신할 수 있습니다. Timer는 기본적으로 게임 스레드에서 호출되며 비동기 작업으로 바뀌는 것이 아닙니다.
- Data Interface: Actor Component·Skeletal Mesh·Static Mesh 등의 지원 데이터를 필요한 때 읽습니다. Blueprint 반복 전달을 줄일 수 있지만 데이터 준비·샘플링·CPU/GPU 지원 비용이 없어지는 것은 아닙니다.
- Collection: 여러 시스템이 공유하는 값은 Niagara Parameter Collection에 둡니다.
서버 권위 값은 게임 코드가 클라이언트에 전달하고 시각 효과는 그 값을 소비하게 합니다. 임의 Async Task에서 UObject나 Niagara Component를 직접 수정하는 것을 주기 최적화 방법으로 사용하지 않습니다.