121 lines
5.0 KiB
C++
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;
|
|
};
|