Loading...
Loading...
Use this skill when working with audio, sound, music, UAudioComponent, PlaySoundAtLocation, SoundCue, MetaSound, attenuation, submix, concurrency, SFX, or spatial audio in Unreal Engine. See references/audio-setup-patterns.md for music system and ambient soundscape architectures. For VFX audio synchronization, see ue-niagara-effects.
npx skill4agent add quodsoler/unreal-engine-skills ue-audio-system.agents/ue-project-context.mdUSoundBase // abstract base (SoundBase.h)
├── USoundWave // raw PCM/compressed audio asset
├── USoundCue // node-graph: random, modulator, mixer, attenuator nodes
└── UMetaSoundSource // procedural audio graph (MetaSound plugin)SoundClassObjectAttenuationSettingsUSoundNodeRandomUSoundNodeModulatorUSoundNodeMixerUSoundNodeAttenuationUSoundNodeLoopingUSoundNodeDelayUSoundNodeDistanceCrossFadeUAudioComponent::SetFloatParameterSetBoolParameterSetIntParameterUSoundWave::LoadingBehaviorESoundWaveLoadingBehavior::ForceInlineRetainOnLoadLoadOnDemand#include "Kismet/GameplayStatics.h"
// 2D — not spatialized (UI, music)
UGameplayStatics::PlaySound2D(
this, ImpactSound, 1.0f /*Vol*/, 1.0f /*Pitch*/, 0.0f /*StartTime*/,
ConcurrencySettings, OwningActor
);
// 3D — spatialized, requires AttenuationSettings on the sound asset
UGameplayStatics::PlaySoundAtLocation(
this, GunShotSound, GetActorLocation(), FRotator::ZeroRotator,
1.0f, 1.0f, 0.0f,
AttenuationOverride, // USoundAttenuation* (nullptr = use asset default)
ConcurrencyOverride, // USoundConcurrency* (nullptr = use asset default)
this // OwningActor for per-owner concurrency
);// Returns UAudioComponent* — auto-destroyed when sound finishes if bAutoDestroy=true
UAudioComponent* Comp = UGameplayStatics::SpawnSoundAtLocation(
this, ExplosionSound, Location, FRotator::ZeroRotator,
1.0f, 1.0f, 0.0f, AttenuationSettings, nullptr, /*bAutoDestroy=*/true
);
// Attach to a moving component (vehicle engine)
UAudioComponent* EngineAudio = UGameplayStatics::SpawnSoundAttached(
EngineLoopSound, GetMesh(), NAME_None,
FVector::ZeroVector, FRotator::ZeroRotator,
EAttachLocation::SnapToTargetIncludingScale,
/*bStopWhenAttachedToDestroyed=*/true,
1.0f, 1.0f, 0.0f, AttenuationSettings, nullptr,
/*bAutoDestroy=*/false // keep alive for looping
);// In constructor:
AudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("AudioComponent"));
AudioComponent->SetupAttachment(RootComponent);
AudioComponent->bAutoActivate = false;
AudioComponent->bStopWhenOwnerDestroyed = true;AudioComponent->SetSound(EngineLoopSound);
AudioComponent->Play(/*StartTime=*/0.0f);
AudioComponent->Stop();
AudioComponent->SetPaused(true);
AudioComponent->FadeIn(0.5f, 1.0f, 0.0f, EAudioFaderCurve::Linear);
AudioComponent->FadeOut(1.0f, 0.0f, EAudioFaderCurve::Linear);
AudioComponent->SetVolumeMultiplier(0.5f);
AudioComponent->SetPitchMultiplier(1.2f);
// Query play state (EAudioComponentPlayState: Playing, Stopped, Paused, FadingIn, FadingOut)
EAudioComponentPlayState State = AudioComponent->GetPlayState();// Declared in AudioComponent.h:
// DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAudioFinished)
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAudioPlaybackPercent, const USoundWave*, PlayingSoundWave, const float, PlaybackPercent)
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAudioPlayStateChanged, EAudioComponentPlayState, PlayState)
AudioComponent->OnAudioFinished.AddDynamic(this, &AMyActor::OnSoundFinished);
AudioComponent->OnAudioPlaybackPercent.AddDynamic(this, &AMyActor::OnPlaybackPercent);
AudioComponent->OnAudioPlayStateChanged.AddDynamic(this, &AMyActor::OnPlayStateChanged);
// Native (non-UObject) binding — no GC overhead:
// DECLARE_MULTICAST_DELEGATE_OneParam(FOnAudioFinishedNative, UAudioComponent*)
// DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnAudioPlaybackPercentNative, const UAudioComponent*, const USoundWave*, const float)
AudioComponent->OnAudioFinishedNative.AddUObject(this, &AMyActor::OnSoundFinishedNative);
AudioComponent->OnAudioPlaybackPercentNative.AddUObject(this, &AMyActor::OnPlaybackPercentNative);USoundAttenuationFSoundAttenuationSettingsUSoundBase::AttenuationSettingsEAttenuationShapeSphereCapsuleBoxConeEAttenuationDistanceModelLinearLogarithmicNaturalSoundInverseLogReverseCustom// Key FSoundAttenuationSettings fields:
uint8 bAttenuate : 1; // enable distance-based volume falloff
uint8 bSpatialize : 1; // enable 3D spatialization
// SpatializationAlgorithm: SPATIALIZATION_Default (panning), SPATIALIZATION_HRTF (binaural plugin)
uint8 bAttenuateWithLPF : 1; // air absorption (distance-based lowpass)
float LPFRadiusMin; // LPF starts at this distance (cm)
float LPFRadiusMax; // LPF fully applied at this distance
float LPFFrequencyAtMin; // Hz at min distance (e.g. 20000.f = bypass)
float LPFFrequencyAtMax; // Hz at max distance (e.g. 800.f = muffled)
uint8 bEnableListenerFocus : 1;
float FocusAzimuth; // in-focus cone half-angle (degrees)
float NonFocusAzimuth; // non-focus cone half-angle (degrees)
float FocusDistanceScale; // < 1.0 makes focused sound seem closer
float NonFocusVolumeAttenuation;
uint8 bEnableOcclusion : 1;
TEnumAsByte<ECollisionChannel> OcclusionTraceChannel; // e.g. ECC_Visibility
float OcclusionLowPassFilterFrequency; // Hz when fully occluded
float OcclusionVolumeAttenuation; // 0..1 volume scale when occluded
float OcclusionInterpolationTime; // seconds to interpolate
uint8 bEnableReverbSend : 1;
EReverbSendMethod ReverbSendMethod; // Linear, CustomCurve, Manual
float ReverbWetLevelMin;
float ReverbWetLevelMax;
float ReverbDistanceMin;
float ReverbDistanceMax;
uint8 bEnablePriorityAttenuation : 1; // reduce priority of distant sounds
float PriorityAttenuationMin;
float PriorityAttenuationMax;
// Override attenuation inline on a UAudioComponent:
AudioComponent->bOverrideAttenuation = true;
AudioComponent->AttenuationOverrides.bAttenuate = true;
AudioComponent->AttenuationOverrides.bSpatialize = true;
AudioComponent->AttenuationOverrides.FalloffDistance = 3000.f;bAttenuateWithLPF = trueLPFRadiusMinLPFRadiusMaxbEnableSendToAudioLink = falseUSoundBase::PriorityUSoundConcurrencyUSoundBase::ConcurrencySetTSet<USoundConcurrency*>ConcurrencyOverridesFSoundConcurrencySettingsbOverrideConcurrency = true// FSoundConcurrencySettings key fields:
int32 MaxCount; // max simultaneous voices in this group
// USoundBase::Priority (0.0–100.0, default 1.0) determines which voices survive at MaxCount
uint8 bLimitToOwner : 1; // limit per owning actor if true
TEnumAsByte<EMaxConcurrentResolutionRule::Type> ResolutionRule;
// PreventNew | StopOldest | StopFarthestThenPreventNew | StopFarthestThenOldest
// StopLowestPriority | StopQuietest | StopLowestPriorityThenPreventNew
float RetriggerTime; // minimum seconds between plays in this group
float VoiceStealReleaseTime; // fade duration (s) for evicted sounds
EConcurrencyVolumeScaleMode VolumeScaleMode; // Default | Distance | Priority
float VolumeScaleAttackTime;
float VolumeScaleReleaseTime;USoundSubmixBase (abstract)
USoundSubmixWithParentBase
USoundSubmix // standard submix with effects chain
USoundfieldSubmix // ambisonics / soundfield
UEndpointSubmix // external endpoint (haptics, extra device)bMuteWhenBackgrounded = true// Runtime volume control:
MusicSubmix->SetSubmixOutputVolume(this, 0.5f); // linear gain multiplier
MusicSubmix->SetSubmixWetLevel(this, 1.0f);
MusicSubmix->SetSubmixDryLevel(this, 0.0f);
// Dynamic parenting:
MyChildSubmix->DynamicConnect(this, MasterSubmix);
MyChildSubmix->DynamicDisconnect(this);USoundSubmix::SubmixEffectChainUSoundEffectSubmixPresetUSubmixEffectReverbPresetUSubmixEffectEQPreset// Add reverb to a submix at runtime
USoundSubmix* ReverbSubmix = LoadObject<USoundSubmix>(nullptr,
TEXT("/Game/Audio/Submixes/ReverbSubmix"));
USubmixEffectReverbPreset* Preset = NewObject<USubmixEffectReverbPreset>();
Preset->Settings.Density = 0.85f;
Preset->Settings.Diffusion = 0.8f;
Preset->Settings.DecayTime = 2.5f;
ReverbSubmix->SubmixEffectChain.Add(Preset);USoundBase::SoundClassObject// Sound Mix modifiers — layer temporary volume/pitch adjustments by sound class:
UGameplayStatics::SetBaseSoundMix(this, DefaultMix); // set the base mix (usually once)
UGameplayStatics::PushSoundMixModifier(this, CombatMix); // push a modifier (stacks)
// ... later, when leaving combat:
UGameplayStatics::PopSoundMixModifier(this, CombatMix); // remove the modifier layer
// ClearSoundMixModifiers removes all pushed modifiers at once
UGameplayStatics::ClearSoundMixModifiers(this);USoundMixUSoundClassUMetaSoundSourceUSoundBaseUSoundBase// UAudioComponent implements ISoundParameterControllerInterface:
MetaComp->SetFloatParameter(FName("Pitch"), 1.5f);
MetaComp->SetBoolParameter(FName("IsUnderwater"), true);
MetaComp->SetIntParameter(FName("SurfaceType"), 2);
MetaComp->SetTriggerParameter(FName("OnImpact")); // impulse trigger input
MetaComp->ResetParameters();| Criterion | SoundCue | MetaSound |
|---|---|---|
| Runtime parameters | Limited (wave params) | Typed inputs, full parameter system |
| Procedural audio | No | Yes (oscillators, noise, DSP filters) |
| Reactive to gameplay | Via parameter nodes | Native — bind any float/bool/trigger |
| Use when | Randomized pre-authored sounds | Adaptive music, procedural SFX, state-driven audio |
// Envelope following:
SFXSubmix->StartEnvelopeFollowing(this);
FOnSubmixEnvelopeBP Env;
Env.BindDynamic(this, &AMyActor::OnSubmixEnvelope);
SFXSubmix->AddEnvelopeFollowerDelegate(this, Env);
SFXSubmix->StopEnvelopeFollowing(this);
// Callback: void OnSubmixEnvelope(const TArray<float>& Envelope);
// Spectrum analysis:
SFXSubmix->StartSpectralAnalysis(this,
EFFTSize::Medium, EFFTPeakInterpolationMethod::Linear,
EFFTWindowType::Hann, 0.0f, EAudioSpectrumType::MagnitudeSpectrum
);
TArray<FSoundSubmixSpectralAnalysisBandSettings> Bands;
FSoundSubmixSpectralAnalysisBandSettings LowBand;
LowBand.BandFrequency = 80.f; LowBand.AttackTimeMsec = 10.f; LowBand.ReleaseTimeMsec = 100.f;
Bands.Add(LowBand);
FOnSubmixSpectralAnalysisBP Spectral;
Spectral.BindDynamic(this, &AMyActor::OnSpectralAnalysis);
SFXSubmix->AddSpectralAnalysisDelegate(
this, Bands, Spectral,
30.f /*UpdateRateHz*/, -40.f /*DecibelFloor*/, true /*Normalize*/, false /*AutoRange*/
);
SFXSubmix->StopSpectralAnalysis(this);
// Callback: void OnSpectralAnalysis(const TArray<float>& Magnitudes);// In .Build.cs:
PublicDependencyModuleNames.AddRange(new string[] { "Engine", "AudioMixer" });
PublicDependencyModuleNames.Add("MetasoundEngine"); // for MetaSound parameters
PublicDependencyModuleNames.Add("MetasoundFrontend");#include "Components/AudioComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Sound/SoundBase.h"
#include "Sound/SoundCue.h"
#include "Sound/SoundWave.h"
#include "Sound/SoundAttenuation.h"
#include "Sound/SoundConcurrency.h"
#include "Sound/SoundSubmix.h"USoundSubmixUAudioAnalyzerNRTULoudnessNRTUOnsetNRTUAudioSynesthesiaNRTUAudioAnalyzerNRT// Build.cs: "AudioSynesthesia"
#include "AudioSynesthesiaModule.h"
// Create an analyzer — LoudnessNRT analyzes an entire sound asset offline
ULoudnessNRT* Analyzer = NewObject<ULoudnessNRT>();
ULoudnessNRTSettings* Settings = NewObject<ULoudnessNRTSettings>();
Settings->AnalysisPeriod = 0.01f; // 10ms windows
Analyzer->Settings = Settings;
Analyzer->Sound = MySoundWave;
Analyzer->AnalyzeAudio(); // WITH_EDITOR only; call from editor utility or cook-time code
// Query results at specific timestamps
float Loudness;
Analyzer->GetLoudnessAtTime(1.5f, Loudness);
// For beat detection, use OnsetNRT:
UOnsetNRT* OnsetAnalyzer = NewObject<UOnsetNRT>();
OnsetAnalyzer->Sound = MySoundWave;
OnsetAnalyzer->AnalyzeAudio(); // WITH_EDITOR only
TArray<float> OnsetTimestamps;
TArray<float> OnsetStrengths;
OnsetAnalyzer->GetNormalizedChannelOnsetsBetweenTimes(
0.f, Duration, 0, OnsetTimestamps, OnsetStrengths);// WRONG — leaks a component instance per call
UAudioComponent* Comp = NewObject<UAudioComponent>(this);
Comp->SetSound(FireSound); Comp->Play();
// CORRECT — fire-and-forget for one-shots
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
// CORRECT — cache a single component for repeated/looping sounds
if (!CachedFireAudio) { CachedFireAudio = UGameplayStatics::SpawnSoundAttached(..., /*bAutoDestroy=*/false); }
CachedFireAudio->Play();// WRONG — full volume at any distance
UGameplayStatics::PlaySoundAtLocation(this, FootstepSound, Location);
// CORRECT — pass or assign USoundAttenuation with sphere falloff tuned to gameplay scale// WRONG — 20 simultaneous explosions with default concurrency
UGameplayStatics::PlaySoundAtLocation(this, ExplosionSound, Location);
// CORRECT — pass USoundConcurrency: MaxCount=4, ResolutionRule=StopFarthestThenOldest// WRONG — FAudioDevice not initialized on server, causes crash/log spam
void AWeapon::Fire() { UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation()); }
// CORRECT
void AWeapon::Fire() {
if (GetNetMode() != NM_DedicatedServer) {
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}
}// In EndPlay:
if (AudioComponent) { AudioComponent->OnAudioFinished.RemoveAll(this); AudioComponent->Stop(); }// WRONG — no attenuation or spatialization
UGameplayStatics::PlaySound2D(this, GunShot);
// CORRECT
UGameplayStatics::PlaySoundAtLocation(this, GunShot, GetActorLocation());GetNetMode() != NM_DedicatedServerLoadingBehavior = LoadOnDemandbMuteWhenBackgrounded = true// In your GameInstance or AudioManager
FCoreDelegates::ApplicationWillDeactivateDelegate.AddUObject(
this, &UMyAudioManager::OnAppDeactivate);
void UMyAudioManager::OnAppDeactivate()
{
FAudioDeviceHandle Device = GEngine->GetMainAudioDevice();
if (Device.IsValid()) { Device->SetTransientPrimaryVolume(0.f); }
}bSpatialize = trueSpatializationAlgorithm = SPATIALIZATION_HRTFbEnablePriorityAttenuationue-actor-component-architectureue-niagara-effectsue-cpp-foundations