(Feat) Add MainMenu

This commit is contained in:
2026-07-28 14:10:17 +02:00
parent ed7c7fd991
commit 175bef228c
33 changed files with 2331 additions and 12 deletions
+38
View File
@@ -89,10 +89,23 @@ public:
UFUNCTION(BlueprintCallable, Category = "Camera")
void AddCameraPunch(float Strength);
/**
* Recopie les reglages du joueur dans le pawn.
*
* Appelee au BeginPlay ET a chaque fois que USurvivalUserSettings diffuse :
* apres un respawn le pawn est un NOUVEL acteur, une valeur poussee une
* seule fois serait perdue avec le cadavre. UFUNCTION obligatoire, c'est ce
* qui permet le AddDynamic sur le delegue.
*/
UFUNCTION()
void ApplyUserSettings();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
// Appelés par le CharacterMovement quand la capsule change de taille (accroupi)
virtual void OnStartCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust) override;
virtual void OnEndCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust) override;
@@ -209,6 +222,10 @@ protected:
/**
* Sensibilité horizontale : degrés de rotation par unité de souris.
* 1.0 = brut (beaucoup trop rapide), ~0.4 est un bon point de départ.
*
* ATTENTION : ces trois valeurs, comme BaseFOV, sont ECRASEES au BeginPlay
* par USurvivalUserSettings. Elles ne servent plus que de repli si l'objet
* de réglages est absent ; pour changer la sensibilité, passe par le menu.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (ClampMin = "0.01"))
float LookSensitivityX = 0.4f;
@@ -344,6 +361,16 @@ protected:
void StopCrouch();
void Interact();
/**
* Relachement de la touche de sprint / d'accroupissement.
*
* Separes de StopSprint et StopCrouch, qui restent des ordres inconditionnels :
* en mode bascule le relachement ne doit rien faire, alors que la mort ou
* l'ouverture de l'inventaire doivent bel et bien couper le sprint.
*/
void HandleSprintReleased();
void HandleCrouchReleased();
/**
* Clic gauche : agit avec l'objet actif de la barre rapide.
* Aujourd'hui seuls les consommables font quelque chose ; c'est ici que
@@ -392,4 +419,15 @@ private:
/** Évite de spammer le log si IA_Look est mal configurée. */
bool bLoggedLookTypeWarning = false;
// ------------------------------------------------------------------
// Copies locales des réglages joueur, rafraîchies par ApplyUserSettings
// ------------------------------------------------------------------
/** 0 = caméra parfaitement fixe, 1 = amplitude d'origine. */
float CameraShakeMultiplier = 1.f;
bool bSprintToggleMode = false;
bool bCrouchToggleMode = false;
};
@@ -6,12 +6,15 @@
#include "GameFramework/PlayerController.h"
#include "FpsPlayerController.generated.h"
class UConfirmDialogWidget;
class UHotbarWidget;
class UInputAction;
class UInventoryComponent;
class UInteractionPromptWidget;
class UInventoryWidget;
class UPauseMenuWidget;
class UScreenFadeComponent;
class USettingsMenuWidget;
class USurvivalStatsWidget;
class UUserWidget;
struct FInputActionValue;
@@ -36,6 +39,21 @@ public:
UFUNCTION(BlueprintPure, Category = "UI")
bool IsInventoryOpen() const { return bInventoryOpen; }
/** Ferme le menu de pause et relance le monde. */
UFUNCTION(BlueprintCallable, Category = "UI|Pause")
void ResumeGame();
/** Ouvre la demande de confirmation. Ne quitte rien par elle-meme. */
UFUNCTION(BlueprintCallable, Category = "UI|Pause")
void RequestQuitToMainMenu();
UFUNCTION(BlueprintPure, Category = "UI|Pause")
bool IsPauseMenuOpen() const { return bPauseMenuOpen; }
/** Ouvre l'ecran de reglages par-dessus le menu de pause. */
UFUNCTION(BlueprintCallable, Category = "UI|Reglages")
void OpenSettings();
protected:
virtual void BeginPlay() override;
virtual void OnPossess(APawn* InPawn) override;
@@ -105,6 +123,17 @@ protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
TObjectPtr<UInputAction> HotbarScrollAction;
/**
* Echap : ferme l'inventaire s'il est ouvert, sinon bascule la pause.
*
* ATTENTION : coche "Trigger When Paused" sur l'asset IA_Pause. Un jeu en
* pause ne fait plus qu'un tick reduit ou seules les actions portant ce
* drapeau sont evaluees -- sans lui, on peut mettre en pause mais plus
* jamais en sortir au clavier.
*/
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
TObjectPtr<UInputAction> PauseAction;
/**
* Viseur, affiche en permanence. Widget separe du prompt : ce dernier se
* met en Collapsed des qu'on ne vise plus rien, et emporterait le viseur
@@ -129,6 +158,42 @@ protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
TSubclassOf<USurvivalStatsWidget> SurvivalStatsClass;
/** Ton WBP_PauseMenu. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Pause")
TSubclassOf<UPauseMenuWidget> PauseMenuClass;
/**
* Ton WBP_ConfirmDialog, le meme que celui du menu principal. Laisse-le
* assigne : sans lui on retourne au menu sans rien demander, et la partie
* est perdue sur un clic.
*/
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Pause")
TSubclassOf<UConfirmDialogWidget> QuitToMenuDialogClass;
/** La question posee avant d'abandonner la partie. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Pause")
FText QuitToMenuMessage;
/**
* Ton WBP_Settings, le meme que celui du menu principal. C'est tout
* l'interet de son delegue OnCloseRequested : l'ecran ignore qui l'ouvre.
*/
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Reglages")
TSubclassOf<USettingsMenuWidget> SettingsMenuClass;
/**
* Ta map de menu principal.
*
* TSoftObjectPtr comme dans AMainMenuPlayerController : une reference dure
* ferait charger la map du menu en meme temps que celle du jeu.
*/
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Pause")
TSoftObjectPtr<UWorld> MainMenuLevel;
/** Fondu au noir avant de rendre la main au menu principal. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Fondu", meta = (ClampMin = "0.1"))
float QuitToMenuFadeDuration = 1.f;
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI")
TObjectPtr<UUserWidget> Crosshair;
@@ -144,9 +209,43 @@ protected:
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI")
TObjectPtr<USurvivalStatsWidget> SurvivalStatsWidget;
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI|Pause")
TObjectPtr<UPauseMenuWidget> PauseMenu;
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI|Pause")
TObjectPtr<UConfirmDialogWidget> QuitToMenuDialog;
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI|Reglages")
TObjectPtr<USettingsMenuWidget> SettingsMenu;
private:
void CreateHudWidgets();
/**
* Echap. Une seule touche pour trois usages, d'ou une priorite explicite :
* boite de confirmation, puis inventaire, puis pause. Sans cet ordre on se
* retrouve avec l'inventaire et la pause ouverts en meme temps.
*/
void HandlePauseInput();
void OpenPauseMenu();
/** Oui : fondu au noir, puis retour au menu principal. */
UFUNCTION()
void HandleQuitToMenuConfirmed();
/** Non : on referme la boite et on rend la main au menu de pause. */
UFUNCTION()
void HandleQuitToMenuCancelled();
/** Le fondu est termine : on charge la map du menu, si c'est ce qu'on attendait. */
UFUNCTION()
void HandleFadeFinished();
/** L'ecran de reglages demande a se fermer : on rend la main au menu de pause. */
UFUNCTION()
void HandleSettingsCloseRequested();
/** Jette la pile survolee dans la grille, si l'inventaire est ouvert. */
void DropHoveredItem();
@@ -166,4 +265,18 @@ private:
FTimerHandle RespawnTimerHandle;
bool bInventoryOpen = false;
bool bPauseMenuOpen = false;
/**
* La boite de confirmation avale Echap elle-meme quand elle a le focus,
* mais un clic ailleurs le lui retire : sans ce booleen, Echap refermerait
* alors le menu de pause en laissant la question affichee dans le vide.
*/
bool bQuitToMenuDialogOpen = false;
bool bSettingsOpen = false;
/** Distingue le fondu de sortie de tous les autres dans HandleFadeFinished. */
bool bQuittingToMainMenu = false;
};
@@ -10,6 +10,7 @@ class AMenuCameraSpot;
class UConfirmDialogWidget;
class UMainMenuWidget;
class UScreenFadeComponent;
class USettingsMenuWidget;
/**
* Ce qu'on fait une fois l'ecran devenu noir.
@@ -45,6 +46,10 @@ public:
UFUNCTION(BlueprintCallable, Category = "Menu")
void QuitGame();
/** Ouvre l'ecran de reglages par-dessus le menu. */
UFUNCTION(BlueprintCallable, Category = "Menu")
void OpenSettings();
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
@@ -75,6 +80,10 @@ protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu")
FText QuitDialogMessage;
/** Ton WBP_Settings, exactement le meme que celui du menu de pause. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu")
TSubclassOf<USettingsMenuWidget> SettingsMenuClass;
/** Fondu d'arrivee sur le menu, au lancement du jeu. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu", meta = (ClampMin = "0.1"))
float OpeningFadeDuration = 1.5f;
@@ -93,6 +102,9 @@ protected:
UPROPERTY(Transient, BlueprintReadOnly, Category = "Menu")
TObjectPtr<UConfirmDialogWidget> QuitDialog;
UPROPERTY(Transient, BlueprintReadOnly, Category = "Menu")
TObjectPtr<USettingsMenuWidget> SettingsMenu;
private:
/** Tire un angle de vue au hasard, en evitant celui du dernier passage. */
AMenuCameraSpot* PickCameraSpot();
@@ -109,6 +121,10 @@ private:
UFUNCTION()
void HandleQuitCancelled();
/** L'ecran de reglages demande a se fermer : on rend la main au menu. */
UFUNCTION()
void HandleSettingsCloseRequested();
/** Ferme le jeu sans rien demander. */
void CloseGame();
@@ -35,6 +35,10 @@ protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> QuitButton;
/** BindWidgetOptional : un WBP_MainMenu sans bouton Reglages reste valide. */
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UButton> SettingsButton;
/**
* Coop a venir. BindWidgetOptional et non BindWidget : tu peux dessiner ton
* WBP_MainMenu sans eux aujourd'hui, le Blueprint compilera quand meme.
@@ -52,4 +56,7 @@ private:
UFUNCTION()
void HandleQuitClicked();
UFUNCTION()
void HandleSettingsClicked();
};
@@ -0,0 +1,57 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "PauseMenuWidget.generated.h"
class UButton;
/**
* Le menu de pause : trois boutons par-dessus la partie figee.
*
* Comme UMainMenuWidget, il ne decide de rien -- il appelle
* AFpsPlayerController. Mettre SetPause ou OpenLevel ici serait doublement
* faux : un widget n'existe que sur la machine du joueur local, et la mise en
* pause appartient a l'autorite.
*
* Classe Abstract : on cree un WBP_PauseMenu qui en herite.
*/
UCLASS(Abstract)
class SURVIVAL_PROJET_API UPauseMenuWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
protected:
/** Reprend la partie. Nomme exactement "ResumeButton" dans le WBP. */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> ResumeButton;
/** Retour au menu principal, apres confirmation. */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> QuitToMenuButton;
/**
* BindWidgetOptional : un WBP_PauseMenu sans bouton Reglages reste valide.
*
* Pas de bouton "Quitter le jeu" ici, volontairement : la sortie se fait
* depuis le menu principal, ou l'on repasse de toute facon.
*/
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UButton> SettingsButton;
private:
UFUNCTION()
void HandleResumeClicked();
UFUNCTION()
void HandleSettingsClicked();
UFUNCTION()
void HandleQuitToMenuClicked();
};
@@ -0,0 +1,127 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "SettingRowWidget.generated.h"
class UButton;
class UProgressBar;
class UTextBlock;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSettingRowIndexChanged, int32, NewIndex);
/**
* Une ligne de reglage : un libelle, une fleche gauche, une valeur, une fleche
* droite, et une barre de remplissage optionnelle derriere la valeur.
*
* TOUT reglage est ramene a un index dans une liste de paliers, y compris les
* nombres : "Champ de vision, 70 a 120 par pas de 5" devient un index de 0 a 10.
* La ligne ne connait donc jamais que CurrentIndex et StepCount, et c'est ce qui
* permet de n'avoir qu'UN SEUL WBP a dessiner pour tout le menu, qu'il s'agisse
* d'un choix (Maintien / Bascule) ou d'une valeur continue.
*
* Elle ne sait pas ce qu'elle regle : elle previent via OnIndexChanged, et c'est
* l'onglet qui traduit l'index en appel sur USurvivalUserSettings. Meme principe
* que UConfirmDialogWidget, qui ignore ce qu'il confirme.
*
* Classe Abstract : on cree un WBP_SettingRow qui en herite.
*/
UCLASS(Abstract)
class SURVIVAL_PROJET_API USettingRowWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeOnInitialized() override;
/** Previent d'un changement venant du joueur, jamais d'un rafraichissement. */
UPROPERTY(BlueprintAssignable, Category = "Reglages")
FOnSettingRowIndexChanged OnIndexChanged;
/**
* Mode choix : la valeur affichee est prise telle quelle dans Options.
* Sert aussi bien a un oui/non qu'a une liste de qualites.
*/
UFUNCTION(BlueprintCallable, Category = "Reglages")
void SetupAsChoice(const FText& Label, const TArray<FText>& Options, int32 Index);
/**
* Mode nombre : la valeur affichee est calculee a partir de l'index.
* Suffix sert aux unites ("%", "°") et peut rester vide.
*/
UFUNCTION(BlueprintCallable, Category = "Reglages")
void SetupAsNumber(const FText& Label, float Min, float Max, float Step, float Value, const FText& Suffix);
UFUNCTION(BlueprintPure, Category = "Reglages")
int32 GetIndex() const { return CurrentIndex; }
/** Reconvertit l'index en valeur reelle. Sans objet en mode choix. */
UFUNCTION(BlueprintPure, Category = "Reglages")
float GetNumericValue() const;
/**
* Repositionne la ligne sans rien diffuser.
*
* A utiliser pour tout rafraichissement venant des reglages : diffuser ici
* ferait boucler l'onglet sur lui-meme (il reecrit le reglage, qui rafraichit
* la ligne, qui rediffuse...).
*/
UFUNCTION(BlueprintCallable, Category = "Reglages")
void SetIndexSilently(int32 NewIndex);
/** Repositionne la ligne sur une valeur reelle, sans rien diffuser. */
UFUNCTION(BlueprintCallable, Category = "Reglages")
void SetNumericValueSilently(float Value);
protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UTextBlock> LabelText;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UTextBlock> ValueText;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> LeftArrowButton;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> RightArrowButton;
/**
* Facultative : une ligne de choix (Maintien / Bascule) n'a rien a remplir.
* Volontairement une ProgressBar et non un Slider : la barre montre ou l'on
* en est, ce sont les fleches qui reglent. Un slider glissable produirait des
* valeurs entre deux paliers que le setter refuserait ensuite.
*/
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UProgressBar> ValueBar;
private:
UFUNCTION()
void HandleLeftClicked();
UFUNCTION()
void HandleRightClicked();
/** Deplace l'index de Delta paliers et diffuse, si le mouvement est possible. */
void StepIndex(int32 Delta);
/** Reecrit le texte, la barre et l'etat des deux fleches. */
void Refresh();
/** Vide en mode nombre. */
TArray<FText> ChoiceLabels;
FText NumericSuffix;
float NumericMin = 0.f;
float NumericStep = 1.f;
int32 CurrentIndex = 0;
/** Nombre de paliers, donc d'index valides. Toujours au moins 1. */
int32 StepCount = 1;
bool bNumeric = false;
};
@@ -0,0 +1,89 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "SettingsTabWidget.h"
#include "SettingsGameplayWidget.generated.h"
class USettingRowWidget;
class USurvivalUserSettings;
/**
* L'onglet "Jeu & Controles".
*
* Il ne stocke rien : chaque ligne ecrit directement dans USurvivalUserSettings,
* qui diffuse au pawn dans la foulee. C'est ce qui rend l'effet immediat sans
* bouton "Appliquer".
*
* Classe Abstract : on cree un WBP_SettingsGameplay qui en herite, avec un
* WBP_SettingRow par ligne, nomme exactement comme le BindWidget correspondant.
*/
UCLASS(Abstract)
class SURVIVAL_PROJET_API USettingsGameplayWidget : public USettingsTabWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void RefreshFromSettings() override;
virtual void ResetToDefaults() override;
protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<USettingRowWidget> SensitivityRow;
UPROPERTY(meta = (BindWidget))
TObjectPtr<USettingRowWidget> SeparateVerticalRow;
/** Masquee tant que la sensibilite verticale n'est pas separee. */
UPROPERTY(meta = (BindWidget))
TObjectPtr<USettingRowWidget> SensitivityVerticalRow;
UPROPERTY(meta = (BindWidget))
TObjectPtr<USettingRowWidget> InvertLookRow;
UPROPERTY(meta = (BindWidget))
TObjectPtr<USettingRowWidget> FieldOfViewRow;
UPROPERTY(meta = (BindWidget))
TObjectPtr<USettingRowWidget> CameraShakeRow;
UPROPERTY(meta = (BindWidget))
TObjectPtr<USettingRowWidget> SprintModeRow;
UPROPERTY(meta = (BindWidget))
TObjectPtr<USettingRowWidget> CrouchModeRow;
private:
UFUNCTION()
void HandleSensitivityChanged(int32 NewIndex);
UFUNCTION()
void HandleSeparateVerticalChanged(int32 NewIndex);
UFUNCTION()
void HandleSensitivityVerticalChanged(int32 NewIndex);
UFUNCTION()
void HandleInvertLookChanged(int32 NewIndex);
UFUNCTION()
void HandleFieldOfViewChanged(int32 NewIndex);
UFUNCTION()
void HandleCameraShakeChanged(int32 NewIndex);
UFUNCTION()
void HandleSprintModeChanged(int32 NewIndex);
UFUNCTION()
void HandleCrouchModeChanged(int32 NewIndex);
/** Ecrit puis diffuse. Tous les handlers finissent ici. */
void PushToPawn(USurvivalUserSettings* Settings);
/** Affiche ou masque la ligne de sensibilite verticale. */
void UpdateVerticalRowVisibility(bool bSeparate);
};
@@ -0,0 +1,135 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "SettingsMenuWidget.generated.h"
class UButton;
class UConfirmDialogWidget;
class USettingsTabWidget;
class UWidgetSwitcher;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSettingsCloseRequested);
/**
* L'ecran de reglages : une barre d'onglets, un WidgetSwitcher, Retour et
* Reinitialiser.
*
* Il ne sait PAS qui l'a ouvert. Il demande a se fermer via OnCloseRequested et
* c'est l'appelant -- menu principal ou menu de pause -- qui decide de ce que
* "Retour" veut dire. Sans ca il faudrait un ecran par contexte.
*
* Il ne connait pas davantage le contenu de ses onglets : il appelle
* RefreshFromSettings et ResetToDefaults sur USettingsTabWidget. Ajouter
* l'onglet Graphismes ne demandera aucune ligne ici.
*
* Classe Abstract : on cree un WBP_Settings qui en herite.
*/
UCLASS(Abstract)
class SURVIVAL_PROJET_API USettingsMenuWidget : public UUserWidget
{
GENERATED_BODY()
public:
USettingsMenuWidget(const FObjectInitializer& ObjectInitializer);
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
UPROPERTY(BlueprintAssignable, Category = "Reglages")
FOnSettingsCloseRequested OnCloseRequested;
/**
* Affiche l'ecran, rafraichit l'onglet visible et prend le focus clavier.
*
* Passe par ici et jamais par SetVisibility : sans le focus, Echap ne
* refermerait pas l'ecran.
*/
UFUNCTION(BlueprintCallable, Category = "Reglages")
void Open();
/** Masque l'ecran et ecrit le fichier de reglages. */
UFUNCTION(BlueprintCallable, Category = "Reglages")
void Close();
protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UWidgetSwitcher> TabSwitcher;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> BackButton;
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UButton> ResetButton;
/**
* Les onglets a venir. Grises automatiquement tant que le WidgetSwitcher ne
* contient pas la page correspondante -- meme principe que les boutons de
* coop du menu principal : ils annoncent la couleur sans mener nulle part.
*/
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UButton> GameplayTabButton;
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UButton> DisplayTabButton;
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UButton> GraphicsTabButton;
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UButton> AudioTabButton;
/**
* Boite de confirmation POSEE DANS le WBP_Settings, et non ajoutee au
* viewport : elle se retrouve ainsi naturellement au-dessus de l'ecran de
* reglages, sans avoir a lui trouver un ZOrder qui n'entre en conflit ni
* avec le menu de pause ni avec la boite d'abandon de partie.
*
* Absente, "Reinitialiser" agit sans rien demander.
*/
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UConfirmDialogWidget> ResetDialog;
/** La question posee avant de reinitialiser l'onglet visible. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
FText ResetDialogMessage;
private:
UFUNCTION()
void HandleBackClicked();
UFUNCTION()
void HandleResetClicked();
UFUNCTION()
void HandleResetConfirmed();
UFUNCTION()
void HandleResetCancelled();
UFUNCTION()
void HandleGameplayTabClicked();
UFUNCTION()
void HandleDisplayTabClicked();
UFUNCTION()
void HandleGraphicsTabClicked();
UFUNCTION()
void HandleAudioTabClicked();
/** Change de page et rafraichit la nouvelle. Ignore un index inexistant. */
void ShowTab(int32 Index);
/** La page actuellement affichee, ou nullptr si le switcher est vide. */
USettingsTabWidget* GetActiveTab() const;
/** Grise les boutons dont la page n'existe pas encore. */
void UpdateTabButtonStates();
bool bResetDialogOpen = false;
};
@@ -0,0 +1,27 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "SettingsTabWidget.generated.h"
/**
* Base commune a tous les onglets de reglages.
*
* Deux verbes, rien de plus : l'ecran parent rafraichit ou reinitialise l'onglet
* visible sans jamais savoir ce qu'il contient. Ajouter l'onglet Graphismes plus
* tard ne demandera donc aucune ligne dans USettingsMenuWidget.
*/
UCLASS(Abstract)
class SURVIVAL_PROJET_API USettingsTabWidget : public UUserWidget
{
GENERATED_BODY()
public:
/** Relit les reglages et repositionne les lignes, sans rien diffuser. */
virtual void RefreshFromSettings() {}
/** Remet a l'usine les seuls reglages de cet onglet, puis se rafraichit. */
virtual void ResetToDefaults() {}
};
@@ -24,6 +24,8 @@ class SURVIVAL_PROJET_API USurvivalGameInstance : public UGameInstance
GENERATED_BODY()
public:
virtual void Init() override;
/** Nom de la derniere camera de menu utilisee. NAME_None au premier lancement. */
UFUNCTION(BlueprintPure, Category = "Menu")
FName GetLastMenuCameraName() const { return LastMenuCameraName; }
@@ -0,0 +1,198 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameUserSettings.h"
#include "SurvivalUserSettings.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSurvivalSettingsApplied);
/**
* Bornes et pas des reglages numeriques.
*
* Ici et pas dans le widget : c'est l'objet de reglages qui borne ses propres
* valeurs, et l'ecran doit proposer exactement les memes paliers. Deux jeux de
* constantes finiraient par diverger et le menu afficherait des valeurs que le
* setter refuse.
*/
namespace SurvivalSettingsRange
{
constexpr float SensitivityMin = 5.f;
constexpr float SensitivityMax = 100.f;
constexpr float SensitivityStep = 5.f;
constexpr float FieldOfViewMin = 70.f;
constexpr float FieldOfViewMax = 120.f;
constexpr float FieldOfViewStep = 5.f;
constexpr float CameraShakeMin = 0.f;
constexpr float CameraShakeMax = 100.f;
constexpr float CameraShakeStep = 10.f;
/**
* Multiplicateurs reels correspondant aux extremites de l'echelle affichee.
*
* Le joueur regle un nombre de 5 a 100 parce que "0.4" ne veut rien dire ;
* le pawn, lui, veut le multiplicateur. Le defaut de 35 retombe sur ~0.41,
* soit la valeur qui etait codee en dur dans AFpsPlayer.
*/
constexpr float LookMultiplierAtMin = 0.05f;
constexpr float LookMultiplierAtMax = 1.2f;
}
/**
* Les reglages du joueur, et le seul endroit qui fasse autorite dessus.
*
* Sous-classe de UGameUserSettings et non un objet a nous : le moteur fournit
* deja le singleton, sa persistance dans Saved/Config/Windows/GameUserSettings.ini
* et tout le volet video (resolution, VSync, qualite). C'est l'equivalent d'un
* PlayerPrefs + QualitySettings + Screen d'Unity reunis, sauf qu'il est deja
* ecrit. On l'etend, on ne le remplace pas.
*
* Pour que le moteur instancie CETTE classe et pas la sienne, Config/DefaultEngine.ini
* contient :
* [/Script/Engine.Engine]
* GameUserSettingsClassName=/Script/Survival_projet.SurvivalUserSettings
*
* Les UPROPERTY(config) sont lues et ecrites automatiquement : aucun code de
* serialisation a maintenir.
*/
UCLASS()
class SURVIVAL_PROJET_API USurvivalUserSettings : public UGameUserSettings
{
GENERATED_BODY()
public:
USurvivalUserSettings();
/**
* Le raccourci typé vers le singleton. GEngine->GetGameUserSettings()
* renvoie la classe de base ; ce helper evite un cast a chaque appel.
*/
UFUNCTION(BlueprintPure, Category = "Reglages", meta = (DisplayName = "Get Survival User Settings"))
static USurvivalUserSettings* Get();
/**
* Previent tout le monde que les reglages de jeu ont change.
*
* Indispensable a cause du respawn : le pawn est un NOUVEL acteur a chaque
* mort, et une valeur poussee une fois dans l'ancien serait perdue. Chaque
* pawn lit les reglages a son BeginPlay et s'abonne ici pour la suite.
*/
UPROPERTY(BlueprintAssignable, Category = "Reglages")
FOnSurvivalSettingsApplied OnSurvivalSettingsApplied;
/**
* Diffuse les reglages de jeu, sans toucher au volet video.
*
* Distinct d'ApplyNonResolutionSettings, qui repousse aussi la qualite et la
* limite de FPS : l'appeler a chaque clic de fleche sur la sensibilite
* provoquerait un a-coup pour rien.
*/
UFUNCTION(BlueprintCallable, Category = "Reglages")
void ApplySurvivalSettings();
/** Remet les seuls reglages de jeu a leur valeur d'usine. */
UFUNCTION(BlueprintCallable, Category = "Reglages")
void SetSurvivalGameplayToDefaults();
virtual void SetToDefaults() override;
virtual void ApplyNonResolutionSettings() override;
// ------------------------------------------------------------------
// Valeurs pretes a l'emploi
//
// Le pawn ne doit jamais lire les champs bruts : ce sont des valeurs
// d'affichage (5 a 100), pas des multiplicateurs.
// ------------------------------------------------------------------
UFUNCTION(BlueprintPure, Category = "Reglages|Controles")
float GetLookMultiplierX() const;
UFUNCTION(BlueprintPure, Category = "Reglages|Controles")
float GetLookMultiplierY() const;
/** 0 = camera parfaitement fixe, 1 = amplitude d'origine. */
UFUNCTION(BlueprintPure, Category = "Reglages|Jeu")
float GetCameraShakeMultiplier() const;
// ------------------------------------------------------------------
// Accesseurs bruts, pour l'ecran de reglages
// ------------------------------------------------------------------
UFUNCTION(BlueprintPure, Category = "Reglages|Controles")
float GetLookSensitivity() const { return LookSensitivity; }
UFUNCTION(BlueprintCallable, Category = "Reglages|Controles")
void SetLookSensitivity(float Value);
UFUNCTION(BlueprintPure, Category = "Reglages|Controles")
bool GetSeparateVerticalSensitivity() const { return bSeparateVerticalSensitivity; }
UFUNCTION(BlueprintCallable, Category = "Reglages|Controles")
void SetSeparateVerticalSensitivity(bool bValue);
UFUNCTION(BlueprintPure, Category = "Reglages|Controles")
float GetLookSensitivityVertical() const { return LookSensitivityVertical; }
UFUNCTION(BlueprintCallable, Category = "Reglages|Controles")
void SetLookSensitivityVertical(float Value);
UFUNCTION(BlueprintPure, Category = "Reglages|Controles")
bool GetInvertLookY() const { return bInvertLookY; }
UFUNCTION(BlueprintCallable, Category = "Reglages|Controles")
void SetInvertLookY(bool bValue);
UFUNCTION(BlueprintPure, Category = "Reglages|Jeu")
float GetFieldOfView() const { return FieldOfView; }
UFUNCTION(BlueprintCallable, Category = "Reglages|Jeu")
void SetFieldOfView(float Value);
UFUNCTION(BlueprintPure, Category = "Reglages|Jeu")
float GetCameraShakeScale() const { return CameraShakeScale; }
UFUNCTION(BlueprintCallable, Category = "Reglages|Jeu")
void SetCameraShakeScale(float Value);
UFUNCTION(BlueprintPure, Category = "Reglages|Controles")
bool GetSprintToggle() const { return bSprintToggle; }
UFUNCTION(BlueprintCallable, Category = "Reglages|Controles")
void SetSprintToggle(bool bValue);
UFUNCTION(BlueprintPure, Category = "Reglages|Controles")
bool GetCrouchToggle() const { return bCrouchToggle; }
UFUNCTION(BlueprintCallable, Category = "Reglages|Controles")
void SetCrouchToggle(bool bValue);
private:
/** Echelle lisible (5 a 100), convertie a la lecture. Jamais brute. */
UPROPERTY(config)
float LookSensitivity;
UPROPERTY(config)
bool bSeparateVerticalSensitivity;
UPROPERTY(config)
float LookSensitivityVertical;
UPROPERTY(config)
bool bInvertLookY;
UPROPERTY(config)
float FieldOfView;
/** Pourcentage. A 0, plus aucun impact camera : pour le mal des transports. */
UPROPERTY(config)
float CameraShakeScale;
UPROPERTY(config)
bool bSprintToggle;
UPROPERTY(config)
bool bCrouchToggle;
};