(Feat) Add footsteps
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
|
||||
#include "MainMenuPlayerController.h"
|
||||
|
||||
#include "CharacterAppearanceComponent.h"
|
||||
#include "CharacterCustomizationWidget.h"
|
||||
#include "CharacterPreviewActor.h"
|
||||
#include "ConfirmDialogWidget.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
@@ -13,6 +16,7 @@
|
||||
#include "SessionSubsystem.h"
|
||||
#include "SettingsMenuWidget.h"
|
||||
#include "SurvivalGameInstance.h"
|
||||
#include "SurvivalUserSettings.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "MainMenu"
|
||||
|
||||
@@ -193,6 +197,25 @@ void AMainMenuPlayerController::StartGame()
|
||||
return;
|
||||
}
|
||||
|
||||
// L'ecran de customisation s'intercale ICI, entre le clic et le fondu :
|
||||
// c'est le choix fait pour ce projet, on ne lance pas une partie sans etre
|
||||
// passe par son personnage. Sans classe assignee, on retombe sur l'ancien
|
||||
// comportement -- pratique pour tester le reste sans le traverser.
|
||||
if (CustomizationMenuClass && !CustomizationMenu)
|
||||
{
|
||||
PendingAction = EMenuPendingAction::OpenCustomization;
|
||||
|
||||
if (MainMenu)
|
||||
{
|
||||
MainMenu->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
|
||||
// On fond au noir AVANT de bouger la camera : le mannequin est ailleurs
|
||||
// dans la meme map, et un travelling jusqu'a lui traverserait le decor.
|
||||
ScreenFade->FadeOut(CustomizationFadeDuration);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameLevel.IsNull())
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("AMainMenuPlayerController : GameLevel n'est pas assignee, impossible de lancer la partie."));
|
||||
@@ -203,7 +226,13 @@ void AMainMenuPlayerController::StartGame()
|
||||
|
||||
// Le menu reste visible pendant le fondu mais cesse de repondre : cliquer
|
||||
// Quitter pendant le fondu vers la partie serait un piege.
|
||||
if (MainMenu)
|
||||
//
|
||||
// Le test de visibilite est indispensable : quand on arrive ici depuis
|
||||
// l'ecran de customisation, le menu a ete Collapsed a son ouverture, et
|
||||
// HitTestInvisible le RESSUSCITERAIT -- on verrait reapparaitre Jouer,
|
||||
// Reglages et Quitter par-dessus le personnage qu'on vient de valider.
|
||||
// HitTestInvisible rend non cliquable, il ne cache pas.
|
||||
if (MainMenu && MainMenu->GetVisibility() != ESlateVisibility::Collapsed)
|
||||
{
|
||||
MainMenu->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
@@ -211,6 +240,139 @@ void AMainMenuPlayerController::StartGame()
|
||||
ScreenFade->FadeOut(LeaveFadeDuration);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Ecran de customisation
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AMainMenuPlayerController::OpenCustomization()
|
||||
{
|
||||
// Cherche au moment de l'ouverture et pas au BeginPlay : c'est le seul
|
||||
// endroit ou l'on est sur que la map est entierement chargee, et un
|
||||
// mannequin absent doit rester rattrapable.
|
||||
if (!CharacterPreview)
|
||||
{
|
||||
for (TActorIterator<ACharacterPreviewActor> It(GetWorld()); It; ++It)
|
||||
{
|
||||
CharacterPreview = *It;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CharacterPreview)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning,
|
||||
TEXT("AMainMenuPlayerController : aucun ACharacterPreviewActor dans la map, l'ecran de customisation est saute."));
|
||||
|
||||
// On ne bloque pas le joueur pour un acteur manquant : meme logique
|
||||
// qu'un echec de session, il a clique Jouer, il veut jouer.
|
||||
CustomizationMenuClass = nullptr;
|
||||
StartGame();
|
||||
return;
|
||||
}
|
||||
|
||||
CustomizationMenu = CreateWidget<UCharacterCustomizationWidget>(this, CustomizationMenuClass);
|
||||
if (!CustomizationMenu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TOUJOURS l'apparence par defaut, jamais celle persistee : cet ecran est
|
||||
// une etape de CREATION, pas une reprise. Le jour ou un lobby verifiera que
|
||||
// les quatre joueurs ont valide leur personnage, il lui faudra un point de
|
||||
// depart identique pour tout le monde.
|
||||
//
|
||||
// La persistance ne devient pas inutile pour autant : elle sert au respawn,
|
||||
// et surtout a qui arrive par invitation Steam -- celui-la ne passe jamais
|
||||
// par ici et doit bien avoir un visage.
|
||||
//
|
||||
// « Par defaut » veut dire le DefaultAppearance du mannequin, pas les zeros
|
||||
// du C++ : le personnage de depart se regle ainsi dans BP_CharacterPreview,
|
||||
// a l'oeil et sans recompiler. C'est le meme reglage que celui pose au
|
||||
// BeginPlay, donc l'ouverture de l'ecran ne change rien a ce qu'on voyait.
|
||||
const UCharacterAppearanceComponent* PreviewAppearance = CharacterPreview->GetAppearanceComponent();
|
||||
CustomizationMenu->SetupCustomization(
|
||||
CharacterPreview,
|
||||
PreviewAppearance ? PreviewAppearance->DefaultAppearance : FCharacterAppearance());
|
||||
|
||||
CustomizationMenu->OnConfirmed.AddDynamic(this, &AMainMenuPlayerController::HandleCustomizationConfirmed);
|
||||
CustomizationMenu->OnCancelled.AddDynamic(this, &AMainMenuPlayerController::HandleCustomizationCancelled);
|
||||
|
||||
// Sans blend : l'ecran est noir a cet instant, personne ne voit la bascule.
|
||||
// C'est justement ce que le fondu achete -- un travelling depuis l'angle de
|
||||
// menu jusqu'au mannequin traverserait le decor de la map.
|
||||
SetViewTarget(CharacterPreview);
|
||||
|
||||
if (MainMenu)
|
||||
{
|
||||
MainMenu->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
|
||||
CustomizationMenu->AddToViewport();
|
||||
|
||||
// L'ecran a des boutons ET un clic-glisser : il lui faut la souris, et le
|
||||
// mode UI-only l'empeche de tomber dans le jeu.
|
||||
SetInputMode(FInputModeUIOnly().SetWidgetToFocus(CustomizationMenu->TakeWidget()));
|
||||
SetShowMouseCursor(true);
|
||||
|
||||
ScreenFade->FadeIn(CustomizationFadeDuration);
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::HandleCustomizationConfirmed()
|
||||
{
|
||||
// Persiste ET annonce -- mais nous sommes dans le menu, aucun serveur
|
||||
// n'ecoute encore. Seule la persistance compte ici ; l'annonce partira du
|
||||
// AnnounceCharacterAppearance() d'AFpsPlayerController, une fois la partie
|
||||
// chargee et le PlayerState en place.
|
||||
if (USurvivalUserSettings* Settings = USurvivalUserSettings::Get())
|
||||
{
|
||||
Settings->SetCharacterAppearance(CustomizationMenu->GetAppearance());
|
||||
Settings->SaveSettings();
|
||||
}
|
||||
|
||||
CloseCustomization();
|
||||
|
||||
// La classe est mise de cote le temps du lancement : sans ca, StartGame
|
||||
// rouvrirait l'ecran qu'on vient de fermer.
|
||||
CustomizationMenuClass = nullptr;
|
||||
StartGame();
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::HandleCustomizationCancelled()
|
||||
{
|
||||
// Le fondu de retour est deja lance : on ignore la demande plutot que de le
|
||||
// relancer depuis le debut. La garde compte plus depuis qu'Echap declenche
|
||||
// ce chemin -- une touche se repete bien plus facilement qu'un clic.
|
||||
if (PendingAction != EMenuPendingAction::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Meme mecanique qu'a l'aller : on fond, PUIS on bascule la vue. Le retour
|
||||
// se termine dans HandleFadeFinished.
|
||||
PendingAction = EMenuPendingAction::CloseCustomization;
|
||||
|
||||
if (CustomizationMenu)
|
||||
{
|
||||
CustomizationMenu->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
|
||||
ScreenFade->FadeOut(CustomizationFadeDuration);
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::CloseCustomization()
|
||||
{
|
||||
if (CustomizationMenu)
|
||||
{
|
||||
CustomizationMenu->RemoveFromParent();
|
||||
CustomizationMenu = nullptr;
|
||||
}
|
||||
|
||||
// Le menu principal reprend la main : son propre mode d'input est pose par
|
||||
// le Blueprint, on remet simplement le curseur dans un etat coherent.
|
||||
SetInputMode(FInputModeUIOnly());
|
||||
SetShowMouseCursor(true);
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::HandleHostFailed(FText Reason)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("AMainMenuPlayerController : session indisponible (%s), la partie se lance hors ligne."), *Reason.ToString());
|
||||
@@ -386,8 +548,41 @@ void AMainMenuPlayerController::CloseGame()
|
||||
|
||||
void AMainMenuPlayerController::HandleFadeFinished()
|
||||
{
|
||||
// L'ecran est noir : c'est ICI qu'on bascule la vue sur le mannequin, et
|
||||
// pas au clic. PendingAction est remise a None tout de suite, sinon le
|
||||
// fondu d'eclaircissement qui suit repasserait par la meme branche.
|
||||
if (PendingAction == EMenuPendingAction::OpenCustomization)
|
||||
{
|
||||
PendingAction = EMenuPendingAction::None;
|
||||
OpenCustomization();
|
||||
return;
|
||||
}
|
||||
|
||||
if (PendingAction == EMenuPendingAction::CloseCustomization)
|
||||
{
|
||||
PendingAction = EMenuPendingAction::None;
|
||||
|
||||
CloseCustomization();
|
||||
|
||||
if (MainMenu)
|
||||
{
|
||||
MainMenu->SetVisibility(ESlateVisibility::Visible);
|
||||
}
|
||||
|
||||
// Un nouvel angle au retour plutot que celui d'avant : PickCameraSpot
|
||||
// evite deja le dernier utilise, donc le menu ne se rouvre jamais sur
|
||||
// exactement la meme image.
|
||||
if (AMenuCameraSpot* Spot = PickCameraSpot())
|
||||
{
|
||||
SetViewTarget(Spot);
|
||||
}
|
||||
|
||||
ScreenFade->FadeIn(CustomizationFadeDuration);
|
||||
return;
|
||||
}
|
||||
|
||||
// Le delegue se declenche aussi a la fin du fondu d'ouverture, ou il n'y a
|
||||
// rien a faire : c'est PendingAction qui distingue les deux cas.
|
||||
// rien a faire : c'est PendingAction qui distingue les cas.
|
||||
if (PendingAction != EMenuPendingAction::StartGame)
|
||||
{
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user