TIL_046: 파티클, 사운드 효과

김펭귄·2025년 10월 15일

Today What I Learned (TIL)

목록 보기
46/139

오늘 학습 키워드

  • 파티클, 사운드 효과

  • 아이템 획득 시, 파티클과 함께 소리가 재생되도록 구현

1. Particle System

  • 수많은 작은 입자(파티클)를 빠르게 생성하고 움직이며, 각 파티클의 위치, 크기, 색깔, 수명 등을 조절해주는 시스템

  • 파티클 효과의 설계도 역할을 하는 Asset

  1. Cascade : 전통적인 파티클 시스템으로, 간단한 효과에 적합
  2. Niagara : 최신 시스템으로, 더 많은 기능과 유연성을 제공

Particle System Component

  • 게임 내에서 실질적으로 파티클을 재생하거나 제어하는 컴포넌트

  • 월드에 배치되어 ParticleSystem(템플릿)을 참조해 파티클을 실제로 보여주는 역할

  • 파티클을 켜거나 끄거나 위치를 조절하는 행동도 여기서 수행

2. SoundBase

  • 언리얼 엔진의 모든 사운드 관련 기본 클래스

  • SoundWave, SoundCue 둘 다 이 클래스를 상속받아 사용

    1. SoundWave : 간단한 단일 사운드
    2. SoundCue : 좀 더 처리가 들어간 복잡한 사운드. 동적 처리 가능

3. 구현부

  • 아이템의 부모클래스인 BaseItem클래스에 파티클, 사운드를 넣어줌

헤더

// BaseItem.h
UCLASS()
class SPARTPROJECT_API ABaseItem : public AActor, public IItemInterface
{
	// ... //
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Effects")
	UParticleSystem* PickupParticle;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Effects")
	USoundBase* PickupSound;
};

cpp

// BaseItem.cpp
void ABaseItem::ActivateItem(AActor* Activator) 
{
	UParticleSystemComponent* Particle = nullptr;
	if (PickupParticle)	{
		Particle = UGameplayStatics::SpawnEmitterAtLocation(
			GetWorld(),	// 파티클 생성할 월드
			PickupParticle,
			GetActorLocation(),
			GetActorRotation(),
			false	// 파티클 효과 종료 후 자동으로 메모리에서 제거 할지 말지
		);
	}
	
    // Particle 직접 제거
	if (Particle) {
		FTimerHandle DestroyParticleTimerHandle;
		GetWorldTimerManager().SetTimer(
			DestroyParticleTimerHandle,
			[Particle]() { Particle->DestroyComponent(); },
			1.0f,
			false
		);
	}
    
    // 사운드 출력
	UAudioComponent* Audio = nullptr;
	if (PickupSound) {
		Audio = UGameplayStatics::SpawnSoundAtLocation(
			GetWorld(),
			PickupSound,
			GetActorLocation()
		);
	}
	
    // 사운드 직접 제거
	if (Audio) {
		FTimerHandle DestroyAudioTimerHandle;
		GetWorldTimerManager().SetTimer(
			DestroyAudioTimerHandle,
			[Audio]() 
            { 
            	Audio->Stop();
            	Audio->DestroyComponent(); 
            },
			1.0f,
			false		
		);
	}
}
  • SpawnEmitterAtLocation : 파티클 생성 함수

    • GetWorld 매개인자 : 파티클 생성할 월드
    • false 매개인자 : 파티클 효과 종료 후 자동으로 메모리에서 제거 할지 말지
      loop 파티클의 경우 종료가 애초에 되지 않으므로, false로 하고 직접 파티클 제거
  • 타이머 핸들러 이용하여 Particle과 Sound를 1초후에 제거

  • 람다함수 통해, 1초 후에 실행될 일회성 콜백 함수를 간결하게 작성, 실행

  • Sound는 Stop 먼저 해주고 제거해야, 소리도 제거됨

  • 지뢰의 경우, 접촉 시뿐만 아니라 폭발시에도 파티클/사운드 존재

  • 따라서, 지뢰에만 추가적으로 폭발 시 사용할 파티클과 사운드 및 동일한 로직 추가

4. 액세스 위반 에러

  • 레벨 시간이 1초 이내로 남았을 때, 아이템을 획득하면 액세스 위반 에러가 발생

  • 이유로는, 레벨이 전환되면서 기존에 생성된 모든 인스턴스는 제거되었는데, 파티클과 사운드를 제거하기 위한 핸들러는 남아있었음

  • 따라서 타이머 1초가 지나고, 이미 사라진 파티클과 사운드에 접근하려해서 에러 발생

해결법

  • 파티클과 사운드 포인터를 TWeakObjectPtr<UParticleSystemComponent>TWeakObjectPtr<UAudioComponent>로 선언

  • IsValid를 이용해 안전하게 체크

TWeakObjectPtr<UParticleSystemComponent> Particle;
// Particle 생성 로직

if (Particle.IsValid())
{
	FTimerHandle DestroyParticleTimerHandle;
	GetWorldTimerManager().SetTimer(
		DestroyParticleTimerHandle,
		[Particle]() 
		{ 
			if(Particle.IsValid())	// Particle 존재할 때만
				Particle->DestroyComponent();
		},
		1.0f,
		false
	);
}

TWeakObjectPtr<UAudioComponent> Audio;
// 사운드 생성 로직

if (Audio.IsValid())
{
	FTimerHandle DestroyAudioTimerHandle;
	GetWorldTimerManager().SetTimer(
		DestroyAudioTimerHandle,
		[Audio]()
		{
			if (Audio.IsValid())
			{
				Audio->Stop();
				Audio->DestroyComponent();
			}
		},
		1.0f,
		false		
	);
}

5. 결과 영상

  • 레벨 시간이 1초 이내였을 때, 아이템을 먹어도 액세스위반 에러가 발생하지 않음
profile
반갑습니다

0개의 댓글