Bind HUD status to GAS and player state
This commit is contained in:
@@ -109,5 +109,11 @@ UCommonActivatableWidget* APHYHUD::PushDefaultHUDWidget(APlayerController* Ownin
|
||||
}
|
||||
|
||||
const FGameplayTag HUDLayerTag = (UISettings && UISettings->HUDLayerTag.IsValid()) ? UISettings->HUDLayerTag : PHYGameplayTags::UI_Layer_HUD;
|
||||
return RootLayout->PushWidgetToLayerStack<UCommonActivatableWidget>(HUDLayerTag, HUDWidgetClass);
|
||||
UCommonActivatableWidget* PushedWidget = RootLayout->PushWidgetToLayerStack<UCommonActivatableWidget>(HUDLayerTag, HUDWidgetClass);
|
||||
if (UPHYHUDStatusWidget* StatusWidget = Cast<UPHYHUDStatusWidget>(PushedWidget))
|
||||
{
|
||||
StatusWidget->InitializeFromPlayer(OwningPlayerController);
|
||||
}
|
||||
|
||||
return PushedWidget;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Class/PHYClassComponent.h"
|
||||
#include "Class/PHYClassSettings.h"
|
||||
#include "GGA_AbilitySystemComponent.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
APHYPlayerState::APHYPlayerState(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
@@ -28,7 +29,69 @@ APHYPlayerState::APHYPlayerState(const FObjectInitializer& ObjectInitializer)
|
||||
ClassComponent->SetClassTag(GetDefault<UPHYClassSettings>()->DefaultPlayerClassTag);
|
||||
}
|
||||
|
||||
void APHYPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(ThisClass, PlayerLevel);
|
||||
DOREPLIFETIME(ThisClass, Experience);
|
||||
DOREPLIFETIME(ThisClass, ExperienceForNextLevel);
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* APHYPlayerState::GetAbilitySystemComponent() const
|
||||
{
|
||||
return AbilitySystemComponent;
|
||||
}
|
||||
|
||||
void APHYPlayerState::SetPlayerLevel(const int32 NewLevel)
|
||||
{
|
||||
SetProgression(NewLevel, Experience, ExperienceForNextLevel);
|
||||
}
|
||||
|
||||
void APHYPlayerState::SetExperience(const int32 NewExperience)
|
||||
{
|
||||
SetProgression(PlayerLevel, NewExperience, ExperienceForNextLevel);
|
||||
}
|
||||
|
||||
void APHYPlayerState::AddExperience(const int32 DeltaExperience)
|
||||
{
|
||||
SetExperience(Experience + DeltaExperience);
|
||||
}
|
||||
|
||||
void APHYPlayerState::SetExperienceForNextLevel(const int32 NewExperienceForNextLevel)
|
||||
{
|
||||
SetProgression(PlayerLevel, Experience, NewExperienceForNextLevel);
|
||||
}
|
||||
|
||||
void APHYPlayerState::SetProgression(const int32 NewLevel, const int32 NewExperience, const int32 NewExperienceForNextLevel)
|
||||
{
|
||||
if (!HasAuthority())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 ClampedLevel = FMath::Max(1, NewLevel);
|
||||
const int32 ClampedExperience = FMath::Max(0, NewExperience);
|
||||
const int32 ClampedExperienceForNextLevel = FMath::Max(1, NewExperienceForNextLevel);
|
||||
|
||||
if (PlayerLevel == ClampedLevel && Experience == ClampedExperience && ExperienceForNextLevel == ClampedExperienceForNextLevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerLevel = ClampedLevel;
|
||||
Experience = ClampedExperience;
|
||||
ExperienceForNextLevel = ClampedExperienceForNextLevel;
|
||||
|
||||
BroadcastProgressionChanged();
|
||||
}
|
||||
|
||||
void APHYPlayerState::OnRep_Progression()
|
||||
{
|
||||
BroadcastProgressionChanged();
|
||||
}
|
||||
|
||||
void APHYPlayerState::BroadcastProgressionChanged()
|
||||
{
|
||||
OnProgressionChanged.Broadcast(PlayerLevel, Experience, ExperienceForNextLevel);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYHUDStatusWidget)
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||
#include "Blueprint/WidgetTree.h"
|
||||
#include "Components/Overlay.h"
|
||||
#include "Components/OverlaySlot.h"
|
||||
@@ -13,6 +15,9 @@
|
||||
#include "Components/VerticalBox.h"
|
||||
#include "Components/VerticalBoxSlot.h"
|
||||
#include "Fonts/SlateFontInfo.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameplayEffectTypes.h"
|
||||
#include "Player/PHYPlayerState.h"
|
||||
#include "Styling/CoreStyle.h"
|
||||
#include "Widgets/SWidget.h"
|
||||
|
||||
@@ -23,10 +28,29 @@ namespace
|
||||
return FText::FromString(FString::Printf(TEXT("%s %.0f / %.0f"), Label, FMath::Max(0.0f, Value), FMath::Max(0.0f, MaxValue)));
|
||||
}
|
||||
|
||||
FText MakeProgressionText(const int32 CurrentExperience, const int32 RequiredExperience)
|
||||
{
|
||||
return FText::FromString(FString::Printf(TEXT("XP %d / %d"), FMath::Max(0, CurrentExperience), FMath::Max(1, RequiredExperience)));
|
||||
}
|
||||
|
||||
float MakeResourcePercent(const float Value, const float MaxValue)
|
||||
{
|
||||
return MaxValue > UE_SMALL_NUMBER ? FMath::Clamp(Value / MaxValue, 0.0f, 1.0f) : 0.0f;
|
||||
}
|
||||
|
||||
float MakeProgressionPercent(const int32 CurrentExperience, const int32 RequiredExperience)
|
||||
{
|
||||
return RequiredExperience > 0 ? FMath::Clamp(static_cast<float>(CurrentExperience) / static_cast<float>(RequiredExperience), 0.0f, 1.0f) : 0.0f;
|
||||
}
|
||||
|
||||
void RemoveAttributeChangedDelegate(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayAttribute& Attribute, FDelegateHandle& DelegateHandle)
|
||||
{
|
||||
if (AbilitySystemComponent && DelegateHandle.IsValid())
|
||||
{
|
||||
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).Remove(DelegateHandle);
|
||||
DelegateHandle.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UPHYHUDStatusWidget::UPHYHUDStatusWidget(const FObjectInitializer& ObjectInitializer)
|
||||
@@ -47,6 +71,35 @@ void UPHYHUDStatusWidget::SynchronizeProperties()
|
||||
UpdateResourceWidgets();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::InitializeFromPlayer(APlayerController* InPlayerController)
|
||||
{
|
||||
if (!InPlayerController)
|
||||
{
|
||||
BoundPlayerController.Reset();
|
||||
UnbindFromPlayerState();
|
||||
UnbindFromAbilitySystem();
|
||||
return;
|
||||
}
|
||||
|
||||
BoundPlayerController = InPlayerController;
|
||||
RefreshFromBoundSources();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::RefreshFromBoundSources()
|
||||
{
|
||||
APlayerController* PlayerController = BoundPlayerController.Get();
|
||||
if (!PlayerController)
|
||||
{
|
||||
UnbindFromPlayerState();
|
||||
UnbindFromAbilitySystem();
|
||||
return;
|
||||
}
|
||||
|
||||
APHYPlayerState* PHYPlayerState = PlayerController->GetPlayerState<APHYPlayerState>();
|
||||
BindToPlayerState(PHYPlayerState);
|
||||
BindToAbilitySystem(PHYPlayerState ? PHYPlayerState->GetAbilitySystemComponent() : nullptr);
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::SetHealth(const float NewHealth, const float NewMaxHealth)
|
||||
{
|
||||
Health = NewHealth;
|
||||
@@ -54,6 +107,13 @@ void UPHYHUDStatusWidget::SetHealth(const float NewHealth, const float NewMaxHea
|
||||
UpdateResourceWidgets();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::SetMana(const float NewMana, const float NewMaxMana)
|
||||
{
|
||||
Mana = NewMana;
|
||||
MaxMana = NewMaxMana;
|
||||
UpdateResourceWidgets();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::SetStamina(const float NewStamina, const float NewMaxStamina)
|
||||
{
|
||||
Stamina = NewStamina;
|
||||
@@ -61,6 +121,54 @@ void UPHYHUDStatusWidget::SetStamina(const float NewStamina, const float NewMaxS
|
||||
UpdateResourceWidgets();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::SetProgression(const int32 NewLevel, const int32 NewExperience, const int32 NewExperienceForNextLevel)
|
||||
{
|
||||
Level = FMath::Max(1, NewLevel);
|
||||
Experience = FMath::Max(0, NewExperience);
|
||||
ExperienceForNextLevel = FMath::Max(1, NewExperienceForNextLevel);
|
||||
UpdateResourceWidgets();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
if (!BoundPlayerController.IsValid())
|
||||
{
|
||||
InitializeFromPlayer(GetOwningPlayer());
|
||||
}
|
||||
else
|
||||
{
|
||||
RefreshFromBoundSources();
|
||||
}
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::NativeDestruct()
|
||||
{
|
||||
UnbindFromPlayerState();
|
||||
UnbindFromAbilitySystem();
|
||||
BoundPlayerController.Reset();
|
||||
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::NativeTick(const FGeometry& MyGeometry, const float InDeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
|
||||
APlayerController* PlayerController = BoundPlayerController.Get();
|
||||
if (!PlayerController)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
APHYPlayerState* CurrentPlayerState = PlayerController->GetPlayerState<APHYPlayerState>();
|
||||
if (CurrentPlayerState != BoundPlayerState.Get() || !BoundAbilitySystemComponent.IsValid())
|
||||
{
|
||||
RefreshFromBoundSources();
|
||||
}
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::BuildNativeWidgetTree()
|
||||
{
|
||||
if (!WidgetTree)
|
||||
@@ -68,7 +176,7 @@ void UPHYHUDStatusWidget::BuildNativeWidgetTree()
|
||||
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||
}
|
||||
|
||||
if (WidgetTree->RootWidget && TitleText && HealthText && HealthBar && StaminaText && StaminaBar)
|
||||
if (WidgetTree->RootWidget && TitleText && LevelText && HealthText && HealthBar && ManaText && ManaBar && StaminaText && StaminaBar && ExperienceText && ExperienceBar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -93,7 +201,15 @@ void UPHYHUDStatusWidget::BuildNativeWidgetTree()
|
||||
TitleText->SetColorAndOpacity(FSlateColor(FLinearColor(0.85f, 0.95f, 1.0f, 1.0f)));
|
||||
if (UVerticalBoxSlot* TitleSlot = StatusBox->AddChildToVerticalBox(TitleText))
|
||||
{
|
||||
TitleSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 8.0f));
|
||||
TitleSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 4.0f));
|
||||
}
|
||||
|
||||
LevelText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("LevelText"));
|
||||
LevelText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||
LevelText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||
if (UVerticalBoxSlot* LevelSlot = StatusBox->AddChildToVerticalBox(LevelText))
|
||||
{
|
||||
LevelSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 8.0f));
|
||||
}
|
||||
|
||||
HealthText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("HealthText"));
|
||||
@@ -108,6 +224,18 @@ void UPHYHUDStatusWidget::BuildNativeWidgetTree()
|
||||
HealthBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 8.0f));
|
||||
}
|
||||
|
||||
ManaText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("ManaText"));
|
||||
ManaText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||
ManaText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||
StatusBox->AddChildToVerticalBox(ManaText);
|
||||
|
||||
ManaBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("ManaBar"));
|
||||
ManaBar->SetFillColorAndOpacity(FLinearColor(0.26f, 0.34f, 0.86f, 1.0f));
|
||||
if (UVerticalBoxSlot* ManaBarSlot = StatusBox->AddChildToVerticalBox(ManaBar))
|
||||
{
|
||||
ManaBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 8.0f));
|
||||
}
|
||||
|
||||
StaminaText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("StaminaText"));
|
||||
StaminaText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||
StaminaText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||
@@ -117,14 +245,146 @@ void UPHYHUDStatusWidget::BuildNativeWidgetTree()
|
||||
StaminaBar->SetFillColorAndOpacity(FLinearColor(0.12f, 0.62f, 0.86f, 1.0f));
|
||||
if (UVerticalBoxSlot* StaminaBarSlot = StatusBox->AddChildToVerticalBox(StaminaBar))
|
||||
{
|
||||
StaminaBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 0.0f));
|
||||
StaminaBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 8.0f));
|
||||
}
|
||||
|
||||
ExperienceText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("ExperienceText"));
|
||||
ExperienceText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||
ExperienceText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||
StatusBox->AddChildToVerticalBox(ExperienceText);
|
||||
|
||||
ExperienceBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("ExperienceBar"));
|
||||
ExperienceBar->SetFillColorAndOpacity(FLinearColor(0.93f, 0.62f, 0.18f, 1.0f));
|
||||
if (UVerticalBoxSlot* ExperienceBarSlot = StatusBox->AddChildToVerticalBox(ExperienceBar))
|
||||
{
|
||||
ExperienceBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
UpdateResourceWidgets();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::BindToPlayerState(APHYPlayerState* NewPlayerState)
|
||||
{
|
||||
if (BoundPlayerState.Get() == NewPlayerState)
|
||||
{
|
||||
RefreshProgressionFromPlayerState();
|
||||
return;
|
||||
}
|
||||
|
||||
UnbindFromPlayerState();
|
||||
BoundPlayerState = NewPlayerState;
|
||||
|
||||
if (NewPlayerState)
|
||||
{
|
||||
NewPlayerState->OnProgressionChanged.AddUniqueDynamic(this, &UPHYHUDStatusWidget::HandleProgressionChanged);
|
||||
RefreshProgressionFromPlayerState();
|
||||
}
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::UnbindFromPlayerState()
|
||||
{
|
||||
if (APHYPlayerState* PlayerState = BoundPlayerState.Get())
|
||||
{
|
||||
PlayerState->OnProgressionChanged.RemoveDynamic(this, &UPHYHUDStatusWidget::HandleProgressionChanged);
|
||||
}
|
||||
BoundPlayerState.Reset();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::BindToAbilitySystem(UAbilitySystemComponent* NewAbilitySystemComponent)
|
||||
{
|
||||
if (BoundAbilitySystemComponent.Get() == NewAbilitySystemComponent && HealthChangedHandle.IsValid())
|
||||
{
|
||||
RefreshResourcesFromAbilitySystem();
|
||||
return;
|
||||
}
|
||||
|
||||
UnbindFromAbilitySystem();
|
||||
BoundAbilitySystemComponent = NewAbilitySystemComponent;
|
||||
|
||||
if (!NewAbilitySystemComponent)
|
||||
{
|
||||
UpdateResourceWidgets();
|
||||
return;
|
||||
}
|
||||
|
||||
HealthChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetHealthAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||
MaxHealthChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetMaxHealthAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||
ManaChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetManaAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||
MaxManaChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetMaxManaAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||
StaminaChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetStaminaAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||
MaxStaminaChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetMaxStaminaAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||
|
||||
RefreshResourcesFromAbilitySystem();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::UnbindFromAbilitySystem()
|
||||
{
|
||||
if (UAbilitySystemComponent* AbilitySystemComponent = BoundAbilitySystemComponent.Get())
|
||||
{
|
||||
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetHealthAttribute(), HealthChangedHandle);
|
||||
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxHealthAttribute(), MaxHealthChangedHandle);
|
||||
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetManaAttribute(), ManaChangedHandle);
|
||||
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxManaAttribute(), MaxManaChangedHandle);
|
||||
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetStaminaAttribute(), StaminaChangedHandle);
|
||||
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxStaminaAttribute(), MaxStaminaChangedHandle);
|
||||
}
|
||||
|
||||
HealthChangedHandle.Reset();
|
||||
MaxHealthChangedHandle.Reset();
|
||||
ManaChangedHandle.Reset();
|
||||
MaxManaChangedHandle.Reset();
|
||||
StaminaChangedHandle.Reset();
|
||||
MaxStaminaChangedHandle.Reset();
|
||||
BoundAbilitySystemComponent.Reset();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::RefreshResourcesFromAbilitySystem()
|
||||
{
|
||||
UAbilitySystemComponent* AbilitySystemComponent = BoundAbilitySystemComponent.Get();
|
||||
if (!AbilitySystemComponent)
|
||||
{
|
||||
UpdateResourceWidgets();
|
||||
return;
|
||||
}
|
||||
|
||||
Health = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetHealthAttribute());
|
||||
MaxHealth = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetMaxHealthAttribute());
|
||||
Mana = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetManaAttribute());
|
||||
MaxMana = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetMaxManaAttribute());
|
||||
Stamina = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetStaminaAttribute());
|
||||
MaxStamina = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetMaxStaminaAttribute());
|
||||
|
||||
UpdateResourceWidgets();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::RefreshProgressionFromPlayerState()
|
||||
{
|
||||
APHYPlayerState* PlayerState = BoundPlayerState.Get();
|
||||
if (!PlayerState)
|
||||
{
|
||||
UpdateResourceWidgets();
|
||||
return;
|
||||
}
|
||||
|
||||
SetProgression(PlayerState->GetPlayerLevel(), PlayerState->GetExperience(), PlayerState->GetExperienceForNextLevel());
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::HandleAttributeChanged(const FOnAttributeChangeData& /*ChangeData*/)
|
||||
{
|
||||
RefreshResourcesFromAbilitySystem();
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::HandleProgressionChanged(const int32 NewLevel, const int32 NewExperience, const int32 NewExperienceForNextLevel)
|
||||
{
|
||||
SetProgression(NewLevel, NewExperience, NewExperienceForNextLevel);
|
||||
}
|
||||
|
||||
void UPHYHUDStatusWidget::UpdateResourceWidgets()
|
||||
{
|
||||
if (LevelText)
|
||||
{
|
||||
LevelText->SetText(FText::FromString(FString::Printf(TEXT("Lv %d"), FMath::Max(1, Level))));
|
||||
}
|
||||
if (HealthText)
|
||||
{
|
||||
HealthText->SetText(MakeResourceText(TEXT("HP"), Health, MaxHealth));
|
||||
@@ -133,6 +393,14 @@ void UPHYHUDStatusWidget::UpdateResourceWidgets()
|
||||
{
|
||||
HealthBar->SetPercent(MakeResourcePercent(Health, MaxHealth));
|
||||
}
|
||||
if (ManaText)
|
||||
{
|
||||
ManaText->SetText(MakeResourceText(TEXT("MP"), Mana, MaxMana));
|
||||
}
|
||||
if (ManaBar)
|
||||
{
|
||||
ManaBar->SetPercent(MakeResourcePercent(Mana, MaxMana));
|
||||
}
|
||||
if (StaminaText)
|
||||
{
|
||||
StaminaText->SetText(MakeResourceText(TEXT("ST"), Stamina, MaxStamina));
|
||||
@@ -141,4 +409,12 @@ void UPHYHUDStatusWidget::UpdateResourceWidgets()
|
||||
{
|
||||
StaminaBar->SetPercent(MakeResourcePercent(Stamina, MaxStamina));
|
||||
}
|
||||
if (ExperienceText)
|
||||
{
|
||||
ExperienceText->SetText(MakeProgressionText(Experience, ExperienceForNextLevel));
|
||||
}
|
||||
if (ExperienceBar)
|
||||
{
|
||||
ExperienceBar->SetPercent(MakeProgressionPercent(Experience, ExperienceForNextLevel));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ class UPHYCoreAttributeSet;
|
||||
class UPHYElementAttributeSet;
|
||||
class UPHYClassComponent;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FPHYPlayerProgressionChangedSignature, int32, NewLevel, int32, NewExperience, int32, NewExperienceForNextLevel);
|
||||
|
||||
/**
|
||||
* @brief PHY 玩家状态。
|
||||
*
|
||||
@@ -27,6 +29,9 @@ public:
|
||||
/** @brief 构造玩家状态与 ASC。 */
|
||||
APHYPlayerState(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||
|
||||
/** @brief 注册 PlayerState 自身复制字段。 */
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
/** @brief 获取玩家持久 AbilitySystemComponent。 */
|
||||
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
|
||||
|
||||
@@ -50,7 +55,50 @@ public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Class")
|
||||
UPHYClassComponent* GetClassComponent() const { return ClassComponent; }
|
||||
|
||||
/** @brief 获取当前角色等级。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Progression")
|
||||
int32 GetPlayerLevel() const { return PlayerLevel; }
|
||||
|
||||
/** @brief 获取当前经验值。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Progression")
|
||||
int32 GetExperience() const { return Experience; }
|
||||
|
||||
/** @brief 获取升到下一级所需经验值。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Progression")
|
||||
int32 GetExperienceForNextLevel() const { return ExperienceForNextLevel; }
|
||||
|
||||
/** @brief 服务端设置角色等级。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="PHY|Progression")
|
||||
void SetPlayerLevel(int32 NewLevel);
|
||||
|
||||
/** @brief 服务端设置当前经验值。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="PHY|Progression")
|
||||
void SetExperience(int32 NewExperience);
|
||||
|
||||
/** @brief 服务端增加当前经验值。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="PHY|Progression")
|
||||
void AddExperience(int32 DeltaExperience);
|
||||
|
||||
/** @brief 服务端设置下一级所需经验值。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="PHY|Progression")
|
||||
void SetExperienceForNextLevel(int32 NewExperienceForNextLevel);
|
||||
|
||||
/** @brief 服务端一次性设置角色成长状态。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="PHY|Progression")
|
||||
void SetProgression(int32 NewLevel, int32 NewExperience, int32 NewExperienceForNextLevel);
|
||||
|
||||
/** @brief 等级或经验变化时通知本地 UI。 */
|
||||
UPROPERTY(BlueprintAssignable, Category="PHY|Progression")
|
||||
FPHYPlayerProgressionChangedSignature OnProgressionChanged;
|
||||
|
||||
protected:
|
||||
/** @brief 复制后通知本地 UI 刷新成长信息。 */
|
||||
UFUNCTION()
|
||||
void OnRep_Progression();
|
||||
|
||||
/** @brief 广播当前成长状态。 */
|
||||
void BroadcastProgressionChanged();
|
||||
|
||||
/** @brief 玩家持久 ASC。 */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|PlayerState")
|
||||
TObjectPtr<UGGA_AbilitySystemComponent> AbilitySystemComponent;
|
||||
@@ -70,4 +118,16 @@ protected:
|
||||
/** @brief 玩家持久职业组件,职业不保存在 PlayerCharacter 上。 */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Class")
|
||||
TObjectPtr<UPHYClassComponent> ClassComponent;
|
||||
|
||||
/** @brief 当前角色等级,由服务端复制给客户端 UI。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, ReplicatedUsing=OnRep_Progression, Category="PHY|Progression")
|
||||
int32 PlayerLevel = 1;
|
||||
|
||||
/** @brief 当前经验值,由服务端复制给客户端 UI。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, ReplicatedUsing=OnRep_Progression, Category="PHY|Progression")
|
||||
int32 Experience = 0;
|
||||
|
||||
/** @brief 当前等级升到下一级所需经验值,由服务端复制给客户端 UI。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, ReplicatedUsing=OnRep_Progression, Category="PHY|Progression")
|
||||
int32 ExperienceForNextLevel = 100;
|
||||
};
|
||||
|
||||
@@ -6,13 +6,17 @@
|
||||
#include "UI/GUIS_ActivatableWidget.h"
|
||||
#include "PHYHUDStatusWidget.generated.h"
|
||||
|
||||
class APlayerController;
|
||||
class APHYPlayerState;
|
||||
class UAbilitySystemComponent;
|
||||
class UProgressBar;
|
||||
class UTextBlock;
|
||||
struct FOnAttributeChangeData;
|
||||
|
||||
/**
|
||||
* @brief PHY 默认 HUD 状态控件。
|
||||
*
|
||||
* 作为基础 UI 的 C++ 可见占位,后续战斗、背包、队伍等系统可以替换或扩展为项目资产。
|
||||
* 作为基础 UI 的 C++ 可见占位,绑定 PlayerState 上的 GAS 资源属性和成长状态。
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class PHY_API UPHYHUDStatusWidget : public UGUIS_ActivatableWidget
|
||||
@@ -29,19 +33,69 @@ public:
|
||||
/** @brief 同步文本和进度条显示。 */
|
||||
virtual void SynchronizeProperties() override;
|
||||
|
||||
/** @brief 绑定本地玩家控制器,随后从 PlayerState 和 GAS 拉取状态。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||
void InitializeFromPlayer(APlayerController* InPlayerController);
|
||||
|
||||
/** @brief 从当前绑定来源重新拉取 PlayerState、ASC、资源和成长信息。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||
void RefreshFromBoundSources();
|
||||
|
||||
/** @brief 设置生命值显示。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||
void SetHealth(float NewHealth, float NewMaxHealth);
|
||||
|
||||
/** @brief 设置法力值显示。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||
void SetMana(float NewMana, float NewMaxMana);
|
||||
|
||||
/** @brief 设置耐力值显示。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||
void SetStamina(float NewStamina, float NewMaxStamina);
|
||||
|
||||
/** @brief 设置等级和经验显示。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||
void SetProgression(int32 NewLevel, int32 NewExperience, int32 NewExperienceForNextLevel);
|
||||
|
||||
protected:
|
||||
/** @brief Widget 构造后尝试自动绑定 OwningPlayer。 */
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
/** @brief Widget 销毁前解除 GAS 与 PlayerState 委托。 */
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
/** @brief PlayerState 延迟到达时重试绑定。 */
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
/** @brief 构造原生 HUD 树。 */
|
||||
void BuildNativeWidgetTree();
|
||||
|
||||
/** @brief 刷新单条资源文本和进度条。 */
|
||||
/** @brief 绑定新的 PlayerState 成长状态来源。 */
|
||||
void BindToPlayerState(APHYPlayerState* NewPlayerState);
|
||||
|
||||
/** @brief 解除 PlayerState 成长状态来源。 */
|
||||
void UnbindFromPlayerState();
|
||||
|
||||
/** @brief 绑定新的 AbilitySystemComponent 属性来源。 */
|
||||
void BindToAbilitySystem(UAbilitySystemComponent* NewAbilitySystemComponent);
|
||||
|
||||
/** @brief 解除 AbilitySystemComponent 属性来源。 */
|
||||
void UnbindFromAbilitySystem();
|
||||
|
||||
/** @brief 从 GAS 当前数值刷新生命、法力、耐力。 */
|
||||
void RefreshResourcesFromAbilitySystem();
|
||||
|
||||
/** @brief 从 PlayerState 当前数值刷新等级、经验。 */
|
||||
void RefreshProgressionFromPlayerState();
|
||||
|
||||
/** @brief GAS 属性变化后刷新资源条。 */
|
||||
void HandleAttributeChanged(const FOnAttributeChangeData& ChangeData);
|
||||
|
||||
/** @brief PlayerState 成长信息变化后刷新显示。 */
|
||||
UFUNCTION()
|
||||
void HandleProgressionChanged(int32 NewLevel, int32 NewExperience, int32 NewExperienceForNextLevel);
|
||||
|
||||
/** @brief 刷新所有文本和进度条。 */
|
||||
void UpdateResourceWidgets();
|
||||
|
||||
/** @brief 当前生命值。 */
|
||||
@@ -52,6 +106,14 @@ protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||
float MaxHealth = 100.0f;
|
||||
|
||||
/** @brief 当前法力值。 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||
float Mana = 100.0f;
|
||||
|
||||
/** @brief 当前最大法力值。 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||
float MaxMana = 100.0f;
|
||||
|
||||
/** @brief 当前耐力值。 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||
float Stamina = 100.0f;
|
||||
@@ -60,10 +122,42 @@ protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||
float MaxStamina = 100.0f;
|
||||
|
||||
/** @brief 当前角色等级。 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||
int32 Level = 1;
|
||||
|
||||
/** @brief 当前经验值。 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||
int32 Experience = 0;
|
||||
|
||||
/** @brief 当前等级升到下一级所需经验值。 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||
int32 ExperienceForNextLevel = 100;
|
||||
|
||||
/** @brief 当前绑定的本地玩家控制器弱引用。 */
|
||||
TWeakObjectPtr<APlayerController> BoundPlayerController;
|
||||
|
||||
/** @brief 当前绑定的玩家状态弱引用。 */
|
||||
TWeakObjectPtr<APHYPlayerState> BoundPlayerState;
|
||||
|
||||
/** @brief 当前绑定的 ASC 弱引用。 */
|
||||
TWeakObjectPtr<UAbilitySystemComponent> BoundAbilitySystemComponent;
|
||||
|
||||
FDelegateHandle HealthChangedHandle;
|
||||
FDelegateHandle MaxHealthChangedHandle;
|
||||
FDelegateHandle ManaChangedHandle;
|
||||
FDelegateHandle MaxManaChangedHandle;
|
||||
FDelegateHandle StaminaChangedHandle;
|
||||
FDelegateHandle MaxStaminaChangedHandle;
|
||||
|
||||
/** @brief 标题文本。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> TitleText;
|
||||
|
||||
/** @brief 等级文本。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> LevelText;
|
||||
|
||||
/** @brief 生命文本。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> HealthText;
|
||||
@@ -72,6 +166,14 @@ protected:
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UProgressBar> HealthBar;
|
||||
|
||||
/** @brief 法力文本。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> ManaText;
|
||||
|
||||
/** @brief 法力进度条。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UProgressBar> ManaBar;
|
||||
|
||||
/** @brief 耐力文本。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> StaminaText;
|
||||
@@ -79,4 +181,12 @@ protected:
|
||||
/** @brief 耐力进度条。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UProgressBar> StaminaBar;
|
||||
|
||||
/** @brief 经验文本。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> ExperienceText;
|
||||
|
||||
/** @brief 经验进度条。 */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UProgressBar> ExperienceBar;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user