diff --git a/.gitignore b/.gitignore index 9e4d22e..9664b5a 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,4 @@ Plugins/**/Intermediate/* DerivedDataCache/* enc_temp_folder/* +TODO.txt diff --git a/Content/G2I_Game/Audio/System/SCM_MainMix.uasset b/Content/G2I_Game/Audio/System/SCM_MainMix.uasset new file mode 100644 index 0000000..7ef8291 Binary files /dev/null and b/Content/G2I_Game/Audio/System/SCM_MainMix.uasset differ diff --git a/Content/G2I_Game/Audio/System/SC_Effects.uasset b/Content/G2I_Game/Audio/System/SC_Effects.uasset new file mode 100644 index 0000000..8bba5c8 Binary files /dev/null and b/Content/G2I_Game/Audio/System/SC_Effects.uasset differ diff --git a/Content/G2I_Game/Audio/System/SC_General.uasset b/Content/G2I_Game/Audio/System/SC_General.uasset new file mode 100644 index 0000000..7b5abf7 Binary files /dev/null and b/Content/G2I_Game/Audio/System/SC_General.uasset differ diff --git a/Content/G2I_Game/Audio/System/SC_Music.uasset b/Content/G2I_Game/Audio/System/SC_Music.uasset new file mode 100644 index 0000000..01ee697 Binary files /dev/null and b/Content/G2I_Game/Audio/System/SC_Music.uasset differ diff --git a/Content/G2I_Game/Audio/System/SC_Speech.uasset b/Content/G2I_Game/Audio/System/SC_Speech.uasset new file mode 100644 index 0000000..ff77da7 Binary files /dev/null and b/Content/G2I_Game/Audio/System/SC_Speech.uasset differ diff --git a/Source/G2I/G2I.Build.cs b/Source/G2I/G2I.Build.cs index ecf4561..bc6beca 100644 --- a/Source/G2I/G2I.Build.cs +++ b/Source/G2I/G2I.Build.cs @@ -50,7 +50,8 @@ public G2I(ReadOnlyTargetRules Target) : base(Target) "G2I/Public/UI/WidgetComponents", "G2I/Public/Components", "G2I/Public/Components/Camera", - "G2I/Public/Components/SteamGlove" + "G2I/Public/Components/SteamGlove", + "G2I/Public/Sound" }); PrivateIncludePaths.AddRange(new string[] { @@ -71,7 +72,8 @@ public G2I(ReadOnlyTargetRules Target) : base(Target) "G2I/Private/UI/WidgetComponents", "G2I/Private/Components", "G2I/Private/Components/Camera", - "G2I/Private/Components/SteamGlove" + "G2I/Private/Components/SteamGlove", + "G2I/Private/Sound" }); // Uncomment if you are using Slate UI diff --git a/Source/G2I/Private/Sound/G2IGameSoundManager.cpp b/Source/G2I/Private/Sound/G2IGameSoundManager.cpp new file mode 100644 index 0000000..44a9bea --- /dev/null +++ b/Source/G2I/Private/Sound/G2IGameSoundManager.cpp @@ -0,0 +1,576 @@ +#include "Sound/G2IGameSoundManager.h" +#define STARTSOUNDSTACKSIZE 20 +#include "G2I.h" +#include "Components/AudioComponent.h" +#include "Components/SceneComponent.h" +#include "Sound/SoundBase.h" +#include "Sound/SoundClass.h" +#include "Sound/SoundMix.h" +#include "Kismet/GameplayStatics.h" +#include "G2IGameInstance.h" + +static const FName OneTimeTag("PlayingOneTime"); + +TObjectPtr UG2IGameSoundManager::GetAudioById(const int32 SoundId) const +{ + if (SoundId < 0) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Wrong SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return nullptr; + } + + const TObjectPtr* FoundAudio = ActiveSounds.Find(SoundId); + if (FoundAudio) { + return *FoundAudio; + } + + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: SoundId %d not found"), *GetName(), *FString(__FUNCTION__), SoundId); + return nullptr; +} + +UG2IGameSoundManager* UG2IGameSoundManager::Get(const UObject* WorldContextObject) +{ + if (!GEngine) { + return nullptr; + } + + if (const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull)) + { + if (const UGameInstance* GameInstance = World->GetGameInstance()) + { + return GameInstance->GetSubsystem(); + } + } + + return nullptr; +} + +void UG2IGameSoundManager::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + + for (int32 i = 0; i < STARTSOUNDSTACKSIZE; ++i) + { + IdStack.Push(i); + } + CurrentNumberAvailable = STARTSOUNDSTACKSIZE; + + GameInstance = Cast(GetGameInstance()); + if (!ensure(GameInstance)) + { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't find %s"), *GetName(), *FString(__FUNCTION__), *UG2IGameInstance::StaticClass()->GetName()); + return; + } + + StartGameDelegateHandle = GameInstance->OnStartLevelInitDelegate.AddUObject(this, &ThisClass::InitializeInStartGame); +} + +void UG2IGameSoundManager::InitializeInStartGame() +{ + if (!ensure(GameInstance)) + { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't find %s"), *GetName(), *FString(__FUNCTION__), *UG2IGameInstance::StaticClass()->GetName()); + return; + } + GameInstance->OnStartLevelInitDelegate.Remove(StartGameDelegateHandle); + + InitializeInStartLevel(); + + GameInstance->OnCloseLevelDelegate.AddUObject(this, &ThisClass::CloseLevelSound); + GameInstance->OnStartLevelInitDelegate.AddUObject(this, &ThisClass::InitializeInStartLevel); +} + +void UG2IGameSoundManager::InitializeInStartLevel() +{ + UWorld* NewWorld = GetWorld(); + + if (MainSoundMix && NewWorld) + { + UGameplayStatics::PushSoundMixModifier(NewWorld, MainSoundMix); + + for (const auto& Iterator : GlobalSoundMultipliers) + { + if (USoundClass* TargetClass = SoundClasses.FindRef(Iterator.Key)) + { + UGameplayStatics::SetSoundMixClassOverride( + NewWorld, + MainSoundMix, + TargetClass, + Iterator.Value, + 1.0f, + 0.0f, + true + ); + } + } + } +} + +void UG2IGameSoundManager::CloseLevelSound() +{ + ActiveSounds.Empty(); + IdStack.Empty(); + CurrentNumberAvailable = STARTSOUNDSTACKSIZE; + + for (int32 i = 0; i < CurrentNumberAvailable; ++i) { + IdStack.Push(i); + } +} + +void UG2IGameSoundManager::Deinitialize() +{ + StopAllSounds(); + ActiveSounds.Empty(); + IdStack.Empty(); + + if (GameInstance) + { + GameInstance->OnStartLevelInitDelegate.RemoveAll(this); + GameInstance->OnCloseLevelDelegate.RemoveAll(this); + } + + Super::Deinitialize(); +} + +int32 UG2IGameSoundManager::AddSound(const FSoundConfig& NewSoundConfig) +{ + if (!ensure(NewSoundConfig.Sound)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager didn't get sound"), *GetName(), *FString(__FUNCTION__)); + return -1; + } + + const UWorld* World = GetWorld(); + if (!ensure(World)) + { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: World is null"), *GetName(), *FString(__FUNCTION__)); + return -1; + } + + AWorldSettings* WorldSettings = World->GetWorldSettings(); + if (!ensure(WorldSettings)) + { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Can't get WorldSettings"), *GetName(), *FString(__FUNCTION__)); + return -1; + } + + UAudioComponent* AudioComponent = NewObject(WorldSettings); + if (!ensure(AudioComponent)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager can't create an audio component"), *GetName(), *FString(__FUNCTION__)); + return -1; + } + + AudioComponent->RegisterComponent(); + + AudioComponent->SetSound(NewSoundConfig.Sound.Get()); + + if (!NewSoundConfig.bIs2D) + { + AudioComponent->SetWorldLocation(NewSoundConfig.WorldLocation); + + if (NewSoundConfig.ResolvedAttachComponent) { + const FAttachmentTransformRules Rules( + NewSoundConfig.AttachmentRules, + NewSoundConfig.AttachmentRules, + NewSoundConfig.AttachmentRules, + false); + if (!NewSoundConfig.ResolvedAttachComponent->IsRegistered()) { + NewSoundConfig.ResolvedAttachComponent->RegisterComponent(); + } + AudioComponent->AttachToComponent(NewSoundConfig.ResolvedAttachComponent, Rules); + } + } + else + { + AudioComponent->bAllowSpatialization = false; + } + + AudioComponent->SetVolumeMultiplier(NewSoundConfig.VolumeMultiplier); + AudioComponent->SetPitchMultiplier(NewSoundConfig.PitchMultiplier); + + const bool bAssetIsLooping = NewSoundConfig.Sound->IsLooping(); + + AudioComponent->bAutoDestroy = bAssetIsLooping ? false : NewSoundConfig.bAutoDestroy; + + if (IdStack.IsEmpty()) { + UpdateStackSize(); + } + + const int32 NewSoundId = IdStack.Pop(); + ActiveSounds.Add(NewSoundId, AudioComponent); + AudioComponent->OnAudioFinishedNative.AddUObject(this, &UG2IGameSoundManager::OnSoundFinished, NewSoundId); + + return NewSoundId; +} + +bool UG2IGameSoundManager::PlaySound(const int32 SoundId, const float FadeInTime) +{ + UAudioComponent* Component = GetAudioById(SoundId); + if (!ensure(Component)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager can't get an audio component for ID: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; + } + + Component->FadeIn(FadeInTime, Component->VolumeMultiplier); + + return true; +} + +bool UG2IGameSoundManager::StopSound(const int32 SoundId, const float FadeOutTime) +{ + UAudioComponent* Component = GetAudioById(SoundId); + if (!ensure(Component)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager can't get an audio component for ID: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; + } + + Component->FadeOut(FadeOutTime, 0.0f); + + return true; +} + +void UG2IGameSoundManager::StopAllSounds() +{ + for (const auto& Pair : ActiveSounds) + { + if (ensure(Pair.Value)) + { + Pair.Value->Stop(); + } + } +} + +bool UG2IGameSoundManager::SetSoundVolume(const int32 SoundId, float NewVolume) +{ + UAudioComponent* Component = GetAudioById(SoundId); + if (!ensure(Component)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager can't get an audio component for ID: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; + } + + NewVolume = FMath::Clamp(NewVolume, 0.0f, 1.0f); + Component->SetVolumeMultiplier(NewVolume); + + return true; +} + +bool UG2IGameSoundManager::SetSoundPitch(const int32 SoundId, float NewPitch) +{ + UAudioComponent* Component = GetAudioById(SoundId); + if (!ensure(Component)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager can't get an audio component for ID: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; + } + + if (NewPitch <= 0.0f) { + NewPitch = 0.0f; + } + Component->SetPitchMultiplier(NewPitch); + + return true; +} + +bool UG2IGameSoundManager::SetSoundLocation(const int32 SoundId, const FVector& NewLocation) +{ + UAudioComponent* Component = GetAudioById(SoundId); + if (!ensure(Component)) { + return false; + } + + Component->SetWorldLocation(NewLocation); + + return true; +} + +bool UG2IGameSoundManager::SetSoundAttachment(const int32 SoundId, + USceneComponent* NewAttachmentComponent, + const EAttachmentRule AttachmentRules) +{ + if (!ensure(NewAttachmentComponent)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager got wrong component to attach"), *GetName(), *FString(__FUNCTION__)); + return false; + } + + const TObjectPtr Component = GetAudioById(SoundId); + if (!ensure(Component)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager can't get an audio component for ID: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; + } + + const FAttachmentTransformRules Rules( + AttachmentRules, + AttachmentRules, + AttachmentRules, + false); + + if (!NewAttachmentComponent->IsRegistered()) { + NewAttachmentComponent->RegisterComponent(); + } + Component->AttachToComponent(NewAttachmentComponent, Rules); + + return true; +} + +bool UG2IGameSoundManager::SetSoundAttachment(const int32 SoundId, + AActor* NewAttachmentActor, + const EAttachmentRule AttachmentRules) +{ + if (!ensure(NewAttachmentActor)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager got wrong actor to attach"), *GetName(), *FString(__FUNCTION__)); + return false; + } + + TObjectPtr RootComponent = NewAttachmentActor->GetRootComponent(); + if (!ensure(RootComponent)) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: The sound manager can't get root component from actor to attach"), *GetName(), *FString(__FUNCTION__)); + return false; + } + + const TObjectPtr Component = GetAudioById(SoundId); + if (!ensure(Component)) { + return false; + } + + const FAttachmentTransformRules Rules( + AttachmentRules, + AttachmentRules, + AttachmentRules, + false); + if (!RootComponent->IsRegistered()) { + RootComponent->RegisterComponent(); + } + Component->AttachToComponent(RootComponent, Rules); + + return true; +} + + +bool UG2IGameSoundManager::RemoveSound(const int32 SoundId) +{ + TObjectPtr Component = GetAudioById(SoundId); + if (!Component) { + return false; + } + + Component->OnAudioFinishedNative.RemoveAll(this); + + Component->Stop(); + ActiveSounds.Remove(SoundId); + + IdStack.Push(SoundId); + + return true; +} + +void UG2IGameSoundManager::RemoveAllSounds() +{ + for (auto Iterator = ActiveSounds.CreateIterator(); Iterator; ++Iterator) + { + if (Iterator.Value()) + { + Iterator.Value()->OnAudioFinishedNative.RemoveAll(this); + + Iterator.Value()->Stop(); + Iterator.RemoveCurrent(); + } + } + + ActiveSounds.Empty(); + IdStack.Empty(); + CurrentNumberAvailable = STARTSOUNDSTACKSIZE; + for (int32 i = 0; i < CurrentNumberAvailable; ++i) { + IdStack.Push(i); + } +} + +void UG2IGameSoundManager::UpdateStackSize() { + for (int32 i = CurrentNumberAvailable; i < CurrentNumberAvailable * 2; ++i) { + IdStack.Push(i); + } + CurrentNumberAvailable *= 2; +} + +void UG2IGameSoundManager::InitGlobalAudio(USoundMix* _MainMix, const TMap& _SoundClasses) +{ + MainSoundMix = _MainMix; + + SoundClasses.Empty(); + for (auto& Iterator : _SoundClasses) + { + SoundClasses.Add(Iterator.Key, Iterator.Value); + } + + GlobalSoundMultipliers.Empty(); + GlobalSoundMultipliers.Add(EG2ISoundType::SpeechSound, 1.0f); + GlobalSoundMultipliers.Add(EG2ISoundType::MusicSound, 1.0f); + GlobalSoundMultipliers.Add(EG2ISoundType::EffectSound, 1.0f); + GlobalSoundMultipliers.Add(EG2ISoundType::DefaultSound, 1.0f); + + UWorld* World = GetWorld(); + if (MainSoundMix && World) + { + UGameplayStatics::PushSoundMixModifier(World, MainSoundMix); + } + else { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Can't get the World or MainSoundMix"), *GetName(), *FString(__FUNCTION__)); + } +} + +void UG2IGameSoundManager::SetGlobalVolume(const EG2ISoundType SoundType, const float NewVolume) +{ + if (!MainSoundMix) + { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Can't get the MainSoundMix"), *GetName(), *FString(__FUNCTION__)); + return; + } + + if (!SoundClasses.Contains(SoundType)) + { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: SoundClasses map doesn't contain SoundType (ID: %d)"), *GetName(), *FString(__FUNCTION__), static_cast(SoundType)); + return; + } + + USoundClass* TargetClass = SoundClasses[SoundType]; + if (!TargetClass) + { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Can't get Sound class for the SoundType (ID: %d)"), *GetName(), *FString(__FUNCTION__), static_cast(SoundType)); + return; + } + + const float ClampedVolume = FMath::Clamp(NewVolume, 0.0f, 1.0f); + + GlobalSoundMultipliers[SoundType] = ClampedVolume; + + if (UWorld* World = GetWorld()) { + UGameplayStatics::SetSoundMixClassOverride( + World, + MainSoundMix, + TargetClass, + ClampedVolume, + 1.0f, + 0.0f, + true + ); + } + else { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Can't get the World"), *GetName(), *FString(__FUNCTION__)); + } +} + +float UG2IGameSoundManager::GetGlobalVolume(const EG2ISoundType SoundType) const { + return GlobalSoundMultipliers[SoundType]; +} + +void UG2IGameSoundManager::OnSoundFinished(UAudioComponent* AudioComponent, const int32 SoundId) +{ + if (!AudioComponent) { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Can't get AudioComponent when sound with ID %d finished"), *GetName(), *FString(__FUNCTION__), SoundId); + ActiveSounds.Remove(SoundId); + IdStack.Push(SoundId); + return; + } + + if (AudioComponent->bAutoDestroy) + { + ActiveSounds.Remove(SoundId); + IdStack.Push(SoundId); + } +} +float UG2IGameSoundManager::GetSoundVolume(const int32 SoundId) const +{ + if (const UAudioComponent* Component = GetAudioById(SoundId)) + { + return Component->VolumeMultiplier; + } + + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Invalid SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return 0.0f; +} + +float UG2IGameSoundManager::GetSoundPitch(const int32 SoundId) const +{ + if (const UAudioComponent* Component = GetAudioById(SoundId)) + { + return Component->PitchMultiplier; + } + + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Invalid SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return 1.0f; +} + +FVector UG2IGameSoundManager::GetSoundLocation(const int32 SoundId) const +{ + if (const UAudioComponent* Component = GetAudioById(SoundId)) + { + return Component->GetComponentLocation(); + } + + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Invalid SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return FVector::ZeroVector; +} + +TObjectPtr UG2IGameSoundManager::GetSoundAttachment(const int32 SoundId) const +{ + if (const UAudioComponent* Component = GetAudioById(SoundId)) + { + return Component->GetAttachParent(); + } + + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Invalid SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return nullptr; +} + +bool UG2IGameSoundManager::SetSoundAutoDestroy(const int32 SoundId, const bool bNewAutoDestroy) +{ + UAudioComponent* Component = GetAudioById(SoundId); + if (!Component) + { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Invalid SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; + } + Component->bAutoDestroy = bNewAutoDestroy; + + return true; +} + +bool UG2IGameSoundManager::GetSoundAutoDestroy(const int32 SoundId) const +{ + if (const UAudioComponent* Component = GetAudioById(SoundId)) + { + return Component->bAutoDestroy; + } + + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Invalid SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; +} + +bool UG2IGameSoundManager::GetSoundPlayingOneTime(const int32 SoundId) const +{ + if (const UAudioComponent* Component = GetAudioById(SoundId)) + { + return Component->ComponentHasTag(OneTimeTag); + } + + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Invalid SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; +} + +bool UG2IGameSoundManager::SetSoundPlayingOneTime(const int32 SoundId, const bool bNewIsPlayingOneTime) +{ + UAudioComponent* Component = GetAudioById(SoundId); + if (!Component) + { + UE_LOG(LogG2I, Warning, TEXT("[%s][%s]: Invalid SoundId: %d"), *GetName(), *FString(__FUNCTION__), SoundId); + return false; + } + + if (bNewIsPlayingOneTime) + { + Component->ComponentTags.AddUnique(OneTimeTag); + } + else + { + Component->ComponentTags.Remove(OneTimeTag); + + } + + return true; +} \ No newline at end of file diff --git a/Source/G2I/Private/Sound/G2ISoundComponent.cpp b/Source/G2I/Private/Sound/G2ISoundComponent.cpp new file mode 100644 index 0000000..5ab1e29 --- /dev/null +++ b/Source/G2I/Private/Sound/G2ISoundComponent.cpp @@ -0,0 +1,176 @@ +#include "Sound/G2ISoundComponent.h" +#include "G2I.h" + +void UG2ISoundComponent::BeginPlay() +{ + Super::BeginPlay(); + + const UWorld* World = GetWorld(); + if (!ensure(World)) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the World"), *GetName(), *FString(__FUNCTION__)); + return; + } + SoundManager = UG2IGameSoundManager::Get(World); + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + } +} + + +int32 UG2ISoundComponent::AddSound(const FSoundConfig& NewSoundConfig) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return -1; + } + + FSoundConfig ConfigToSend = NewSoundConfig; + AActor* Owner = GetOwner(); + if (!ensure(Owner)) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the owner of component"), *GetName(), *FString(__FUNCTION__)); + return -1; + } + + + + ConfigToSend.ResolvedAttachComponent = Cast(ConfigToSend.AttachToComponent.GetComponent(Owner)); + + if (!ConfigToSend.ResolvedAttachComponent && !ConfigToSend.bIs2D) + { + ConfigToSend.ResolvedAttachComponent = this; + } + + return SoundManager->AddSound(ConfigToSend); +} + +bool UG2ISoundComponent::PlaySound(const int32 SoundId) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + return SoundManager->PlaySound(SoundId); +} + +bool UG2ISoundComponent::StopSound(const int32 SoundId, const float FadeOutTime) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + return SoundManager->StopSound(SoundId, FadeOutTime); +} + +void UG2ISoundComponent::StopAllSounds() +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return; + } + SoundManager->StopAllSounds(); +} + +bool UG2ISoundComponent::SetSoundVolume(const int32 SoundId, const float NewVolume) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + return SoundManager->SetSoundVolume(SoundId, NewVolume); +} + +bool UG2ISoundComponent::SetSoundPitch(const int32 SoundId, const float NewPitch) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + return SoundManager->SetSoundPitch(SoundId, NewPitch); +} + +bool UG2ISoundComponent::SetSoundLocation(const int32 SoundId, const FVector& NewLocation) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + return SoundManager->SetSoundLocation(SoundId, NewLocation); +} + +bool UG2ISoundComponent::SetSoundAttachmentComponent(const int32 SoundId, + USceneComponent* NewAttachmentComponent, + const EAttachmentRule AttachmentRules) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + + return SoundManager->SetSoundAttachment(SoundId, NewAttachmentComponent, AttachmentRules); +} + +bool UG2ISoundComponent::SetSoundAttachmentActor(const int32 SoundId, + AActor* NewAttachmentActor, + const EAttachmentRule AttachmentRules) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + + return SoundManager->SetSoundAttachment(SoundId, NewAttachmentActor, AttachmentRules); +} + +bool UG2ISoundComponent::RemoveSound(const int32 SoundId) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + return SoundManager->RemoveSound(SoundId); +} + +bool UG2ISoundComponent::SetSoundAutoDestroy(const int32 SoundId, const bool bNewAutoDestroy) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + return SoundManager->SetSoundAutoDestroy(SoundId, bNewAutoDestroy); +} + +float UG2ISoundComponent::GetSoundVolume(const int32 SoundId) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return 0.0f; + } + return SoundManager->GetSoundVolume(SoundId); +} + +float UG2ISoundComponent::GetSoundPitch(const int32 SoundId) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return 1.0f; + } + return SoundManager->GetSoundPitch(SoundId); +} + +FVector UG2ISoundComponent::GetSoundLocation(const int32 SoundId) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return FVector::ZeroVector; + } + return SoundManager->GetSoundLocation(SoundId); +} + +bool UG2ISoundComponent::GetSoundAutoDestroy(const int32 SoundId) +{ + if (!SoundManager.IsValid()) { + UE_LOG(LogG2I, Error, TEXT("[%s][%s]: Couldn't get the Sound Manager"), *GetName(), *FString(__FUNCTION__)); + return false; + } + return SoundManager->GetSoundAutoDestroy(SoundId); +} \ No newline at end of file diff --git a/Source/G2I/Public/Sound/G2IGameSoundManager.h b/Source/G2I/Public/Sound/G2IGameSoundManager.h new file mode 100644 index 0000000..75fbb87 --- /dev/null +++ b/Source/G2I/Public/Sound/G2IGameSoundManager.h @@ -0,0 +1,140 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "Engine/EngineTypes.h" +#include "G2IGameSoundManager.generated.h" + +class USoundBase; +class USoundClass; +class USoundMix; +class USceneComponent; +class UAudioComponent; +class UG2IGameInstance; + +USTRUCT(BlueprintType) +struct FSoundConfig +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Sound") + TObjectPtr Sound; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Location", meta = (EditCondition = "!bIs2D", EditConditionHides)) + FVector WorldLocation = FVector::ZeroVector; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Attachment", meta = (EditCondition = "!bIs2D", EditConditionHides)) + FComponentReference AttachToComponent; + + UPROPERTY(VisibleAnywhere) + TObjectPtr ResolvedAttachComponent = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Attachment", meta = (EditCondition = "!bIs2D", EditConditionHides)) + EAttachmentRule AttachmentRules = EAttachmentRule::SnapToTarget; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Play", meta = (ClampMin = "0.0", ClampMax = "1.0")) + float VolumeMultiplier = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Play", meta = (ClampMin = "0.1", ClampMax = "2.0")) + float PitchMultiplier = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Play") + bool bIsPlayingOneTime = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Lifetime") + bool bAutoDestroy = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound|Location") + bool bIs2D = false; +}; + +UENUM(BlueprintType) +enum class EG2ISoundType: uint8 +{ + SpeechSound UMETA(DisplayName = "Sound for dialogs"), + MusicSound UMETA(DisplayName = "Sound for music"), + EffectSound UMETA(DisplayName = "Sound for effects"), + DefaultSound UMETA(DisplayName = "Default sound") +}; + +UCLASS() +class G2I_API UG2IGameSoundManager : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +private: + UPROPERTY() + TObjectPtr GameInstance; + + FDelegateHandle StartGameDelegateHandle; + + UPROPERTY() + TArray IdStack; + + int32 CurrentNumberAvailable; + + UPROPERTY() + TMap> ActiveSounds; + + void UpdateStackSize(); + UPROPERTY() + TMap GlobalSoundMultipliers; + + UPROPERTY() + TObjectPtr MainSoundMix; + + UPROPERTY() + TMap> SoundClasses; + +protected: + void InitializeInStartGame(); + void InitializeInStartLevel(); + void CloseLevelSound(); + + TObjectPtr GetAudioById(const int32 SoundId) const; + + void OnSoundFinished(UAudioComponent* AudioComp, const int32 SoundId); +public: + + UFUNCTION(BlueprintPure, Category = "Sound Manager", meta = (WorldContext = "WorldContextObject")) + static UG2IGameSoundManager* Get(const UObject* WorldContextObject); + + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + + int32 AddSound(const FSoundConfig& NewSoundConfig); + bool RemoveSound(const int32 SoundId); + void RemoveAllSounds(); + + bool PlaySound(const int32 SoundId, const float FadeInTime = 0.0f); + bool StopSound(const int32 SoundId, const float FadeOutTime = 0.0f); + void StopAllSounds(); + + bool SetSoundVolume(const int32 SoundId, const float NewVolume); + bool SetSoundPitch(const int32 SoundId, const float NewPitch); + bool SetSoundLocation(const int32 SoundId, const FVector& NewLocation); + bool SetSoundAttachment(const int32 SoundId, + USceneComponent* NewAttachemntComponent, + const EAttachmentRule AttachmentRules = EAttachmentRule::SnapToTarget); + bool SetSoundAttachment(const int32 SoundId, + AActor* NewAttachemntActor, + const EAttachmentRule AttachmentRules = EAttachmentRule::SnapToTarget); + bool SetSoundAutoDestroy(const int32 SoundId, const bool bNewAutoDestroy); + bool SetSoundPlayingOneTime(const int32 SoundId, const bool bNewIsPlayingOneTime); + + float GetSoundVolume(const int32 SoundId) const; + float GetSoundPitch(const int32 SoundId) const; + FVector GetSoundLocation(const int32 SoundId) const; + TObjectPtr GetSoundAttachment(const int32 SoundId) const; + bool GetSoundAutoDestroy(const int32 SoundId) const; + bool GetSoundPlayingOneTime(const int32 SoundId) const; + + UFUNCTION(BlueprintCallable, Category = "Sound|Global") + void InitGlobalAudio(USoundMix* _MainMix, const TMap& _SoundClasses); + + UFUNCTION(BlueprintCallable, Category = "Sound|Global") + void SetGlobalVolume(const EG2ISoundType SoundType, const float NewVolume); + + UFUNCTION(BlueprintPure, Category = "Sound|Global") + float GetGlobalVolume(const EG2ISoundType SoundType) const; +}; diff --git a/Source/G2I/Public/Sound/G2ISoundComponent.h b/Source/G2I/Public/Sound/G2ISoundComponent.h new file mode 100644 index 0000000..ee62910 --- /dev/null +++ b/Source/G2I/Public/Sound/G2ISoundComponent.h @@ -0,0 +1,77 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Sound/G2IGameSoundManager.h" +#include "G2ISoundComponent.generated.h" + +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +class G2I_API UG2ISoundComponent : public USceneComponent +{ + GENERATED_BODY() + +private: + UPROPERTY() + TWeakObjectPtr SoundManager; +protected: + virtual void BeginPlay() override; + +public: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Sounds") + TMap SetupSounds; + + UFUNCTION(BlueprintCallable, Category = "Sounds") + int32 AddSound(const FSoundConfig& NewSoundConfig); + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool RemoveSound(const int32 SoundId); + + + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool PlaySound(const int32 SoundId); + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool StopSound(const int32 SoundId, const float FadeOutTime = 0.0f); + + UFUNCTION(BlueprintCallable, Category = "Sounds") + void StopAllSounds(); + + + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool SetSoundVolume(const int32 SoundId, const float NewVolume); + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool SetSoundPitch(const int32 SoundId, const float NewPitch); + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool SetSoundLocation(const int32 SoundId, const FVector& NewLocation); + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool SetSoundAttachmentComponent(const int32 SoundId, + USceneComponent* NewAttachemntComponent, + const EAttachmentRule AttachmentRules = EAttachmentRule::SnapToTarget); + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool SetSoundAttachmentActor(const int32 SoundId, + AActor* NewAttachemntActor, + const EAttachmentRule AttachmentRules = EAttachmentRule::SnapToTarget); + + UFUNCTION(BlueprintCallable, Category = "Sounds") + bool SetSoundAutoDestroy(const int32 SoundId, const bool bNewAutoDestroy); + + + + + UFUNCTION(BlueprintPure, Category = "Sounds") + float GetSoundVolume(const int32 SoundId); + + UFUNCTION(BlueprintPure, Category = "Sounds") + float GetSoundPitch(const int32 SoundId); + + UFUNCTION(BlueprintPure, Category = "Sounds") + FVector GetSoundLocation(const int32 SoundId); + + UFUNCTION(BlueprintPure, Category = "Sounds") + bool GetSoundAutoDestroy(const int32 SoundId); +};