(Feat) Add Main Menu + Music Manger
This commit is contained in:
@@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user