Grid Placement System
Library Components
SoundManager

SoundManager

The SoundManager is a specialized library component that handles all audio feedback for the Grid Placement System. It provides contextual sound effects for snapping, placement, and rotation actions while managing volume controls and preventing audio spam through intelligent throttling systems.

Overview

SoundManager enhances the placement experience with audio feedback that provides immediate confirmation of user actions. It manages three distinct sound types with individual volume controls and includes smart throttling to prevent audio overlap and spam during rapid interactions.

Key Features

  • Contextual Audio Feedback - Different sounds for snap, place, and rotate actions
  • Volume Management - Master volume control with per-action scaling
  • Spam Prevention - Intelligent throttling prevents audio overload
  • Spatial Audio Support - 3D positioned audio for placement sounds
  • Global Enable/Disable - Easy audio system toggle
  • Performance Optimized - Minimal overhead with efficient sound management

Core Methods

Initialization

Initialize

void Initialize(UWorld* World);

Initializes the SoundManager with a world context for audio playback.

Parameters:

  • World - The world context for sound playback

Audio Playback

PlaySnapSound

void PlaySnapSound();

Plays the snap sound when objects snap to grid positions.

Features:

  • Automatic throttling with 50ms cooldown to prevent spam
  • Volume: 40% of master volume (configurable)
  • 2D audio (no spatial positioning)

Usage:

// Called during grid snapping
if (!FinalPosition.Equals(LastSnappedPosition, 5.0f))
{
    SoundManager->PlaySnapSound();
    LastSnappedPosition = FinalPosition;
}

PlayPlaceSound

void PlayPlaceSound(const FVector& Location = FVector::ZeroVector);

Plays the placement sound when objects are successfully placed.

Parameters:

  • Location - World position for spatial audio (optional)

Features:

  • Volume: 80% of master volume
  • Spatial audio when location provided, 2D when location is zero
  • No throttling (placement is typically a single action)

Usage:

// 3D positioned audio
SoundManager->PlayPlaceSound(PlacedObjectLocation);
 
// 2D audio fallback
SoundManager->PlayPlaceSound(); // Uses FVector::ZeroVector

PlayRotateSound

void PlayRotateSound();

Plays the rotation sound when objects are rotated during placement.

Features:

  • Volume: 60% of master volume
  • 2D audio (no spatial positioning)
  • No throttling (rotation actions are discrete)

Usage:

// Called when object is rotated
if (ObjectRotationChanged)
{
    SoundManager->PlayRotateSound();
}

Configuration

Settings Methods

SetMasterVolume

void SetMasterVolume(float Volume);

Sets the master volume multiplier for all placement sounds.

Parameters:

  • Volume - Volume multiplier (0.0 to 1.0, clamped automatically)

SetSoundsEnabled

void SetSoundsEnabled(bool bEnabled);

Enables or disables all placement audio feedback.

Parameters:

  • bEnabled - Whether to play placement sounds

SetSnapSound

void SetSnapSound(USoundBase* Sound);

Sets the sound asset for grid snapping feedback.

SetPlaceSound

void SetPlaceSound(USoundBase* Sound);

Sets the sound asset for object placement feedback.

SetRotateSound

void SetRotateSound(USoundBase* Sound);

Sets the sound asset for object rotation feedback.

Audio System Design

Volume Hierarchy

SoundManager uses a hierarchical volume system:

// Master volume acts as global multiplier
float EffectiveVolume = MasterVolume * ActionVolumeMultiplier;
 
// Volume multipliers by action:
// - Snap: 0.4f (40% - frequent action, should be subtle)
// - Place: 0.8f (80% - important action, should be clear)
// - Rotate: 0.6f (60% - moderate importance)

Throttling System

Snap sounds include intelligent throttling to prevent audio spam:

// Snap sound throttling in PlacementSystemComponent
if (!FinalPosition.Equals(LastSnappedPosition, 5.0f) && SoundManager)
{
    float CurrentTime = GetWorld()->GetTimeSeconds();
    if (CurrentTime - LastSnapSoundTime > 0.05f) // 50ms cooldown
    {
        SoundManager->PlaySnapSound();
        LastSnappedPosition = FinalPosition;
        LastSnapSoundTime = CurrentTime;
    }
}

Spatial Audio Logic

void PlayPlaceSound(const FVector& Location)
{
    if (bSoundsEnabled && PlaceSound && CachedWorld)
    {
        if (Location.IsZero())
        {
            // 2D audio fallback
            UGameplayStatics::PlaySound2D(CachedWorld, PlaceSound, MasterVolume * 0.8f);
        }
        else
        {
            // 3D positioned audio
            UGameplayStatics::PlaySoundAtLocation(CachedWorld, PlaceSound, Location, MasterVolume * 0.8f);
        }
    }
}

Integration with PlacementSystemComponent

Automatic Configuration

// In PlacementSystemComponent::ConfigureComponents()
if (SoundManager)
{
    SoundManager->SetSoundsEnabled(bSoundsEnabled);
    SoundManager->SetMasterVolume(MasterSoundVolume);
    SoundManager->SetSnapSound(SnapSound);
    SoundManager->SetPlaceSound(PlaceSound);
    SoundManager->SetRotateSound(RotateSound);
}

Audio Asset Auto-Loading

// In PlacementSystemComponent constructor
{
    const FSoftObjectPath SnapSoundPath(TEXT("/GridPlacementSystem/GPS_Core/Audio/SFX_GPS_Snap.SFX_GPS_Snap"));
    if (UObject* Obj = SnapSoundPath.TryLoad())
    {
        SnapSound = Cast<USoundBase>(Obj);
    }
 
    const FSoftObjectPath PlaceSoundPath(TEXT("/GridPlacementSystem/GPS_Core/Audio/SFX_GPS_Place.SFX_GPS_Place"));
    if (UObject* Obj = PlaceSoundPath.TryLoad())
    {
        PlaceSound = Cast<USoundBase>(Obj);
    }
 
    const FSoftObjectPath RotateSoundPath(TEXT("/GridPlacementSystem/GPS_Core/Audio/SFX_GPS_Rotate.SFX_GPS_Rotate"));
    if (UObject* Obj = RotateSoundPath.TryLoad())
    {
        RotateSound = Cast<USoundBase>(Obj);
    }
}

Event-Driven Audio

// Audio triggers throughout placement system
 
// Snap sound during position updates
if (SoundManager && PositionChanged)
{
    SoundManager->PlaySnapSound();
}
 
// Place sound during confirmation
if (SoundManager && PlacementSuccessful)
{
    SoundManager->PlayPlaceSound(PlacementLocation);
}
 
// Rotate sound during rotation
if (SoundManager && RotationChanged)
{
    SoundManager->PlayRotateSound();
}

Usage Examples

Basic Setup

// Initialize sound manager
SoundManager->Initialize(GetWorld());
SoundManager->SetSoundsEnabled(true);
SoundManager->SetMasterVolume(0.8f);
 
// Configure sound assets
SoundManager->SetSnapSound(SnapSoundAsset);
SoundManager->SetPlaceSound(PlaceSoundAsset);
SoundManager->SetRotateSound(RotateSoundAsset);

Custom Audio Feedback

// Custom placement with audio feedback
void PlaceObjectWithAudio(TSubclassOf<AActor> ObjectClass, const FVector& Location)
{
    // Spawn the object
    AActor* PlacedObject = GetWorld()->SpawnActor<AActor>(ObjectClass, Location, FRotator::ZeroRotator);
    
    if (PlacedObject)
    {
        // Success - play place sound at location
        SoundManager->PlayPlaceSound(Location);
        UE_LOG(LogTemp, Log, TEXT("Object placed with audio feedback"));
    }
    else
    {
        // Could play error sound if you have one
        UE_LOG(LogTemp, Warning, TEXT("Failed to place object"));
    }
}

Dynamic Volume Control

// Adjust volume based on game settings
void UpdateAudioSettings(float UIVolume, bool bAudioEnabled)
{
    SoundManager->SetSoundsEnabled(bAudioEnabled);
    
    // Convert UI slider (0-100) to volume (0.0-1.0)
    float NormalizedVolume = FMath::Clamp(UIVolume / 100.0f, 0.0f, 1.0f);
    SoundManager->SetMasterVolume(NormalizedVolume);
    
    UE_LOG(LogTemp, Log, TEXT("Audio settings updated: Enabled=%s, Volume=%.2f"), 
           bAudioEnabled ? TEXT("Yes") : TEXT("No"), NormalizedVolume);
}

Conditional Audio Playback

// Play sounds based on context
void PlayContextualFeedback(EPlacementAction Action, const FVector& Location = FVector::ZeroVector)
{
    // Check if audio should play
    if (!SoundManager || !bAudioEnabled) return;
    
    switch (Action)
    {
        case EPlacementAction::Snap:
            // Only play snap if enough time has passed
            if (CanPlaySnapSound())
            {
                SoundManager->PlaySnapSound();
                LastSnapTime = GetWorld()->GetTimeSeconds();
            }
            break;
            
        case EPlacementAction::Place:
            SoundManager->PlayPlaceSound(Location);
            break;
            
        case EPlacementAction::Rotate:
            SoundManager->PlayRotateSound();
            break;
    }
}

Audio Testing

// Test all placement sounds
void TestPlacementAudio()
{
    if (!SoundManager) return;
    
    UE_LOG(LogTemp, Log, TEXT("Testing placement audio system..."));
    
    // Test with delay between sounds
    FTimerHandle AudioTestTimer;
    
    // Test snap sound
    SoundManager->PlaySnapSound();
    
    // Test rotate sound after 0.5 seconds
    GetWorld()->GetTimerManager().SetTimer(AudioTestTimer, [this]()
    {
        SoundManager->PlayRotateSound();
    }, 0.5f, false);
    
    // Test place sound after 1.0 seconds
    GetWorld()->GetTimerManager().SetTimer(AudioTestTimer, [this]()
    {
        FVector TestLocation = GetActorLocation();
        SoundManager->PlayPlaceSound(TestLocation);
    }, 1.0f, false);
}

Performance Considerations

Efficient Audio Management

  1. Throttling: Prevents audio spam during rapid interactions
  2. Conditional Playback: Checks enable state before attempting playback
  3. Minimal Overhead: Simple boolean checks and volume calculations
  4. Asset Caching: Sound assets are loaded once and reused

Best Practices

// Cache frequently checked values
class UOptimizedSoundManager
{
    bool bCachedSoundsEnabled = true;
    float CachedMasterVolume = 1.0f;
    
public:
    void PlaySnapSoundOptimized()
    {
        // Use cached values to avoid property access
        if (bCachedSoundsEnabled && SnapSound && CachedWorld)
        {
            UGameplayStatics::PlaySound2D(CachedWorld, SnapSound, CachedMasterVolume * 0.4f);
        }
    }
    
    void UpdateCachedValues()
    {
        bCachedSoundsEnabled = bSoundsEnabled;
        CachedMasterVolume = MasterVolume;
    }
};

Memory Management

// Proper asset management
void LoadAudioAssets()
{
    // Use soft references to prevent always-loaded assets
    if (SnapSoundPath.IsValid())
    {
        SnapSound = SnapSoundPath.LoadSynchronous();
    }
    
    // Validate loaded assets
    if (!SnapSound)
    {
        UE_LOG(LogTemp, Warning, TEXT("Failed to load snap sound asset"));
    }
}

Troubleshooting

Common Issues

"No sounds playing"

  • Verify bSoundsEnabled is true
  • Check that MasterVolume is greater than 0
  • Ensure sound assets are properly loaded
  • Verify world context is valid

"Snap sound too frequent/annoying"

  • Increase throttling cooldown time
  • Reduce snap sound volume multiplier
  • Implement distance-based throttling

"Placement sounds not positioned correctly"

  • Verify 3D audio settings in project
  • Check that valid world positions are passed to PlayPlaceSound
  • Ensure audio listener is properly configured

"Performance issues with audio"

  • Verify throttling is working correctly
  • Check for memory leaks with sound assets
  • Consider reducing audio quality for placement sounds

Debug Commands

// Debug audio system state
void DebugSoundManager()
{
    if (!SoundManager) return;
    
    UE_LOG(LogTemp, Log, TEXT("=== SoundManager Debug ==="));
    UE_LOG(LogTemp, Log, TEXT("Sounds Enabled: %s"), bSoundsEnabled ? TEXT("Yes") : TEXT("No"));
    UE_LOG(LogTemp, Log, TEXT("Master Volume: %.2f"), MasterVolume);
    
    // Test sound asset availability
    UE_LOG(LogTemp, Log, TEXT("Snap Sound: %s"), SnapSound ? TEXT("Loaded") : TEXT("Missing"));
    UE_LOG(LogTemp, Log, TEXT("Place Sound: %s"), PlaceSound ? TEXT("Loaded") : TEXT("Missing"));
    UE_LOG(LogTemp, Log, TEXT("Rotate Sound: %s"), RotateSound ? TEXT("Loaded") : TEXT("Missing"));
    
    // Test world context
    UE_LOG(LogTemp, Log, TEXT("World Context: %s"), CachedWorld ? TEXT("Valid") : TEXT("Invalid"));
    
    // Test audio playback
    UE_LOG(LogTemp, Log, TEXT("Testing audio playback..."));
    SoundManager->PlaySnapSound();
}
 
// Test volume calculations
void TestVolumeCalculations()
{
    float TestMasterVolume = 0.8f;
    
    float SnapVolume = TestMasterVolume * 0.4f;      // = 0.32
    float PlaceVolume = TestMasterVolume * 0.8f;     // = 0.64
    float RotateVolume = TestMasterVolume * 0.6f;    // = 0.48
    
    UE_LOG(LogTemp, Log, TEXT("Volume Test - Master: %.2f, Snap: %.2f, Place: %.2f, Rotate: %.2f"),
           TestMasterVolume, SnapVolume, PlaceVolume, RotateVolume);
}

SoundManager provides comprehensive audio feedback that enhances the placement experience with intelligent throttling, spatial audio support, and flexible volume management while maintaining excellent performance.