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
bMouseInverted=False
fMouseSensetivity=1.000000
fMasterVolume=0.500000
fMusicVolume=1.000000
fEffectsVolume=1.000000
fVoicesVolume=1.000000
fMenuVolume=1.000000
bUseVSync=False
bUseDynamicResolution=False
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);
}
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)
{
TArray<AActor*> childs;

View File

@ -25,6 +25,9 @@ public:
UFUNCTION(BlueprintPure)
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) */
UFUNCTION(BlueprintCallable, Category = Actor)
static void DestroyActorRecursively(class AActor* actor);

View File

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

View File

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

View File

@ -4,12 +4,35 @@
#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)
{
bUseMotionBlur = 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()
@ -17,12 +40,89 @@ UCustomGameSettings* UCustomGameSettings::Get()
return Cast<UCustomGameSettings>(UGameUserSettings::GetGameUserSettings());
}
/** Game */
float UCustomGameSettings::GetDefaultMouseSensetivity() const
{
return fDefaultMouseSensetivity;
}
float UCustomGameSettings::GetMouseSensetivity() const
{
return fMouseSensetivity;
}
void UCustomGameSettings::SetMouseSensetivity(float value)
{
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.
* Mouse sensetivity and inversion, motion blur usage, fps show.
*/
UCLASS(Config = Game, defaultconfig)
UCLASS(defaultconfig)
class UCustomGameSettings : public UGameUserSettings
{
GENERATED_UCLASS_BODY()
GENERATED_BODY()
public:
// 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"))
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)
bool bUseMotionBlur;
@ -39,11 +30,79 @@ public:
UPROPERTY(Config, BlueprintReadWrite)
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)
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:
UPROPERTY(Config)
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);
}
void ACustomPlayerController::ApplyMouseSettings()
{
if(auto settings = UCustomGameSettings::Get())
for(auto& context : contexts)
ApplyMouseSettings(context, settings);
}
void ACustomPlayerController::BeginPlay()
{
Super::BeginPlay();
@ -71,15 +78,19 @@ void ACustomPlayerController::EndPlay(const EEndPlayReason::Type 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(!gameSettings)
return;
if(!settings)
{
settings = UCustomGameSettings::Get();
if(!settings)
return;
}
if(!context.LoadSynchronous())
return;
bool contextChanged = false;
for(auto& mapping : context.LoadSynchronous()->GetMappings())
{
if(mapping.Key != EKeys::Mouse2D)
@ -87,20 +98,27 @@ void ACustomPlayerController::ApplyMouseSettings(TSoftObjectPtr<class UInputMapp
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;
continue;
}
negate_modifier->bY = !settings->bMouseInverted;
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)

View File

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

View File

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

View File

@ -113,7 +113,6 @@ protected:
UFUNCTION(BlueprintCallable, Category = Character)
void ShowMenu();
class APlayerController* playerController;
UPROPERTY(EditAnywhere)
float moveSpeed = 200;
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)
FMainMenuClosedDelegate OnMainMenuClosedDelegate;
UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
class UMainMenuButtonWidget* ButtonContinue;
UPROPERTY(meta = (BindWidget))
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))
UPROPERTY(BlueprintReadOnly, Transient, meta = (BindWidgetAnim))
class UWidgetAnimation* extendAnimation;
UPROPERTY(BlueprintReadOnly, Transient, meta = (BindWidgetAnim))
class UWidgetAnimation* closeAnimation;
protected: