(Feat) Add Animation

This commit is contained in:
2026-08-06 13:27:21 +02:00
parent fca1b271f1
commit 72a34f6e40
64 changed files with 1943 additions and 83 deletions
@@ -3,7 +3,14 @@
#include "SurvivalUserSettings.h"
#include "AudioDevice.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "Misc/App.h"
#include "Sound/AudioSettings.h"
#include "Sound/SoundClass.h"
#include "Sound/SoundMix.h"
#include "SurvivalAudioSettings.h"
namespace
{
@@ -13,6 +20,88 @@ namespace
const float Snapped = Min + FMath::RoundToFloat((Value - Min) / Step) * Step;
return FMath::Clamp(Snapped, Min, Max);
}
/** Pourcentage affiche -> amplitude reelle, courbe perceptive comprise. */
float VolumePercentToAmplitude(float Percent)
{
using namespace SurvivalSettingsRange;
const float Normalized = FMath::Clamp(Percent, VolumeMin, VolumeMax) / 100.f;
return FMath::Pow(Normalized, VolumeCurveExponent);
}
/**
* Le peripherique audio a qui parler.
*
* En PIE le monde de jeu peut avoir le sien, distinct de celui de l'editeur :
* viser systematiquement le peripherique principal ferait regler le volume de
* l'editeur pendant que le jeu, lui, ne bouge pas.
*/
FAudioDevice* GetSettingsAudioDevice()
{
if (!GEngine)
{
return nullptr;
}
if (const UWorld* PlayWorld = GEngine->GetCurrentPlayWorld())
{
if (FAudioDevice* WorldDevice = PlayWorld->GetAudioDeviceRaw())
{
return WorldDevice;
}
}
return GEngine->GetMainAudioDeviceRaw();
}
/**
* Le mix par lequel passent les volumes du joueur.
*
* C'est celui de Project Settings > Audio > Default Base Sound Mix, et pas une
* reference a nous. Deux raisons : le moteur le REPOSE a chaque chargement de
* map (UWorld::UpdateWorldComponents -> SetDefaultBaseSoundMix), donc il est
* toujours actif sans qu'on ait a le pousser nous-memes ; et un mix de base est
* evalue a plein regime en permanence, alors qu'un mix pousse a la main
* s'eteindrait au bout de sa duree.
*/
USoundMix* GetSettingsSoundMix()
{
const FSoftObjectPath& MixPath = GetDefault<UAudioSettings>()->DefaultBaseSoundMix;
return MixPath.IsValid() ? Cast<USoundMix>(MixPath.TryLoad()) : nullptr;
}
/**
* Pose le volume d'une SoundClass, en pourcentage.
*
* bApplyToChildren merite une explication, parce que se tromper la donne un
* volume maitre qui n'agit que sur les sons non tagues : le moteur propage les
* proprietes du parent vers les enfants (ParseSoundClasses) AVANT d'appliquer
* les ajusteurs du mix (ApplyClassAdjusters). Un ajusteur pose sur la classe
* maitre arrive donc trop tard pour redescendre tout seul -- il faut demander
* explicitement la recursion. Les feuilles, elles, gardent false : leur
* ajusteur n'a personne a qui redescendre.
*
* Aucun fondu non plus, et ce n'est pas un oubli : toute duree non nulle rend le
* curseur inutilisable. Quand on repose un override alors que le precedent n'a
* pas fini son fondu, UpdateClassAdjustorOverrideEntry relance l'interpolation
* (FDynamicParameter::Set remet CurrTimeSec a zero) mais n'appelle PAS
* Update(DeltaTime) cette frame-la. Un curseur qu'on glisse repose l'override a
* chaque image : la rampe repart indefiniment de zero et GetValue() rend toujours
* la valeur precedente. Le volume ne rattrape son retard qu'une fois le geste
* ARRETE. A zero, Set() ecrit directement la valeur -- et le mixeur lisse deja le
* gain de chaque source dans son buffer de rendu, donc pas de clic pour autant.
*/
void ApplyClassVolume(FAudioDevice& AudioDevice, USoundMix& Mix, USoundClass* SoundClass, float Percent, bool bApplyToChildren)
{
if (!SoundClass)
{
return;
}
AudioDevice.SetSoundMixClassOverride(&Mix, SoundClass,
VolumePercentToAmplitude(Percent), /*Pitch*/ 1.f, /*FadeInTime*/ 0.f, bApplyToChildren);
}
}
USurvivalUserSettings::USurvivalUserSettings()
@@ -22,6 +111,7 @@ USurvivalUserSettings::USurvivalUserSettings()
// fichier s'il existe.
SetSurvivalGameplayToDefaults();
SetSurvivalDisplayToDefaults();
SetSurvivalAudioToDefaults();
}
USurvivalUserSettings* USurvivalUserSettings::Get()
@@ -46,6 +136,22 @@ void USurvivalUserSettings::SetSurvivalDisplayToDefaults()
Brightness = 100.f;
}
void USurvivalUserSettings::SetSurvivalAudioToDefaults()
{
// 80 et non 100 sur le maitre : il faut laisser au joueur de la marge vers le
// haut, sinon le seul reglage possible est de baisser.
MasterVolume = 80.f;
// La musique sous les effets, comme partout : elle est continue, eux sont des
// evenements, et un evenement doit passer par-dessus le fond.
MusicVolume = 60.f;
EffectsVolume = 100.f;
UiVolume = 80.f;
// Le defaut du moteur, qu'on se contente d'exposer.
bPlayInBackground = false;
}
void USurvivalUserSettings::SetToDefaults()
{
// Le "Reinitialiser" global du moteur doit aussi remettre NOS valeurs :
@@ -54,6 +160,7 @@ void USurvivalUserSettings::SetToDefaults()
SetSurvivalGameplayToDefaults();
SetSurvivalDisplayToDefaults();
SetSurvivalAudioToDefaults();
}
void USurvivalUserSettings::SetBrightness(float Value)
@@ -85,6 +192,40 @@ void USurvivalUserSettings::ApplySurvivalSettings()
OnSurvivalSettingsApplied.Broadcast();
}
void USurvivalUserSettings::ApplyAudioSettings()
{
// Ce reglage-la ne passe pas par le mix : c'est l'application entiere qui est
// baissee quand la fenetre perd le focus, bien en amont des SoundClass. Le
// moteur le stocke dans son propre .ini, d'ou une valeur reposee a chaque
// application plutot que memorisee ailleurs.
FApp::SetUnfocusedVolumeMultiplier(bPlayInBackground ? 1.f : 0.f);
FAudioDevice* AudioDevice = GetSettingsAudioDevice();
if (!AudioDevice)
{
// Normal tres tot au demarrage, ou en serveur dedie : les valeurs sont
// deja enregistrees, le prochain passage les posera.
return;
}
USoundMix* Mix = GetSettingsSoundMix();
if (!Mix)
{
UE_LOG(LogTemp, Warning, TEXT("USurvivalUserSettings : aucun Default Base Sound Mix dans Project Settings > Audio, les curseurs de volume resteront sans effet."));
return;
}
USurvivalAudioSettings* AudioAssets = USurvivalAudioSettings::Get();
// Le maitre EN PREMIER et avec recursion : il redescend sur les trois autres,
// qui reappliquent ensuite la leur par-dessus. Les deux se multiplient, ce qui
// est exactement ce qu'on attend d'un volume general.
ApplyClassVolume(*AudioDevice, *Mix, AudioAssets->GetMasterSoundClass(), MasterVolume, /*bApplyToChildren*/ true);
ApplyClassVolume(*AudioDevice, *Mix, AudioAssets->GetMusicSoundClass(), MusicVolume, /*bApplyToChildren*/ false);
ApplyClassVolume(*AudioDevice, *Mix, AudioAssets->GetEffectsSoundClass(), EffectsVolume, /*bApplyToChildren*/ false);
ApplyClassVolume(*AudioDevice, *Mix, AudioAssets->GetUiSoundClass(), UiVolume, /*bApplyToChildren*/ false);
}
void USurvivalUserSettings::ApplyNonResolutionSettings()
{
Super::ApplyNonResolutionSettings();
@@ -92,6 +233,7 @@ void USurvivalUserSettings::ApplyNonResolutionSettings()
// ApplySettings() passe forcement par ici : brancher la diffusion a ce
// niveau garantit que nos reglages suivent, quel que soit le chemin.
ApplySurvivalSettings();
ApplyAudioSettings();
}
float USurvivalUserSettings::GetLookMultiplierX() const
@@ -173,3 +315,32 @@ void USurvivalUserSettings::SetCrouchToggle(bool bValue)
{
bCrouchToggle = bValue;
}
void USurvivalUserSettings::SetMasterVolume(float Value)
{
using namespace SurvivalSettingsRange;
MasterVolume = SnapToStep(Value, VolumeMin, VolumeMax, VolumeStep);
}
void USurvivalUserSettings::SetMusicVolume(float Value)
{
using namespace SurvivalSettingsRange;
MusicVolume = SnapToStep(Value, VolumeMin, VolumeMax, VolumeStep);
}
void USurvivalUserSettings::SetEffectsVolume(float Value)
{
using namespace SurvivalSettingsRange;
EffectsVolume = SnapToStep(Value, VolumeMin, VolumeMax, VolumeStep);
}
void USurvivalUserSettings::SetUiVolume(float Value)
{
using namespace SurvivalSettingsRange;
UiVolume = SnapToStep(Value, VolumeMin, VolumeMax, VolumeStep);
}
void USurvivalUserSettings::SetPlayInBackground(bool bValue)
{
bPlayInBackground = bValue;
}