Import initial du projet Survival (UE 5.8)
Boucle de jeu complète : récolte, inventaire, consommation, stats de survie, mort et respawn. Gameplay en C++, Blueprints réservés au câblage d'assets. Assets binaires (.uasset, .umap, textures, audio) suivis via Git LFS. Binaries/, Intermediate/, Saved/ et DerivedDataCache/ sont ignorés : régénérés au build, ils pèsent 3 Go pour 1,3 Go de contenu utile. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "MainMenuPlayerController.h"
|
||||
|
||||
#include "ConfirmDialogWidget.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "MainMenuWidget.h"
|
||||
#include "MenuCameraSpot.h"
|
||||
#include "ScreenFadeComponent.h"
|
||||
#include "SurvivalGameInstance.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "MainMenu"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Le menu reste sous le voile de fondu, qui est en 1000.
|
||||
constexpr int32 ZOrderMainMenu = 0;
|
||||
|
||||
// La boite de confirmation passe devant le menu, mais sous le fondu.
|
||||
constexpr int32 ZOrderQuitDialog = 100;
|
||||
}
|
||||
|
||||
AMainMenuPlayerController::AMainMenuPlayerController()
|
||||
{
|
||||
// Meme composant que dans le jeu : un seul fondu a maintenir.
|
||||
ScreenFade = CreateDefaultSubobject<UScreenFadeComponent>(TEXT("ScreenFade"));
|
||||
|
||||
bShowMouseCursor = true;
|
||||
|
||||
QuitDialogMessage = LOCTEXT("QuitConfirm", "Quitter le jeu ?");
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// L'ecran part du noir : sans ca on verrait une frame de la map, cadree
|
||||
// n'importe comment, avant que le fondu ne commence.
|
||||
ScreenFade->SnapToBlack();
|
||||
|
||||
if (AMenuCameraSpot* Spot = PickCameraSpot())
|
||||
{
|
||||
// Sans blend : on arrive directement sur l'angle tire au sort.
|
||||
SetViewTarget(Spot);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("AMainMenuPlayerController : aucune AMenuCameraSpot active dans la map, le menu s'affichera sur la vue par defaut."));
|
||||
}
|
||||
|
||||
if (MainMenuClass)
|
||||
{
|
||||
MainMenu = CreateWidget<UMainMenuWidget>(this, MainMenuClass);
|
||||
if (MainMenu)
|
||||
{
|
||||
MainMenu->AddToViewport(ZOrderMainMenu);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("AMainMenuPlayerController : MainMenuClass n'est pas assignee, aucun menu ne s'affichera."));
|
||||
}
|
||||
|
||||
// GameAndUI et non UIOnly : UIOnly avale aussi la console et les raccourcis
|
||||
// du moteur, ce qui rend le debug penible. Il n'y a de toute facon aucun
|
||||
// input de jeu dans cette map, rien ne peut passer a travers.
|
||||
FInputModeGameAndUI Mode;
|
||||
Mode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
|
||||
Mode.SetHideCursorDuringCapture(false);
|
||||
SetInputMode(Mode);
|
||||
SetShowMouseCursor(true);
|
||||
|
||||
ScreenFade->OnFadeFinished.AddDynamic(this, &AMainMenuPlayerController::HandleFadeFinished);
|
||||
ScreenFade->FadeIn(OpeningFadeDuration);
|
||||
}
|
||||
|
||||
AMenuCameraSpot* AMainMenuPlayerController::PickCameraSpot()
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (!World)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
USurvivalGameInstance* SurvivalGameInstance = GetGameInstance<USurvivalGameInstance>();
|
||||
const FName LastUsed = SurvivalGameInstance ? SurvivalGameInstance->GetLastMenuCameraName() : NAME_None;
|
||||
|
||||
// TActorIterator plutot que GetAllActorsOfClass : pas de tableau d'AActor*
|
||||
// intermediaire, pas de cast par element.
|
||||
TArray<AMenuCameraSpot*> AllSpots;
|
||||
TArray<AMenuCameraSpot*> Candidates;
|
||||
for (TActorIterator<AMenuCameraSpot> It(World); It; ++It)
|
||||
{
|
||||
AMenuCameraSpot* Spot = *It;
|
||||
if (!Spot->bEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AllSpots.Add(Spot);
|
||||
|
||||
// On ecarte l'angle du dernier passage pour ne pas revoir deux fois de
|
||||
// suite le meme decor en revenant au menu.
|
||||
if (Spot->GetFName() != LastUsed)
|
||||
{
|
||||
Candidates.Add(Spot);
|
||||
}
|
||||
}
|
||||
|
||||
// Une seule camera dans la map : on la reprend plutot que de n'afficher
|
||||
// aucune vue.
|
||||
if (Candidates.Num() == 0)
|
||||
{
|
||||
Candidates = MoveTemp(AllSpots);
|
||||
}
|
||||
|
||||
if (Candidates.Num() == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AMenuCameraSpot* Chosen = Candidates[FMath::RandRange(0, Candidates.Num() - 1)];
|
||||
|
||||
if (SurvivalGameInstance)
|
||||
{
|
||||
SurvivalGameInstance->SetLastMenuCameraName(Chosen->GetFName());
|
||||
}
|
||||
|
||||
return Chosen;
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::StartSoloGame()
|
||||
{
|
||||
// Le fondu est deja lance : on ignore le second clic plutot que d'empiler
|
||||
// deux chargements.
|
||||
if (PendingAction != EMenuPendingAction::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameLevel.IsNull())
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("AMainMenuPlayerController : GameLevel n'est pas assignee, impossible de lancer la partie."));
|
||||
return;
|
||||
}
|
||||
|
||||
PendingAction = EMenuPendingAction::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)
|
||||
{
|
||||
MainMenu->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
|
||||
ScreenFade->FadeOut(LeaveFadeDuration);
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::QuitGame()
|
||||
{
|
||||
// Une partie est deja en cours de chargement : la question n'a plus de sens.
|
||||
if (PendingAction != EMenuPendingAction::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Sans boite assignee on ne bloque pas le joueur dans son menu : on ferme.
|
||||
if (!QuitDialogClass)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("AMainMenuPlayerController : QuitDialogClass n'est pas assignee, fermeture sans confirmation."));
|
||||
CloseGame();
|
||||
return;
|
||||
}
|
||||
|
||||
// Creee au premier clic seulement, puis reutilisee : une boite qu'on ouvre
|
||||
// et ferme plusieurs fois n'a aucune raison d'etre reconstruite.
|
||||
if (!QuitDialog)
|
||||
{
|
||||
QuitDialog = CreateWidget<UConfirmDialogWidget>(this, QuitDialogClass);
|
||||
if (!QuitDialog)
|
||||
{
|
||||
CloseGame();
|
||||
return;
|
||||
}
|
||||
|
||||
QuitDialog->OnConfirmed.AddDynamic(this, &AMainMenuPlayerController::HandleQuitConfirmed);
|
||||
QuitDialog->OnCancelled.AddDynamic(this, &AMainMenuPlayerController::HandleQuitCancelled);
|
||||
QuitDialog->AddToViewport(ZOrderQuitDialog);
|
||||
}
|
||||
|
||||
QuitDialog->Open(QuitDialogMessage);
|
||||
|
||||
// Le menu reste lisible derriere mais cesse de repondre : sans ca, on
|
||||
// pourrait relancer une partie alors que la question est encore posee.
|
||||
if (MainMenu)
|
||||
{
|
||||
MainMenu->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::HandleQuitConfirmed()
|
||||
{
|
||||
// Pas de fondu : le joueur vient de confirmer, le faire patienter une
|
||||
// seconde de plus ne serait qu'agacant.
|
||||
CloseGame();
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::HandleQuitCancelled()
|
||||
{
|
||||
if (QuitDialog)
|
||||
{
|
||||
QuitDialog->Close();
|
||||
}
|
||||
|
||||
if (MainMenu)
|
||||
{
|
||||
MainMenu->SetVisibility(ESlateVisibility::Visible);
|
||||
}
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::CloseGame()
|
||||
{
|
||||
UKismetSystemLibrary::QuitGame(this, this, EQuitPreference::Quit, /*bIgnorePlatformRestrictions=*/false);
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::HandleFadeFinished()
|
||||
{
|
||||
// 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.
|
||||
if (PendingAction == EMenuPendingAction::StartGame)
|
||||
{
|
||||
UGameplayStatics::OpenLevelBySoftObjectPtr(this, GameLevel);
|
||||
}
|
||||
}
|
||||
|
||||
void AMainMenuPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (QuitDialog)
|
||||
{
|
||||
QuitDialog->RemoveFromParent();
|
||||
QuitDialog = nullptr;
|
||||
}
|
||||
|
||||
if (MainMenu)
|
||||
{
|
||||
MainMenu->RemoveFromParent();
|
||||
MainMenu = nullptr;
|
||||
}
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
Reference in New Issue
Block a user