// Fill out your copyright notice in the Description page of Project Settings. #include "ScreenFadeComponent.h" #include "Blueprint/UserWidget.h" #include "Camera/PlayerCameraManager.h" #include "GameFramework/PlayerController.h" namespace { // Le voile doit passer devant absolument tout, y compris les widgets qu'on // ajouterait plus tard sans y penser. constexpr int32 ZOrderFade = 1000; } UScreenFadeComponent::UScreenFadeComponent() { // Le composant ne tourne QUE pendant un fondu : on allume le tick dans // FadeIn/FadeOut et on l'eteint des la cible atteinte. Ticker en permanence // pour une valeur qui ne bouge qu'une seconde par mort serait du gaspillage. PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.bStartWithTickEnabled = false; } void UScreenFadeComponent::BeginPlay() { Super::BeginPlay(); // Cree des maintenant pour que le voile puisse couvrir des la premiere // frame, avant meme que le reste du HUD existe. EnsureWidget(); } APlayerController* UScreenFadeComponent::GetOwningController() const { return Cast(GetOwner()); } void UScreenFadeComponent::EnsureWidget() { if (FadeWidget) { return; } APlayerController* Controller = GetOwningController(); if (!Controller) { UE_LOG(LogTemp, Error, TEXT("UScreenFadeComponent : doit etre pose sur un PlayerController, pas sur %s."), *GetNameSafe(GetOwner())); return; } // Un widget n'existe que sur la machine du joueur local : inutile et // incorrect d'en creer un sur un serveur dedie. if (!Controller->IsLocalController()) { return; } if (!FadeWidgetClass) { UE_LOG(LogTemp, Warning, TEXT("UScreenFadeComponent : FadeWidgetClass n'est pas assignee, aucun fondu ne s'affichera.")); return; } FadeWidget = CreateWidget(Controller, FadeWidgetClass); if (FadeWidget) { FadeWidget->AddToViewport(ZOrderFade); SetCoverage(1.f - Alpha); } } void UScreenFadeComponent::SetCoverage(float Coverage) { if (!FadeWidget) { return; } const float Clamped = FMath::Clamp(Coverage, 0.f, 1.f); FadeWidget->SetRenderOpacity(Clamped); // HitTestInvisible et jamais Visible : un voile qui capture la souris // bloquerait les boutons du menu meme completement transparent. FadeWidget->SetVisibility(FMath::IsNearlyZero(Clamped) ? ESlateVisibility::Collapsed : ESlateVisibility::HitTestInvisible); } void UScreenFadeComponent::FadeIn(float Duration) { EnsureWidget(); if (APlayerController* Controller = GetOwningController()) { if (Controller->PlayerCameraManager) { Controller->PlayerCameraManager->StartCameraFade( /*FromAlpha=*/1.f, /*ToAlpha=*/0.f, Duration, FadeColor, /*bShouldFadeAudio=*/true); } } CurrentDuration = FMath::Max(Duration, 0.01f); Alpha = 0.f; Target = 1.f; bFading = true; SetCoverage(1.f); SetComponentTickEnabled(true); } void UScreenFadeComponent::FadeOut(float Duration) { EnsureWidget(); if (APlayerController* Controller = GetOwningController()) { if (Controller->PlayerCameraManager) { // bHoldWhenFinished : l'ecran reste noir au lieu de revenir tout seul. Controller->PlayerCameraManager->StartCameraFade( /*FromAlpha=*/0.f, /*ToAlpha=*/1.f, Duration, FadeColor, /*bShouldFadeAudio=*/true, /*bHoldWhenFinished=*/true); } } CurrentDuration = FMath::Max(Duration, 0.01f); Target = 0.f; bFading = true; SetComponentTickEnabled(true); } void UScreenFadeComponent::SnapToBlack() { EnsureWidget(); if (APlayerController* Controller = GetOwningController()) { if (Controller->PlayerCameraManager) { Controller->PlayerCameraManager->SetManualCameraFade(1.f, FadeColor, /*bFadeAudio=*/true); } } Alpha = 0.f; Target = 0.f; bFading = false; SetCoverage(1.f); SetComponentTickEnabled(false); } void UScreenFadeComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (!bFading) { return; } const float Step = DeltaTime / CurrentDuration; Alpha = FMath::Clamp(Alpha + Step * FMath::Sign(Target - Alpha), 0.f, 1.f); // Alpha va de 0 (ecran couvert) a 1 (jeu visible) : le voile suit l'inverse. SetCoverage(1.f - Alpha); if (FMath::IsNearlyEqual(Alpha, Target)) { bFading = false; // On eteint le tick AVANT de prevenir : un abonne qui relance un fondu // dans son handler doit pouvoir le rallumer. SetComponentTickEnabled(false); OnFadeFinished.Broadcast(); } } void UScreenFadeComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { if (FadeWidget) { FadeWidget->RemoveFromParent(); FadeWidget = nullptr; } Super::EndPlay(EndPlayReason); }