feat: add Battle HUD(health) display
This commit is contained in:
Binary file not shown.
5
Config/DefaultEditor.ini
Normal file
5
Config/DefaultEditor.ini
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
BIN
Content/Surviver/UI/WBP_BattleHUD.uasset
LFS
Normal file
BIN
Content/Surviver/UI/WBP_BattleHUD.uasset
LFS
Normal file
Binary file not shown.
@@ -18,12 +18,15 @@ void UHealthComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief UHealthComponent的构造函数。
|
* @brief UHealthComponent的构造函数。
|
||||||
|
* @note 必须在这里初始化最大生命值,不然就有问题
|
||||||
*/
|
*/
|
||||||
UHealthComponent::UHealthComponent() {
|
UHealthComponent::UHealthComponent() {
|
||||||
// 关闭每帧Tick以提高性能
|
// 关闭每帧Tick以提高性能
|
||||||
PrimaryComponentTick.bCanEverTick = false;
|
PrimaryComponentTick.bCanEverTick = false;
|
||||||
// 默认启用网络复制
|
// 默认启用网络复制
|
||||||
SetIsReplicatedByDefault(true);
|
SetIsReplicatedByDefault(true);
|
||||||
|
// 初始化当前生命值为最大生命值
|
||||||
|
CurrentHealth = MaxHealth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,8 +34,7 @@ UHealthComponent::UHealthComponent() {
|
|||||||
*/
|
*/
|
||||||
void UHealthComponent::BeginPlay() {
|
void UHealthComponent::BeginPlay() {
|
||||||
Super::BeginPlay();
|
Super::BeginPlay();
|
||||||
// 初始化当前生命值为最大生命值
|
// OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
|
||||||
CurrentHealth = MaxHealth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,6 +42,7 @@ void UHealthComponent::BeginPlay() {
|
|||||||
*/
|
*/
|
||||||
void UHealthComponent::OnRep_CurrentHealth() {
|
void UHealthComponent::OnRep_CurrentHealth() {
|
||||||
// 在客户端上,当CurrentHealth被复制时调用
|
// 在客户端上,当CurrentHealth被复制时调用
|
||||||
|
OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -60,6 +63,10 @@ void UHealthComponent::HandleDamage(float DamageAmount) {
|
|||||||
// 仅在服务器上处理伤害逻辑
|
// 仅在服务器上处理伤害逻辑
|
||||||
if (GetOwner()->HasAuthority()) {
|
if (GetOwner()->HasAuthority()) {
|
||||||
CurrentHealth = FMath::Clamp(CurrentHealth - DamageAmount, 0.0f, MaxHealth);
|
CurrentHealth = FMath::Clamp(CurrentHealth - DamageAmount, 0.0f, MaxHealth);
|
||||||
|
|
||||||
|
// 在服务器上直接广播事件
|
||||||
|
OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
|
||||||
|
|
||||||
if (CurrentHealth <= 0.0f) {
|
if (CurrentHealth <= 0.0f) {
|
||||||
OnDeath.Broadcast();
|
OnDeath.Broadcast();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,13 @@
|
|||||||
*/
|
*/
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeath);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 当生命值变化时调用的多播委托。
|
||||||
|
* @param CurrentHealth 当前生命值
|
||||||
|
* @param MaxHealth 最大生命值
|
||||||
|
*/
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnHealthChanged, float, CurrentHealth, float, MaxHealth);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class UHealthComponent
|
* @class UHealthComponent
|
||||||
* @brief 生命组件
|
* @brief 生命组件
|
||||||
@@ -31,6 +38,12 @@ public:
|
|||||||
UPROPERTY(BlueprintAssignable, Category = "Health")
|
UPROPERTY(BlueprintAssignable, Category = "Health")
|
||||||
FOnDeath OnDeath;
|
FOnDeath OnDeath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 生命值变化时广播的多播委托。可以在蓝图中绑定事件。
|
||||||
|
*/
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Health")
|
||||||
|
FOnHealthChanged OnHealthChanged;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief 角色的最大生命值。
|
* @brief 角色的最大生命值。
|
||||||
@@ -67,4 +80,10 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category="Health")
|
UFUNCTION(BlueprintCallable, Category="Health")
|
||||||
void HandleDamage(float DamageAmount);
|
void HandleDamage(float DamageAmount);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure, Category="Health")
|
||||||
|
float GetCurrentHealth() const { return CurrentHealth; }
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure, Category="Health")
|
||||||
|
float GetMaxHealth() const { return MaxHealth; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class UHealthComponent;
|
|||||||
/**
|
/**
|
||||||
* @class ASurviverPlayer
|
* @class ASurviverPlayer
|
||||||
* @brief 玩家角色基类,具有网络同步的移动组件,实现了可受伤害接口。
|
* @brief 玩家角色基类,具有网络同步的移动组件,实现了可受伤害接口。
|
||||||
* @ingroup Battle
|
* @ingroup GameCore
|
||||||
*/
|
*/
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class FIRSTPERSONDEMO_API ASurviverPlayer : public ACharacter, public IDamageableInterface {
|
class FIRSTPERSONDEMO_API ASurviverPlayer : public ACharacter, public IDamageableInterface {
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include "SurviverPlayerController.h"
|
#include "SurviverPlayerController.h"
|
||||||
|
|
||||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
#include "EnhancedInputSubsystems.h"
|
#include "EnhancedInputSubsystems.h"
|
||||||
#include "Engine/LocalPlayer.h"
|
#include "Engine/LocalPlayer.h"
|
||||||
#include "InputMappingContext.h"
|
#include "InputMappingContext.h"
|
||||||
@@ -12,6 +10,7 @@
|
|||||||
#include "Blueprint/UserWidget.h"
|
#include "Blueprint/UserWidget.h"
|
||||||
#include "FirstPersonDemo.h"
|
#include "FirstPersonDemo.h"
|
||||||
#include "Widgets/Input/SVirtualJoystick.h"
|
#include "Widgets/Input/SVirtualJoystick.h"
|
||||||
|
#include "FirstPersonDemo/Surviver_FPS/UI/BattleHUD.h"
|
||||||
|
|
||||||
ASurviverPlayerController::ASurviverPlayerController() {
|
ASurviverPlayerController::ASurviverPlayerController() {
|
||||||
// set the player camera manager class
|
// set the player camera manager class
|
||||||
@@ -34,6 +33,17 @@ void ASurviverPlayerController::BeginPlay() {
|
|||||||
UE_LOG(LogFirstPersonDemo, Error, TEXT("Could not spawn mobile controls widget."));
|
UE_LOG(LogFirstPersonDemo, Error, TEXT("Could not spawn mobile controls widget."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 只在本地玩家控制器上创建 HUD
|
||||||
|
if (IsLocalPlayerController() && BattleHUDClass) {
|
||||||
|
BattleHUD = CreateWidget<UBattleHUD>(this, BattleHUDClass);
|
||||||
|
if (BattleHUD) {
|
||||||
|
BattleHUD->AddToViewport();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UE_LOG(LogFirstPersonDemo, Error, TEXT("Could not create BattleHUD widget."));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASurviverPlayerController::SetupInputComponent() {
|
void ASurviverPlayerController::SetupInputComponent() {
|
||||||
|
|||||||
@@ -7,8 +7,11 @@
|
|||||||
#include "SurviverPlayerController.generated.h"
|
#include "SurviverPlayerController.generated.h"
|
||||||
|
|
||||||
class UInputMappingContext;
|
class UInputMappingContext;
|
||||||
|
class UBattleHUD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @brief 玩家控制器类,包括玩家移动、玩家战斗中 HOD 的控制
|
||||||
|
* @ingroup GameCore
|
||||||
*/
|
*/
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class FIRSTPERSONDEMO_API ASurviverPlayerController : public APlayerController {
|
class FIRSTPERSONDEMO_API ASurviverPlayerController : public APlayerController {
|
||||||
@@ -18,6 +21,13 @@ public:
|
|||||||
/** Constructor */
|
/** Constructor */
|
||||||
ASurviverPlayerController();
|
ASurviverPlayerController();
|
||||||
|
|
||||||
|
/** 玩家的 Battle HUD 类 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, Category = "UI")
|
||||||
|
TSubclassOf<class UBattleHUD> BattleHUDClass;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
UBattleHUD* BattleHUD;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/** Input Mapping Contexts */
|
/** Input Mapping Contexts */
|
||||||
@@ -40,7 +50,10 @@ protected:
|
|||||||
UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls")
|
UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls")
|
||||||
bool bForceTouchControls = false;
|
bool bForceTouchControls = false;
|
||||||
|
|
||||||
/** Gameplay initialization */
|
/**
|
||||||
|
* @brief 初始化 Gameplay
|
||||||
|
* 初始化移动控制、初始化Battle HUD(仅限本地玩家控制器)
|
||||||
|
*/
|
||||||
virtual void BeginPlay() override;
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
/** Input mapping context setup */
|
/** Input mapping context setup */
|
||||||
|
|||||||
34
Source/FirstPersonDemo/Surviver_FPS/UI/BattleHUD.cpp
Normal file
34
Source/FirstPersonDemo/Surviver_FPS/UI/BattleHUD.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright Majowaveon Games. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "BattleHUD.h"
|
||||||
|
#include "Components/ProgressBar.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Kismet/GameplayStatics.h"
|
||||||
|
#include "FirstPersonDemo/Surviver_FPS/Battle/HealthComponent.h"
|
||||||
|
|
||||||
|
void UBattleHUD::UpdateHealth(float CurrentHealth, float MaxHealth) {
|
||||||
|
if (HealthBar) {
|
||||||
|
HealthBar->SetPercent(CurrentHealth / MaxHealth);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HealthText) {
|
||||||
|
HealthText->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentHealth, MaxHealth)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBattleHUD::NativeConstruct() {
|
||||||
|
Super::NativeConstruct();
|
||||||
|
|
||||||
|
// 获取玩家Pawn
|
||||||
|
APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(this, 0);
|
||||||
|
if (PlayerPawn) {
|
||||||
|
// 获取生命值组件
|
||||||
|
UHealthComponent* HealthComponent = PlayerPawn->FindComponentByClass<UHealthComponent>();
|
||||||
|
if (HealthComponent) {
|
||||||
|
// 绑定生命值变化事件
|
||||||
|
HealthComponent->OnHealthChanged.AddDynamic(this, &UBattleHUD::UpdateHealth);
|
||||||
|
// 初始化血量显示
|
||||||
|
UpdateHealth(HealthComponent->GetCurrentHealth(), HealthComponent->GetMaxHealth());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Source/FirstPersonDemo/Surviver_FPS/UI/BattleHUD.h
Normal file
36
Source/FirstPersonDemo/Surviver_FPS/UI/BattleHUD.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright Majowaveon Games. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Blueprint/UserWidget.h"
|
||||||
|
#include "BattleHUD.generated.h"
|
||||||
|
|
||||||
|
class UProgressBar;
|
||||||
|
class UTextBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 战斗界面的 HUD,显示当前血量
|
||||||
|
* @ingroup UI
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class FIRSTPERSONDEMO_API UBattleHUD : public UUserWidget {
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "HUD")
|
||||||
|
void UpdateHealth(float CurrentHealth, float MaxHealth);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** @brief 绑定生命显示逻辑
|
||||||
|
* 获取 @ref UHealthComponent,绑定生命值变化 @ref OnHealthChanged 事件到 @ref UpdateHealth 函数
|
||||||
|
*/
|
||||||
|
virtual void NativeConstruct() override;
|
||||||
|
|
||||||
|
// 通过 meta = (BindWidget) 将此变量绑定到蓝图中的同名控件
|
||||||
|
UPROPERTY(meta = (BindWidget))
|
||||||
|
UProgressBar* HealthBar;
|
||||||
|
|
||||||
|
UPROPERTY(meta = (BindWidget))
|
||||||
|
UTextBlock* HealthText;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user