조건문과 루프 구조의 기초
나이아가라 스크립트에서 조건문과 루프 구조는 파티클의 동작을 제어하고 복잡한 효과를 구현하는 데 필수적인 요소입니다.
이들을 효과적으로 활용하면 다양하고 역동적인 파티클 시스템을 만들 수 있습니다.
조건문
if-else 문
기본 구문
if (condition) {
// 조건이 참일 때 실행될 코드
} else {
// 조건이 거짓일 때 실행될 코드
}
예제 : 파티클 수명에 따른 색상 변경
void UpdateParticleColor(inout float4 Color, float Age, float Lifetime)
{
if (Age < Lifetime * 0.5) {
Color = float4(1, 0, 0, 1); // 빨간색
} else {
Color = float4(0, 0, 1, 1); // 파란색
}
}
else if 문
- 여러 조건을 연속적으로 검사할 때 사용합니다.
예제 : 파티클 위치에 따른 동작 변경
void UpdateParticleMovement(inout float3 Velocity, float3 Position)
{
if (Position.y > 100) {
Velocity.y = -50; // 높이가 100 초과시 아래로 이동
} else if (Position.y < 0) {
Velocity.y = 50; // 높이가 0 미만시 위로 이동
} else {
Velocity.y = 0; // 그 외의 경우 수평 이동
}
}
switch 문
- 여러 가지 경우 중 하나를 선택할 때 사용합니다.
예제 : 파티클 타입에 따른 동작 설정
void SetParticleBehavior(int ParticleType, inout float3 Velocity, inout float4 Color)
{
switch (ParticleType) {
case 0: // 불 파티클
Velocity.y = 10;
Color = float4(1, 0.5, 0, 1);
break;
case 1: // 물 파티클
Velocity.y = -5;
Color = float4(0, 0.5, 1, 1);
break;
default: // 기본 파티클
Velocity = float3(0, 0, 0);
Color = float4(1, 1, 1, 1);
break;
}
}
루프 구조
for 루프
기본 구문
for (초기화; 조건; 증감) {
// 반복 실행될 코드
}
예제 : 주변 파티클과의 상호작용
void InteractWithNeighbors(inout float3 Velocity, float3 Position, float3 Neighbors[10])
{
for (int i = 0; i < 10; i++) {
float3 ToNeighbor = Neighbors[i] - Position;
float Distance = length(ToNeighbor);
if (Distance > 0 && Distance < 10) {
Velocity += normalize(ToNeighbor) / Distance;
}
}
}
while 루프
- 조건이 참인 동안 계속 실행됩니다.
예제 : 충돌 회피
void AvoidObstacles(inout float3 Position, float3 Velocity, float3 Obstacles[5])
{
int i = 0;
while (i < 5) {
float3 ToObstacle = Obstacles[i] - Position;
if (length(ToObstacle) < 5) {
Position -= normalize(ToObstacle);
}
i++;
}
}
실제 파티클 시스템 적용 사례
복합 파티클 효과 구현
- 여러 조건과 루프를 조합하여 복잡한 파티클 효과를 만들 수 있습니다.
예제 : 폭죽 효과
void FireworkEffect(inout float3 Position, inout float3 Velocity, inout float4 Color,
float Age, float Lifetime, int ParticleID)
{
if (Age < Lifetime * 0.5) {
// 상승 단계
Velocity.y += 9.8 * DeltaTime;
Color = float4(1, 1, 0, 1);
} else if (Age < Lifetime * 0.6) {
// 폭발 단계
for (int i = 0; i < 10; i++) {
float Angle = ParticleID * 36 + i * 36;
Velocity.x = cos(Angle) * 50;
Velocity.y = sin(Angle) * 50;
}
Color = float4(1, 0, 0, 1);
} else {
// 소멸 단계
Velocity *= 0.98;
Color.a = lerp(1, 0, (Age - Lifetime * 0.6) / (Lifetime * 0.4));
}
Position += Velocity * DeltaTime;
}
이 예제에서는 조건문을 사용하여 파티클의 생명 주기를 여러 단계로 나누고, 루프를 사용하여 폭발 효과를 생성합니다.
성능 고려사항 및 최적화 기법
1. 조건문 최소화
- 과도한 조건 분기는 성능 저하를 초래할 수 있습니다.
- 가능한 경우 수학적 표현식으로 대체하세요.
예제
// 대신에
float Value = condition ? a : b;
2. 루프 언롤링
- 작은 크기의 고정 루프는 수동으로 풀어쓰는 것이 더 효율적일 수 있습니다.
예제
// 대신에
Result += Data[0];
Result += Data[1];
Result += Data[2];
Result += Data[3];
3. Early Exit 활용
- 조건을 만족하면 빠르게 루프나 함수를 종료하세요.
예제
for (int i = 0; i < 100; i++) {
if (Condition) {
Result = Value;
break; // 조건 만족 시 즉시 종료
}
}
4. 병렬 처리 고려
- 나이아가라는 GPU에서 병렬로 실행됩니다. 파티클 간 독립적인 연산을 선호하세요.
5. 동적 분기 최소화
- 파티클마다 다른 조건으로 분기되는 것을 피하세요. 공통된 조건을 사용하세요.
복잡한 로직 구현 팁
1. 모듈화
- 복잡한 로직을 작은 함수로 분리하여 가독성과 재사용성을 높이세요.
2. 상태 머신 패턴
- 여러 상태를 가진 파티클 시스템의 경우, 상태 머신 패턴을 고려하세요.
예제
void UpdateParticleState(inout int State, float Age, float Lifetime)
{
switch (State) {
case 0: // 초기 상태
if (Age > Lifetime * 0.2) State = 1;
break;
case 1: // 중간 상태
if (Age > Lifetime * 0.8) State = 2;
break;
case 2: // 최종 상태
// 최종 상태 로직
break;
}
}
3. Look-up Tables (LUT) 활용
- 복잡한 계산을 미리 계산된 테이블로 대체하여 성능을 향상시킬 수 있습니다.
4. 조건부 실행 최소화
- 가능한 경우 조건부 실행 대신 수학적 표현식을 사용하세요.
예제
// 대신에
float Factor = saturate((Threshold - Value) / Threshold);
Result *= Factor;
나이아가라 스크립트에서 조건문과 루프 구조를 효과적으로 활용하면 복잡하고 역동적인 파티클 시스템을 구현할 수 있습니다.
하지만 성능 최적화를 위해 이들의 사용을 신중하게 고려해야 합니다.
가능한 수학적 표현식과 병렬 처리에 적합한 구조를 우선적으로 고려하고 필요한 경우에만 조건문과 루프를 사용하세요.