Main, Options, Audio, Game menu and settings

This commit is contained in:
Oleg Petruny 2025-01-04 13:35:00 +01:00
parent 498dc7ec8e
commit 3dd1788338
49 changed files with 390 additions and 99 deletions

View File

@ -3,6 +3,11 @@ bUseMotionBlur=False
bShowFps=False bShowFps=False
bMouseInverted=False bMouseInverted=False
fMouseSensetivity=1.000000 fMouseSensetivity=1.000000
fMasterVolume=0.500000
fMusicVolume=1.000000
fEffectsVolume=1.000000
fVoicesVolume=1.000000
fMenuVolume=1.000000
bUseVSync=False bUseVSync=False
bUseDynamicResolution=False bUseDynamicResolution=False
ResolutionSizeX=1920 ResolutionSizeX=1920

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -29,6 +29,16 @@ FText UCommonFunctions::GetKeyDisplayName(FKey key)
return key.GetDisplayName(false); return key.GetDisplayName(false);
} }
int32 UCommonFunctions::StringIndexInTextArray(const TArray<FText>& array, const FString& value)
{
FString v = value.ToLower();
for(int32 i = 0; i < array.Num(); ++i)
if(FTextInspector::GetSourceString(array[i])->ToLower().Equals(v))
return i;
return -1;
}
void UCommonFunctions::DestroyActorRecursively(AActor* actor) void UCommonFunctions::DestroyActorRecursively(AActor* actor)
{ {
TArray<AActor*> childs; TArray<AActor*> childs;

View File

@ -25,6 +25,9 @@ public:
UFUNCTION(BlueprintPure) UFUNCTION(BlueprintPure)
static FText GetKeyDisplayName(struct FKey key); static FText GetKeyDisplayName(struct FKey key);
UFUNCTION(BlueprintPure)
static int32 StringIndexInTextArray(const TArray<FText>& array, const FString& value);
/** Recursively destroy actor and all its childs (the default Destroy doesn't have consistent behavior) */ /** Recursively destroy actor and all its childs (the default Destroy doesn't have consistent behavior) */
UFUNCTION(BlueprintCallable, Category = Actor) UFUNCTION(BlueprintCallable, Category = Actor)
static void DestroyActorRecursively(class AActor* actor); static void DestroyActorRecursively(class AActor* actor);

View File

@ -6,6 +6,7 @@
#include "Kismet/KismetSystemLibrary.h" #include "Kismet/KismetSystemLibrary.h"
#include "ContentLoader.h" #include "ContentLoader.h"
#include "CustomGameSettings.h"
#include "Levels/LevelBase.h" #include "Levels/LevelBase.h"
#include "PlayerBase.h" #include "PlayerBase.h"
#include "SaveData.h" #include "SaveData.h"
@ -23,7 +24,15 @@ void UCustomGameInstance::Init()
UGameInstance::Init(); UGameInstance::Init();
instance = this; instance = this;
contentLoader = NewObject<UContentLoader>(this); contentLoader = NewObject<UContentLoader>(this);
for(auto& _class : globalInstancesClasses)
{
UObject* gi = NewObject<UObject>(_class);
gi->AddToRoot();
globalInstances.Add(_class.Get(), gi);
}
globalInstancesClasses.Empty();
if(auto save = UGameplayStatics::LoadGameFromSlot(saveName, saveIndex)) if(auto save = UGameplayStatics::LoadGameFromSlot(saveName, saveIndex))
saveData = Cast<USaveData>(save); saveData = Cast<USaveData>(save);
@ -31,6 +40,14 @@ void UCustomGameInstance::Init()
saveData = Cast<USaveData>(UGameplayStatics::CreateSaveGameObject(USaveData::StaticClass())); saveData = Cast<USaveData>(UGameplayStatics::CreateSaveGameObject(USaveData::StaticClass()));
} }
void UCustomGameInstance::Shutdown()
{
if(auto settings = UCustomGameSettings::Get())
settings->SaveSettings();
Super::Shutdown();
}
UCustomGameInstance* UCustomGameInstance::Get() UCustomGameInstance* UCustomGameInstance::Get()
{ {
return instance; return instance;
@ -44,6 +61,14 @@ UContentLoader* UCustomGameInstance::GetContentLoader()
return nullptr; return nullptr;
} }
UObject* UCustomGameInstance::GetGlobalInstance(UClass* _class)
{
if(auto GI = Get())
return *(GI->globalInstances.Find(_class));
return nullptr;
}
bool UCustomGameInstance::IsGameSaved() bool UCustomGameInstance::IsGameSaved()
{ {
if(auto GI = Get()) if(auto GI = Get())

View File

@ -20,19 +20,24 @@ class UCustomGameInstance : public UGameInstance
public: public:
/** Instantiates content loader, dummy save data and applies settings */ /** Instantiates content loader, dummy save data and applies settings */
virtual void Init() override; virtual void Init() override;
/** Saves settings */
virtual void Shutdown() override;
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Custom Game Instance")) UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Custom Game Instance"))
static UCustomGameInstance* Get(); static UCustomGameInstance* Get();
UFUNCTION(BlueprintPure) UFUNCTION(BlueprintPure)
static class UContentLoader* GetContentLoader(); static class UContentLoader* GetContentLoader();
UFUNCTION(BlueprintPure)
static UObject* GetGlobalInstance(UClass* _class);
/** Return true is save data are present and valid */ /** Return true is save data are present and valid */
UFUNCTION(BlueprintPure) UFUNCTION(BlueprintPure)
static bool IsGameSaved(); static bool IsGameSaved();
UFUNCTION(BlueprintCallable, Category = Save) UFUNCTION(BlueprintCallable, Category = Save)
void SaveGame(FName checkpointName); void SaveGame(FName checkpointName);
UFUNCTION(BlueprintCallable, Category = Save) UFUNCTION(BlueprintCallable, Category = Save)
void LoadGame(); void LoadGame();
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void ExitGame(); void ExitGame();
@ -48,4 +53,7 @@ protected:
UPROPERTY() UPROPERTY()
class UContentLoader* contentLoader; class UContentLoader* contentLoader;
UPROPERTY(EditDefaultsOnly)
TSet<TSubclassOf<UObject>> globalInstancesClasses;
TMap<UClass*, UObject*> globalInstances;
}; };

View File

@ -4,12 +4,35 @@
#include "Kismet/GameplayStatics.h" #include "Kismet/GameplayStatics.h"
namespace
{
/** Game */
constexpr float fDefaultMouseSensetivity = 0.5f;
constexpr bool bDefaultMouseInverted = false;
/** Audio */
constexpr float fDefaultMasterVolume = 0.4f;
constexpr float fDefaultMusicVolume = 1.0f;
constexpr float fDefaultEffectsVolume = 1.0f;
constexpr float fDefaultVoicesVolume = 1.0f;
constexpr float fDefaultMenuVolume = 1.0f;
}
UCustomGameSettings::UCustomGameSettings(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer) UCustomGameSettings::UCustomGameSettings(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer)
{ {
bUseMotionBlur = false; bUseMotionBlur = false;
bShowFps = false; bShowFps = false;
bMouseInverted = false;
fMouseSensetivity = 1.0f; /** Game */
fMouseSensetivity = GetDefaultMouseSensetivity();
bMouseInverted = GetDefaultMouseInverted();
/** Audio */
fMasterVolume = GetDefaultMasterVolume();
fMusicVolume = GetDefaultMusicVolume();
fEffectsVolume = GetDefaultEffectsVolume();
fVoicesVolume = GetDefaultVoicesVolume();
fMenuVolume = GetDefaultMenuVolume();
} }
UCustomGameSettings* UCustomGameSettings::Get() UCustomGameSettings* UCustomGameSettings::Get()
@ -17,12 +40,89 @@ UCustomGameSettings* UCustomGameSettings::Get()
return Cast<UCustomGameSettings>(UGameUserSettings::GetGameUserSettings()); return Cast<UCustomGameSettings>(UGameUserSettings::GetGameUserSettings());
} }
/** Game */
float UCustomGameSettings::GetDefaultMouseSensetivity() const
{
return fDefaultMouseSensetivity;
}
float UCustomGameSettings::GetMouseSensetivity() const
{
return fMouseSensetivity;
}
void UCustomGameSettings::SetMouseSensetivity(float value) void UCustomGameSettings::SetMouseSensetivity(float value)
{ {
fMouseSensetivity = FMath::Clamp(value, 0.1f, 2.0f); fMouseSensetivity = FMath::Clamp(value, 0.1f, 2.0f);
} }
float UCustomGameSettings::GetMouseSensetivity() const bool UCustomGameSettings::GetDefaultMouseInverted() const
{ {
return fMouseSensetivity; return bDefaultMouseInverted;
}
/** Audio */
float UCustomGameSettings::GetDefaultMasterVolume() const
{
return fDefaultMasterVolume;
}
float UCustomGameSettings::GetDefaultMusicVolume() const
{
return fDefaultMusicVolume;
}
float UCustomGameSettings::GetDefaultEffectsVolume() const
{
return fDefaultEffectsVolume;
}
float UCustomGameSettings::GetDefaultVoicesVolume() const
{
return fDefaultVoicesVolume;
}
float UCustomGameSettings::GetDefaultMenuVolume() const
{
return fDefaultMenuVolume;
}
float UCustomGameSettings::GetMasterVolume() const
{
return fMasterVolume;
}
float UCustomGameSettings::GetMusicVolume() const
{
return fMusicVolume;
}
float UCustomGameSettings::GetEffectsVolume() const
{
return fEffectsVolume;
}
float UCustomGameSettings::GetVoicesVolume() const
{
return fVoicesVolume;
}
float UCustomGameSettings::GetMenuVolume() const
{
return fMenuVolume;
}
void UCustomGameSettings::SetMasterVolume(float value)
{
fMasterVolume = FMath::Clamp(value, 0.0f, 1.0f);
}
void UCustomGameSettings::SetMusicVolume(float value)
{
fMusicVolume = FMath::Clamp(value, 0.0f, 1.0f);
}
void UCustomGameSettings::SetEffectsVolume(float value)
{
fEffectsVolume = FMath::Clamp(value, 0.0f, 1.0f);
}
void UCustomGameSettings::SetVoicesVolume(float value)
{
fVoicesVolume = FMath::Clamp(value, 0.0f, 1.0f);
}
void UCustomGameSettings::SetMenuVolume(float value)
{
fMenuVolume = FMath::Clamp(value, 0.0f, 1.0f);
} }

View File

@ -10,28 +10,19 @@
* Manages custom game settings. * Manages custom game settings.
* Mouse sensetivity and inversion, motion blur usage, fps show. * Mouse sensetivity and inversion, motion blur usage, fps show.
*/ */
UCLASS(Config = Game, defaultconfig) UCLASS(defaultconfig)
class UCustomGameSettings : public UGameUserSettings class UCustomGameSettings : public UGameUserSettings
{ {
GENERATED_UCLASS_BODY() GENERATED_BODY()
public: public:
// Is auto defined by UE but implementation is in cpp // Is auto defined by UE but implementation is in cpp
//UCustomGameSettings(const FObjectInitializer& ObjectInitializer); UCustomGameSettings(const FObjectInitializer& ObjectInitializer);
UFUNCTION(BlueprintPure, Category = Settings, meta = (DisplayName = "Get Custom Game Settings")) UFUNCTION(BlueprintPure, Category = Settings, meta = (DisplayName = "Get Custom Game Settings"))
static UCustomGameSettings* Get(); static UCustomGameSettings* Get();
/**
* Sets mouse sensetivity multiplier
* @param value [0.1 - 2.0]
*/
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMouseSensetivity(float value);
/** Returns mouse sensetivity multiplier in [0.1 - 2.0] */
UFUNCTION(BlueprintCallable, Category = Settings)
float GetMouseSensetivity() const;
UPROPERTY(Config, BlueprintReadWrite) UPROPERTY(Config, BlueprintReadWrite)
bool bUseMotionBlur; bool bUseMotionBlur;
@ -39,11 +30,79 @@ public:
UPROPERTY(Config, BlueprintReadWrite) UPROPERTY(Config, BlueprintReadWrite)
bool bShowFps; bool bShowFps;
/** Game */
UFUNCTION(BlueprintPure, Category = Settings)
float GetDefaultMouseSensetivity() const;
/** Returns mouse sensetivity multiplier in [0.1 - 2.0] */
UFUNCTION(BlueprintPure, Category = Settings)
float GetMouseSensetivity() const;
/**
* Sets mouse sensetivity multiplier
* @param value [0.1 - 2.0]
*/
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMouseSensetivity(float value);
UFUNCTION(BlueprintPure, Category = Settings)
bool GetDefaultMouseInverted() const;
UPROPERTY(Config, BlueprintReadWrite) UPROPERTY(Config, BlueprintReadWrite)
bool bMouseInverted; bool bMouseInverted;
/**
* Audio
* All values are clamped in [0.0 - 1.0]
*/
UFUNCTION(BlueprintPure, Category = Settings)
float GetDefaultMasterVolume() const; // UFUNCTION doesn't support constexpr functions
UFUNCTION(BlueprintPure, Category = Settings)
float GetDefaultMusicVolume() const; // UFUNCTION doesn't support constexpr functions
UFUNCTION(BlueprintPure, Category = Settings)
float GetDefaultEffectsVolume() const; // UFUNCTION doesn't support constexpr functions
UFUNCTION(BlueprintPure, Category = Settings)
float GetDefaultVoicesVolume() const; // UFUNCTION doesn't support constexpr functions
UFUNCTION(BlueprintPure, Category = Settings)
float GetDefaultMenuVolume() const; // UFUNCTION doesn't support constexpr functions
UFUNCTION(BlueprintPure, Category = Settings)
float GetMasterVolume() const;
UFUNCTION(BlueprintPure, Category = Settings)
float GetMusicVolume() const;
UFUNCTION(BlueprintPure, Category = Settings)
float GetEffectsVolume() const;
UFUNCTION(BlueprintPure, Category = Settings)
float GetVoicesVolume() const;
UFUNCTION(BlueprintPure, Category = Settings)
float GetMenuVolume() const;
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMasterVolume(float value);
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMusicVolume(float value);
UFUNCTION(BlueprintCallable, Category = Settings)
void SetEffectsVolume(float value);
UFUNCTION(BlueprintCallable, Category = Settings)
void SetVoicesVolume(float value);
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMenuVolume(float value);
protected: protected:
UPROPERTY(Config) UPROPERTY(Config)
float fMouseSensetivity; float fMouseSensetivity;
/** Audio */
UPROPERTY(Config)
float fMasterVolume;
UPROPERTY(Config)
float fMusicVolume;
UPROPERTY(Config)
float fEffectsVolume;
UPROPERTY(Config)
float fVoicesVolume;
UPROPERTY(Config)
float fMenuVolume;
}; };

View File

@ -33,6 +33,13 @@ void ACustomPlayerController::AppendInputContext(TSoftObjectPtr<class UInputMapp
subsystem->AddMappingContext(context.LoadSynchronous(), 0); subsystem->AddMappingContext(context.LoadSynchronous(), 0);
} }
void ACustomPlayerController::ApplyMouseSettings()
{
if(auto settings = UCustomGameSettings::Get())
for(auto& context : contexts)
ApplyMouseSettings(context, settings);
}
void ACustomPlayerController::BeginPlay() void ACustomPlayerController::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
@ -71,36 +78,47 @@ void ACustomPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
Super::EndPlay(EndPlayReason); Super::EndPlay(EndPlayReason);
} }
void ACustomPlayerController::ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context) void ACustomPlayerController::ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context, UCustomGameSettings* settings)
{ {
auto gameSettings = UCustomGameSettings::Get(); if(!settings)
if(!gameSettings) {
settings = UCustomGameSettings::Get();
if(!settings)
return; return;
}
if(!context.LoadSynchronous()) if(!context.LoadSynchronous())
return; return;
bool contextChanged = false;
for(auto& mapping : context.LoadSynchronous()->GetMappings()) for(auto& mapping : context.LoadSynchronous()->GetMappings())
{ {
if(mapping.Key != EKeys::Mouse2D) if(mapping.Key != EKeys::Mouse2D)
continue; continue;
for(auto& modifier : mapping.Modifiers) for(auto& modifier : mapping.Modifiers)
{
if(gameSettings->bMouseInverted)
{ {
if(auto negate_modifier = Cast<UInputModifierNegate>(modifier)) if(auto negate_modifier = Cast<UInputModifierNegate>(modifier))
{ {
negate_modifier->bY = !negate_modifier->bY; negate_modifier->bY = !settings->bMouseInverted;
continue; contextChanged = true;
} }
else if(auto scalar_modifier = Cast<UInputModifierScalar>(modifier))
{
scalar_modifier->Scalar = FVector{ settings->GetMouseSensetivity() };
contextChanged = true;
} }
if(auto scalar_modifier = Cast<UInputModifierScalar>(modifier))
scalar_modifier->Scalar = FVector{ gameSettings->GetMouseSensetivity() * 0.5 };
} }
} }
UEnhancedInputLibrary::RequestRebuildControlMappingsUsingContext(context.LoadSynchronous()); if(contextChanged)
{
if(subsystem)
{
FModifyContextOptions options;
subsystem->RequestRebuildControlMappings(options, EInputMappingRebuildType::RebuildWithFlush);
}
}
} }
void ACustomPlayerController::OnAnyKeyPressed(FKey key) void ACustomPlayerController::OnAnyKeyPressed(FKey key)

View File

@ -30,6 +30,10 @@ public:
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
static void AppendInputContext(TSoftObjectPtr<class UInputMappingContext> context); static void AppendInputContext(TSoftObjectPtr<class UInputMappingContext> context);
/** Applies mouse settings to all loaded contexts */
UFUNCTION(BlueprintCallable)
static void ApplyMouseSettings();
FPlayerAnyKeyPressedDelegate onAnyKeyPressed; FPlayerAnyKeyPressedDelegate onAnyKeyPressed;
FPlayerAnyKeyReleasedDelegate onAnyKeyReleased; FPlayerAnyKeyReleasedDelegate onAnyKeyReleased;
@ -38,7 +42,8 @@ protected:
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private: private:
static void ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context); /** Applies mouse settings to the specific context */
static void ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context, class UCustomGameSettings* settings = nullptr);
void OnAnyKeyPressed(FKey key); void OnAnyKeyPressed(FKey key);
void OnAnyKeyReleased(FKey key); void OnAnyKeyReleased(FKey key);

View File

@ -371,16 +371,22 @@ void APlayerBase::ShowMenu()
if(GetWorld()->IsPaused()) if(GetWorld()->IsPaused())
{ {
WM->HideMainMenu(); WM->HideMainMenu();
playerController->SetShowMouseCursor(false); if(auto PC = ACustomPlayerController::Get())
playerController->SetInputMode(FInputModeGameOnly{}); {
PC->SetShowMouseCursor(false);
PC->SetInputMode(FInputModeGameOnly{});
}
UnlockPlayer(FPlayerLock::All()); UnlockPlayer(FPlayerLock::All());
UGameplayStatics::SetGamePaused(GetWorld(), false); UGameplayStatics::SetGamePaused(GetWorld(), false);
} }
else else
{ {
WM->ShowMainMenu(); WM->ShowMainMenu();
playerController->SetShowMouseCursor(true); if(auto PC = ACustomPlayerController::Get())
playerController->SetInputMode(FInputModeGameAndUI{}); {
PC->SetShowMouseCursor(true);
PC->SetInputMode(FInputModeGameAndUI{});
}
LockPlayer(FPlayerLock::All()); LockPlayer(FPlayerLock::All());
UGameplayStatics::SetGamePaused(GetWorld(), true); UGameplayStatics::SetGamePaused(GetWorld(), true);
} }

View File

@ -113,7 +113,6 @@ protected:
UFUNCTION(BlueprintCallable, Category = Character) UFUNCTION(BlueprintCallable, Category = Character)
void ShowMenu(); void ShowMenu();
class APlayerController* playerController;
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
float moveSpeed = 200; float moveSpeed = 200;
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)

View File

@ -0,0 +1,20 @@
// Oleg Petruny proprietary.
#include "ComboBoxText.h"
void UComboBoxText::PostInitProperties()
{
Super::PostInitProperties();
SetOptions(_DefaultOptions);
_DefaultOptions.Empty();
}
void UComboBoxText::SetOptions(const TArray<FText>& options)
{
ClearSelection();
Options.Empty();
for(auto& o : options)
Options.Add(MakeShareable(new FString(o.ToString())));
RefreshOptions();
}

View File

@ -0,0 +1,23 @@
// Oleg Petruny proprietary.
#pragma once
#include "Components/ComboBoxString.h"
#include "ComboBoxText.generated.h"
UCLASS(Blueprintable, meta = (DisplayName = "ComboBox (Text)"), MinimalAPI)
class UComboBoxText : public UComboBoxString
{
GENERATED_BODY()
public:
virtual void PostInitProperties() override;
UFUNCTION(BlueprintCallable)
void SetOptions(const TArray<FText>& options);
protected:
UPROPERTY(EditAnywhere, Category = ComboBoxTextDefault)
TArray<FText> _DefaultOptions;
};

View File

@ -30,20 +30,9 @@ public:
UPROPERTY(BlueprintAssignable) UPROPERTY(BlueprintAssignable)
FMainMenuClosedDelegate OnMainMenuClosedDelegate; FMainMenuClosedDelegate OnMainMenuClosedDelegate;
UPROPERTY(BlueprintReadWrite, meta = (BindWidget)) UPROPERTY(BlueprintReadOnly, Transient, meta = (BindWidgetAnim))
class UMainMenuButtonWidget* ButtonContinue; class UWidgetAnimation* extendAnimation;
UPROPERTY(meta = (BindWidget)) UPROPERTY(BlueprintReadOnly, Transient, meta = (BindWidgetAnim))
class UMainMenuButtonWidget* ButtonOptions;
UPROPERTY(meta = (BindWidget))
class UMainMenuButtonWidget* ButtonCredits;
UPROPERTY(meta = (BindWidget))
class UMainMenuButtonWidget* ButtonExit;
UPROPERTY(Transient, meta = (BindWidgetAnim))
class UWidgetAnimation* showFullAnimation;
UPROPERTY(Transient, meta = (BindWidgetAnim))
class UWidgetAnimation* showFastAnimation;
UPROPERTY(Transient, meta = (BindWidgetAnim))
class UWidgetAnimation* closeAnimation; class UWidgetAnimation* closeAnimation;
protected: protected: