多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

UE5动态血条实现:从蓝图到C++的呼吸感UI系统设计

UE5动态血条实现:从蓝图到C++的呼吸感UI系统设计 1. 项目概述为什么我们需要一个“会呼吸”的血条在UE5里做UI尤其是游戏里最常见的血条很多教程会告诉你拖个进度条控件、绑定个变量就完事了。但如果你想让你的游戏在视觉反馈上脱颖而出让玩家能直观感受到角色状态的变化一个静态的、只会线性增减的血条是远远不够的。这就是为什么我们要“手搓”一个会“呼吸”的血条——它不仅仅是数值的显示器更是游戏体验的放大器。所谓“呼吸”在这里指的是一种动态的、富有生命力的视觉表现。想象一下当角色受到伤害时血条不是瞬间减少而是有一个平滑的过渡甚至减少的部分会短暂高亮或残留提示玩家伤害的来源和量级当角色生命值较低时血条可能会伴随心跳的节奏轻微脉动或者颜色从健康的绿色逐渐变为警示的红色再变为危险的闪烁暗红同时边缘模糊、抖动营造出危机感当角色回复生命时回复的部分可能会有流光溢彩的填充效果。这些动态细节能极大地增强游戏的沉浸感和操作反馈的清晰度。这个教程将从最基础的蓝图可视化脚本开始带大家搭建一个功能完整的动态血条UI框架然后深入C层实现更高效、更可控、更易于复用的底层逻辑。无论你是UE蓝图爱好者希望提升UI表现力还是C程序员想要理解如何将游戏逻辑与UMGUnreal Motion Graphics深度结合这套从蓝图到C的完整流程都能给你提供一条清晰的路径。我们将不止步于“怎么做”更会探讨“为什么这么做”以及在实际项目中可能踩到的“坑”。2. 核心思路与架构设计2.1 动态血条的视觉元素拆解一个高级的动态血条通常由多个视觉层叠加构成而不是一个简单的ProgressBar。理解这些层次是设计的基础背景层通常是血条的外框或底色用于定义血条的整体形状和位置保持静态。生命值填充层这是核心显示当前生命值。我们需要两个部分当前生命值填充实时反映角色的当前HP。伤害延迟填充或称“假血”当受到伤害时当前填充减少但减少的部分不会立刻消失而是以另一种颜色如白色或深红色保留并在一段延迟后缓慢减少至零。这直观地显示了“已失去的生命值”是“呼吸感”的重要来源。护盾/缓冲层如果游戏有护盾机制这一层会覆盖在生命值之上通常用蓝色或半透明效果表示。边缘高光与特效层用于状态反馈。例如低生命值时边缘泛红并脉动受到巨额伤害时整个血条闪烁白光获得治疗时出现绿色流光粒子。这些是增强表现力的关键。数值文本层可选直接显示“HP: 150/200”这样的具体数字满足硬核玩家的需求。我们的目标就是通过蓝图和C驱动这些图层进行协调的动态变化。2.2 蓝图与C的分工与通信策略在UE中UI通常用UMG蓝图控件蓝图来构建视觉和简单的交互逻辑而复杂的游戏状态管理、数据计算则更适合用C。一个高效的架构是C 侧游戏逻辑层定义角色的基础属性类如UHealthComponent健康组件。在组件内管理当前生命值、最大生命值等核心变量并处理伤害、治疗等逻辑。通过UE的委托Delegate系统在生命值发生变化时发出通知。蓝图 侧表现层创建控件蓝图如WBP_HealthBar来布置上述的视觉元素。在控件蓝图中绑定Bind到C组件暴露的变量或委托。在蓝图内实现具体的动画效果如控制填充条的百分比、播放颜色渐变动画、触发粒子特效等。两者之间的桥梁就是UE的属性绑定系统和委托系统。C将数据变化以事件形式“广播”出去蓝图“订阅”这些事件并做出视觉响应。这样做的好处是逻辑与表现分离C代码干净且可复用蓝图则可以自由发挥创意调整效果而不必重新编译代码。注意很多新手会尝试在蓝图中直接写伤害计算逻辑这在小项目中可行但随着项目扩大会导致逻辑分散、难以维护。最佳实践是将核心逻辑放在C中。2.3 项目结构与资源准备在开始动手前规划好你的内容浏览器结构Content/ ├── Blueprints/ │ ├── Components/ (存放C生成的蓝图组件如HealthComponent) │ └── UI/ (存放控件蓝图) ├── Materials/ (存放血条用到的材质球) │ └── UI/ └── Textures/ (存放血条用的贴图) └── UI/你需要准备或创建一些基本的美术资源血条贴图至少需要一张用于填充条的平滑渐变贴图或者使用纯色配合材质节点生成渐变。外框贴图血条的背景边框。材质球为了达到更好的效果如边缘发光、扭曲效果我们可能需要创建材质。最简单的填充材质可以使用LinearGradient节点配合Percent参数来控制显示范围。3. 蓝图篇快速搭建动态血条原型3.1 创建角色属性和UI控件蓝图首先我们暂时用纯蓝图实现以便快速验证想法。步骤1创建角色健康变量打开你的角色蓝图或任何需要血条的Actor蓝图。在“变量”面板创建两个浮点数Float变量CurrentHealth(默认值100.0) 和MaxHealth(默认值100.0)。可以再创建一个布尔Boolean变量bIsAlive用于判断角色生存状态。步骤2创建控件蓝图在Content/Blueprints/UI/文件夹右键选择“用户界面” - “控件蓝图”命名为WBP_HealthBar。双击打开进入设计器界面。3.2 UMG控件布局与绑定这是构建视觉部分的核心。步骤1布置画布从控件面板拖拽一个Canvas Panel画布面板到层级中作为根容器。画布面板允许我们自由放置控件。在画布上按顺序添加以下控件层级从上到下覆盖Image背景重命名为Background设置锚点为拉伸Stretch调整边距使其稍大于血条赋予外框贴图。Progress Bar延迟伤害条重命名为DamageBar。这是我们的“假血”层。将其锚点设为拉伸调整边距与填充区域一致。在“外观”中将Fill Color and Opacity设置为半透明的白色或暗红色。关键一步在“进度”设置中将Percent绑定到一个新的蓝图变量后面创建。Progress Bar当前生命条重命名为HealthBar。这是主血条。确保它在层级中位于DamageBar之上。同样锚点拉伸边距与填充区一致。设置一个健康的填充颜色如绿色。绑定它的Percent。Image边缘高光重命名为EdgeGlow使用一个边缘发光的材质锚点拉伸边距略小于或等于背景。我们通过控制其可见性和材质参数来实现低血预警。Text Block数值显示重命名为HealthText放置在合适位置用于显示“当前/最大”生命值。步骤2创建蓝图变量并实现绑定切换到控件蓝图的“图表”视图。创建两个浮点型变量HealthPercent和DamagePercent。为HealthBar的Percent创建绑定选择“绑定” - “创建绑定”在新函数里直接返回HealthPercent变量。为DamageBar的Percent创建绑定返回DamagePercent变量。为HealthText的Text创建绑定返回一个格式化的文本如CurrentHealth/MaxHealth这里需要从角色蓝图获取我们稍后通过事件驱动来更新。现在UI的视觉框架和基础数据绑定就完成了。但HealthPercent和DamagePercent还不会自动变化。3.3 实现伤害延迟与动画效果“呼吸感”的精髓在于伤害延迟条的动画。步骤1更新生命值逻辑在角色蓝图中创建一个自定义事件例如ApplyDamage输入参数DamageAmount浮点数。在这个事件里CurrentHealth CurrentHealth - DamageAmount然后Clamp钳制CurrentHealth在0到MaxHealth之间。计算新的HealthPercent CurrentHealth / MaxHealth。关键此时不要立即更新伤害延迟条。我们需要先记录下伤害前的百分比。创建一个变量LastHealthPercent在应用伤害前存储旧的HealthPercent。应用伤害后将DamagePercent设置为LastHealthPercent。这样伤害延迟条会暂时停留在受伤前的位置。调用一个延迟减少DamagePercent的函数。步骤2驱动UI更新角色蓝图如何通知UI我们需要获取UI控件并调用其函数。在角色蓝图的BeginPlay事件中使用Create Widget节点创建WBP_HealthBar的实例然后使用Add to Viewport将其添加到屏幕。将创建的控件实例保存到一个变量中如HealthBarWidget。在ApplyDamage事件中在计算完新的HealthPercent和DamagePercent后调用HealthBarWidget上的一个自定义事件来更新这些值。步骤3在控件蓝图中实现动画在WBP_HealthBar图表中创建一个自定义事件UpdateHealth输入参数NewHealthPercent和NewDamagePercent。在这个事件里设置HealthPercent为NewHealthPercent。由于绑定了进度条会自动更新。对于DamagePercent我们不要直接设置而是使用Interp To插值到节点让DamagePercent平滑地过渡到NewDamagePercent。你可以将这个插值计算放在Tick事件中或者用一个定时器Timer来驱动以实现平滑的“假血”消退效果。// 伪代码逻辑在Tick或Timer函数中 float DeltaTime 获取增量时间 float TargetDamagePercent 从角色传来的目标值 float CurrentDamagePercent DamagePercent变量 float InterpSpeed 2.0 // 消退速度可调 DamagePercent FMath::FInterpTo(CurrentDamagePercent, TargetDamagePercent, DeltaTime, InterpSpeed);低生命值特效在UpdateHealth事件中判断NewHealthPercent是否低于某个阈值如0.3。如果是则设置EdgeGlow可见并可能动态修改其材质的颜色或亮度参数通过Set Scalar Parameter Value on Materials节点甚至启动一个脉动动画通过Sin函数和时间节点驱动材质参数或控件渲染不透明度。实操心得在蓝图中做插值动画时Interp To节点比Lerp(线性插值) 更常用因为它考虑了帧时间DeltaTime能保证在不同帧率下动画速度一致。调整InterpSpeed参数可以控制动画的缓入缓出效果。3.4 蓝图方案的优缺点总结通过以上步骤一个具备基础动态效果的血条就在蓝图中实现了。它的优点是迭代速度快美术和策划可以非常方便地调整效果、颜色和动画曲线无需程序员介入。对于原型开发和小型项目来说完全够用。但其缺点也很明显性能开销大量的Tick事件、绑定和蓝图间的频繁通信在UI元素多时可能成为性能瓶颈。难以复用逻辑写在角色蓝图和控件蓝图里如果想给另一种敌人也加上血条需要复制大量逻辑。维护性差游戏核心逻辑伤害计算与表现逻辑混杂后期修改容易出错。网络同步复杂对于多人游戏蓝图的变量复制和RPC调用需要精心设计不如C直观可靠。因此对于中大型项目或追求性能与架构优雅的开发者将核心逻辑迁移到C是必然选择。4. C篇构建健壮高效的血条底层逻辑4.1 创建健康组件Health Component我们将使用UE的组件系统创建一个可复用的UHealthComponent。步骤1创建C类在编辑器中选择“工具”-“新建C类”。选择“Actor组件”作为父类命名为HealthComponent。点击创建UE会生成.h和.cpp文件并编译。步骤2定义头文件HealthComponent.h我们需要声明必要的变量、函数和委托。#pragma once #include CoreMinimal.h #include Components/ActorComponent.h #include HealthComponent.generated.h // 声明一个多播委托当生命值发生变化时广播 DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComp, float, Health, float, HealthDelta, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser); UCLASS( ClassGroup(Custom), meta(BlueprintSpawnableComponent) ) class YOURPROJECT_API UHealthComponent : public UActorComponent { GENERATED_BODY() public: UHealthComponent(); protected: // 基础属性 UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category Health) float DefaultHealth; UPROPERTY(BlueprintReadOnly, Category Health) float Health; // 是否已死亡 UPROPERTY(BlueprintReadOnly, Category Health) bool bIsDead; // 用于伤害延迟计算的“假血”值 UPROPERTY(BlueprintReadOnly, Category Health UI) float DisplayHealth; // 对应UI的HealthPercent UPROPERTY(BlueprintReadOnly, Category Health UI) float PendingDamage; // 对应UI的DamagePercent的“目标值” virtual void BeginPlay() override; // 处理实际伤害的函数 UFUNCTION() void HandleTakeAnyDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser); public: // 公开的委托供UI蓝图绑定 UPROPERTY(BlueprintAssignable, Category Events) FOnHealthChangedSignature OnHealthChanged; // 获取当前生命值百分比 (0-1) UFUNCTION(BlueprintPure, Category Health) float GetHealthPercent() const; // 获取用于UI显示的“假血”百分比 UFUNCTION(BlueprintPure, Category Health UI) float GetDisplayHealthPercent() const; // 获取待消退伤害的百分比 UFUNCTION(BlueprintPure, Category Health UI) float GetPendingDamagePercent() const; // 每帧更新显示值实现平滑过渡 virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; };关键点解析FOnHealthChangedSignature: 这是一个动态多播委托允许蓝图动态绑定。它传递了丰富的上下文信息伤害量、伤害类型、施加者等UI或其他系统可以根据需要做出不同反应。DisplayHealth和PendingDamage: 这两个变量是专门为UI平滑动画服务的。Health是真实逻辑值DisplayHealth是平滑过渡后显示给玩家的值。PendingDamage记录了需要延迟消退的伤害量。TickComponent: 我们将在这里实现DisplayHealth向Health平滑插值以及PendingDamage的消退逻辑。步骤3实现核心逻辑HealthComponent.cpp#include HealthComponent.h #include GameFramework/Actor.h #include Engine/Engine.h UHealthComponent::UHealthComponent() { PrimaryComponentTick.bCanEverTick true; // 启用Tick以更新UI动画 DefaultHealth 100.0f; bIsDead false; } void UHealthComponent::BeginPlay() { Super::BeginPlay(); Health DefaultHealth; DisplayHealth Health; PendingDamage 0.0f; // 注册伤害事件监听 AActor* MyOwner GetOwner(); if (MyOwner) { MyOwner-OnTakeAnyDamage.AddDynamic(this, UHealthComponent::HandleTakeAnyDamage); } } void UHealthComponent::HandleTakeAnyDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatedBy, AActor* DamageCauser) { if (Damage 0.0f || bIsDead) { return; } // 应用伤害 Health FMath::Clamp(Health - Damage, 0.0f, DefaultHealth); // 更新待消退伤害值。注意PendingDamage记录的是“尚未消退的伤害部分” // 当新伤害到来时旧的PendingDamage可能还没消完我们需要累加。 // 但更常见的做法是PendingDamage DisplayHealth - Health; // 我们先采用简单逻辑PendingDamage DisplayHealth - Health; // DisplayHealth会在Tick中平滑追Health所以PendingDamage就是两者差值。 UE_LOG(LogTemp, Log, TEXT(Health Changed: %s), *FString::SanitizeFloat(Health)); // 检查死亡 if (Health 0.0f) { bIsDead true; // 可以在这里触发死亡事件 } // 广播生命值变化事件UI和其他系统会监听这个 OnHealthChanged.Broadcast(this, Health, -Damage, DamageType, InstigatedBy, DamageCauser); } float UHealthComponent::GetHealthPercent() const { return Health / DefaultHealth; } float UHealthComponent::GetDisplayHealthPercent() const { return DisplayHealth / DefaultHealth; } float UHealthComponent::GetPendingDamagePercent() const { // PendingDamagePercent 应该等于 (DisplayHealth - Health) / DefaultHealth // 但更直观的UI做法是DamageBar的百分比 DisplayHealthPercent // HealthBar的百分比 HealthPercent // 这样DamageBar始终覆盖在HealthBar“之前”的位置。 // 所以我们不需要这个函数UI直接取DisplayHealthPercent和HealthPercent即可。 // 这里保留作为示例实际可能不需要。 return (DisplayHealth - Health) / DefaultHealth; } void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (bIsDead) return; // 平滑更新DisplayHealth使其逐渐接近真实的Health float HealthInterpSpeed 8.0f; // 生命条跟进速度 DisplayHealth FMath::FInterpTo(DisplayHealth, Health, DeltaTime, HealthInterpSpeed); // 计算并更新PendingDamage如果需要单独控制伤害条消退 // 假设DamageBar直接使用DisplayHealthPercent那么PendingDamage的逻辑已内含。 // 如果我们想单独控制一个伤害延迟条的消退更慢可以这样 // float DamageInterpSpeed 2.0f; // PendingDamage FMath::FInterpTo(PendingDamage, Health, DeltaTime, DamageInterpSpeed); // 但这样需要两个插值。更常见的简化方案是 // UI的HealthBar绑定GetHealthPercent() // UI的DamageBar绑定GetDisplayHealthPercent() // DisplayHealth以较慢速度追Health自然就产生了伤害延迟效果。 }代码逻辑解读BeginPlay中初始化并注册了UE内置的OnTakeAnyDamage委托。这意味着任何对拥有此组件的Actor造成的伤害通过ApplyDamage函数都会触发HandleTakeAnyDamage。HandleTakeAnyDamage中处理核心伤害逻辑更新Health并广播OnHealthChanged事件。TickComponent是实现“呼吸感”的关键。FMath::FInterpTo函数根据DeltaTime和速度参数让DisplayHealth平滑地过渡到Health。DisplayHealth总是滞后于Health这个滞后就表现为伤害延迟条的“假血”。调整HealthInterpSpeed可以控制生命条跟进的速度值越小延迟越明显“假血”停留时间越长。4.2 将组件添加到角色并暴露给蓝图创建好C组件后我们需要将其添加到角色蓝图中。编译C代码。打开你的角色蓝图或新建一个继承自Character的C类再制作蓝图。在“组件”面板点击“添加组件”搜索并添加HealthComponent。现在角色的伤害逻辑就可以由这个组件统一管理了。你可以在角色蓝图或其他蓝图中通过获取这个组件来调用其函数或绑定其委托。4.3 重构UMG控件蓝图以绑定C数据现在我们需要修改之前创建的WBP_HealthBar让它从C组件获取数据。步骤1优化控件蓝图变量打开WBP_HealthBar。可以删除之前创建的HealthPercent和DamagePercent变量。创建一个对象类型变量命名为OwningHealthComponent类型设置为HealthComponent需要先编译C代码才能在蓝图下拉菜单中找到。步骤2创建初始化函数创建一个新的函数命名为InitHealthBar输入参数为TargetHealthComp类型为HealthComponent对象引用。在函数内设置OwningHealthComponent变量为传入的TargetHealthComp。关键绑定事件。拖出OwningHealthComponent变量获取其OnHealthChanged委托然后调用Bind Event节点将其绑定到一个新的自定义事件例如OnHealthChangedEvent。这样每当C组件的生命值变化这个蓝图事件就会被触发。步骤3响应生命值变化事件创建自定义事件OnHealthChangedEvent其参数与C委托签名匹配会自动生成。在这个事件中我们可以直接调用C组件提供的纯函数BlueprintPure来更新UI。拖出OwningHealthComponent调用GetHealthPercent将返回值直接用于设置HealthBar的百分比可以通过Set Percent节点或更优的做法是像之前一样在HealthBar的Percent绑定中调用这个函数。拖出OwningHealthComponent调用GetDisplayHealthPercent用于设置DamageBar的百分比。同时在这里可以根据当前的Health或HealthPercent触发低生命值特效如修改EdgeGlow的可见性和材质参数。步骤4在角色蓝图中初始化UI在角色蓝图的BeginPlay事件中创建WBP_HealthBar控件实例。从角色身上获取HealthComponent组件。调用控件实例的InitHealthBar函数将HealthComponent传递进去。将控件添加到视口。现在一个由C驱动数据、由蓝图负责表现的动态血条系统就构建完成了。C组件高效地处理了游戏逻辑和数值计算并通过委托通知UI蓝图UI则专注于视觉效果和动画两者各司其职耦合度低。5. 高级优化与效果增强5.1 使用材质实例动态控制视觉效果直接在蓝图中修改控件颜色和透明度可行但对于复杂效果如扭曲、流光、边缘发光动画材质是更强大的工具。创建血条材质为HealthBar和DamageBar的填充部分创建材质。在材质中使用Percent参数通过Scalar Parameter节点暴露来控制一个LinearGradient或TextureCoordinate的UV偏移实现填充效果。创建材质实例在内容浏览器中基于该材质创建材质实例Material Instance。在控件蓝图中动态控制在WBP_HealthBar中将HealthBar的Brush设置为这个材质实例。然后在OnHealthChangedEvent中使用Set Scalar Parameter Value节点动态修改材质实例的Percent参数。你还可以暴露Color、Glow Intensity等参数根据生命值百分比动态调整实现从绿到红的渐变或者低生命值时提高发光强度。5.2 伤害数字与浮动文本除了血条本身在受击位置显示伤害数字能极大提升打击感。创建一个新的控件蓝图WBP_DamageText包含一个Text Block可以添加一些简单的动画如向上飘移、渐隐。在C的HealthComponent的HandleTakeAnyDamage函数中在广播OnHealthChanged委托时可以额外广播一个专门用于伤害数字的委托比如FOnDamageReceivedSignature传递伤害值和伤害位置。在角色蓝图或一个全局的UI管理器中监听这个委托。当收到事件时在伤害位置DamageCauser的位置或通过射线检测确定的受击点创建WBP_DamageText控件实例设置文本为伤害值并播放其动画。5.3 性能优化要点减少Tick依赖我们的HealthComponent开启了Tick来插值。如果游戏中有大量单位每个都Tick插值可能会带来开销。优化方案是将插值计算移到UI控件的Tick中C组件只提供目标值。或者使用UE的FTimerManager设置一个低频定时器来更新而不是每帧更新。控件池对于频繁创建销毁的UI元素如伤害数字使用对象池Object Pool技术来复用避免频繁的内存分配和垃圾回收。绑定优化确保在UI控件被移除时如角色死亡解绑Unbind所有委托防止内存泄漏。材质参数集合如果大量UI需要使用相同的动态参数如全局的游戏色调可以考虑使用Material Parameter Collection只需更新一次所有引用该集合的材质都会生效比更新每个材质实例更高效。6. 常见问题与调试技巧6.1 血条不显示或位置不对检查控件是否被添加到视口确保在角色BeginPlay后成功调用了Add to Viewport。检查控件层级在UMG设计器中确保血条控件位于视图层的最上层没有被其他全屏控件覆盖。检查锚点和位置确认血条的锚点设置正确。如果想固定在屏幕某个角落使用对应的锚点如左上角如果想跟随角色在3D世界中需要使用Widget Component附加到角色上并调整好相对位置和朝向使其始终面向摄像机。检查玩家控制器UMG控件需要关联到一个玩家控制器。在单玩家游戏中通常就是Get Player Controller获取到的那个。确保创建控件的上下文正确。6.2 伤害延迟效果不生效检查插值逻辑确认DisplayHealth在TickComponent中确实在以小于HealthInterpSpeed的速度向Health插值。可以在C代码中打印DisplayHealth和Health的值来观察。检查UI绑定确认DamageBar绑定的百分比是GetDisplayHealthPercent()而HealthBar绑定的是GetHealthPercent()。顺序不能反。检查速度参数HealthInterpSpeed值越大跟进越快延迟效果越不明显。尝试将其调小如从8.0调到2.0。6.3 委托事件没有触发检查绑定时机确保在控件初始化函数InitHealthBar中成功获取到了HealthComponent并绑定了OnHealthChanged事件。可以在绑定后打印一条日志来确认。检查广播时机在C的HandleTakeAnyDamage函数中确保在修改Health后调用了OnHealthChanged.Broadcast(...)。检查伤害来源角色是否真的收到了伤害确保伤害是通过标准的AActor::ApplyDamage函数施加的这样才能触发OnTakeAnyDamage委托。6.4 在多人游戏中血条不同步变量复制在C组件的变量声明中为需要同步的变量添加Replicated或ReplicatedUsing说明符。例如UPROPERTY(ReplicatedUsingOnRep_Health, BlueprintReadOnly, CategoryHealth) float Health; UFUNCTION() void OnRep_Health();然后在GetLifetimeReplicatedProps函数中注册要复制的属性。UI更新时机在客户端的OnRep_Health函数中调用OnHealthChanged.Broadcast来通知UI更新而不是在服务器的HandleTakeAnyDamage中。因为广播只在服务器执行客户端需要靠复制属性后的回调来触发。控件创建权限确保UI控件只在所属玩家的客户端创建而不是在服务器或所有客户端上创建。通常在角色蓝图的BeginPlay中判断IsLocallyControlled()再创建UI。6.5 内存泄漏排查解绑委托在控件蓝图的Destruct事件中如果之前绑定了C组件的委托记得调用Unbind节点进行解绑。使用弱引用在蓝图中持有对C组件或其他对象的引用时可以考虑使用弱引用指针防止意外延长对象生命周期。这套从蓝图到C的动态血条实现方案不仅提供了一个功能丰富的UI元素更重要的是展示了一种在UE5中构建可维护、高性能游戏系统的设计思路。你可以以此为基础扩展出护盾条、经验条、体力条等各种动态进度条UI并根据自己游戏的视觉风格尽情发挥创意打磨出独一无二的“呼吸感”。
返回列表