添加overlap界面

This commit is contained in:
不明不惑
2026-03-03 23:01:47 +08:00
parent 8574e8bfb8
commit 6d0fb2c928
18 changed files with 295 additions and 129 deletions

View File

@@ -8,4 +8,5 @@ CopyrightNotice=
[/Script/GenericUISystem.GUIS_GenericUISystemSettings]
; Set this in editor to a BP subclass of UPHYGameUIPolicy (recommended), e.g.
; /Game/AGame/UI/BP_PHYGameUIPolicy.BP_PHYGameUIPolicy_C
GameUIPolicyClass=
GameUIPolicyClass=/Game/AGame/UI/GameUIPolicy.GameUIPolicy_C

View File

@@ -90,11 +90,15 @@ void APHYPlayerCharacter::PossessedBy(AController* NewController)
void APHYPlayerCharacter::InitializeHUD()
{
APHYPlayerState* PS = GetPlayerState<APHYPlayerState>();
if (!PS) return;
UAbilitySystemComponent* ASC = PS->GetAbilitySystemComponent();
if (!ASC) return;
if (const APlayerController* PC = Cast<APlayerController>(GetController()))
{
if (APHYGameHUD* GameHUD = Cast<APHYGameHUD>(PC->GetHUD()))
{
GameHUD->InitializeHUD();
GameHUD->InitializeHUD(ASC, PS);
}
}
}
@@ -180,18 +184,6 @@ void APHYPlayerCharacter::InitializeGAS()
}
}
// Push MoveSpeed to CharacterMovement (both sides, will converge via replication)
if (UCharacterMovementComponent* MoveComp = GetCharacterMovement())
{
if (const UPHYAttributeSet* AS = PS->GetAttributeSet())
{
const float DesiredSpeed = AS->GetMoveSpeed();
if (DesiredSpeed > 0.f)
{
MoveComp->MaxWalkSpeed = DesiredSpeed;
}
}
}
}
void APHYPlayerCharacter::StopRegen()

View File

@@ -10,4 +10,7 @@ namespace UITags
UE_DEFINE_GAMEPLAY_TAG(Tag__UI_Layer_GameMenu, "UI.Layer.GameMenu");
UE_DEFINE_GAMEPLAY_TAG(Tag__UI_Layer_Menu, "UI.Layer.Menu");
UE_DEFINE_GAMEPLAY_TAG(Tag__UI_Layer_Modal, "UI.Layer.Modal");
// 拓展点
UE_DEFINE_GAMEPLAY_TAG(Tag__UI_HUD_CharacterInfo, "UI.HUD.CharacterInfo");
}

View File

@@ -14,4 +14,7 @@ namespace UITags
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__UI_Layer_GameMenu);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__UI_Layer_Menu);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__UI_Layer_Modal);
// 拓展点
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__UI_HUD_CharacterInfo);
};

View File

@@ -0,0 +1,14 @@
//
#include "GameUIExtensionPointWidget.h"
UUserWidget* UGameUIExtensionPointWidget::FindExtensionWidgetByHandle(const FGUIS_GameUIExtHandle& Handle) const
{
return ExtensionMapping.FindRef(Handle);
}
void UGameUIExtensionPointWidget::SetExtensionTag(const FGameplayTag& InExtensionPointTag)
{
ExtensionPointTag = InExtensionPointTag;
}

View File

@@ -0,0 +1,20 @@
//
#pragma once
#include "CoreMinimal.h"
#include "UIExtension/GUIS_GameUIExtensionPointWidget.h"
#include "GameUIExtensionPointWidget.generated.h"
/**
*
*/
UCLASS()
class PHY_API UGameUIExtensionPointWidget : public UGUIS_GameUIExtensionPointWidget
{
GENERATED_BODY()
public:
UUserWidget* FindExtensionWidgetByHandle(const FGUIS_GameUIExtHandle& Handle) const;
void SetExtensionTag(const FGameplayTag& InExtensionPointTag);
};

View File

@@ -3,8 +3,12 @@
#include "PHYGameHUD.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "Gameplay/Player/PHYPlayerState.h"
#include "GameplayTags/UITags.h"
#include "UI/Actions/GUIS_AsyncAction_PushContentToUILayer.h"
#include "UI/Menu/Menu_Overlap.h"
UGUIS_GameUISubsystem* APHYGameHUD::GetUISubsystem() const
{
@@ -15,15 +19,7 @@ UGUIS_GameUISubsystem* APHYGameHUD::GetUISubsystem() const
return nullptr;
}
void APHYGameHUD::OnBeforePushOverlapWidget(UCommonActivatableWidget* UserWidget)
{
}
void APHYGameHUD::OnAfterPushOverlapWidget(UCommonActivatableWidget* UserWidget)
{
}
void APHYGameHUD::InitializeHUD()
void APHYGameHUD::InitializeHUD(UAbilitySystemComponent* InAbilitySystemComponent, APHYPlayerState* InPlayerState)
{
// 获取当前的player controller
APlayerController* PC = GetOwningPlayerController();
@@ -44,7 +40,37 @@ void APHYGameHUD::InitializeHUD()
PushAction->AfterPush.AddDynamic(this, &APHYGameHUD::OnAfterPushOverlapWidget);
PushAction->Activate();
}
if (InAbilitySystemComponent)
{
BoundAbilitySystemComponent = InAbilitySystemComponent;
} else
{
BoundAbilitySystemComponent = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetOwningPawn());
}
if (InPlayerState)
{
BoundPlayerState = InPlayerState;
} else
{
BoundPlayerState = PC->GetPlayerState<APHYPlayerState>();
}
}
void APHYGameHUD::OnBeforePushOverlapWidget(UCommonActivatableWidget* UserWidget)
{
}
void APHYGameHUD::OnAfterPushOverlapWidget(UCommonActivatableWidget* UserWidget)
{
if (UMenu_Overlap* OverlapWidget = Cast<UMenu_Overlap>(UserWidget))
{
OverlapWidgetInstance = OverlapWidget;
OverlapWidgetInstance->BindAscEvents(BoundAbilitySystemComponent);
}
}
void APHYGameHUD::EndPlay(const EEndPlayReason::Type EndPlayReason)
{

View File

@@ -8,6 +8,9 @@
#include "PHYGameHUD.generated.h"
class UMenu_Overlap;
class APHYPlayerState;
class UAbilitySystemComponent;
class UCommonActivatableWidget;
/**
*
@@ -22,7 +25,7 @@ class PHY_API APHYGameHUD : public AHUD
public:
void InitializeHUD();
void InitializeHUD(UAbilitySystemComponent* InAbilitySystemComponent, APHYPlayerState* InPlayerState);
protected:
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
@@ -35,4 +38,8 @@ private:
void OnBeforePushOverlapWidget(UCommonActivatableWidget* UserWidget);
UFUNCTION()
void OnAfterPushOverlapWidget(UCommonActivatableWidget* UserWidget);
UPROPERTY()
UMenu_Overlap* OverlapWidgetInstance;
UAbilitySystemComponent* BoundAbilitySystemComponent;
APHYPlayerState* BoundPlayerState;
};

View File

@@ -2,3 +2,77 @@
#include "Menu_Overlap.h"
#include "AbilitySystemComponent.h"
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
#include "GameplayTags/UITags.h"
#include "UI/GameUIExtensionPointWidget.h"
#include "UI/Widget/Widget_CharacterInfo.h"
void UMenu_Overlap::NativePreConstruct()
{
Super::NativePreConstruct();
}
void UMenu_Overlap::NativeOnActivated()
{
Super::NativeOnActivated();
}
void UMenu_Overlap::NativeOnInitialized()
{
Super::NativeOnInitialized();
// 推送拓展点
if (UGUIS_ExtensionSubsystem* ExtensionSubsystem = GetWorld()->GetSubsystem<UGUIS_ExtensionSubsystem>())
{
if (CharacterInfoWidgetClass)
{
CharacterInfoExtHandle = ExtensionSubsystem->RegisterExtensionAsWidget(UITags::Tag__UI_HUD_CharacterInfo,CharacterInfoWidgetClass, 0);
}
}
}
void UMenu_Overlap::BindAscEvents(UAbilitySystemComponent* AbilitySystemComponent)
{
if (AbilitySystemComponent == nullptr) return;
const UPHYAttributeSet* Attributes = AbilitySystemComponent->GetSet<UPHYAttributeSet>();
if (Attributes == nullptr) return;
// 从拓展点获取widget
if (!UserInfoExtensionPoint) return;
UUserWidget* CurrentUserInfoWidget = UserInfoExtensionPoint->FindExtensionWidgetByHandle(CharacterInfoExtHandle);
UWidget_CharacterInfo* CharacterInfoWidget = Cast<UWidget_CharacterInfo>(CurrentUserInfoWidget);
if (CharacterInfoWidget == nullptr) return;
// 同步初始属性值
CurrentHp = Attributes->GetHealth();
MaxHp = Attributes->GetMaxHealth();
CurrentMp = Attributes->GetInnerPower();
MaxMp = Attributes->GetMaxInnerPower();
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attributes->GetHealthAttribute()).AddLambda(
[this, CharacterInfoWidget](const FOnAttributeChangeData& Data)
{
CurrentHp = Data.NewValue;
CharacterInfoWidget->SetHp(CurrentHp, MaxHp);
});
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attributes->GetMaxHealthAttribute()).AddLambda(
[this, CharacterInfoWidget](const FOnAttributeChangeData& Data)
{
MaxHp = Data.NewValue;
CharacterInfoWidget->SetHp(CurrentHp, MaxHp);
});
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attributes->GetInnerPowerAttribute()).AddLambda(
[this, CharacterInfoWidget](const FOnAttributeChangeData& Data)
{
CurrentMp = Data.NewValue;
CharacterInfoWidget->SetMp(CurrentMp, MaxMp);
});
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attributes->GetMaxInnerPowerAttribute()).AddLambda(
[this, CharacterInfoWidget](const FOnAttributeChangeData& Data)
{
MaxMp = Data.NewValue;
CharacterInfoWidget->SetMp(CurrentMp, MaxMp);
});
CharacterInfoWidget->SetHp(CurrentHp, MaxHp);
CharacterInfoWidget->SetMp(CurrentMp, MaxMp);
}

View File

@@ -4,8 +4,11 @@
#include "CoreMinimal.h"
#include "UI/GUIS_ActivatableWidget.h"
#include "UIExtension/GUIS_GameUIExtensionSubsystem.h"
#include "Menu_Overlap.generated.h"
class UAbilitySystemComponent;
class UGameUIExtensionPointWidget;
/**
*
*/
@@ -13,4 +16,26 @@ UCLASS()
class PHY_API UMenu_Overlap : public UGUIS_ActivatableWidget
{
GENERATED_BODY()
UPROPERTY(meta=(BindWidget))
UGameUIExtensionPointWidget* UserInfoExtensionPoint;
UPROPERTY(EditDefaultsOnly, Category="Sub Widget Class")
TSubclassOf<UUserWidget> CharacterInfoWidgetClass;
FGUIS_GameUIExtHandle CharacterInfoExtHandle;
protected:
virtual void NativePreConstruct() override;
virtual void NativeOnActivated() override;
virtual void NativeOnInitialized() override;
public:
void BindAscEvents(UAbilitySystemComponent* AbilitySystemComponent);
private:
float CurrentHp = 0.f;
float MaxHp = 0.f;
float CurrentMp = 0.f;
float MaxMp = 0.f;
float CurrentXp = 0.f;
float MaxXp = 0.f;
};

View File

@@ -1,37 +0,0 @@
// Copyright 2026 PHY. All Rights Reserved.
#include "UI/PHYGameUILayout.h"
#include "GameplayTags/UITags.h"
#include "Widgets/CommonActivatableWidgetContainer.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameUILayout)
UPHYGameUILayout::UPHYGameUILayout(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UPHYGameUILayout::NativeOnInitialized()
{
Super::NativeOnInitialized();
// Register available layers. Widget Blueprint can bind any subset.
if (Layer_Game)
{
RegisterLayer(UITags::Tag__UI_Layer_Game, Layer_Game);
}
if (Layer_GameMenu)
{
RegisterLayer(UITags::Tag__UI_Layer_GameMenu, Layer_GameMenu);
}
if (Layer_Menu)
{
RegisterLayer(UITags::Tag__UI_Layer_Menu, Layer_Menu);
}
if (Layer_Modal)
{
RegisterLayer(UITags::Tag__UI_Layer_Modal, Layer_Modal);
}
}

View File

@@ -1,47 +0,0 @@
// Copyright 2026 PHY. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UI/GUIS_GameUILayout.h"
#include "PHYGameUILayout.generated.h"
class UCommonActivatableWidgetStack;
class UCommonActivatableWidgetContainerBase;
/**
* Root UI Layout for a single local player.
*
* In BP (Widget Blueprint derived from this), you should:
* - Create several UCommonActivatableWidgetStack (or other ContainerBase) widgets
* - Bind them to the properties below
* - In PreConstruct/Construct, call RegisterLayer for each stack with tags from UITags
*/
UCLASS(Abstract, BlueprintType)
class PHY_API UPHYGameUILayout : public UGUIS_GameUILayout
{
GENERATED_BODY()
public:
UPHYGameUILayout(const FObjectInitializer& ObjectInitializer);
protected:
virtual void NativeOnInitialized() override;
/** Game HUD layer stack. Tag: UI.Layer.Game */
UPROPERTY(meta=(BindWidgetOptional), BlueprintReadOnly)
TObjectPtr<UCommonActivatableWidgetContainerBase> Layer_Game = nullptr;
/** In-game menu layer stack. Tag: UI.Layer.GameMenu */
UPROPERTY(meta=(BindWidgetOptional), BlueprintReadOnly)
TObjectPtr<UCommonActivatableWidgetContainerBase> Layer_GameMenu = nullptr;
/** Main menu layer stack. Tag: UI.Layer.Menu */
UPROPERTY(meta=(BindWidgetOptional), BlueprintReadOnly)
TObjectPtr<UCommonActivatableWidgetContainerBase> Layer_Menu = nullptr;
/** Modal layer stack. Tag: UI.Layer.Modal */
UPROPERTY(meta=(BindWidgetOptional), BlueprintReadOnly)
TObjectPtr<UCommonActivatableWidgetContainerBase> Layer_Modal = nullptr;
};

View File

@@ -1,20 +0,0 @@
// Copyright 2026 PHY. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UI/GUIS_GameUIPolicy.h"
#include "PHYGameUIPolicy.generated.h"
/**
* Game-specific UI policy.
*
* This class mainly exists so we can set up a default LayoutClass in a Blueprint child,
* and keep all UI wiring inside the game module.
*/
UCLASS(BlueprintType)
class PHY_API UPHYGameUIPolicy : public UGUIS_GameUIPolicy
{
GENERATED_BODY()
};

View File

@@ -0,0 +1,63 @@
//
#include "Widget_CharacterInfo.h"
#include "Components/TextBlock.h"
#include "UI/Synty/Synty_ProgressBar.h"
void UWidget_CharacterInfo::SetXp(const float InCurrentXp, const float InMaxXp) const
{
if (InMaxXp == 0)
{
return;
}
if (XP)
{
XP->SetTargetFillAmount(InCurrentXp / InMaxXp);
}
if (XPText)
{
XPText->SetText(FText::FromString(FString::Printf(TEXT("%d / %d"), FMath::RoundToInt(InCurrentXp), FMath::RoundToInt(InMaxXp))));
}
}
void UWidget_CharacterInfo::SetHp(const float InCurrentHp, const float InMaxHp) const
{
if (InMaxHp == 0)
{
return;
}
if (HP)
{
HP->SetTargetFillAmount(InCurrentHp / InMaxHp);
}
if (HPText)
{
HPText->SetText(FText::FromString(FString::Printf(TEXT("%d / %d"), FMath::RoundToInt(InCurrentHp), FMath::RoundToInt(InMaxHp))));
}
}
void UWidget_CharacterInfo::SetMp(const float InCurrentMp, const float InMaxMp) const
{
if (InMaxMp == 0)
{
return;
}
if (MP)
{
MP->SetTargetFillAmount(InCurrentMp / InMaxMp);
}
if (MPText)
{
MPText->SetText(FText::FromString(FString::Printf(TEXT("%d / %d"), FMath::RoundToInt(InCurrentMp), FMath::RoundToInt(InMaxMp))));
}
}
void UWidget_CharacterInfo::SetLevel(const int32 InLevel) const
{
if (LevelText)
{
LevelText->SetText(FText::FromString(FString::Printf(TEXT("%d"), InLevel)));
}
}

View File

@@ -0,0 +1,42 @@
//
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Widget_CharacterInfo.generated.h"
/**
*
*/
UCLASS()
class PHY_API UWidget_CharacterInfo : public UUserWidget
{
GENERATED_BODY()
// xp
UPROPERTY(meta=(BindWidget))
class USynty_ProgressBar* XP;
// Hp
UPROPERTY(meta=(BindWidget))
class USynty_ProgressBar* HP;
// Mp
UPROPERTY(meta=(BindWidget))
class USynty_ProgressBar* MP;
UPROPERTY(meta=(BindWidget))
class UTextBlock* LevelText;
UPROPERTY(meta=(BindWidget))
class UTextBlock* XPText;
UPROPERTY(meta=(BindWidget))
class UTextBlock* HPText;
UPROPERTY(meta=(BindWidget))
class UTextBlock* MPText;
public:
void SetXp(float InCurrentXp, float InMaxXp) const;
void SetHp(float InCurrentHp, float InMaxHp) const;
void SetMp(float InCurrentMp, float InMaxMp) const;
void SetLevel(int32 InLevel) const;
};