// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Engine/DeveloperSettings.h" // Sound/SoundClass.h en entier, et pas une simple declaration anticipee : // TStrongObjectPtr exige un type COMPLET, la ou TObjectPtr s'en passait. #include "Sound/SoundClass.h" #include "UObject/StrongObjectPtr.h" #include "SurvivalAudioSettings.generated.h" /** * Ou le C++ trouve les SoundClass du jeu. Reglable dans Project Settings, section * "Survival Audio". * * UDeveloperSettings et pas des chemins codes en dur dans le .cpp : renommer ou * deplacer SC_Music casserait silencieusement le curseur Musique, sans la moindre * erreur de compilation. Ici la reference est un asset comme un autre, suivie par * le redirecteur de l'editeur et par le cooker. * * Ces references sont SOFT : un UDeveloperSettings est construit tres tot, bien * avant que le gestionnaire d'assets ne soit pret. Une reference dure forcerait le * chargement des SoundClass au demarrage du moteur, ce qui est exactement le genre * de dependance qui fait echouer un cook. * * A NOTER : le SoundMix, lui, n'est PAS ici. Il est deja declare dans * Project Settings > Audio > Default Base Sound Mix, d'ou le moteur le reactive a * chaque chargement de map -- c'est precisement ce qu'on veut, et le redeclarer ici * ouvrirait la porte a deux references qui divergent. */ UCLASS(config = Game, defaultconfig, meta = (DisplayName = "Survival Audio")) class SURVIVAL_PROJET_API USurvivalAudioSettings : public UDeveloperSettings { GENERATED_BODY() public: /** Range la page sous "Game" et non sous "Plugins". */ virtual FName GetCategoryName() const override; /** * Le CDO, seul exemplaire qui existe. * * Non const alors que rien n'est modifie du point de vue du joueur : les * accesseurs ci-dessous memorisent l'asset resolu dans un champ Transient, comme * le fait UAudioSettings pour sa propre SoundClass par defaut. */ static USurvivalAudioSettings* Get(); /** * Le sommet de la hierarchie : toutes les autres classes en descendent, donc son * volume les multiplie toutes. */ UPROPERTY(config, EditAnywhere, Category = "Sound Classes") TSoftObjectPtr MasterSoundClass; /** * Imposee au lecteur de musique par UMusicSubsystem, donc les morceaux eux-memes * n'ont rien a declarer -- ajouter une piste a une playlist suffit. */ UPROPERTY(config, EditAnywhere, Category = "Sound Classes") TSoftObjectPtr MusicSoundClass; /** * A declarer aussi comme Default Sound Class dans Project Settings > Audio : * tout son qui n'annonce pas sa classe atterrit alors ici, ce qui evite qu'un * bruitage oublie echappe au curseur Effets. */ UPROPERTY(config, EditAnywhere, Category = "Sound Classes") TSoftObjectPtr EffectsSoundClass; /** A poser a la main sur les sons d'interface (survol, validation). */ UPROPERTY(config, EditAnywhere, Category = "Sound Classes") TSoftObjectPtr UiSoundClass; USoundClass* GetMasterSoundClass(); USoundClass* GetMusicSoundClass(); USoundClass* GetEffectsSoundClass(); USoundClass* GetUiSoundClass(); private: /** * Charge au premier appel, puis rend le pointeur memorise. * * Le cache n'est pas qu'une optimisation : il fait aussi office d'ancre pour le * ramasse-miettes. Une SoundClass chargee par un LoadSynchronous et referencee * nulle part serait collectee, et le curseur cesserait d'agir en pleine partie. */ USoundClass* Resolve(const TSoftObjectPtr& Asset, TStrongObjectPtr& Cache); /** * TStrongObjectPtr et surtout PAS UPROPERTY(Transient) : c'est ce qui plantait * le jeu package au demarrage, alors que tout allait bien dans l'editeur. * * Le CDO d'un UDeveloperSettings naƮt avant CloseDisregardForGC, donc il vit dans * le pool des objets PERMANENTS. Le moteur ne les scanne jamais -- ils sont * immortels par construction -- et exige en echange qu'ils ne referencent que des * objets du root set, sans quoi ils garderaient un pointeur sur un objet collecte. * VerifyGCAssumptions le controle et c'est une erreur FATALE : * * Disregard for GC object Default__SurvivalAudioSettings referencing SoundClass * SC_Master which is not part of root set. Property: CachedMasterSoundClass * Fatal error: Encountered 3 object(s) breaking Disregard for GC assumptions. * * Invisible dans l'editeur, ou les SoundClass sont deja chargees quand le pool se * ferme, donc permanentes elles aussi. En jeu package elles arrivent apres, et le * jeu meurt au premier ramassage -- pendant le chargement de la premiere map. * * TStrongObjectPtr ancre l'asset exactement comme le faisait la UPROPERTY, mais * range sa reference dans GGCObjectReferencer, le seul objet que VerifyGCAssumptions * saute explicitement. Le CDO ne reference donc plus rien, et l'asset reste vivant. */ TStrongObjectPtr CachedMasterSoundClass; TStrongObjectPtr CachedMusicSoundClass; TStrongObjectPtr CachedEffectsSoundClass; TStrongObjectPtr CachedUiSoundClass; };