동적 액터와의 기본 상호작용
나이아가라 파티클 시스템과 동적으로 움직이는 액터 간의 상호작용은 생동감 있고 반응적인 게임 환경을 만드는 데 중요한 역할을 합니다.
이 절에서는 파티클과 동적 액터 간의 다양한 상호작용 구현 방법을 살펴보겠습니다.
동적 액터 정보 파티클 시스템에 반영
- 데이터 인터페이스 생성
UCLASS()
class UNiagaraDataInterfaceDynamicActor : public UNiagaraDataInterface
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
AActor* TargetActor;
virtual void GetActorData(FVector& OutPosition, FVector& OutVelocity) const;
};
- 나이아가라 모듈에서 데이터 사용
void UpdateParticleWithActorData(inout float3 Position, inout float3 Velocity)
{
float3 ActorPosition, ActorVelocity;
DynamicActor.GetActorData(ActorPosition, ActorVelocity);
float3 ToActor = ActorPosition - Position;
float Distance = length(ToActor);
// 액터 주변에 파티클 집중
Velocity += normalize(ToActor) * 10.0 / (Distance + 1.0);
// 액터 속도에 영향 받음
Velocity += ActorVelocity * 0.1;
}
동적 액터에 미치는 영향 시뮬레이션
- 파티클 데이터 수집
void ANiagaraActor::CollectParticleData(TArray<FVector>& Positions, TArray<FVector>& Velocities)
{
UNiagaraComponent* NiagaraComp = GetComponentByClass<UNiagaraComponent>();
if (NiagaraComp)
{
NiagaraComp->GetParticleData(Positions, "Position");
NiagaraComp->GetParticleData(Velocities, "Velocity");
}
}
- 액터에 영향 적용
void ADynamicActor::ApplyParticleEffects(const TArray<FVector>& ParticlePositions, const TArray<FVector>& ParticleVelocities)
{
FVector TotalForce = FVector::ZeroVector;
for (int i = 0; i < ParticlePositions.Num(); ++i)
{
FVector ToParticle = ParticlePositions[i] - GetActorLocation();
float Distance = ToParticle.Size();
TotalForce += ToParticle.GetSafeNormal() * (1.0f / (Distance * Distance));
}
UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(GetRootComponent());
if (PrimComp)
{
PrimComp->AddForce(TotalForce * ForceMultiplier);
}
}
동적 콜리전 처리
- 콜리전 데이터 업데이트
void UNiagaraDataInterfaceDynamicActor::UpdateCollisionData()
{
if (TargetActor)
{
FTransform ActorTransform = TargetActor->GetTransform();
// 콜리전 데이터 구조 업데이트
}
}
- 파티클 콜리전 체크
void CheckDynamicCollision(inout float3 Position, inout float3 Velocity)
{
float3 ClosestPoint, Normal;
if (DynamicActor.CheckCollision(Position, ClosestPoint, Normal))
{
Position = ClosestPoint + Normal * 0.01;
Velocity = reflect(Velocity, Normal) * 0.8;
}
}
파티클-액터 간 포스 적용
- 파티클에서 액터로의 포스
void ApplyForceToActor(float3 ParticlePosition, float3 ParticleVelocity)
{
float3 Force = ParticleVelocity * ParticleMass;
DynamicActor.AddForce(Force);
}
- 액터에서 파티클로의 포스
void ApplyActorForceToParticle(inout float3 Velocity)
{
float3 ActorForce;
DynamicActor.GetAppliedForce(ActorForce);
Velocity += ActorForce * ForceTransferFactor;
}
복잡한 상호작용 시나리오 구현
예 : 캐릭터 주변의 마법 오라 효과
void UpdateMagicAura(inout float3 Position, inout float3 Velocity, inout float4 Color,
float Time, float DeltaTime)
{
float3 CharacterPosition, CharacterVelocity;
DynamicCharacter.GetData(CharacterPosition, CharacterVelocity);
// 캐릭터 주변 궤도 운동
float Angle = Time * 2.0 + InitialAngle;
float Radius = 100.0 + sin(Time * 3.0) * 20.0;
float3 Offset = float3(cos(Angle), sin(Angle), 0) * Radius;
Position = CharacterPosition + Offset;
// 캐릭터 속도에 따른 왜곡
Velocity = CharacterVelocity * 0.2 + normalize(Offset) * 50.0;
// 캐릭터 상태에 따른 색상 변화
float CharacterHealth;
DynamicCharacter.GetHealth(CharacterHealth);
Color = lerp(float4(1,0,0,1), float4(0,1,0,1), CharacterHealth);
// 주변 적 감지 및 반응
float3 NearestEnemyPosition;
if (DynamicCharacter.GetNearestEnemy(NearestEnemyPosition))
{
float3 ToEnemy = NearestEnemyPosition - Position;
Velocity += normalize(ToEnemy) * 30.0;
Color.g += 0.5; // 방어적 색상 강화
}
}
성능 최적화 전략
1. 데이터 버퍼링
- 매 프레임이 아닌 일정 간격으로 동적 액터 데이터 업데이트
2. 공간 분할
- 옥트리 또는 균일 그리드를 사용하여 상호작용 체크 최적화
3. LOD (Level of Detail) 시스템
void ApplyLOD(inout bool PerformInteraction, float DistanceFromCamera)
{
PerformInteraction = DistanceFromCamera < InteractionThreshold;
}
4. 배치 처리
- 유사한 상호작용을 그룹화하여 한 번에 처리
5. GPU 가속
- 가능한 경우 상호작용 계산을 GPU로 오프로드
효과적인 디버깅 방법
1. 시각적 디버깅
void VisualizeParticleActorInteraction(UWorld* World, const FVector& ParticlePos, const FVector& ActorPos)
{
DrawDebugLine(World, ParticlePos, ActorPos, FColor::Red, false, -1.0f, 0, 1.0f);
}
2. 로깅 시스템 구현
void LogInteractionData(const FString& InteractionType, const FVector& Position, const FVector& Force)
{
UE_LOG(LogNiagara, Verbose, TEXT("Interaction: %s, Pos: %s, Force: %s"),
*InteractionType, *Position.ToString(), *Force.ToString());
}
3. 파라미터 튜닝 인터페이스
- 런타임에 상호작용 파라미터를 조정할 수 있는 UI 구현
4. 성능 프로파일링
void ProfileInteractionPerformance()
{
SCOPE_CYCLE_COUNTER(STAT_NiagaraActorInteraction);
// 상호작용 코드
}
적용 예시 : 물리 기반 눈보라 효과
다음은 동적 캐릭터와 상호작용하는 물리 기반 눈보라 효과의 구현 예시입니다.
void UpdateSnowstormParticle(inout float3 Position, inout float3 Velocity, inout float Size,
inout float4 Color, float DeltaTime)
{
// 기본 눈보라 동작
float3 WindForce = float3(sin(Time * 0.1), cos(Time * 0.15), -0.1) * 100.0;
Velocity += WindForce * DeltaTime;
// 캐릭터와의 상호작용
float3 CharacterPosition, CharacterVelocity;
DynamicCharacter.GetData(CharacterPosition, CharacterVelocity);
float3 ToCharacter = CharacterPosition - Position;
float DistanceToCharacter = length(ToCharacter);
// 캐릭터 주변 눈 회오리 효과
if (DistanceToCharacter < 200.0)
{
float3 TangentialForce = cross(normalize(ToCharacter), float3(0,0,1)) * 150.0;
Velocity += TangentialForce * DeltaTime;
// 캐릭터 속도에 의한 영향
Velocity += CharacterVelocity * 0.5;
// 눈 입자 크기 및 색상 변화
Size *= 1.2;
Color.a *= 0.8;
}
// 지형 콜리전
float3 TerrainNormal;
float TerrainHeight = Terrain.SampleHeight(Position.xy, TerrainNormal);
if (Position.z < TerrainHeight)
{
Position.z = TerrainHeight + 0.1;
Velocity = reflect(Velocity, TerrainNormal) * 0.3;
Size *= 0.9;
}
// 최종 위치 업데이트
Position += Velocity * DeltaTime;
// 파티클 생존 영역 제한
if (abs(Position.x) > 1000.0 || abs(Position.y) > 1000.0 || Position.z > 500.0)
{
Position = float3(Random.x * 2000.0 - 1000.0, Random.y * 2000.0 - 1000.0, 500.0);
Velocity = float3(0, 0, -50.0);
Size = lerp(0.5, 2.0, Random.z);
Color = float4(0.9, 0.95, 1.0, 1.0);
}
}
이 예시에서는 눈보라 파티클이 바람의 영향을 받으면서 동시에 캐릭터와 상호작용합니다.
캐릭터 주변에서는 눈이 회오리치는 효과가 생기며, 캐릭터의 움직임에 따라 눈 입자들이 영향을 받습니다.
또한 지형과의 콜리전을 처리하고 파티클의 생존 영역을 제한하여 최적화를 고려하고 있습니다.
동적 액터와 파티클 시스템의 상호작용을 효과적으로 구현하면 더욱 생동감 있고 몰입도 높은 게임 환경을 만들 수 있습니다.