5970db0512
Lot 3 : l'apparence survit desormais au respawn, au changement de map et au relancement du jeu, et les trois autres joueurs la voient. Elle vit sur ASurvivalPlayerState et pas sur le pawn : c'est le JOUEUR qu'elle decrit, pas le corps. Le pawn meurt et reapparait, le PlayerState non -- reapparaitre avec le visage de quelqu'un d'autre n'aurait aucun sens, alors que perdre son inventaire au sol est la regle du jeu. SubmitCharacterAppearance() sur AFpsPlayerController est le point d'entree UNIQUE : il persiste ET annonce au serveur, jamais l'un sans l'autre. Persister sans annoncer laisserait le joueur seul a se voir change, annoncer sans persister le ferait repartir au visage par defaut au lancement suivant. C'est par la que passent les commandes console, et par la que passera l'ecran du lot 4. La course pawn / PlayerState est resolue des DEUX cotes. Chez un client ils arrivent par deux chemins de replication independants et rien ne dit lequel gagne : OnRep_CharacterAppearance monte le personnage si le pawn est deja la, AFpsPlayer::OnRep_PlayerState va chercher l'apparence si c'est l'inverse. Les deux appellent la meme fonction idempotente. Chez l'hote aucun OnRep ne se declenche, d'ou l'appel local dans SetCharacterAppearance -- sans lui, l'hote serait le seul a ne pas voir son propre personnage. Rien n'est valide cote PlayerState, deliberement : borner un index demande le catalogue, que seul UCharacterAppearanceComponent connait, et celui-ci passe deja tout ce qu'il monte par Catalog->Sanitize() sur chaque machine. Piege de test note dans CLAUDE.md : en PIE a deux joueurs, les deux fenetres lisent le meme .ini, donc les deux personnages demarrent identiques. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
373 lines
13 KiB
C++
373 lines
13 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "GameFramework/GameUserSettings.h"
|
|
#include "CharacterAppearanceTypes.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
|
|
{
|
|
// 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 = 1.f;
|
|
constexpr float SensitivityArrowStep = 5.f;
|
|
|
|
constexpr float FieldOfViewMin = 70.f;
|
|
constexpr float FieldOfViewMax = 120.f;
|
|
constexpr float FieldOfViewStep = 1.f;
|
|
constexpr float FieldOfViewArrowStep = 5.f;
|
|
|
|
constexpr float CameraShakeMin = 0.f;
|
|
constexpr float CameraShakeMax = 100.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.
|
|
*
|
|
* 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 quatre volumes partagent la meme echelle : 0 a 100 %. */
|
|
constexpr float VolumeMin = 0.f;
|
|
constexpr float VolumeMax = 100.f;
|
|
constexpr float VolumeStep = 1.f;
|
|
constexpr float VolumeArrowStep = 10.f;
|
|
|
|
/**
|
|
* Exposant applique au pourcentage avant d'en faire une amplitude.
|
|
*
|
|
* L'oreille repond au logarithme de l'amplitude, pas a l'amplitude : un
|
|
* curseur envoye tel quel donne un reglage inutilisable, ou tout se joue dans
|
|
* les vingt derniers pourcents et ou la moitie basse ne s'entend deja plus.
|
|
* Elever au carre est l'approximation habituelle -- 50 % rendent 0,25 en
|
|
* amplitude, soit environ la moitie du volume PERCU. 0 reste le silence exact.
|
|
*/
|
|
constexpr float VolumeCurveExponent = 2.f;
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
|
|
/**
|
|
* Pousse les volumes sur le peripherique audio.
|
|
*
|
|
* Volontairement SEPAREE d'ApplySurvivalSettings : cinq commandes vers le
|
|
* thread audio a chaque clic de fleche sur le champ de vision seraient du
|
|
* bruit pur. Les deux sont appelees ensemble par ApplyNonResolutionSettings,
|
|
* donc le chemin de demarrage n'en oublie aucune.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages|Audio")
|
|
void ApplyAudioSettings();
|
|
|
|
/** Remet les seuls reglages de jeu a leur valeur d'usine. */
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages")
|
|
void SetSurvivalGameplayToDefaults();
|
|
|
|
/** Remet a l'usine les seuls reglages de l'onglet Audio. */
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages|Audio")
|
|
void SetSurvivalAudioToDefaults();
|
|
|
|
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);
|
|
|
|
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();
|
|
|
|
// ------------------------------------------------------------------
|
|
// Audio
|
|
//
|
|
// Les quatre volumes sont des POURCENTAGES, comme le bouge de camera : c'est
|
|
// ce que la ligne de reglage affiche, et la conversion en amplitude (courbe
|
|
// comprise) n'appartient qu'a ApplyAudioSettings.
|
|
// ------------------------------------------------------------------
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Reglages|Audio")
|
|
float GetMasterVolume() const { return MasterVolume; }
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages|Audio")
|
|
void SetMasterVolume(float Value);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Reglages|Audio")
|
|
float GetMusicVolume() const { return MusicVolume; }
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages|Audio")
|
|
void SetMusicVolume(float Value);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Reglages|Audio")
|
|
float GetEffectsVolume() const { return EffectsVolume; }
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages|Audio")
|
|
void SetEffectsVolume(float Value);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Reglages|Audio")
|
|
float GetUiVolume() const { return UiVolume; }
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages|Audio")
|
|
void SetUiVolume(float Value);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Reglages|Audio")
|
|
bool GetPlayInBackground() const { return bPlayInBackground; }
|
|
|
|
/** Vrai = le jeu continue de s'entendre quand la fenetre n'a plus le focus. */
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages|Audio")
|
|
void SetPlayInBackground(bool bValue);
|
|
|
|
// ------------------------------------------------------------------
|
|
// Apparence du personnage
|
|
//
|
|
// Ici et pas dans une sauvegarde de partie : l'apparence appartient au
|
|
// JOUEUR et pas a une partie donnee, elle doit le suivre d'une session a
|
|
// l'autre. C'est aussi ce qui donne un visage a qui arrive par invitation
|
|
// Steam sans passer par l'ecran de customisation.
|
|
// ------------------------------------------------------------------
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Reglages|Apparence")
|
|
const FCharacterAppearance& GetCharacterAppearance() const { return CharacterAppearance; }
|
|
|
|
/**
|
|
* N'ecrit rien sur le disque : la sauvegarde a lieu a la fermeture de
|
|
* l'ecran, comme pour tous les autres reglages. Ne diffuse pas non plus
|
|
* OnSurvivalSettingsApplied -- le pawn ne relit pas son apparence dans les
|
|
* reglages, il la tient du PlayerState.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "Reglages|Apparence")
|
|
void SetCharacterAppearance(const FCharacterAppearance& InAppearance);
|
|
|
|
private:
|
|
/**
|
|
* Serialisee telle quelle dans le .ini, sous la forme
|
|
* `CharacterAppearance=(BodyType=BodyA,HeadIndex=0,...)`.
|
|
*
|
|
* C'est le dividende de la struct d'index : une apparence faite de
|
|
* TSoftObjectPtr aurait donne une douzaine de chemins d'assets a ecrire, et
|
|
* une seule renommee d'asset aurait suffi a la casser silencieusement.
|
|
*/
|
|
UPROPERTY(config)
|
|
FCharacterAppearance CharacterAppearance;
|
|
|
|
/** 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;
|
|
|
|
/** Pourcentage, converti en gamma a l'application. */
|
|
UPROPERTY(config)
|
|
float Brightness;
|
|
|
|
/** Pourcentages. Le maitre multiplie les trois autres via la hierarchie des SoundClass. */
|
|
UPROPERTY(config)
|
|
float MasterVolume;
|
|
|
|
UPROPERTY(config)
|
|
float MusicVolume;
|
|
|
|
UPROPERTY(config)
|
|
float EffectsVolume;
|
|
|
|
UPROPERTY(config)
|
|
float UiVolume;
|
|
|
|
/**
|
|
* Le moteur coupe le son des qu'il perd le focus, et c'est son defaut d'usine.
|
|
* En coop c'est justement le mauvais defaut pour qui bascule sur Discord.
|
|
*/
|
|
UPROPERTY(config)
|
|
bool bPlayInBackground;
|
|
};
|