Files

121 lines
5.0 KiB
C++

// 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 EMBERWILD_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<USoundClass> 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<USoundClass> 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<USoundClass> EffectsSoundClass;
/** A poser a la main sur les sons d'interface (survol, validation). */
UPROPERTY(config, EditAnywhere, Category = "Sound Classes")
TSoftObjectPtr<USoundClass> 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<USoundClass>& Asset, TStrongObjectPtr<USoundClass>& 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<USoundClass> CachedMasterSoundClass;
TStrongObjectPtr<USoundClass> CachedMusicSoundClass;
TStrongObjectPtr<USoundClass> CachedEffectsSoundClass;
TStrongObjectPtr<USoundClass> CachedUiSoundClass;
};