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,133 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "InteractionPromptWidget.h"
|
||||
|
||||
#include "Components/TextBlock.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "FpsPlayer.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "InteractionComponent.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "InteractionPrompt"
|
||||
|
||||
UInteractionPromptWidget::UInteractionPromptWidget(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
PromptFormat = LOCTEXT("PromptFormat", "[{0}] {1}");
|
||||
}
|
||||
|
||||
void UInteractionPromptWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
// NativeConstruct est l'equivalent de BeginPlay pour un widget : il est
|
||||
// appele une fois que le widget est ajoute au viewport.
|
||||
BindToOwningPawn();
|
||||
}
|
||||
|
||||
void UInteractionPromptWidget::BindToOwningPawn()
|
||||
{
|
||||
const APlayerController* OwningController = GetOwningPlayer();
|
||||
const AFpsPlayer* Player = OwningController ? Cast<AFpsPlayer>(OwningController->GetPawn()) : nullptr;
|
||||
UInteractionComponent* NewComponent = Player ? Player->GetInteractionComponent() : nullptr;
|
||||
|
||||
if (BoundComponent.Get() == NewComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (BoundComponent.IsValid())
|
||||
{
|
||||
BoundComponent->OnFocusChanged.RemoveDynamic(this, &UInteractionPromptWidget::HandleFocusChanged);
|
||||
}
|
||||
|
||||
BoundComponent = NewComponent;
|
||||
|
||||
if (BoundComponent.IsValid())
|
||||
{
|
||||
// Abonnement au delegue : le widget ne se reveille que quand la cible
|
||||
// change vraiment. Pas de Tick, pas de polling.
|
||||
BoundComponent->OnFocusChanged.AddDynamic(this, &UInteractionPromptWidget::HandleFocusChanged);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("InteractionPromptWidget : le pawn possede n'a pas de UInteractionComponent, le prompt restera cache."));
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void UInteractionPromptWidget::NativeDestruct()
|
||||
{
|
||||
// Desabonnement systematique. Sans ca, le delegue garderait une reference
|
||||
// vers un widget detruit -- typiquement au changement de niveau.
|
||||
if (BoundComponent.IsValid())
|
||||
{
|
||||
BoundComponent->OnFocusChanged.RemoveDynamic(this, &UInteractionPromptWidget::HandleFocusChanged);
|
||||
}
|
||||
BoundComponent.Reset();
|
||||
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
void UInteractionPromptWidget::HandleFocusChanged(AActor* NewFocus, AActor* OldFocus)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void UInteractionPromptWidget::Refresh()
|
||||
{
|
||||
const FText Prompt = BoundComponent.IsValid() ? BoundComponent->GetFocusedPrompt() : FText::GetEmpty();
|
||||
|
||||
if (Prompt.IsEmpty())
|
||||
{
|
||||
// Collapsed et non Hidden : Hidden garde la place reservee dans la
|
||||
// mise en page, Collapsed la libere completement.
|
||||
SetVisibility(ESlateVisibility::Collapsed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PromptText)
|
||||
{
|
||||
PromptText->SetText(FText::Format(PromptFormat, GetInteractKeyDisplayName(), Prompt));
|
||||
}
|
||||
|
||||
// HitTestInvisible : le prompt s'affiche mais ne capture pas la souris,
|
||||
// sinon il volerait les clics destines au jeu.
|
||||
SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
|
||||
FText UInteractionPromptWidget::GetInteractKeyDisplayName() const
|
||||
{
|
||||
const APlayerController* OwningController = GetOwningPlayer();
|
||||
const AFpsPlayer* Player = OwningController ? Cast<AFpsPlayer>(OwningController->GetPawn()) : nullptr;
|
||||
if (!Player)
|
||||
{
|
||||
return FText::GetEmpty();
|
||||
}
|
||||
|
||||
const UInputAction* Action = Player->GetInteractAction();
|
||||
if (!Action)
|
||||
{
|
||||
return FText::GetEmpty();
|
||||
}
|
||||
|
||||
// On demande au sous-systeme Enhanced Input quelle touche est REELLEMENT
|
||||
// mappee sur l'action. Si tu changes E en F dans IMC_Default, ou si le
|
||||
// joueur remappe ses touches un jour, le texte suit tout seul.
|
||||
if (const UEnhancedInputLocalPlayerSubsystem* Subsystem =
|
||||
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(OwningController->GetLocalPlayer()))
|
||||
{
|
||||
const TArray<FKey> Keys = Subsystem->QueryKeysMappedToAction(Action);
|
||||
if (Keys.Num() > 0)
|
||||
{
|
||||
return Keys[0].GetDisplayName(/*bLongDisplayName=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
return FText::GetEmpty();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
Reference in New Issue
Block a user