(Feat) Add Main Menu + Music Manger
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Containers/Ticker.h"
|
||||
#include "ConfirmDialogWidget.generated.h"
|
||||
|
||||
class UButton;
|
||||
@@ -48,6 +49,20 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Dialogue")
|
||||
void Open(const FText& InMessage);
|
||||
|
||||
/**
|
||||
* Meme chose, mais avec un retour automatique passe le delai.
|
||||
*
|
||||
* @param InMessageFormat doit contenir {0}, remplace par les secondes
|
||||
* restantes. Exemple : "Garder ce mode ?\nRetour
|
||||
* automatique dans {0} s."
|
||||
*
|
||||
* A l'expiration, OnCancelled est diffuse -- comme si le joueur avait
|
||||
* refuse. C'est le seul filet quand la question porte sur un changement qui
|
||||
* peut rendre l'ecran illisible.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Dialogue")
|
||||
void OpenWithCountdown(const FText& InMessageFormat, float Seconds);
|
||||
|
||||
/** Referme la boite. Ne previent personne : l'appelant sait ce qu'il fait. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Dialogue")
|
||||
void Close();
|
||||
@@ -82,4 +97,27 @@ private:
|
||||
|
||||
UFUNCTION()
|
||||
void HandleCancelClicked();
|
||||
|
||||
/** Un tick par frame tant que le rebours tourne. Renvoie false pour s'arreter. */
|
||||
bool TickCountdown(float DeltaTime);
|
||||
|
||||
void StopCountdown();
|
||||
|
||||
void RefreshCountdownText();
|
||||
|
||||
/**
|
||||
* Le rebours passe par le ticker du moteur et non par un timer du monde.
|
||||
*
|
||||
* Deux raisons : l'ecran de reglages s'ouvre depuis le menu de PAUSE, ou les
|
||||
* timers du monde sont geles -- le rebours ne repartirait jamais ; et un
|
||||
* changement de resolution rate doit se defaire meme si le jeu bloque.
|
||||
*/
|
||||
FTSTicker::FDelegateHandle CountdownTickerHandle;
|
||||
|
||||
FText CountdownMessageFormat;
|
||||
|
||||
float CountdownRemaining = 0.f;
|
||||
|
||||
/** Evite de reecrire le texte 60 fois par seconde pour rien. */
|
||||
int32 LastDisplayedSeconds = -1;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "KeyCaptureWidget.generated.h"
|
||||
|
||||
class UButton;
|
||||
class UTextBlock;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnKeyCaptured, FKey, CapturedKey);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnKeyCaptureCancelled);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnKeyConflictAnswered, bool, bConfirmed);
|
||||
|
||||
/** Ce que le voile attend a un instant donne. */
|
||||
UENUM()
|
||||
enum class EKeyCaptureState : uint8
|
||||
{
|
||||
/** Ferme. */
|
||||
Idle,
|
||||
|
||||
/** Ouvert, en attente d'une touche. Les boutons sont replies. */
|
||||
Capturing,
|
||||
|
||||
/** Ouvert, la touche est prise : on demande quoi faire. Les boutons apparaissent. */
|
||||
Conflict
|
||||
};
|
||||
|
||||
/**
|
||||
* Le voile de remappage, en deux temps : "Appuie sur une touche", puis
|
||||
* eventuellement "cette touche est deja prise, on la vole ?".
|
||||
*
|
||||
* Les deux etapes vivent dans le MEME widget et non dans deux ecrans successifs.
|
||||
* Le conflit fait partie du geste de remappage : refermer le voile pour ouvrir
|
||||
* une boite par-dessus produirait un clignotement et ferait perdre le fil de ce
|
||||
* qu'on etait en train de modifier. UConfirmDialogWidget garde les questions
|
||||
* generiques -- reinitialiser, abandonner une partie, valider une resolution.
|
||||
*
|
||||
* Classe Abstract : on cree un WBP_KeyCapture qui en herite.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API UKeyCaptureWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UKeyCaptureWidget(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
virtual void NativeOnInitialized() override;
|
||||
virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
|
||||
virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Reglages")
|
||||
FOnKeyCaptured OnKeyCaptured;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Reglages")
|
||||
FOnKeyCaptureCancelled OnCaptureCancelled;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Reglages")
|
||||
FOnKeyConflictAnswered OnConflictAnswered;
|
||||
|
||||
/** Affiche le voile, ecrit la consigne, replie les boutons et prend le focus. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void BeginCapture(const FText& ActionLabel);
|
||||
|
||||
/**
|
||||
* Bascule le voile deja ouvert sur la question de conflit.
|
||||
*
|
||||
* Ne referme rien : c'est tout l'interet d'avoir garde un seul widget.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void ShowConflict(const FText& Message);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void EndCapture();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Reglages")
|
||||
EKeyCaptureState GetCaptureState() const { return State; }
|
||||
|
||||
protected:
|
||||
/** Optionnel : un voile dont le texte est fige dans le Blueprint reste valide. */
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UTextBlock> PromptText;
|
||||
|
||||
/**
|
||||
* Boutons de la seule etape de conflit. Replies pendant la capture : les
|
||||
* afficher inertes inviterait a cliquer alors qu'on attend une touche.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UButton> ConfirmButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UButton> CancelButton;
|
||||
|
||||
/** Doit contenir {0}, remplace par le nom de l'action en cours de remappage. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
|
||||
FText PromptFormat;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleConfirmClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleCancelClicked();
|
||||
|
||||
/** Facteur commun a NativeOnKeyDown et NativeOnMouseButtonDown. */
|
||||
FReply CaptureKey(const FKey& Key);
|
||||
|
||||
/** Affiche ou replie les deux boutons selon l'etat. */
|
||||
void UpdateButtonVisibility();
|
||||
|
||||
EKeyCaptureState State = EKeyCaptureState::Idle;
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Info.h"
|
||||
#include "MusicSubsystem.h"
|
||||
#include "MusicDirector.generated.h"
|
||||
|
||||
class UMusicPlaylistDataAsset;
|
||||
|
||||
/**
|
||||
* Declare la musique du jeu. UN SEUL exemplaire suffit, dans la premiere map
|
||||
* chargee -- typiquement la scene de menu.
|
||||
*
|
||||
* Il ne joue rien lui-meme et ne survit pas a sa map : il transmet sa consigne a
|
||||
* UMusicSubsystem, qui, lui, vit sur le GameInstance et traverse tous les
|
||||
* changements de niveau. C'est pour ca que les maps suivantes n'ont besoin de
|
||||
* rien : la musique continue toute seule, et la regle de transition a deja ete
|
||||
* posee ici.
|
||||
*
|
||||
* En poser un second dans une autre map reste possible : il change alors de
|
||||
* playlist a l'arrivee sur celle-la.
|
||||
*
|
||||
* Derive d'AInfo : aucun visuel, aucun tick, et AInfo se declare non
|
||||
* spatialement charge, donc il est present des le demarrage meme en World
|
||||
* Partition -- un acteur ordinaire hors de la zone de streaming ne ferait jamais
|
||||
* son BeginPlay.
|
||||
*/
|
||||
UCLASS(ClassGroup = "Audio", meta = (DisplayName = "Music Director"))
|
||||
class SURVIVAL_PROJET_API AMusicDirector : public AInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMusicDirector();
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/**
|
||||
* La playlist a lancer en arrivant sur cette map.
|
||||
*
|
||||
* Si c'est deja elle qui tourne, elle n'est pas relancee. La laisser vide veut
|
||||
* dire "je ne change pas la musique en cours", et n'a d'interet que sur un
|
||||
* second Director pose uniquement pour changer la regle de transition.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Musique")
|
||||
TObjectPtr<UMusicPlaylistDataAsset> Playlist;
|
||||
|
||||
/**
|
||||
* Ce que devient le morceau en cours a CHAQUE changement de scene suivant.
|
||||
*
|
||||
* Continue : il poursuit sa route par-dessus le chargement.
|
||||
* New : il s'eteint et le lecteur en demarre un autre -- tirage aleatoire
|
||||
* ou morceau suivant, selon le mode de la playlist.
|
||||
*
|
||||
* La consigne est envoyee au lecteur, qui la garde : les autres maps n'ont
|
||||
* rien a declarer.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Musique")
|
||||
EMusicLevelChangeMode LevelChangeMode = EMusicLevelChangeMode::Continue;
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "MusicPlaylistDataAsset.generated.h"
|
||||
|
||||
class USoundBase;
|
||||
|
||||
/**
|
||||
* Une liste de morceaux et la facon de les enchainer.
|
||||
*
|
||||
* DataAsset et pas une variable sur un acteur : c'est de la DONNEE, au meme titre
|
||||
* qu'un UItemDataAsset. On en cree autant qu'on veut (menu, exploration, nuit,
|
||||
* combat) sans toucher au code, et la meme playlist peut etre referencee par
|
||||
* plusieurs maps -- ce qui est exactement ce qui permet au menu et au jeu de
|
||||
* partager une musique qui ne se coupe pas entre les deux.
|
||||
*
|
||||
* Aucun etat ici : l'index en cours, l'ordre tire au sort et le volume vivent
|
||||
* dans UMusicSubsystem. Un DataAsset est partage par toutes les references, y
|
||||
* mettre de l'etat le partagerait aussi.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class SURVIVAL_PROJET_API UMusicPlaylistDataAsset : public UPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Nom lisible, pour un futur affichage "en cours de lecture". */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist")
|
||||
FText DisplayName;
|
||||
|
||||
/**
|
||||
* Les morceaux, dans l'ordre de lecture quand l'aleatoire est desactive.
|
||||
*
|
||||
* Les cases vides sont ignorees a la lecture : une reference oubliee ne coupe
|
||||
* pas la musique, elle est simplement sautee.
|
||||
*
|
||||
* IMPORTANT : aucun de ces sons ne doit boucler sur lui-meme (ni "Looping" sur
|
||||
* la SoundWave, ni un noeud Looping dans la SoundCue). L'enchainement repose
|
||||
* sur OnAudioFinished, qui n'est jamais diffuse par un son qui boucle.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist")
|
||||
TArray<TObjectPtr<USoundBase>> Tracks;
|
||||
|
||||
/**
|
||||
* Mode par defaut au demarrage de CETTE playlist.
|
||||
*
|
||||
* Le joueur peut ensuite basculer en cours de route via
|
||||
* UMusicSubsystem::SetShuffleEnabled ; cette valeur n'est relue qu'au
|
||||
* prochain demarrage de playlist.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist")
|
||||
bool bShuffle = true;
|
||||
|
||||
/** Faux = la musique s'arrete quand tous les morceaux ont ete joues une fois. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist")
|
||||
bool bLoop = true;
|
||||
|
||||
/**
|
||||
* Silence entre deux morceaux, tire au hasard dans l'intervalle.
|
||||
*
|
||||
* Une musique d'ambiance qui respire quelques secondes se remarque beaucoup
|
||||
* moins qu'un flux continu, et c'est ce qui evite la lassitude sur une longue
|
||||
* session. A zero, l'enchainement est immediat.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist", meta = (ClampMin = "0.0", Units = "s"))
|
||||
float MinGapBetweenTracks = 0.f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist", meta = (ClampMin = "0.0", Units = "s"))
|
||||
float MaxGapBetweenTracks = 0.f;
|
||||
|
||||
/** Correction de niveau propre a cette playlist, par-dessus le volume joueur. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist", meta = (ClampMin = "0.0", ClampMax = "2.0"))
|
||||
float VolumeMultiplier = 1.f;
|
||||
|
||||
/** Fondu entrant au demarrage de la playlist (pas entre deux morceaux). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist", meta = (ClampMin = "0.0", Units = "s"))
|
||||
float FadeInDuration = 2.f;
|
||||
|
||||
/** Fondu sortant quand une autre playlist prend la main, ou a l'arret. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playlist", meta = (ClampMin = "0.0", Units = "s"))
|
||||
float FadeOutDuration = 2.f;
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Containers/Ticker.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "MusicSubsystem.generated.h"
|
||||
|
||||
class UAudioComponent;
|
||||
class UMusicPlaylistDataAsset;
|
||||
class USoundBase;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMusicTrackChanged, USoundBase*, NewTrack);
|
||||
|
||||
/**
|
||||
* Ce qui arrive au morceau en cours quand on change de scene.
|
||||
*
|
||||
* La question ne se pose que parce que le lecteur, lui, ne s'arrete jamais :
|
||||
* sans consigne, la musique du menu se poursuit telle quelle dans la partie, au
|
||||
* milieu du morceau ou elle en etait.
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EMusicLevelChangeMode : uint8
|
||||
{
|
||||
/** Le morceau en cours poursuit sa route, comme si de rien n'etait. */
|
||||
Continue UMETA(DisplayName = "Continue"),
|
||||
|
||||
/**
|
||||
* Le morceau en cours s'eteint et le suivant demarre : nouveau tirage si
|
||||
* l'aleatoire est actif, morceau suivant de la liste sinon. La playlist, elle,
|
||||
* ne change pas.
|
||||
*/
|
||||
New UMETA(DisplayName = "New")
|
||||
};
|
||||
|
||||
/**
|
||||
* Le lecteur de musique du jeu. Un seul, pour toute la duree de l'application.
|
||||
*
|
||||
* UGameInstanceSubsystem et pas un acteur : il naît avec le GameInstance et meurt
|
||||
* avec le jeu, exactement comme USurvivalGameInstance. Aucune map ne le possede,
|
||||
* donc aucun OpenLevel ne le detruit -- c'est le seul endroit d'ou une musique
|
||||
* peut traverser le passage menu -> partie sans se couper. Le moteur l'instancie
|
||||
* tout seul, il n'y a rien a poser dans une scene ni a cocher nulle part.
|
||||
*
|
||||
* Le composant audio lui-meme est cree via CreateSound2D avec
|
||||
* bPersistAcrossLevelTransition : le moteur le construit alors SANS monde et sans
|
||||
* proprietaire, et lui pose bIgnoreForFlushing -- le drapeau que le vidage du
|
||||
* peripherique audio, declenche a chaque changement de niveau, saute
|
||||
* explicitement. Sans ce parametre le son s'arreterait net au chargement, meme si
|
||||
* l'objet C++, lui, survivait.
|
||||
*
|
||||
* Effet de bord voulu : ces composants sont marques bIsUISound, donc ils
|
||||
* continuent de jouer quand le jeu est en pause. Le menu de pause garde sa
|
||||
* musique sans une ligne de plus.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API UMusicSubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
/** Raccourci depuis n'importe quel objet ayant un monde. */
|
||||
UFUNCTION(BlueprintPure, Category = "Musique", meta = (WorldContext = "WorldContextObject", DisplayName = "Get Music Subsystem"))
|
||||
static UMusicSubsystem* Get(const UObject* WorldContextObject);
|
||||
|
||||
/**
|
||||
* Demarre une playlist.
|
||||
*
|
||||
* Si c'est DEJA celle qui joue, ne fait rien : c'est ce comportement qui rend
|
||||
* la transition menu -> jeu inaudible quand les deux maps designent la meme
|
||||
* playlist. Si c'en est une autre, croisement entre l'ancienne qui s'eteint et
|
||||
* la nouvelle qui entre.
|
||||
*
|
||||
* @param bForceRestart Repart du debut meme si la playlist est deja en cours.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Musique")
|
||||
void PlayPlaylist(UMusicPlaylistDataAsset* Playlist, bool bForceRestart = false);
|
||||
|
||||
/** @param FadeOutDuration Negatif = utiliser le fondu declare par la playlist. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Musique")
|
||||
void StopMusic(float FadeOutDuration = -1.f);
|
||||
|
||||
/** Passe au morceau suivant, dans l'ordre courant (aleatoire ou non). */
|
||||
UFUNCTION(BlueprintCallable, Category = "Musique")
|
||||
void SkipToNextTrack();
|
||||
|
||||
/**
|
||||
* Bascule aleatoire / ordre de la liste.
|
||||
*
|
||||
* Ne coupe jamais le morceau en cours : seul l'enchainement change. Le curseur
|
||||
* se recale sur le morceau qui joue, donc en repassant en mode ordonne la
|
||||
* lecture reprend juste apres lui au lieu de sauter au debut de la liste.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Musique")
|
||||
void SetShuffleEnabled(bool bEnabled);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Musique")
|
||||
bool IsShuffleEnabled() const { return bShuffleEnabled; }
|
||||
|
||||
/**
|
||||
* Ce que devient le morceau en cours au prochain changement de scene.
|
||||
*
|
||||
* La consigne vit ICI et pas sur l'acteur qui l'a posee : l'acteur meurt avec
|
||||
* sa map, alors que le changement de scene se produit justement apres. Une
|
||||
* seule map -- le menu -- suffit donc a la declarer pour tout le jeu.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Musique")
|
||||
void SetLevelChangeMode(EMusicLevelChangeMode Mode) { LevelChangeMode = Mode; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Musique")
|
||||
EMusicLevelChangeMode GetLevelChangeMode() const { return LevelChangeMode; }
|
||||
|
||||
/** Volume joueur, 0 a 1. A brancher sur l'onglet Audio des reglages plus tard. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Musique")
|
||||
void SetMusicVolume(float NewVolume);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Musique")
|
||||
float GetMusicVolume() const { return MusicVolume; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Musique")
|
||||
bool IsPlaying() const { return bIsPlaying; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Musique")
|
||||
USoundBase* GetCurrentTrack() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Musique")
|
||||
UMusicPlaylistDataAsset* GetCurrentPlaylist() const { return CurrentPlaylist; }
|
||||
|
||||
/** Diffuse a chaque changement de morceau, nullptr a l'arret. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Musique")
|
||||
FOnMusicTrackChanged OnMusicTrackChanged;
|
||||
|
||||
private:
|
||||
/** Fin NATURELLE d'un morceau uniquement -- voir FadeOutAndRelease. */
|
||||
UFUNCTION()
|
||||
void HandleTrackFinished();
|
||||
|
||||
/** Applique LevelChangeMode a chaque map chargee. */
|
||||
void HandlePostLoadMap(UWorld* LoadedWorld);
|
||||
|
||||
/** (Re)construit l'ordre de lecture du cycle a venir. */
|
||||
void BuildPlayOrder();
|
||||
|
||||
void AdvanceAndPlay(float FadeInDuration);
|
||||
void PlayTrackAtCursor(float FadeInDuration);
|
||||
UAudioComponent* CreateMusicComponent(USoundBase* Sound);
|
||||
|
||||
/**
|
||||
* Detache puis eteint un composant : apres cet appel, plus rien de ce qu'il
|
||||
* diffuse ne revient vers nous.
|
||||
*/
|
||||
void FadeOutAndRelease(TObjectPtr<UAudioComponent>& Component, float FadeOutDuration);
|
||||
|
||||
void ScheduleGap(float DelaySeconds);
|
||||
void CancelPendingGap();
|
||||
float GetEffectiveVolume() const;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UMusicPlaylistDataAsset> CurrentPlaylist;
|
||||
|
||||
/**
|
||||
* UPROPERTY obligatoire : ces composants n'ont ni monde ni acteur proprietaire,
|
||||
* cette reference est la SEULE qui les tienne hors de portee du ramasse-miettes.
|
||||
*/
|
||||
UPROPERTY()
|
||||
TObjectPtr<UAudioComponent> MusicComponent;
|
||||
|
||||
/** La voie de sortie, le temps d'un croisement. Une seule a la fois. */
|
||||
UPROPERTY()
|
||||
TObjectPtr<UAudioComponent> FadingOutComponent;
|
||||
|
||||
/**
|
||||
* Index dans Tracks, deja melangee si l'aleatoire est actif.
|
||||
*
|
||||
* Un ordre pre-melange plutot qu'un tirage a chaque morceau : le tirage
|
||||
* independant rejoue le meme titre deux fois de suite une fois sur N, et sur
|
||||
* une playlist de cinq morceaux ca s'entend tout de suite.
|
||||
*/
|
||||
TArray<int32> PlayOrder;
|
||||
|
||||
int32 OrderCursor = INDEX_NONE;
|
||||
int32 LastPlayedTrackIndex = INDEX_NONE;
|
||||
|
||||
bool bShuffleEnabled = true;
|
||||
bool bIsPlaying = false;
|
||||
float MusicVolume = 1.f;
|
||||
|
||||
EMusicLevelChangeMode LevelChangeMode = EMusicLevelChangeMode::Continue;
|
||||
|
||||
/** Abonnement au chargement de map, retire a la fermeture du jeu. */
|
||||
FDelegateHandle PostLoadMapHandle;
|
||||
|
||||
/**
|
||||
* Le silence entre deux morceaux passe par le ticker de Core, pas par un
|
||||
* FTimerHandle : un timer appartient au monde, donc il disparaîtrait au
|
||||
* chargement de la map suivante -- et il ne tourne pas en pause. Le ticker de
|
||||
* Core ignore les deux.
|
||||
*/
|
||||
FTSTicker::FDelegateHandle GapTickerHandle;
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "SettingKeyRowWidget.generated.h"
|
||||
|
||||
class UButton;
|
||||
class UTextBlock;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSettingKeyRebindRequested, FName, MappingName);
|
||||
|
||||
/**
|
||||
* Une ligne de remappage : un libelle, et un bouton portant la touche actuelle.
|
||||
*
|
||||
* Widget distinct de USettingRowWidget et non un mode de plus : une touche n'est
|
||||
* pas un index dans une liste de paliers, il n'y a ni fleches ni curseur, et
|
||||
* l'interaction est d'une autre nature -- on ouvre un voile de capture.
|
||||
*
|
||||
* Elle ne sait pas remapper. Elle previent via OnRebindRequested et c'est
|
||||
* l'onglet qui pilote, comme partout ailleurs dans les reglages.
|
||||
*
|
||||
* Classe Abstract : on cree un WBP_SettingKeyRow qui en herite.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API USettingKeyRowWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeOnInitialized() override;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Reglages")
|
||||
FOnSettingKeyRebindRequested OnRebindRequested;
|
||||
|
||||
/** Pose le libelle et la touche. Le nom de mapping est renvoye tel quel au clic. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void Setup(FName InMappingName, const FText& Label, const FKey& Key);
|
||||
|
||||
/** Met a jour la seule touche affichee. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void SetKey(const FKey& Key);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Reglages")
|
||||
FName GetMappingName() const { return MappingName; }
|
||||
|
||||
protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UTextBlock> LabelText;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> KeyButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UTextBlock> KeyText;
|
||||
|
||||
/** Couleur du texte quand l'action n'a plus aucune touche. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
|
||||
FLinearColor UnboundColor = FLinearColor(0.9f, 0.2f, 0.2f);
|
||||
|
||||
/** Couleur normale. Reprise telle quelle a la premiere affectation valide. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
|
||||
FLinearColor BoundColor = FLinearColor::White;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleKeyButtonClicked();
|
||||
|
||||
/** Identifiant Enhanced Input du mapping, pas le libelle affiche. */
|
||||
FName MappingName = NAME_None;
|
||||
};
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
class UButton;
|
||||
class UProgressBar;
|
||||
class USlider;
|
||||
class UTextBlock;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSettingRowIndexChanged, int32, NewIndex);
|
||||
@@ -49,10 +50,17 @@ public:
|
||||
|
||||
/**
|
||||
* Mode nombre : la valeur affichee est calculee a partir de l'index.
|
||||
* Suffix sert aux unites ("%", "°") et peut rester vide.
|
||||
*
|
||||
* @param Step pas fin, celui du glissement. C'est lui qui definit la
|
||||
* granularite de l'index.
|
||||
* @param ArrowStep bond des fleches, forcement un multiple de Step. Les
|
||||
* fleches se calent sur le multiple suivant plutot que
|
||||
* d'ajouter betement leur bond : depuis 37 avec un bond de
|
||||
* 5 on arrive a 40, pas a 42.
|
||||
* @param Suffix unite ("%", "°"), peut rester vide.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void SetupAsNumber(const FText& Label, float Min, float Max, float Step, float Value, const FText& Suffix);
|
||||
void SetupAsNumber(const FText& Label, float Min, float Max, float Step, float ArrowStep, float Value, const FText& Suffix);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Reglages")
|
||||
int32 GetIndex() const { return CurrentIndex; }
|
||||
@@ -75,6 +83,18 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void SetNumericValueSilently(float Value);
|
||||
|
||||
/**
|
||||
* Derniere valeur que le JOUEUR peut atteindre, les suivantes restant
|
||||
* affichables par le code.
|
||||
*
|
||||
* Sert a "Personnalise" en qualite globale : c'est un etat dans lequel on
|
||||
* tombe en bougeant un curseur individuel, jamais un choix qu'on fait.
|
||||
* Sans ca la fleche droite proposerait de le "selectionner", ce qui ne veut
|
||||
* rien dire. INDEX_NONE pour lever la limite.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void SetMaxSelectableIndex(int32 InMaxSelectableIndex);
|
||||
|
||||
protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UTextBlock> LabelText;
|
||||
@@ -89,14 +109,29 @@ protected:
|
||||
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.
|
||||
* Le REMPLISSAGE visible, et rien d'autre.
|
||||
*
|
||||
* Le Slider d'Unreal ne sait pas dessiner de portion remplie : il rend une
|
||||
* barre uniforme plus une poignee. On empile donc les deux -- cette
|
||||
* ProgressBar pour le rendu, le Slider transparent par-dessus pour le
|
||||
* geste. C'est le montage habituel, pas un bricolage.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UProgressBar> ValueBar;
|
||||
|
||||
/**
|
||||
* Le GESTE, et rien d'autre : a styler entierement transparent dans le WBP.
|
||||
*
|
||||
* Sa course est exprimee en INDEX de palier (0 a StepCount-1) et non dans
|
||||
* l'unite du reglage -- c'est ce qui garantit qu'un glissement ne peut pas
|
||||
* produire une valeur hors palier, que le setter refuserait ensuite.
|
||||
*
|
||||
* Les deux sont facultatifs : une ligne de choix (Maintien / Bascule) les
|
||||
* replie tous les deux, il n'y a rien a doser.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<USlider> ValueSlider;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleLeftClicked();
|
||||
@@ -104,9 +139,19 @@ private:
|
||||
UFUNCTION()
|
||||
void HandleRightClicked();
|
||||
|
||||
/** Glissement souris : on arrondit sur le palier le plus proche. */
|
||||
UFUNCTION()
|
||||
void HandleSliderValueChanged(float NewValue);
|
||||
|
||||
/** Deplace l'index de Delta paliers et diffuse, si le mouvement est possible. */
|
||||
void StepIndex(int32 Delta);
|
||||
|
||||
/** Index vise par une fleche : le multiple de son bond suivant ou precedent. */
|
||||
int32 ArrowTargetIndex(int32 Direction) const;
|
||||
|
||||
/** Plus grand index atteignable par le joueur. */
|
||||
int32 MaxSelectable() const;
|
||||
|
||||
/** Reecrit le texte, la barre et l'etat des deux fleches. */
|
||||
void Refresh();
|
||||
|
||||
@@ -120,8 +165,23 @@ private:
|
||||
|
||||
int32 CurrentIndex = 0;
|
||||
|
||||
/** Nombre de paliers, donc d'index valides. Toujours au moins 1. */
|
||||
/** Nombre de paliers fins, donc d'index valides. Toujours au moins 1. */
|
||||
int32 StepCount = 1;
|
||||
|
||||
/** Bond des fleches, exprime en nombre de paliers fins. 1 en mode choix. */
|
||||
int32 ArrowIndexDelta = 1;
|
||||
|
||||
/** INDEX_NONE = aucune limite, tous les paliers sont selectionnables. */
|
||||
int32 MaxSelectableIndex = INDEX_NONE;
|
||||
|
||||
bool bNumeric = false;
|
||||
|
||||
/**
|
||||
* Vrai pendant qu'on repositionne le curseur depuis le code.
|
||||
*
|
||||
* USlider::SetValue ne rediffuse pas son delegue aujourd'hui, mais s'en
|
||||
* remettre a ce detail d'implementation coute une boucle infinie le jour ou
|
||||
* il change.
|
||||
*/
|
||||
bool bRefreshingSlider = false;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "SettingsTabWidget.h"
|
||||
#include "SettingsControlsWidget.generated.h"
|
||||
|
||||
class UEnhancedInputUserSettings;
|
||||
class USettingKeyRowWidget;
|
||||
|
||||
/**
|
||||
* L'onglet "Touches".
|
||||
*
|
||||
* Le stockage, la persistance et l'application des surcharges appartiennent a
|
||||
* UEnhancedInputUserSettings, natif depuis 5.3 : on n'ecrit ni serialisation ni
|
||||
* reconstruction d'IMC. Cet onglet n'est qu'une facade -- afficher les touches
|
||||
* courantes, capturer la nouvelle, arbitrer les conflits.
|
||||
*
|
||||
* PREREQUIS EDITEUR : sans "Enable User Settings" dans Project Settings ->
|
||||
* Enhanced Input, et sans Player Mappable Key Settings sur chaque mapping de
|
||||
* IMC_Default, toutes les lignes afficheront "Non assignée".
|
||||
*
|
||||
* Classe Abstract : on cree un WBP_SettingsControls qui en herite.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API USettingsControlsWidget : public USettingsTabWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
USettingsControlsWidget(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
virtual void RefreshFromSettings() override;
|
||||
virtual void ResetToDefaults() override;
|
||||
|
||||
protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> MoveForwardRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> MoveBackwardRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> MoveLeftRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> MoveRightRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> JumpRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> SprintRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> CrouchRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> InteractRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> UseItemRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> ToggleInventoryRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingKeyRowWidget> DropItemRow;
|
||||
|
||||
/**
|
||||
* Question posee en cas de conflit. Trois emplacements :
|
||||
* {0} la touche, {1} l'action qui la possede, {2} l'action qui la reclame.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
|
||||
FText ConflictMessage;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleRebindRequested(FName MappingName);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleKeyCaptured(FKey CapturedKey);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleConflictAnswered(bool bConfirmed);
|
||||
|
||||
/** Ecrit la touche en attente sur le mapping en attente, puis rafraichit. */
|
||||
void ApplyPendingRebind();
|
||||
|
||||
/** Le sous-systeme de reglages d'input du joueur local, ou nullptr. */
|
||||
UEnhancedInputUserSettings* GetInputSettings() const;
|
||||
|
||||
/** Toutes les lignes, dans l'ordre d'affichage. Ignore les nulles. */
|
||||
TArray<USettingKeyRowWidget*> GetAllRows() const;
|
||||
|
||||
/** La ligne portant ce nom de mapping, ou nullptr. */
|
||||
USettingKeyRowWidget* FindRow(FName MappingName) const;
|
||||
|
||||
/**
|
||||
* Donne son identite a une ligne et retient son libelle.
|
||||
*
|
||||
* Le libelle vient du C++ et non du "Display Name" des Player Mappable Key
|
||||
* Settings : ca t'evite de le saisir onze fois dans l'editeur, et ca garde
|
||||
* les textes traduisibles au meme endroit que le reste du menu.
|
||||
*/
|
||||
void SetupRow(USettingKeyRowWidget* Row, FName MappingName, const FText& Label);
|
||||
|
||||
/** Libelle lisible d'un mapping. Retombe sur l'identifiant si inconnu. */
|
||||
FText GetMappingDisplayName(FName MappingName) const;
|
||||
|
||||
/** Libelles retenus a la construction, pour les messages de conflit. */
|
||||
TMap<FName, FText> MappingLabels;
|
||||
|
||||
/** Mapping en cours de remappage, et touche capturee en attente d'arbitrage. */
|
||||
FName PendingMappingName = NAME_None;
|
||||
|
||||
FKey PendingKey;
|
||||
|
||||
/** Mapping a qui l'on volerait la touche si le joueur confirme. */
|
||||
FName ConflictingMappingName = NAME_None;
|
||||
};
|
||||
@@ -0,0 +1,121 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SettingsTabWidget.h"
|
||||
#include "SettingsDisplayWidget.generated.h"
|
||||
|
||||
class USettingRowWidget;
|
||||
class USurvivalUserSettings;
|
||||
|
||||
/**
|
||||
* L'onglet "Affichage".
|
||||
*
|
||||
* Presque tout est deja ecrit dans UGameUserSettings : mode d'affichage,
|
||||
* resolution, VSync, limite de FPS, echelle de resolution. On ne fait que
|
||||
* l'habiller. Seule la luminosite nous appartient, parce que le gamma vit sur
|
||||
* le moteur et non dans les reglages.
|
||||
*
|
||||
* Le changement de resolution ou de mode passe par une CONFIRMATION A REBOURS :
|
||||
* c'est le seul filet contre un mode que l'ecran ne sait pas afficher, situation
|
||||
* dont on ne peut sinon sortir qu'en supprimant GameUserSettings.ini a la main.
|
||||
*
|
||||
* Classe Abstract : on cree un WBP_SettingsDisplay qui en herite.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API USettingsDisplayWidget : public USettingsTabWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
USettingsDisplayWidget(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
virtual void RefreshFromSettings() override;
|
||||
virtual void ResetToDefaults() override;
|
||||
|
||||
protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> DisplayModeRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> ResolutionRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> FrameRateLimitRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> VSyncRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> ResolutionScaleRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> BrightnessRow;
|
||||
|
||||
/** Doit contenir {0}, remplace par les secondes restantes. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
|
||||
FText VideoConfirmMessage;
|
||||
|
||||
/** Delai avant retour automatique au mode precedent. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages", meta = (ClampMin = "3", ClampMax = "30"))
|
||||
float VideoConfirmSeconds = 10.f;
|
||||
|
||||
/**
|
||||
* Proposer aussi les formats ultralarges (21:9) en plus du 16:9.
|
||||
*
|
||||
* La liste que renvoie le systeme contient TOUT ce que la carte accepte :
|
||||
* 4:3, 5:4, 16:10, des modes d'un autre age. On n'en garde que deux familles.
|
||||
* Le 16:9 est toujours accepte -- il represente plus des trois quarts des
|
||||
* joueurs Steam a lui seul. Le 21:9 est marginal mais reel, d'ou l'option.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
|
||||
bool bAllowUltrawide = true;
|
||||
|
||||
/** Plus petite resolution proposee. En dessous, le HUD devient illisible. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
|
||||
FIntPoint MinimumResolution = FIntPoint(1280, 720);
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleDisplayModeChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleResolutionChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleFrameRateLimitChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleVSyncChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleResolutionScaleChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleBrightnessChanged(int32 NewIndex);
|
||||
|
||||
/** Reponse a la question posee apres un changement de mode ou de resolution. */
|
||||
UFUNCTION()
|
||||
void HandleVideoConfirmAnswered(bool bConfirmed);
|
||||
|
||||
/** Applique le mode video puis demande confirmation. */
|
||||
void ApplyVideoModeAndConfirm();
|
||||
|
||||
/** Recharge la liste des resolutions de l'ecran et repositionne la ligne. */
|
||||
void RebuildResolutionList();
|
||||
|
||||
/** Ecarte les formats non desires. Ne vide jamais la liste completement. */
|
||||
void FilterResolutions(const FIntPoint& CurrentResolution);
|
||||
|
||||
/** Index de la resolution courante dans AvailableResolutions, 0 si inconnue. */
|
||||
int32 FindCurrentResolutionIndex(const USurvivalUserSettings* Settings) const;
|
||||
|
||||
/** Index de la plus grande resolution proposee, INDEX_NONE si la liste est vide. */
|
||||
int32 FindLargestResolutionIndex() const;
|
||||
|
||||
/** Resolutions proposees, dans l'ordre de la ligne. */
|
||||
TArray<FIntPoint> AvailableResolutions;
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SettingsTabWidget.h"
|
||||
#include "SettingsGraphicsWidget.generated.h"
|
||||
|
||||
class USettingRowWidget;
|
||||
class USurvivalUserSettings;
|
||||
|
||||
/**
|
||||
* L'onglet "Graphismes" : les dix curseurs de scalability du moteur, plus une
|
||||
* qualite globale qui les pousse tous.
|
||||
*
|
||||
* Zero logique metier ici : chaque ligne appelle un Set...Quality(0-3) de
|
||||
* UGameUserSettings, le moteur fait le reste. C'est l'onglet qui rapporte le
|
||||
* plus pour le moins d'effort.
|
||||
*
|
||||
* Vegetation et Distance d'affichage sont places EN HAUT et non dans l'ordre du
|
||||
* moteur : avec SoStylized, ce sont les deux seuls qui decident vraiment si le
|
||||
* jeu tourne sur une machine moyenne.
|
||||
*
|
||||
* Classe Abstract : on cree un WBP_SettingsGraphics qui en herite.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API USettingsGraphicsWidget : public USettingsTabWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
virtual void RefreshFromSettings() override;
|
||||
virtual void ResetToDefaults() override;
|
||||
|
||||
protected:
|
||||
/** Bas / Moyen / Élevé / Épique, plus "Personnalisé" que le joueur ne peut pas choisir. */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> OverallQualityRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> ViewDistanceRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> FoliageRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> ShadowRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> TextureRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> VisualEffectRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> PostProcessRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> AntiAliasingRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> ReflectionRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> GlobalIlluminationRow;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USettingRowWidget> ShadingRow;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleOverallQualityChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleViewDistanceChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleFoliageChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleShadowChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleTextureChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleVisualEffectChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandlePostProcessChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleAntiAliasingChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleReflectionChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleGlobalIlluminationChanged(int32 NewIndex);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleShadingChanged(int32 NewIndex);
|
||||
|
||||
/** Prepare une ligne de qualite : memes quatre libelles partout. */
|
||||
void SetupQualityRow(USettingRowWidget* Row, const FText& Label);
|
||||
|
||||
/** Pousse la scalability et rafraichit la ligne de qualite globale. */
|
||||
void ApplyAndRefreshOverall();
|
||||
};
|
||||
@@ -8,11 +8,18 @@
|
||||
|
||||
class UButton;
|
||||
class UConfirmDialogWidget;
|
||||
class UKeyCaptureWidget;
|
||||
class USettingsTabWidget;
|
||||
class UWidgetSwitcher;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSettingsCloseRequested);
|
||||
|
||||
/** Reponse a une demande de confirmation. bConfirmed est faux sur un refus ou une expiration. */
|
||||
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSettingsConfirmationResult, bool, bConfirmed);
|
||||
|
||||
/** Touche capturee. Invalide si le joueur a annule avec Echap. */
|
||||
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSettingsKeyCaptureResult, FKey, CapturedKey);
|
||||
|
||||
/**
|
||||
* L'ecran de reglages : une barre d'onglets, un WidgetSwitcher, Retour et
|
||||
* Reinitialiser.
|
||||
@@ -55,6 +62,57 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void Close();
|
||||
|
||||
/**
|
||||
* ZOrder de la boite de confirmation, impose par celui qui ouvre l'ecran.
|
||||
*
|
||||
* L'ecran ne le choisit PAS lui-meme : il est ouvert depuis deux contextes
|
||||
* dont les empilements n'ont rien a voir (50 au menu principal, 150 en jeu).
|
||||
* Un numero code en dur ici obligerait le widget a savoir qui l'affiche,
|
||||
* exactement ce qu'on evite avec OnCloseRequested. A appeler AVANT Open().
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void SetDialogZOrder(int32 InZOrder) { DialogZOrder = InZOrder; }
|
||||
|
||||
/**
|
||||
* Pose une question au joueur pour le compte d'un onglet.
|
||||
*
|
||||
* L'ecran est le seul a posseder une boite : un onglet qui creerait la
|
||||
* sienne devrait a son tour se demander a quel ZOrder la poser, et on
|
||||
* repartirait pour un tour.
|
||||
*
|
||||
* @param MessageOrFormat texte simple, ou format contenant {0} si
|
||||
* CountdownSeconds est superieur a zero.
|
||||
* @param CountdownSeconds 0 pour une question sans limite de temps.
|
||||
* @return false si aucune boite n'est configuree -- l'appelant doit alors
|
||||
* decider seul, sa reponse n'arrivera jamais.
|
||||
*/
|
||||
bool RequestConfirmation(const FText& MessageOrFormat, float CountdownSeconds, const FOnSettingsConfirmationResult& OnResult);
|
||||
|
||||
/**
|
||||
* Ouvre le voile de capture de touche pour le compte d'un onglet.
|
||||
*
|
||||
* Meme raison que la boite : un voile pose DANS l'onglet serait clippe a la
|
||||
* zone du WidgetSwitcher au lieu de couvrir l'ecran, et devrait a son tour
|
||||
* se trouver un ZOrder.
|
||||
*
|
||||
* @return false si aucun voile n'est configure -- la reponse n'arrivera
|
||||
* jamais, l'appelant doit renoncer.
|
||||
*/
|
||||
bool RequestKeyCapture(const FText& ActionLabel, const FOnSettingsKeyCaptureResult& OnResult);
|
||||
|
||||
/**
|
||||
* Bascule le voile deja ouvert sur la question de conflit.
|
||||
*
|
||||
* Le voile RESTE ouvert entre la capture et la reponse : c'est le meme geste
|
||||
* pour le joueur, il n'a pas a le voir disparaitre puis revenir.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void ShowKeyCaptureConflict(const FText& Message, const FOnSettingsConfirmationResult& OnAnswer);
|
||||
|
||||
/** Referme le voile. A appeler quand le remappage est termine ou abandonne. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
||||
void CloseKeyCapture();
|
||||
|
||||
protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UWidgetSwitcher> TabSwitcher;
|
||||
@@ -62,41 +120,58 @@ protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> BackButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
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.
|
||||
* Les trois onglets qui existent sont OBLIGATOIRES : un WBP_Settings sans eux
|
||||
* ne compile pas.
|
||||
*
|
||||
* BindWidgetOptional echoue en silence -- un nom mal orthographie donnait un
|
||||
* bouton parfaitement inerte, sans le moindre message. Pour un element dont
|
||||
* l'absence n'a aucun sens, on veut l'erreur a la compilation du Blueprint.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> GameplayTabButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> DisplayTabButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> GraphicsTabButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> ControlsTabButton;
|
||||
|
||||
/**
|
||||
* Celui-la reste optionnel : sa page n'existe pas encore. Il passera en
|
||||
* BindWidget le jour ou l'onglet Audio sera ecrit -- en attendant, il se
|
||||
* grise tout seul si tu le poses.
|
||||
*/
|
||||
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.
|
||||
* Ton WBP_ConfirmDialog, a renseigner dans les Class Defaults de
|
||||
* WBP_Settings. Laisse-le vide et "Reinitialiser" agit sans rien demander.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UConfirmDialogWidget> ResetDialog;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Reglages")
|
||||
TSubclassOf<UConfirmDialogWidget> ResetDialogClass;
|
||||
|
||||
/** La question posee avant de reinitialiser l'onglet visible. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages")
|
||||
FText ResetDialogMessage;
|
||||
|
||||
/** Ton WBP_KeyCapture. Sans lui, l'onglet Touches ne peut rien remapper. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Reglages")
|
||||
TSubclassOf<UKeyCaptureWidget> KeyCaptureClass;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Reglages")
|
||||
TObjectPtr<UConfirmDialogWidget> ResetDialog;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Reglages")
|
||||
TObjectPtr<UKeyCaptureWidget> KeyCapture;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleBackClicked();
|
||||
@@ -104,11 +179,21 @@ private:
|
||||
UFUNCTION()
|
||||
void HandleResetClicked();
|
||||
|
||||
/** Reponse a la question du bouton Reinitialiser. */
|
||||
UFUNCTION()
|
||||
void HandleResetConfirmed();
|
||||
void HandleResetAnswered(bool bConfirmed);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleResetCancelled();
|
||||
void HandleDialogConfirmed();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleDialogCancelled();
|
||||
|
||||
/** Referme la boite et rend la reponse a qui a pose la question. */
|
||||
void FinishConfirmation(bool bConfirmed);
|
||||
|
||||
/** Cree la boite au premier besoin. Renvoie nullptr si aucune classe n'est assignee. */
|
||||
UConfirmDialogWidget* EnsureDialog();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleGameplayTabClicked();
|
||||
@@ -119,9 +204,24 @@ private:
|
||||
UFUNCTION()
|
||||
void HandleGraphicsTabClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleControlsTabClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleAudioTabClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleKeyCaptured(FKey CapturedKey);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleKeyCaptureCancelled();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleKeyConflictAnswered(bool bConfirmed);
|
||||
|
||||
/** A qui rendre la reponse du conflit affiche dans le voile. */
|
||||
FOnSettingsConfirmationResult PendingConflictAnswer;
|
||||
|
||||
/** Change de page et rafraichit la nouvelle. Ignore un index inexistant. */
|
||||
void ShowTab(int32 Index);
|
||||
|
||||
@@ -131,5 +231,16 @@ private:
|
||||
/** Grise les boutons dont la page n'existe pas encore. */
|
||||
void UpdateTabButtonStates();
|
||||
|
||||
/** A qui rendre la reponse de la boite ouverte. Vide quand rien n'est pose. */
|
||||
FOnSettingsConfirmationResult PendingConfirmation;
|
||||
|
||||
/** A qui rendre la touche capturee. Vide quand le voile est ferme. */
|
||||
FOnSettingsKeyCaptureResult PendingKeyCapture;
|
||||
|
||||
bool bResetDialogOpen = false;
|
||||
|
||||
bool bKeyCaptureOpen = false;
|
||||
|
||||
/** Valeur de repli si l'appelant oublie SetDialogZOrder : devant le fondu exclu. */
|
||||
int32 DialogZOrder = 500;
|
||||
};
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "SettingsTabWidget.generated.h"
|
||||
|
||||
class USettingsMenuWidget;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* visible sans jamais savoir ce qu'il contient. Ajouter un onglet ne demande
|
||||
* donc aucune ligne dans USettingsMenuWidget.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API USettingsTabWidget : public UUserWidget
|
||||
@@ -24,4 +26,22 @@ public:
|
||||
|
||||
/** Remet a l'usine les seuls reglages de cet onglet, puis se rafraichit. */
|
||||
virtual void ResetToDefaults() {}
|
||||
|
||||
/**
|
||||
* L'ecran se presente a ses onglets a la construction.
|
||||
*
|
||||
* Pointeur explicite plutot qu'un GetTypedOuter au moment de s'en servir :
|
||||
* un onglet doit pouvoir demander une confirmation, et faire dependre ca de
|
||||
* la forme de l'arbre de widgets casserait le jour ou on l'imbrique
|
||||
* autrement.
|
||||
*
|
||||
* Defini dans le .cpp et non inline : TWeakObjectPtr exige un type complet
|
||||
* pour s'assigner, et inclure l'en-tete de l'ecran ici ferait dependre tous
|
||||
* les onglets de lui.
|
||||
*/
|
||||
void SetOwningMenu(USettingsMenuWidget* InMenu);
|
||||
|
||||
protected:
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Reglages")
|
||||
TWeakObjectPtr<USettingsMenuWidget> OwningMenu;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "SurvivalGameInstance.generated.h"
|
||||
|
||||
class UInputMappingContext;
|
||||
class ULocalPlayer;
|
||||
|
||||
/**
|
||||
* Le seul objet qui survit aux changements de map.
|
||||
*
|
||||
@@ -33,6 +36,32 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Menu")
|
||||
void SetLastMenuCameraName(FName SpotName) { LastMenuCameraName = SpotName; }
|
||||
|
||||
/**
|
||||
* Declare les IMC remappables aupres des reglages d'input du joueur.
|
||||
*
|
||||
* Appelee A LA DEMANDE, par l'ecran de reglages avant de lire les touches, et
|
||||
* non depuis un evenement de demarrage. Raison : UserSettings n'est cree que
|
||||
* dans UEnhancedInputLocalPlayerSubsystem::PlayerControllerChanged, et
|
||||
* ULocalPlayer diffuse ses evenements AVANT de prevenir ses sous-systemes --
|
||||
* tout hook precoce arrive donc trop tot, quel qu'il soit.
|
||||
*
|
||||
* Idempotente : le moteur ignore un contexte deja connu.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Input")
|
||||
void RegisterMappableContexts();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Les mapping contexts dont les touches sont remappables. Mets-y IMC_Default.
|
||||
*
|
||||
* Ici et PAS sur le pawn : le menu principal n'a aucun pawn, et l'ecran de
|
||||
* reglages doit pourtant afficher les touches. C'est le critere habituel --
|
||||
* "est-ce que ca doit survivre au changement de map ?" -- qui envoie cette
|
||||
* liste sur le GameInstance.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TArray<TObjectPtr<UInputMappingContext>> MappableContexts;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Stocke un NOM et pas un index : les index changent des qu'on ajoute ou
|
||||
|
||||
@@ -18,17 +18,52 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSurvivalSettingsApplied);
|
||||
*/
|
||||
namespace SurvivalSettingsRange
|
||||
{
|
||||
// Deux granularites par reglage :
|
||||
// Step = le pas FIN, celui du glissement au curseur. C'est aussi
|
||||
// celui sur lequel les setters arrondissent : le stocker plus
|
||||
// grossier annulerait le reglage fin juste apres.
|
||||
// ArrowStep = le bond des fleches, pour traverser la plage vite.
|
||||
constexpr float SensitivityMin = 5.f;
|
||||
constexpr float SensitivityMax = 100.f;
|
||||
constexpr float SensitivityStep = 5.f;
|
||||
constexpr float SensitivityStep = 1.f;
|
||||
constexpr float SensitivityArrowStep = 5.f;
|
||||
|
||||
constexpr float FieldOfViewMin = 70.f;
|
||||
constexpr float FieldOfViewMax = 120.f;
|
||||
constexpr float FieldOfViewStep = 5.f;
|
||||
constexpr float FieldOfViewStep = 1.f;
|
||||
constexpr float FieldOfViewArrowStep = 5.f;
|
||||
|
||||
constexpr float CameraShakeMin = 0.f;
|
||||
constexpr float CameraShakeMax = 100.f;
|
||||
constexpr float CameraShakeStep = 10.f;
|
||||
constexpr float CameraShakeStep = 1.f;
|
||||
constexpr float CameraShakeArrowStep = 10.f;
|
||||
|
||||
/**
|
||||
* Luminosite en POURCENTAGE et non en gamma.
|
||||
*
|
||||
* Le gamma utile va de 0.8 a 1.4 ; affiche tel quel il tiendrait dans un
|
||||
* intervalle de 0.6 que la ligne, qui n'affiche que des entiers, arrondirait
|
||||
* a "1" partout. On stocke donc 80 a 140 et on divise a l'application.
|
||||
*/
|
||||
constexpr float BrightnessMin = 80.f;
|
||||
constexpr float BrightnessMax = 140.f;
|
||||
constexpr float BrightnessStep = 1.f;
|
||||
constexpr float BrightnessArrowStep = 10.f;
|
||||
|
||||
/**
|
||||
* Gamma d'affichage a 100 % de luminosite.
|
||||
*
|
||||
* C'est la valeur du moteur (BaseEngine.ini), PAS un neutre a 1.0 :
|
||||
* GEngine->DisplayGamma est un exposant, pas un multiplicateur. Y ecrire 1.0
|
||||
* assombrit tout l'ecran -- et comme GEngine est global, l'editeur avec.
|
||||
*/
|
||||
constexpr float BaseDisplayGamma = 2.2f;
|
||||
|
||||
/** Echelle de resolution, en pourcentage de la resolution native. */
|
||||
constexpr float ResolutionScaleMin = 50.f;
|
||||
constexpr float ResolutionScaleMax = 100.f;
|
||||
constexpr float ResolutionScaleStep = 1.f;
|
||||
constexpr float ResolutionScaleArrowStep = 10.f;
|
||||
|
||||
/**
|
||||
* Multiplicateurs reels correspondant aux extremites de l'echelle affichee.
|
||||
@@ -169,6 +204,17 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages|Controles")
|
||||
void SetCrouchToggle(bool bValue);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Reglages|Affichage")
|
||||
float GetBrightness() const { return Brightness; }
|
||||
|
||||
/** Ne fait qu'enregistrer : c'est ApplySurvivalSettings qui pousse le gamma. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages|Affichage")
|
||||
void SetBrightness(float Value);
|
||||
|
||||
/** Remet a l'usine les seuls reglages de l'onglet Affichage qui nous appartiennent. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Reglages|Affichage")
|
||||
void SetSurvivalDisplayToDefaults();
|
||||
|
||||
private:
|
||||
/** Echelle lisible (5 a 100), convertie a la lecture. Jamais brute. */
|
||||
UPROPERTY(config)
|
||||
@@ -195,4 +241,8 @@ private:
|
||||
|
||||
UPROPERTY(config)
|
||||
bool bCrouchToggle;
|
||||
|
||||
/** Pourcentage, converti en gamma a l'application. */
|
||||
UPROPERTY(config)
|
||||
float Brightness;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user