Add weather audio cues

This commit is contained in:
2026-05-16 01:58:05 -07:00
parent dddac97658
commit 08a1df6ebe
7 changed files with 324 additions and 4 deletions
@@ -0,0 +1,149 @@
// Copyright Pacificao. All Rights Reserved.
#include "AgrarianWeatherAudioController.h"
#include "AgrarianGameState.h"
#include "Components/AudioComponent.h"
#include "Components/SceneComponent.h"
#include "Engine/World.h"
AAgrarianWeatherAudioController::AAgrarianWeatherAudioController()
{
PrimaryActorTick.bCanEverTick = true;
bReplicates = false;
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
RootComponent = SceneRoot;
AmbientAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("AmbientAudio"));
AmbientAudio->SetupAttachment(SceneRoot);
AmbientAudio->bAutoActivate = false;
RainAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("RainAudio"));
RainAudio->SetupAttachment(SceneRoot);
RainAudio->bAutoActivate = false;
WindAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("WindAudio"));
WindAudio->SetupAttachment(SceneRoot);
WindAudio->bAutoActivate = false;
StormAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("StormAudio"));
StormAudio->SetupAttachment(SceneRoot);
StormAudio->bAutoActivate = false;
}
void AAgrarianWeatherAudioController::BeginPlay()
{
Super::BeginPlay();
AssignConfiguredSounds();
RefreshWeatherAudio(0.0f);
}
void AAgrarianWeatherAudioController::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
RefreshWeatherAudio(DeltaSeconds);
}
void AAgrarianWeatherAudioController::RefreshWeatherAudio(float DeltaSeconds)
{
const UWorld* World = GetWorld();
const AAgrarianGameState* GameState = World ? World->GetGameState<AAgrarianGameState>() : nullptr;
if (!GameState)
{
return;
}
CurrentWeather = GameState->Weather;
const float WindAlpha = GetProviderWindAlpha(GameState->ActiveWeatherInputs.WindSpeedKmh, GameState->ActiveWeatherInputs.bHasProviderData);
float TargetRainVolume = 0.0f;
float TargetWindVolume = WindAlpha * MaxWindVolume;
float TargetStormVolume = 0.0f;
switch (GameState->Weather)
{
case EAgrarianWeatherType::Rain:
TargetRainVolume = MaxRainVolume;
TargetWindVolume = FMath::Max(TargetWindVolume, MaxWindVolume * 0.25f);
break;
case EAgrarianWeatherType::ColdWind:
TargetWindVolume = FMath::Max(TargetWindVolume, MaxWindVolume * 0.7f);
break;
case EAgrarianWeatherType::Storm:
TargetRainVolume = MaxRainVolume;
TargetWindVolume = MaxWindVolume;
TargetStormVolume = MaxStormVolume;
break;
default:
break;
}
const float TargetAmbientVolume = GameState->IsNight() ? AmbientNightVolume : AmbientDayVolume;
const float InterpSpeed = FMath::Max(0.1f, VolumeInterpSpeed);
CurrentAmbientVolume = FMath::FInterpTo(CurrentAmbientVolume, TargetAmbientVolume, DeltaSeconds, InterpSpeed);
CurrentRainVolume = FMath::FInterpTo(CurrentRainVolume, TargetRainVolume, DeltaSeconds, InterpSpeed);
CurrentWindVolume = FMath::FInterpTo(CurrentWindVolume, TargetWindVolume, DeltaSeconds, InterpSpeed);
CurrentStormVolume = FMath::FInterpTo(CurrentStormVolume, TargetStormVolume, DeltaSeconds, InterpSpeed);
if (DeltaSeconds <= 0.0f)
{
CurrentAmbientVolume = TargetAmbientVolume;
CurrentRainVolume = TargetRainVolume;
CurrentWindVolume = TargetWindVolume;
CurrentStormVolume = TargetStormVolume;
}
ApplyComponentVolume(AmbientAudio, CurrentAmbientVolume);
ApplyComponentVolume(RainAudio, CurrentRainVolume);
ApplyComponentVolume(WindAudio, CurrentWindVolume);
ApplyComponentVolume(StormAudio, CurrentStormVolume);
}
void AAgrarianWeatherAudioController::AssignConfiguredSounds()
{
if (AmbientAudio && ClearAmbientSound)
{
AmbientAudio->SetSound(ClearAmbientSound);
}
if (RainAudio && RainLoopSound)
{
RainAudio->SetSound(RainLoopSound);
}
if (WindAudio && WindLoopSound)
{
WindAudio->SetSound(WindLoopSound);
}
if (StormAudio && StormLoopSound)
{
StormAudio->SetSound(StormLoopSound);
}
}
void AAgrarianWeatherAudioController::ApplyComponentVolume(UAudioComponent* AudioComponent, float Volume) const
{
if (!AudioComponent)
{
return;
}
const float SafeVolume = FMath::Clamp(Volume, 0.0f, 1.0f);
AudioComponent->SetVolumeMultiplier(SafeVolume);
if (AudioComponent->Sound && SafeVolume > 0.01f && !AudioComponent->IsPlaying())
{
AudioComponent->Play();
}
else if (SafeVolume <= 0.01f && AudioComponent->IsPlaying())
{
AudioComponent->Stop();
}
}
float AAgrarianWeatherAudioController::GetProviderWindAlpha(float WindSpeedKmh, bool bHasProviderData) const
{
if (!bHasProviderData)
{
return 0.0f;
}
return FMath::Clamp(WindSpeedKmh / 55.0f, 0.0f, 1.0f);
}
@@ -0,0 +1,92 @@
// Copyright Pacificao. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AgrarianTypes.h"
#include "AgrarianWeatherAudioController.generated.h"
class UAudioComponent;
class USceneComponent;
class USoundBase;
UCLASS(Blueprintable)
class AAgrarianWeatherAudioController : public AActor
{
GENERATED_BODY()
public:
AAgrarianWeatherAudioController();
virtual void BeginPlay() override;
virtual void Tick(float DeltaSeconds) override;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
TObjectPtr<USceneComponent> SceneRoot;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
TObjectPtr<UAudioComponent> AmbientAudio;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
TObjectPtr<UAudioComponent> RainAudio;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
TObjectPtr<UAudioComponent> WindAudio;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
TObjectPtr<UAudioComponent> StormAudio;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio")
TObjectPtr<USoundBase> ClearAmbientSound;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio")
TObjectPtr<USoundBase> RainLoopSound;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio")
TObjectPtr<USoundBase> WindLoopSound;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio")
TObjectPtr<USoundBase> StormLoopSound;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio", meta = (ClampMin = "0.1"))
float VolumeInterpSpeed = 1.5f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float AmbientDayVolume = 0.35f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float AmbientNightVolume = 0.22f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float MaxRainVolume = 0.8f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float MaxWindVolume = 0.7f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float MaxStormVolume = 0.9f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
EAgrarianWeatherType CurrentWeather = EAgrarianWeatherType::Clear;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
float CurrentAmbientVolume = 0.0f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
float CurrentRainVolume = 0.0f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
float CurrentWindVolume = 0.0f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Audio")
float CurrentStormVolume = 0.0f;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather Audio")
void RefreshWeatherAudio(float DeltaSeconds);
protected:
void AssignConfiguredSounds();
void ApplyComponentVolume(UAudioComponent* AudioComponent, float Volume) const;
float GetProviderWindAlpha(float WindSpeedKmh, bool bHasProviderData) const;
};