VFX 그래프와의 연동
언리얼 엔진의 Niagara VFX 시스템은 강력하고 유연한 비주얼 이펙트 제작 도구입니다.
이 절에서는 C++를 사용하여 Niagara 시스템을 제어하고 확장하는 방법을 살펴보겠습니다.
Niagara 시스템의 프로그래밍적 접근
Niagara 시스템에 프로그래밍적으로 접근하려면 UNiagaraSystem
및 UNiagaraComponent
클래스를 사용합니다.
##include "NiagaraSystem.h"
##include "NiagaraComponent.h"
##include "NiagaraFunctionLibrary.h"
UCLASS()
class MYGAME_API AMyVFXActor : public AActor
{
GENERATED_BODY()
public:
AMyVFXActor();
UPROPERTY(VisibleAnywhere, Category = "VFX")
UNiagaraComponent* NiagaraComponent;
UFUNCTION(BlueprintCallable, Category = "VFX")
void ActivateVFX();
};
AMyVFXActor::AMyVFXActor()
{
NiagaraComponent = CreateDefaultSubobject<UNiagaraComponent>(TEXT("NiagaraVFX"));
RootComponent = NiagaraComponent;
}
void AMyVFXActor::ActivateVFX()
{
if (NiagaraComponent)
{
NiagaraComponent->Activate();
}
}
C++에서 VFX 시스템 생성 및 제어
런타임에 동적으로 VFX 시스템을 생성하고 제어할 수 있습니다.
void AMyGameMode::SpawnDynamicVFX(FVector Location)
{
UNiagaraSystem* NiagaraSystem = LoadObject<UNiagaraSystem>(nullptr, TEXT("/Game/VFX/NS_Explosion"));
if (NiagaraSystem)
{
UNiagaraComponent* NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
this,
NiagaraSystem,
Location,
FRotator::ZeroRotator,
FVector::OneVector,
true,
true,
ENCPoolMethod::AutoRelease
);
// VFX 시스템 추가 설정
if (NiagaraComponent)
{
NiagaraComponent->SetVariableFloat(FName("ExplosionSize"), 200.0f);
}
}
}
커스텀 Niagara 모듈 구현
C++에서 커스텀 Niagara 모듈을 구현하여 VFX 시스템의 기능을 확장할 수 있습니다.
##include "NiagaraDataInterfaceSimpleCounter.h"
UCLASS()
class MYGAME_API UNiagaraDICustomLogic : public UNiagaraDataInterface
{
GENERATED_BODY()
public:
virtual void GetFunctions(TArray<FNiagaraFunctionSignature>& OutFunctions) override;
virtual bool CanExecuteOnTarget(ENiagaraSimTarget SimTarget) const override { return SimTarget == ENiagaraSimTarget::GPUComputeSim; }
DECLARE_FUNCTION(CalculateCustomValue);
};
void UNiagaraDICustomLogic::GetFunctions(TArray<FNiagaraFunctionSignature>& OutFunctions)
{
FNiagaraFunctionSignature Sig;
Sig.Name = TEXT("CalculateCustomValue");
Sig.Inputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("Input")));
Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("Output")));
Sig.bMemberFunction = true;
Sig.bRequiresContext = false;
OutFunctions.Add(Sig);
}
void UNiagaraDICustomLogic::CalculateCustomValue(FVectorVMContext& Context)
{
VectorVM::FUserPtrHandler<UNiagaraDICustomLogic> InstanceData(Context);
VectorVM::FExternalFuncInputHandler<float> InputParam(Context);
VectorVM::FExternalFuncRegisterHandler<float> OutValue(Context);
for (int32 i = 0; i < Context.NumInstances; ++i)
{
float Input = InputParam.GetAndAdvance();
float Result = FMath::Sin(Input) * 0.5f + 0.5f; // 예시 계산
*OutValue.GetDestAndAdvance() = Result;
}
}
VFX 파라미터의 동적 업데이트
게임플레이 중 VFX 시스템의 파라미터를 동적으로 업데이트할 수 있습니다.
void AMyCharacter::UpdateVFXIntensity(float Intensity)
{
if (AuraVFXComponent)
{
AuraVFXComponent->SetVariableFloat(FName("Intensity"), Intensity);
}
}
게임플레이 이벤트와 VFX 시스템 연동
게임플레이 이벤트에 따라 VFX 시스템을 트리거하고 제어할 수 있습니다.
void AMyCharacter::OnTakeDamage(float Damage)
{
// 기존 데미지 처리 로직
// VFX 트리거
if (DamageVFXSystem)
{
UNiagaraComponent* DamageVFX = UNiagaraFunctionLibrary::SpawnSystemAttached(
DamageVFXSystem,
GetMesh(),
NAME_None,
FVector::ZeroVector,
FRotator::ZeroRotator,
EAttachLocation::SnapToTarget,
true
);
if (DamageVFX)
{
DamageVFX->SetVariableFloat(FName("DamageAmount"), Damage);
}
}
}
대규모 파티클 시스템의 최적화 전략
- GPU 시뮬레이션 활용
- LOD (Level of Detail) 시스템 구현
- 인스턴싱 및 풀링 사용
UCLASS()
class MYGAME_API UVFXOptimizer : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "VFX Optimization")
void OptimizeVFXSystem(UNiagaraComponent* NiagaraComponent, float DistanceFromCamera);
private:
UPROPERTY(EditAnywhere, Category = "VFX Optimization")
TArray<float> LODDistances;
UPROPERTY(EditAnywhere, Category = "VFX Optimization")
TArray<int32> LODParticleCounts;
};
void UVFXOptimizer::OptimizeVFXSystem(UNiagaraComponent* NiagaraComponent, float DistanceFromCamera)
{
if (NiagaraComponent)
{
int32 LODLevel = LODDistances.Num() - 1;
for (int32 i = 0; i < LODDistances.Num(); ++i)
{
if (DistanceFromCamera < LODDistances[i])
{
LODLevel = i;
break;
}
}
NiagaraComponent->SetVariableInt(FName("ParticleCount"), LODParticleCounts[LODLevel]);
}
}
VFX 그래프 에셋 로드 및 인스턴스화
VFX 그래프 에셋을 동적으로 로드하고 인스턴스화할 수 있습니다.
void AMyVFXManager::LoadAndSpawnVFX(FString VFXAssetPath, FVector Location)
{
UNiagaraSystem* NiagaraSystem = LoadObject<UNiagaraSystem>(nullptr, *VFXAssetPath);
if (NiagaraSystem)
{
UNiagaraComponent* NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
this,
NiagaraSystem,
Location
);
// 필요한 경우 추가 설정
}
}
런타임 VFX 속성 수정
게임 중 VFX 시스템의 속성을 동적으로 수정할 수 있습니다.
void AMyVFXController::ModifyVFXProperties(UNiagaraComponent* NiagaraComponent, float Intensity, FLinearColor Color)
{
if (NiagaraComponent)
{
NiagaraComponent->SetVariableFloat(FName("Intensity"), Intensity);
NiagaraComponent->SetVariableLinearColor(FName("Color"), Color);
}
}
VFX 시스템의 성능 모니터링 및 디버깅
VFX 시스템의 성능을 모니터링하고 디버깅하기 위한 도구를 구현할 수 있습니다.
UCLASS()
class MYGAME_API UVFXDebugger : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "VFX Debug")
void LogVFXPerformance(UNiagaraComponent* NiagaraComponent);
};
void UVFXDebugger::LogVFXPerformance(UNiagaraComponent* NiagaraComponent)
{
if (NiagaraComponent)
{
int32 ParticleCount = NiagaraComponent->GetSystemInstanceCount();
float SimulationTime = NiagaraComponent->GetSystemSimulationTime();
UE_LOG(LogTemp, Log, TEXT("VFX Performance - Particles: %d, Simulation Time: %f ms"), ParticleCount, SimulationTime);
}
}
고급 VFX 기법 구현
GPU 시뮬레이션
GPU 시뮬레이션을 활용하여 대규모 파티클 시스템의 성능을 향상시킬 수 있습니다.
UCLASS()
class MYGAME_API UNiagaraDataInterfaceGPUSimulation : public UNiagaraDataInterface
{
GENERATED_BODY()
public:
virtual bool InitializePerInstanceData(void* PerInstanceData, FNiagaraSystemInstance* SystemInstance) override;
virtual bool PerInstanceTick(void* PerInstanceData, FNiagaraSystemInstance* SystemInstance, float DeltaSeconds) override;
virtual void GetFunctions(TArray<FNiagaraFunctionSignature>& OutFunctions) override;
// GPU 시뮬레이션 관련 함수 선언
};
메시 파티클
복잡한 형태의 파티클을 위해 메시 파티클을 구현할 수 있습니다.
UCLASS()
class MYGAME_API UNiagaraDataInterfaceMeshParticle : public UNiagaraDataInterface
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Mesh Particle")
UStaticMesh* ParticleMesh;
virtual void GetFunctions(TArray<FNiagaraFunctionSignature>& OutFunctions) override;
// 메시 파티클 관련 함수 선언
};
VFX 시스템과 물리 엔진의 통합
VFX 시스템을 물리 엔진과 통합하여 더욱 사실적인 효과를 만들 수 있습니다.
UCLASS()
class MYGAME_API UNiagaraDataInterfacePhysics : public UNiagaraDataInterface
{
GENERATED_BODY()
public:
virtual void GetFunctions(TArray<FNiagaraFunctionSignature>& OutFunctions) override;
UFUNCTION(BlueprintCallable, Category = "VFX Physics")
void ApplyForceToParticles(UNiagaraComponent* NiagaraComponent, FVector Force);
};
void UNiagaraDataInterfacePhysics::ApplyForceToParticles(UNiagaraComponent* NiagaraComponent, FVector Force)
{
if (NiagaraComponent)
{
NiagaraComponent->SetVariableVec3(FName("ExternalForce"), Force);
}
}
C++를 통해 Niagara VFX 시스템을 효과적으로 제어하고 확장할 수 있습니다.
게임플레이와 VFX를 긴밀하게 연동하고, 성능을 최적화하며, 고급 VFX 기법을 구현할 수 있습니다.