Add Generic UI foundation
This commit is contained in:
@@ -2,6 +2,9 @@
|
|||||||
[/Script/CommonUI.CommonUISettings]
|
[/Script/CommonUI.CommonUISettings]
|
||||||
CommonButtonAcceptKeyHandling=TriggerClick
|
CommonButtonAcceptKeyHandling=TriggerClick
|
||||||
|
|
||||||
|
[/Script/GenericUISystem.GUIS_GenericUISystemSettings]
|
||||||
|
GameUIPolicyClass="/Script/PHY.PHYGameUIPolicy"
|
||||||
|
|
||||||
[/Script/EngineSettings.GeneralProjectSettings]
|
[/Script/EngineSettings.GeneralProjectSettings]
|
||||||
ProjectID=E7C26E1F4D195F0DBE49C2A3E5017988
|
ProjectID=E7C26E1F4D195F0DBE49C2A3E5017988
|
||||||
CopyrightNotice=
|
CopyrightNotice=
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
[/Script/PHY.PHYUISettings]
|
[/Script/PHY.PHYUISettings]
|
||||||
bUseCommonUI=True
|
bUseCommonUI=True
|
||||||
|
bUseGenericUISystem=True
|
||||||
bOpenMenusWithGameplayPause=False
|
bOpenMenusWithGameplayPause=False
|
||||||
|
RootLayoutClass="/Script/PHY.PHYGameUILayout"
|
||||||
|
DefaultHUDWidgetClass="/Script/PHY.PHYHUDStatusWidget"
|
||||||
|
GameplayLayerTag=(TagName="UI.Layer.Game")
|
||||||
|
HUDLayerTag=(TagName="UI.Layer.HUD")
|
||||||
|
MenuLayerTag=(TagName="UI.Layer.Menu")
|
||||||
|
ModalLayerTag=(TagName="UI.Layer.Modal")
|
||||||
DefaultHUDLayerName=HUD
|
DefaultHUDLayerName=HUD
|
||||||
|
|||||||
@@ -77,6 +77,10 @@
|
|||||||
{
|
{
|
||||||
"Name": "EnhancedInput",
|
"Name": "EnhancedInput",
|
||||||
"Enabled": true
|
"Enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "CommonUI",
|
||||||
|
"Enabled": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -19,10 +19,16 @@ public class PHY : ModuleRules
|
|||||||
"GameplayAbilities",
|
"GameplayAbilities",
|
||||||
"GameplayTasks",
|
"GameplayTasks",
|
||||||
"ModularGameplay",
|
"ModularGameplay",
|
||||||
|
"CommonUI",
|
||||||
|
"CommonInput",
|
||||||
|
"UMG",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore",
|
||||||
"GenericGameplayAbilities",
|
"GenericGameplayAbilities",
|
||||||
"GenericCombatSystem",
|
"GenericCombatSystem",
|
||||||
"GenericInputSystem",
|
"GenericInputSystem",
|
||||||
"GenericGameSystem",
|
"GenericGameSystem",
|
||||||
|
"GenericUISystem",
|
||||||
"GenericEffectsSystem",
|
"GenericEffectsSystem",
|
||||||
"SLSCore",
|
"SLSCore",
|
||||||
"SmoothLocomotionSystem",
|
"SmoothLocomotionSystem",
|
||||||
|
|||||||
@@ -4,6 +4,16 @@
|
|||||||
|
|
||||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYHUD)
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYHUD)
|
||||||
|
|
||||||
|
#include "CommonActivatableWidget.h"
|
||||||
|
#include "Engine/GameInstance.h"
|
||||||
|
#include "GameplayTags/PHYGameplayTags_UI.h"
|
||||||
|
#include "PHYConfigSettings.h"
|
||||||
|
#include "UI/GUIS_GameUILayout.h"
|
||||||
|
#include "UI/GUIS_GameUIPolicy.h"
|
||||||
|
#include "UI/GUIS_GameUISubsystem.h"
|
||||||
|
#include "UI/PHYHUDStatusWidget.h"
|
||||||
|
#include "UI/PHYPlayerUIContext.h"
|
||||||
|
|
||||||
APHYHUD::APHYHUD(const FObjectInitializer& ObjectInitializer)
|
APHYHUD::APHYHUD(const FObjectInitializer& ObjectInitializer)
|
||||||
: Super(ObjectInitializer)
|
: Super(ObjectInitializer)
|
||||||
{
|
{
|
||||||
@@ -23,6 +33,81 @@ void APHYHUD::InitializeHUDForPlayer(APlayerController* OwningPlayerController)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI 专家后续在这里接入 GenericUISystem/CommonUI 的根层级。
|
if (InitializeGenericUIForPlayer(OwningPlayerController))
|
||||||
|
{
|
||||||
|
DefaultHUDWidget = PushDefaultHUDWidget(OwningPlayerController);
|
||||||
bHUDInitialized = true;
|
bHUDInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYHUD::InitializeGenericUIForPlayer(APlayerController* OwningPlayerController)
|
||||||
|
{
|
||||||
|
if (!OwningPlayerController || !OwningPlayerController->IsLocalController())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYUISettings* UISettings = GetDefault<UPHYUISettings>();
|
||||||
|
if (!UISettings || !UISettings->bUseCommonUI || !UISettings->bUseGenericUISystem)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULocalPlayer* LocalPlayer = OwningPlayerController->GetLocalPlayer();
|
||||||
|
if (!LocalPlayer)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UGameInstance* GameInstance = OwningPlayerController->GetGameInstance();
|
||||||
|
UGUIS_GameUISubsystem* GameUISubsystem = GameInstance ? GameInstance->GetSubsystem<UGUIS_GameUISubsystem>() : nullptr;
|
||||||
|
if (!GameUISubsystem)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameUISubsystem->AddPlayer(LocalPlayer);
|
||||||
|
|
||||||
|
if (!PlayerUIContext)
|
||||||
|
{
|
||||||
|
PlayerUIContext = NewObject<UPHYPlayerUIContext>(this);
|
||||||
|
}
|
||||||
|
PlayerUIContext->InitializeForPlayer(OwningPlayerController);
|
||||||
|
|
||||||
|
GameUISubsystem->RegisterUIContextForPlayer(LocalPlayer, PlayerUIContext, PlayerUIContextBindingHandle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidget* APHYHUD::PushDefaultHUDWidget(APlayerController* OwningPlayerController)
|
||||||
|
{
|
||||||
|
if (!OwningPlayerController || DefaultHUDWidget)
|
||||||
|
{
|
||||||
|
return DefaultHUDWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYUISettings* UISettings = GetDefault<UPHYUISettings>();
|
||||||
|
TSubclassOf<UCommonActivatableWidget> HUDWidgetClass = UPHYHUDStatusWidget::StaticClass();
|
||||||
|
if (UISettings && !UISettings->DefaultHUDWidgetClass.IsNull())
|
||||||
|
{
|
||||||
|
if (UClass* LoadedHUDWidgetClass = UISettings->DefaultHUDWidgetClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
HUDWidgetClass = LoadedHUDWidgetClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ULocalPlayer* LocalPlayer = OwningPlayerController->GetLocalPlayer();
|
||||||
|
if (!LocalPlayer)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UGUIS_GameUIPolicy* UIPolicy = UGUIS_GameUIPolicy::GetGameUIPolicy(OwningPlayerController);
|
||||||
|
UGUIS_GameUILayout* RootLayout = UIPolicy ? UIPolicy->GetRootLayout(LocalPlayer) : nullptr;
|
||||||
|
if (!RootLayout)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FGameplayTag HUDLayerTag = (UISettings && UISettings->HUDLayerTag.IsValid()) ? UISettings->HUDLayerTag : PHYGameplayTags::UI_Layer_HUD;
|
||||||
|
return RootLayout->PushWidgetToLayerStack<UCommonActivatableWidget>(HUDLayerTag, HUDWidgetClass);
|
||||||
}
|
}
|
||||||
|
|||||||
12
Source/PHY/Private/GameplayTags/PHYGameplayTags_UI.cpp
Normal file
12
Source/PHY/Private/GameplayTags/PHYGameplayTags_UI.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "GameplayTags/PHYGameplayTags_UI.h"
|
||||||
|
|
||||||
|
// UI 层级 Tag 是 GenericUISystem 注册和推送界面的稳定路由,不使用裸字符串。
|
||||||
|
namespace PHYGameplayTags
|
||||||
|
{
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(UI_Layer_Game, "UI.Layer.Game");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(UI_Layer_HUD, "UI.Layer.HUD");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(UI_Layer_Menu, "UI.Layer.Menu");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(UI_Layer_Modal, "UI.Layer.Modal");
|
||||||
|
}
|
||||||
93
Source/PHY/Private/UI/PHYGameUILayout.cpp
Normal file
93
Source/PHY/Private/UI/PHYGameUILayout.cpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYGameUILayout.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameUILayout)
|
||||||
|
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Overlay.h"
|
||||||
|
#include "Components/OverlaySlot.h"
|
||||||
|
#include "GameplayTags/PHYGameplayTags_UI.h"
|
||||||
|
#include "Widgets/CommonActivatableWidgetContainer.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
UPHYGameUILayout::UPHYGameUILayout(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYGameUILayout::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeLayoutTree();
|
||||||
|
RegisterNativeLayers();
|
||||||
|
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYGameUILayout::BuildNativeLayoutTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && GameLayerStack && HUDLayerStack && MenuLayerStack && ModalLayerStack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UOverlay* RootOverlay = WidgetTree->ConstructWidget<UOverlay>(UOverlay::StaticClass(), TEXT("RootOverlay"));
|
||||||
|
WidgetTree->RootWidget = RootOverlay;
|
||||||
|
|
||||||
|
GameLayerStack = AddLayerStack(RootOverlay, TEXT("GameLayerStack"));
|
||||||
|
HUDLayerStack = AddLayerStack(RootOverlay, TEXT("HUDLayerStack"));
|
||||||
|
MenuLayerStack = AddLayerStack(RootOverlay, TEXT("MenuLayerStack"));
|
||||||
|
ModalLayerStack = AddLayerStack(RootOverlay, TEXT("ModalLayerStack"));
|
||||||
|
|
||||||
|
bNativeLayersRegistered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidgetStack* UPHYGameUILayout::AddLayerStack(UOverlay* RootOverlay, const FName StackName) const
|
||||||
|
{
|
||||||
|
if (!RootOverlay || !WidgetTree)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidgetStack* LayerStack = WidgetTree->ConstructWidget<UCommonActivatableWidgetStack>(UCommonActivatableWidgetStack::StaticClass(), StackName);
|
||||||
|
if (UOverlaySlot* LayerSlot = RootOverlay->AddChildToOverlay(LayerStack))
|
||||||
|
{
|
||||||
|
LayerSlot->SetHorizontalAlignment(HAlign_Fill);
|
||||||
|
LayerSlot->SetVerticalAlignment(VAlign_Fill);
|
||||||
|
LayerSlot->SetPadding(FMargin(0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return LayerStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYGameUILayout::RegisterNativeLayers()
|
||||||
|
{
|
||||||
|
if (bNativeLayersRegistered)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GameLayerStack)
|
||||||
|
{
|
||||||
|
RegisterLayer(PHYGameplayTags::UI_Layer_Game, GameLayerStack);
|
||||||
|
}
|
||||||
|
if (HUDLayerStack)
|
||||||
|
{
|
||||||
|
RegisterLayer(PHYGameplayTags::UI_Layer_HUD, HUDLayerStack);
|
||||||
|
}
|
||||||
|
if (MenuLayerStack)
|
||||||
|
{
|
||||||
|
RegisterLayer(PHYGameplayTags::UI_Layer_Menu, MenuLayerStack);
|
||||||
|
}
|
||||||
|
if (ModalLayerStack)
|
||||||
|
{
|
||||||
|
RegisterLayer(PHYGameplayTags::UI_Layer_Modal, ModalLayerStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
bNativeLayersRegistered = true;
|
||||||
|
}
|
||||||
37
Source/PHY/Private/UI/PHYGameUIPolicy.cpp
Normal file
37
Source/PHY/Private/UI/PHYGameUIPolicy.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYGameUIPolicy.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameUIPolicy)
|
||||||
|
|
||||||
|
#include "PHYConfigSettings.h"
|
||||||
|
#include "UI/PHYGameUILayout.h"
|
||||||
|
#include "UObject/UnrealType.h"
|
||||||
|
|
||||||
|
UPHYGameUIPolicy::UPHYGameUIPolicy(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
ApplyConfiguredLayoutClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYGameUIPolicy::ApplyConfiguredLayoutClass()
|
||||||
|
{
|
||||||
|
TSubclassOf<UGUIS_GameUILayout> SelectedLayoutClass = UPHYGameUILayout::StaticClass();
|
||||||
|
|
||||||
|
if (const UPHYUISettings* UISettings = GetDefault<UPHYUISettings>())
|
||||||
|
{
|
||||||
|
if (!UISettings->RootLayoutClass.IsNull())
|
||||||
|
{
|
||||||
|
if (UClass* LoadedLayoutClass = UISettings->RootLayoutClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
SelectedLayoutClass = LoadedLayoutClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenericUISystem 将 LayoutClass 设为私有 EditAnywhere 属性;这里通过反射写入 C++ 默认布局,避免创建仅用于赋默认值的蓝图资产。
|
||||||
|
if (const FSoftClassProperty* LayoutClassProperty = FindFProperty<FSoftClassProperty>(UGUIS_GameUIPolicy::StaticClass(), TEXT("LayoutClass")))
|
||||||
|
{
|
||||||
|
LayoutClassProperty->SetPropertyValue_InContainer(this, FSoftObjectPtr(SelectedLayoutClass.Get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
144
Source/PHY/Private/UI/PHYHUDStatusWidget.cpp
Normal file
144
Source/PHY/Private/UI/PHYHUDStatusWidget.cpp
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYHUDStatusWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYHUDStatusWidget)
|
||||||
|
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Overlay.h"
|
||||||
|
#include "Components/OverlaySlot.h"
|
||||||
|
#include "Components/ProgressBar.h"
|
||||||
|
#include "Components/SafeZone.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Components/VerticalBox.h"
|
||||||
|
#include "Components/VerticalBoxSlot.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
FText MakeResourceText(const TCHAR* Label, const float Value, const float MaxValue)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("%s %.0f / %.0f"), Label, FMath::Max(0.0f, Value), FMath::Max(0.0f, MaxValue)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float MakeResourcePercent(const float Value, const float MaxValue)
|
||||||
|
{
|
||||||
|
return MaxValue > UE_SMALL_NUMBER ? FMath::Clamp(Value / MaxValue, 0.0f, 1.0f) : 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYHUDStatusWidget::UPHYHUDStatusWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
InputConfig = EGUIS_ActivatableWidgetInputMode::Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYHUDStatusWidget::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::SynchronizeProperties()
|
||||||
|
{
|
||||||
|
Super::SynchronizeProperties();
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::SetHealth(const float NewHealth, const float NewMaxHealth)
|
||||||
|
{
|
||||||
|
Health = NewHealth;
|
||||||
|
MaxHealth = NewMaxHealth;
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::SetStamina(const float NewStamina, const float NewMaxStamina)
|
||||||
|
{
|
||||||
|
Stamina = NewStamina;
|
||||||
|
MaxStamina = NewMaxStamina;
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::BuildNativeWidgetTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && TitleText && HealthText && HealthBar && StaminaText && StaminaBar)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
USafeZone* RootSafeZone = WidgetTree->ConstructWidget<USafeZone>(USafeZone::StaticClass(), TEXT("RootSafeZone"));
|
||||||
|
UOverlay* RootOverlay = WidgetTree->ConstructWidget<UOverlay>(UOverlay::StaticClass(), TEXT("RootOverlay"));
|
||||||
|
UVerticalBox* StatusBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("StatusBox"));
|
||||||
|
|
||||||
|
WidgetTree->RootWidget = RootSafeZone;
|
||||||
|
RootSafeZone->SetContent(RootOverlay);
|
||||||
|
|
||||||
|
if (UOverlaySlot* StatusSlot = RootOverlay->AddChildToOverlay(StatusBox))
|
||||||
|
{
|
||||||
|
StatusSlot->SetHorizontalAlignment(HAlign_Left);
|
||||||
|
StatusSlot->SetVerticalAlignment(VAlign_Top);
|
||||||
|
StatusSlot->SetPadding(FMargin(32.0f, 24.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("TitleText"));
|
||||||
|
TitleText->SetText(FText::FromString(TEXT("PHY")));
|
||||||
|
TitleText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 22));
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
HealthText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("HealthText"));
|
||||||
|
HealthText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
HealthText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||||
|
StatusBox->AddChildToVerticalBox(HealthText);
|
||||||
|
|
||||||
|
HealthBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("HealthBar"));
|
||||||
|
HealthBar->SetFillColorAndOpacity(FLinearColor(0.76f, 0.12f, 0.12f, 1.0f));
|
||||||
|
if (UVerticalBoxSlot* HealthBarSlot = StatusBox->AddChildToVerticalBox(HealthBar))
|
||||||
|
{
|
||||||
|
HealthBarSlot->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)));
|
||||||
|
StatusBox->AddChildToVerticalBox(StaminaText);
|
||||||
|
|
||||||
|
StaminaBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("StaminaBar"));
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::UpdateResourceWidgets()
|
||||||
|
{
|
||||||
|
if (HealthText)
|
||||||
|
{
|
||||||
|
HealthText->SetText(MakeResourceText(TEXT("HP"), Health, MaxHealth));
|
||||||
|
}
|
||||||
|
if (HealthBar)
|
||||||
|
{
|
||||||
|
HealthBar->SetPercent(MakeResourcePercent(Health, MaxHealth));
|
||||||
|
}
|
||||||
|
if (StaminaText)
|
||||||
|
{
|
||||||
|
StaminaText->SetText(MakeResourceText(TEXT("ST"), Stamina, MaxStamina));
|
||||||
|
}
|
||||||
|
if (StaminaBar)
|
||||||
|
{
|
||||||
|
StaminaBar->SetPercent(MakeResourcePercent(Stamina, MaxStamina));
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Source/PHY/Private/UI/PHYPlayerUIContext.cpp
Normal file
18
Source/PHY/Private/UI/PHYPlayerUIContext.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYPlayerUIContext.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerUIContext)
|
||||||
|
|
||||||
|
#include "GameFramework/PlayerController.h"
|
||||||
|
|
||||||
|
void UPHYPlayerUIContext::InitializeForPlayer(APlayerController* InPlayerController)
|
||||||
|
{
|
||||||
|
PlayerController = InPlayerController;
|
||||||
|
RefreshPawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYPlayerUIContext::RefreshPawn()
|
||||||
|
{
|
||||||
|
Pawn = PlayerController.IsValid() ? PlayerController->GetPawn() : nullptr;
|
||||||
|
}
|
||||||
@@ -4,8 +4,12 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "GameFramework/HUD.h"
|
#include "GameFramework/HUD.h"
|
||||||
|
#include "UI/GUIS_GameUIStructLibrary.h"
|
||||||
#include "PHYHUD.generated.h"
|
#include "PHYHUD.generated.h"
|
||||||
|
|
||||||
|
class UCommonActivatableWidget;
|
||||||
|
class UPHYPlayerUIContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief PHY HUD 根入口。
|
* @brief PHY HUD 根入口。
|
||||||
*
|
*
|
||||||
@@ -33,6 +37,23 @@ public:
|
|||||||
bool IsHUDInitialized() const { return bHUDInitialized; }
|
bool IsHUDInitialized() const { return bHUDInitialized; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/** @brief 注册 GenericUISystem 本地玩家根布局和项目 UI 上下文。 */
|
||||||
|
virtual bool InitializeGenericUIForPlayer(APlayerController* OwningPlayerController);
|
||||||
|
|
||||||
|
/** @brief 推入默认 HUD 控件。 */
|
||||||
|
virtual UCommonActivatableWidget* PushDefaultHUDWidget(APlayerController* OwningPlayerController);
|
||||||
|
|
||||||
|
/** @brief 当前本地玩家 UI 上下文。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UPHYPlayerUIContext> PlayerUIContext;
|
||||||
|
|
||||||
|
/** @brief 当前本地玩家 UI 上下文注册句柄。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
FGUIS_UIContextBindingHandle PlayerUIContextBindingHandle;
|
||||||
|
|
||||||
|
/** @brief 默认 HUD 控件实例。 */
|
||||||
|
UPROPERTY(Transient, BlueprintReadOnly, Category="PHY|UI")
|
||||||
|
TObjectPtr<UCommonActivatableWidget> DefaultHUDWidget;
|
||||||
/** @brief HUD 是否已经完成基础初始化。 */
|
/** @brief HUD 是否已经完成基础初始化。 */
|
||||||
UPROPERTY(Transient, BlueprintReadOnly, Category="PHY|UI")
|
UPROPERTY(Transient, BlueprintReadOnly, Category="PHY|UI")
|
||||||
bool bHUDInitialized = false;
|
bool bHUDInitialized = false;
|
||||||
|
|||||||
23
Source/PHY/Public/GameplayTags/PHYGameplayTags_UI.h
Normal file
23
Source/PHY/Public/GameplayTags/PHYGameplayTags_UI.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "NativeGameplayTags.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHY UI 层级 Gameplay Tag 定义。
|
||||||
|
*/
|
||||||
|
namespace PHYGameplayTags
|
||||||
|
{
|
||||||
|
/** @brief 非阻塞 gameplay 承载层,用于后续准星、交互提示等常驻玩法 UI。 */
|
||||||
|
UE_DECLARE_GAMEPLAY_TAG_EXTERN(UI_Layer_Game);
|
||||||
|
|
||||||
|
/** @brief HUD 承载层,用于血条、资源、快捷栏等常驻玩家界面。 */
|
||||||
|
UE_DECLARE_GAMEPLAY_TAG_EXTERN(UI_Layer_HUD);
|
||||||
|
|
||||||
|
/** @brief 菜单承载层,用于背包、角色、设置等可交互界面。 */
|
||||||
|
UE_DECLARE_GAMEPLAY_TAG_EXTERN(UI_Layer_Menu);
|
||||||
|
|
||||||
|
/** @brief 模态承载层,用于确认框、错误提示等最高优先级界面。 */
|
||||||
|
UE_DECLARE_GAMEPLAY_TAG_EXTERN(UI_Layer_Modal);
|
||||||
|
}
|
||||||
@@ -3,11 +3,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameplayTagContainer.h"
|
||||||
#include "UObject/Object.h"
|
#include "UObject/Object.h"
|
||||||
#include "UObject/SoftObjectPtr.h"
|
#include "UObject/SoftObjectPtr.h"
|
||||||
#include "PHYConfigSettings.generated.h"
|
#include "PHYConfigSettings.generated.h"
|
||||||
|
|
||||||
|
class UCommonActivatableWidget;
|
||||||
class UGIPS_InputProcessor;
|
class UGIPS_InputProcessor;
|
||||||
|
class UGUIS_GameUILayout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief PHY 项目核心配置。
|
* @brief PHY 项目核心配置。
|
||||||
@@ -148,10 +151,38 @@ public:
|
|||||||
UPROPERTY(Config)
|
UPROPERTY(Config)
|
||||||
bool bUseCommonUI = true;
|
bool bUseCommonUI = true;
|
||||||
|
|
||||||
|
/** @brief 是否启用 GenericUISystem 作为项目 UI 根层级和推送系统。 */
|
||||||
|
UPROPERTY(Config)
|
||||||
|
bool bUseGenericUISystem = true;
|
||||||
|
|
||||||
/** @brief 打开菜单时是否暂停 Gameplay。 */
|
/** @brief 打开菜单时是否暂停 Gameplay。 */
|
||||||
UPROPERTY(Config)
|
UPROPERTY(Config)
|
||||||
bool bOpenMenusWithGameplayPause = false;
|
bool bOpenMenusWithGameplayPause = false;
|
||||||
|
|
||||||
|
/** @brief 原生根布局类,默认使用项目 C++ 布局并在其中注册 Generic UI 层。 */
|
||||||
|
UPROPERTY(Config)
|
||||||
|
TSoftClassPtr<UGUIS_GameUILayout> RootLayoutClass;
|
||||||
|
|
||||||
|
/** @brief 默认 HUD 控件类,本地玩家 HUD 初始化时推入 HUD 层。 */
|
||||||
|
UPROPERTY(Config)
|
||||||
|
TSoftClassPtr<UCommonActivatableWidget> DefaultHUDWidgetClass;
|
||||||
|
|
||||||
|
/** @brief 默认 gameplay 层 Tag。 */
|
||||||
|
UPROPERTY(Config)
|
||||||
|
FGameplayTag GameplayLayerTag;
|
||||||
|
|
||||||
|
/** @brief 默认 HUD 层 Tag。 */
|
||||||
|
UPROPERTY(Config)
|
||||||
|
FGameplayTag HUDLayerTag;
|
||||||
|
|
||||||
|
/** @brief 默认菜单层 Tag。 */
|
||||||
|
UPROPERTY(Config)
|
||||||
|
FGameplayTag MenuLayerTag;
|
||||||
|
|
||||||
|
/** @brief 默认模态层 Tag。 */
|
||||||
|
UPROPERTY(Config)
|
||||||
|
FGameplayTag ModalLayerTag;
|
||||||
|
|
||||||
/** @brief 默认 HUD 层名称。 */
|
/** @brief 默认 HUD 层名称。 */
|
||||||
UPROPERTY(Config)
|
UPROPERTY(Config)
|
||||||
FName DefaultHUDLayerName = TEXT("HUD");
|
FName DefaultHUDLayerName = TEXT("HUD");
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "GameplayTags/PHYGameplayTags_Event.h"
|
#include "GameplayTags/PHYGameplayTags_Event.h"
|
||||||
#include "GameplayTags/PHYGameplayTags_Input.h"
|
#include "GameplayTags/PHYGameplayTags_Input.h"
|
||||||
#include "GameplayTags/PHYGameplayTags_State.h"
|
#include "GameplayTags/PHYGameplayTags_State.h"
|
||||||
|
#include "GameplayTags/PHYGameplayTags_UI.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief PHY 项目的原生 Gameplay Tag 聚合入口。
|
* @brief PHY 项目的原生 Gameplay Tag 聚合入口。
|
||||||
|
|||||||
58
Source/PHY/Public/UI/PHYGameUILayout.h
Normal file
58
Source/PHY/Public/UI/PHYGameUILayout.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UI/GUIS_GameUILayout.h"
|
||||||
|
#include "PHYGameUILayout.generated.h"
|
||||||
|
|
||||||
|
class UCommonActivatableWidgetStack;
|
||||||
|
class UOverlay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHY 原生游戏 UI 根布局。
|
||||||
|
*
|
||||||
|
* 以 GenericUISystem 的 UGUIS_GameUILayout 为基类,在 C++ 中创建并注册 Game、HUD、Menu、Modal 四个层级。
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class PHY_API UPHYGameUILayout : public UGUIS_GameUILayout
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 构造根布局。 */
|
||||||
|
UPHYGameUILayout(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||||
|
|
||||||
|
/** @brief 构建原生 WidgetTree。 */
|
||||||
|
virtual TSharedRef<SWidget> RebuildWidget() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** @brief 构造根 Overlay 和各层 Stack。 */
|
||||||
|
void BuildNativeLayoutTree();
|
||||||
|
|
||||||
|
/** @brief 向根 Overlay 添加一个全屏层 Stack。 */
|
||||||
|
UCommonActivatableWidgetStack* AddLayerStack(UOverlay* RootOverlay, FName StackName) const;
|
||||||
|
|
||||||
|
/** @brief 注册 Native GameplayTag 到 GenericUISystem 层级表。 */
|
||||||
|
void RegisterNativeLayers();
|
||||||
|
|
||||||
|
/** @brief Gameplay 常驻层。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UCommonActivatableWidgetStack> GameLayerStack;
|
||||||
|
|
||||||
|
/** @brief HUD 常驻层。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UCommonActivatableWidgetStack> HUDLayerStack;
|
||||||
|
|
||||||
|
/** @brief 菜单交互层。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UCommonActivatableWidgetStack> MenuLayerStack;
|
||||||
|
|
||||||
|
/** @brief 模态最高优先级层。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UCommonActivatableWidgetStack> ModalLayerStack;
|
||||||
|
|
||||||
|
/** @brief 是否已将当前层 Stack 注册到 GenericUISystem。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
bool bNativeLayersRegistered = false;
|
||||||
|
};
|
||||||
26
Source/PHY/Public/UI/PHYGameUIPolicy.h
Normal file
26
Source/PHY/Public/UI/PHYGameUIPolicy.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UI/GUIS_GameUIPolicy.h"
|
||||||
|
#include "PHYGameUIPolicy.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHY GenericUISystem 策略。
|
||||||
|
*
|
||||||
|
* 负责把 GenericUISystem 的根布局指向项目 C++ 布局类,避免在 GameMode 或 HUD 中直接装配 UMG 层级。
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class PHY_API UPHYGameUIPolicy : public UGUIS_GameUIPolicy
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 构造策略并写入项目根布局类。 */
|
||||||
|
UPHYGameUIPolicy(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** @brief 将项目配置中的 RootLayoutClass 写入 GenericUISystem 策略基类。 */
|
||||||
|
void ApplyConfiguredLayoutClass();
|
||||||
|
};
|
||||||
82
Source/PHY/Public/UI/PHYHUDStatusWidget.h
Normal file
82
Source/PHY/Public/UI/PHYHUDStatusWidget.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UI/GUIS_ActivatableWidget.h"
|
||||||
|
#include "PHYHUDStatusWidget.generated.h"
|
||||||
|
|
||||||
|
class UProgressBar;
|
||||||
|
class UTextBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHY 默认 HUD 状态控件。
|
||||||
|
*
|
||||||
|
* 作为基础 UI 的 C++ 可见占位,后续战斗、背包、队伍等系统可以替换或扩展为项目资产。
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class PHY_API UPHYHUDStatusWidget : public UGUIS_ActivatableWidget
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 构造 HUD 状态控件。 */
|
||||||
|
UPHYHUDStatusWidget(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||||
|
|
||||||
|
/** @brief 构建原生 HUD WidgetTree。 */
|
||||||
|
virtual TSharedRef<SWidget> RebuildWidget() override;
|
||||||
|
|
||||||
|
/** @brief 同步文本和进度条显示。 */
|
||||||
|
virtual void SynchronizeProperties() override;
|
||||||
|
|
||||||
|
/** @brief 设置生命值显示。 */
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||||
|
void SetHealth(float NewHealth, float NewMaxHealth);
|
||||||
|
|
||||||
|
/** @brief 设置耐力值显示。 */
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||||
|
void SetStamina(float NewStamina, float NewMaxStamina);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** @brief 构造原生 HUD 树。 */
|
||||||
|
void BuildNativeWidgetTree();
|
||||||
|
|
||||||
|
/** @brief 刷新单条资源文本和进度条。 */
|
||||||
|
void UpdateResourceWidgets();
|
||||||
|
|
||||||
|
/** @brief 当前生命值。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||||
|
float Health = 100.0f;
|
||||||
|
|
||||||
|
/** @brief 当前最大生命值。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||||
|
float MaxHealth = 100.0f;
|
||||||
|
|
||||||
|
/** @brief 当前耐力值。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||||
|
float Stamina = 100.0f;
|
||||||
|
|
||||||
|
/** @brief 当前最大耐力值。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="PHY|UI")
|
||||||
|
float MaxStamina = 100.0f;
|
||||||
|
|
||||||
|
/** @brief 标题文本。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UTextBlock> TitleText;
|
||||||
|
|
||||||
|
/** @brief 生命文本。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UTextBlock> HealthText;
|
||||||
|
|
||||||
|
/** @brief 生命进度条。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UProgressBar> HealthBar;
|
||||||
|
|
||||||
|
/** @brief 耐力文本。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UTextBlock> StaminaText;
|
||||||
|
|
||||||
|
/** @brief 耐力进度条。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UProgressBar> StaminaBar;
|
||||||
|
};
|
||||||
47
Source/PHY/Public/UI/PHYPlayerUIContext.h
Normal file
47
Source/PHY/Public/UI/PHYPlayerUIContext.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UI/GUIS_GameUIContext.h"
|
||||||
|
#include "PHYPlayerUIContext.generated.h"
|
||||||
|
|
||||||
|
class APlayerController;
|
||||||
|
class APawn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 本地玩家 UI 上下文。
|
||||||
|
*
|
||||||
|
* 用于把 PlayerController、Pawn 等本地只读引用提供给 HUD、菜单和后续扩展界面。
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType)
|
||||||
|
class PHY_API UPHYPlayerUIContext : public UGUIS_GameUIContext
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 设置本地玩家控制器并刷新 Pawn 引用。 */
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||||
|
void InitializeForPlayer(APlayerController* InPlayerController);
|
||||||
|
|
||||||
|
/** @brief 刷新当前 Pawn 引用。 */
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PHY|UI")
|
||||||
|
void RefreshPawn();
|
||||||
|
|
||||||
|
/** @brief 获取本地玩家控制器。 */
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|UI")
|
||||||
|
APlayerController* GetPlayerController() const { return PlayerController.Get(); }
|
||||||
|
|
||||||
|
/** @brief 获取当前 Pawn。 */
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|UI")
|
||||||
|
APawn* GetPawn() const { return Pawn.Get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** @brief 本地玩家控制器弱引用。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TWeakObjectPtr<APlayerController> PlayerController;
|
||||||
|
|
||||||
|
/** @brief 当前 Pawn 弱引用。 */
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TWeakObjectPtr<APawn> Pawn;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user