显示NPC生命条
2023年11月25日 2024年1月11日
Widget Component / Health Bar
素材
用作生命条的前景图片和背景图片, UE可以修改颜色
导入素材
创建文件夹 Content/ExternalContent/HealthBar
, 将图片导入
说明
- 生命条使用窗口部件实现, 有窗口部件组件, 可以拿来给AICharacter用
- 血量较多时不显示生命条, NPC差不多挂了不再显示, NPC距离较远时也不显示
和NPC存在阻挡时显示 - 血量较低时, 剩余血量显示为红色
- 修改生命条背景和前景图片: 黑色用作背景, 白色用作前景
创建C++类
- | |
---|---|
基类 | UserWidget |
路径 | UI |
名称 | STUHealthBarWidget |
属性 | Public |
创建蓝图窗口
- | |
---|---|
基类 | STUHealthBarWidget |
路径 | UI |
名称 | WBP_HealthBar |
设计WBP_HealthBar
-
添加
Canvas Panel
-
添加
Progress Bar
- Percent 进度 Fill Color and Opacity 填充色 设置大小, 查看默认状态
修改背景图片和背景色; 修改前景图片; 修改填充色; 修改变量名
- Background Image > Image 背景图片 Content/ExternalContent/HealthBar/white Background Image > Tint 背景色 000000FF Fill Image > Image 前景图片 Content/ExternalContent/HealthBar/white Fill Image > Tint 前景色; 填充时, 和填充色叠加显示 Fill Color and Opacity 填充色 FFFFFFFF 变量名 HealthProgressBar
演示NPC顶部添加生命条
设置AICharacter
BP_STUAICharacter
-
添加窗口部件组件
-
设置窗口部件组件
用来渲染窗口部件
- Widget Class WBP_HealthBar Transform > Location > Z 120 Draw as Desired Size 勾选; 按设计大小显示 Space 默认值为World, 即在世界座标系中渲染窗口部件
设置行为树
BT_STUCharacter
屏蔽NPC移动, 便于测试
查看
-
在世界座标系中渲染, 正面查看
-
在世界座标系中渲染, 侧面查看
设置生命条在STUGameHUD中渲染
BP_STUAICharacter
-
生命条无侧面表示, 远处看较大
-
近处看生命条大小合适, 侧面显示ok
-
即使玩家操控的游戏角色和NPC在视线上有阻挡, 仍可见生命条
本节不会解决该问题
也可以通过按摄像机角度旋转生命条来解决
移除窗口部件组件
BP_STUAICharacter
API导航
ProgressBar
- 设置进度百分比
1/** Sets the current value of the ProgressBar. */ 2UFUNCTION(BlueprintCallable, Category="Progress") 3void SetPercent(float InPercent);
- 设置填充色
1/** Sets the fill color of the progress bar. */ 2UFUNCTION(BlueprintCallable, Category="Progress") 3void SetFillColorAndOpacity(FLinearColor InColor);
WidgetComponent
-
设置渲染模式
1void SetWidgetSpace( EWidgetSpace NewSpace ) { Space = NewSpace; }
- World 在世界座标系中渲染 Screen 在HUD中渲染 1UENUM(BlueprintType) 2enum class EWidgetSpace : uint8 3{ 4 /** The widget is rendered in the world as mesh, it can be occluded like any other mesh in the world. */ 5 World, 6 /** The widget is rendered in the screen, completely outside of the world, never occluded. */ 7 Screen 8};
-
设置绘制大小
按窗口部件设计中的大小进行绘制1/** */ 2UFUNCTION(BlueprintCallable, Category = UserInterface) 3void SetDrawAtDesiredSize(bool bInDrawAtDesiredSize) { bDrawAtDesiredSize = bInDrawAtDesiredSize; }
-
获取窗口部件
1/** Returns the user widget object displayed by this component */ 2UFUNCTION(BlueprintCallable, Category=UserInterface, meta=(UnsafeDuringActorConstruction=true)) 3UUserWidget* GetUserWidgetObject() const;
PlayerController
如果玩家操控的游戏角色死亡, PlayerController会有观察Pawn, 不然, 当前Pawn
优先返回当前Pawn
1/** Returns the first of GetPawn() or GetSpectatorPawn() that is not nullptr, or nullptr otherwise. */ 2APawn* GetPawnOrSpectator() const; 3 4APawn* APlayerController::GetPawnOrSpectator() const 5{ 6 return GetPawn() ? GetPawn() : GetSpectatorPawn(); 7}
World
获取PlayerController
1/** @return Returns the first player controller, or NULL if there is not one. */ 2APlayerController* GetFirstPlayerController() const;
FVector
计算距离
1/** 2 * Euclidean distance between two points. 3 * 4 * @param V1 The first point. 5 * @param V2 The second point. 6 * @return The distance between two points. 7 */ 8static FORCEINLINE T Dist(const TVector<T> &V1, const TVector<T> &V2); 9static FORCEINLINE T Distance(const TVector<T> &V1, const TVector<T> &V2) { return Dist(V1, V2); }
类的派生关系
1class UMG_API UWidgetComponent : public UMeshComponent 2{ 3 // ... 4}; 5 6class ENGINE_API UMeshComponent : public UPrimitiveComponent 7{ 8 // ... 9}; 10 11class ENGINE_API UPrimitiveComponent : public USceneComponent, public INavRelevantInterface, public IInterface_AsyncCompilation 12{ 13 // ... 14};
实现STUHealthBarWidget
添加属性: 进度条
ShootThemUp: UI/STUHealthBarWidget.h
1class UProgressBar; 2 3 UPROPERTY(meta = (BindWidget)) 4 UProgressBar *HealthProgressBar;
添加属性: 影响生命条可见性和填充色
- | |
---|---|
PercentVisibilityThreshold | 生命值百分比大于该数值时, 不显示生命条 |
PercentColorThreshold | 生命值百分比大于该数值时, 使用默认填充色, 否则, 使用告警填充色 |
GoodColor | 默认填充色 |
BadColor | 生命值较低时, 使用的填充色 |
protected
ShootThemUp: UI/STUHealthBarWidget.h
1UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 2float PercentVisibilityThreshold = 0.8f; 3 4UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 5float PercentColorThreshold = 0.3f; 6 7UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 8FLinearColor GoodColor = FLinearColor::White; 9 10UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 11FLinearColor BadColor = FLinearColor::Red;
添加接口: 设置生命条可见性和生命值百分比
public
ShootThemUp: UI/STUHealthBarWidget.h
1void SetHealthPercent(float HealthPercent);
NPC近乎死亡时, 不再显示生命条
ShootThemUp: UI/STUHealthBarWidget.cpp
1#include "Components/ProgressBar.h" 2 3void USTUHealthBarWidget::SetHealthPercent(float HealthPercent) 4{ 5 if (!HealthProgressBar) return; 6 7 HealthProgressBar->SetPercent(HealthPercent); 8 const auto Visibility = (HealthPercent < PercentVisibilityThreshold) && !FMath::IsNearlyZero(HealthPercent); 9 HealthProgressBar->SetVisibility(Visibility ? ESlateVisibility::Visible : ESlateVisibility::Hidden); 10 HealthProgressBar->SetFillColorAndOpacity(HealthPercent > PercentColorThreshold ? GoodColor : BadColor); 11}
修改STUAICharacter
添加窗口部件组件
protected
ShootThemUp: AI/STUAICharacter.h
1class UWidgetComponent; 2 3UPROPERTY(VisibleAnywhere, BlueprintReadWrite) 4UWidgetComponent *HealthWidgetComponent;
在构造函数中初始化并设置
ShootThemUp: AI/STUAICharacter.cpp
1#include "Components/WidgetComponent.h" 2 3// 构造函数 4 5HealthWidgetComponent = CreateDefaultSubobject<UWidgetComponent>("HealthWidgetComponent"); 6HealthWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen); 7HealthWidgetComponent->SetDrawAtDesiredSize(true); 8HealthWidgetComponent->SetupAttachment(GetRootComponent());
覆写BeginPlay函数: 确定窗口部件组件不为空
protected
ShootThemUp: AI/STUAICharacter.h
1virtual void BeginPlay() override;
ShootThemUp: AI/STUAICharacter.cpp
1void ASTUAICharacter::BeginPlay() 2{ 3 Super::BeginPlay(); 4 5 check(HealthWidgetComponent); 6}
将生命值修改回调函数改为虚函数
STUAICharacter需要对其进行覆写
虚函数, 且访问属性为 protected
ShootThemUp: Player/STUBaseCharacter.h
1// private 2// void OnChangeHealth(float HealthDelta); 3 4// protected 5virtual void OnChangeHealth(float HealthDelta);
STUAICharacter覆写OnChangeHealth
protected
ShootThemUp: AI/STUAICharacter.h
1virtual void OnChangeHealth(float HealthDelta) override;
获取生命条窗口部件组件, 访问其包含的窗口部件, 调用提供的接口
ShootThemUp: AI/STUAICharacter.cpp
1#include "UI/STUHealthBarWidget.h" 2#include "Components/STUHealthComponent.h" 3 4void ASTUAICharacter::OnChangeHealth(float HealthDelta) 5{ 6 Super::OnChangeHealth(HealthDelta); 7 8 const auto HealthBarWidget = Cast<USTUHealthBarWidget>(HealthWidgetComponent->GetUserWidgetObject()); 9 if (HealthBarWidget) 10 { 11 HealthBarWidget->SetHealthPercent(HealthComponent->GetHealthPercent()); 12 } 13}
若NPC与玩家操控的游戏角色距离较远, 不显示生命条
- 添加属性: 生命条显示距离
protected
ShootThemUp: AI/STUAICharacter.h
1UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 2float HealthVisibilityDistance = 1000.0f;
- 添加美化函数
private
ShootThemUp: AI/STUAICharacter.h
1void UpdateHealthWidgetVisibility();
ShootThemUp: AI/STUAICharacter.cpp
1void ASTUAICharacter::UpdateHealthWidgetVisibility() 2{ 3 if (!GetWorld() || !GetWorld()->GetFirstPlayerController() || !GetWorld()->GetFirstPlayerController()->GetPawnOrSpectator()) return; 4 5 const auto Location = GetWorld()->GetFirstPlayerController()->GetPawnOrSpectator()->GetActorLocation(); 6 const auto Distance = FVector::Distance(Location, GetActorLocation()); 7 8 HealthWidgetComponent->SetVisibility(Distance < HealthVisibilityDistance, true); 9}
- 覆写Tick函数
public
ShootThemUp: AI/STUAICharacter.h
1virtual void Tick(float DeltaTime) override;
ShootThemUp: AI/STUAICharacter.cpp
1void ASTUAICharacter::Tick(float DeltaTime) 2{ 3 Super::Tick(DeltaTime); 4 5 UpdateHealthWidgetVisibility(); 6}
完善逻辑: NPC生命条组件拥有初始值
当前, NPC首次受伤之后, 才拥有正确的生命值
ShootThemUp: AI/STUAICharacter.cpp
1// BeginPlay 2 3const auto HealthBarWidget = Cast<USTUHealthBarWidget>(HealthWidgetComponent->GetUserWidgetObject()); 4if (HealthBarWidget) 5{ 6 HealthBarWidget->SetHealthPercent(HealthComponent->GetHealthPercent()); 7}
查看
设置窗口部件
修改回合时长为120s Round Time
BP_STUGameModeBase
可以把玩家个数设多点, 方便测试 Players Num
-
NPC未受伤时无生命条
-
NPC受伤后出现生命条
-
NPC生命值较低时, 生命条填充色为红色
-
NPC死亡, 生命条消失
NPC受伤, 但与玩家操控的游戏角色距离较远时, 生命条不可见