
观众老爷们大家好 我是邪修KING 欢迎来到我的TA->UE游戏引擎博客---入门篇! C++!高门槛!精选学习! 系列衔接:上一篇我们解锁了 UE 多物体联动的核心 ------ 碰撞与触发系统,实现了碰撞销毁、自动门机关,并完成了蓝图函数的模块化封装。本篇我们将进入玩家与游戏交互的核心窗口 ------UMG UI 系统,制作文字提示、可交互按钮、血量条、主菜单 4 种最常用的 UI 界面,同时结合 C++ 面向对象知识,拆解 UMG 的底层逻辑与通信机制,彻底打通 "游戏逻辑→UI 显示" 的完整闭环。适配人群:有 C/C++ 基础、已掌握 UE 蓝图基础与碰撞系统的新手,全程沿用前序项目,无需从零搭建场景。
前置准备
1.沿用前三篇的 UE 项目,场景中保留玩家控制的BP_CubeTest、障碍物BP_Obstacle与自动门BP_Door
2.已掌握蓝图函数封装、变量定义、事件绑定等基础操作
3.引擎版本:UE5.0+,全版本操作通用,无兼容问题
一、核心概念前置:用 C++ 知识秒懂 UMG 体系
很多新手学 UMG 会觉得 "拖控件很简单,但逻辑联动一头雾水",本质是没有理解 UMG 的面向对象本质。只要你有 C++ 基础,就能一秒对应底层逻辑:
| UMG 术语 | 对应 C++ 核心概念 | 一句话核心说明 |
|---|---|---|
| Widget | C++ 的控件基类 | 所有 UI 元素的父类,按钮、文字、图片、进度条都是UWidget的子类,对应 C++ 的继承体系 |
| Widget Blueprint | C++ 的 UI 类定义 | 一个蓝图控件就是一个 C++ 类的可视化封装,包含控件布局、样式、逻辑,最终编译为 C++ 代码执行 |
| 锚点(Anchor) | C++ 的布局约束 | 定义 UI 元素在父控件中的相对位置与缩放规则,保证不同分辨率下界面布局一致,对应 C++ 的布局管理器 |
| 绑定(Binding) | C++ 的属性绑定与委托 | 将 UI 控件的属性(如文字内容、进度值)与游戏逻辑变量关联,变量变化时 UI 自动更新,对应 C++ 的观察者模式 |
| 事件分发器 | C++ 的多播委托 | 实现 UI 与游戏逻辑的解耦通信,UI 触发事件时通知游戏逻辑,游戏逻辑更新时通知 UI 刷新 |
| 视口(Viewport) | C++ 的主窗口 | 游戏运行时显示的主窗口,所有 UI 都需要添加到视口才能显示,对应 C++ 的应用程序主窗口 |
二、分步实操:4 个核心 UI 效果,从静态布局到逻辑联动
实操 1:创建第一个 UI------ 实时显示玩家移动速度
这是 UMG 最基础的应用,我们将实现:游戏运行时,屏幕左上角实时显示玩家的当前移动速度,掌握 "创建 UI→添加控件→数据绑定→添加到视口" 的完整流程。
步骤 1:创建 Widget Blueprint
1.在内容浏览器中右键 → 用户界面 → 控件蓝图 ,命名为WBP_PlayerHUD(HUD = 抬头显示,游戏中常驻的状态界面)
2.双击打开WBP_PlayerHUD,进入 UMG 编辑器,核心关注 3 个面板:
--设计器:可视化拖拽布局 UI 控件,对应 C++ 的 UI 布局代码
--图表:编写 UI 逻辑与事件绑定,对应 C++ 的 UI 逻辑代码
--细节:设置控件的属性、样式、锚点,对应 C++ 的控件属性设置
步骤 2:布局文字控件
1.在左侧「控制板」面板,搜索文本 控件,拖入设计器画布中
2.选中文字控件,在右侧「细节」面板设置:
--内容 → 文本:修改为 "移动速度:0.0"
--字体 → 大小:设置为 24,颜色改为白色
--锚点:点击左上角的锚点预设,选择左上角对齐(保证 UI 在不同分辨率下始终位于左上角)
--位置 → X=50,Y=50(调整到合适的位置)
步骤 3:实现数据绑定 ------ 实时更新移动速度
1.回到BP_CubeTest蓝图,找到我们之前定义的MoveSpeed变量,确保其为可编辑实例 且为浮点型
2.打开WBP_PlayerHUD蓝图,在「我的蓝图」→「变量」中,创建一个变量:
--名称:PlayerRef
--类型:BP_CubeTest(选择你自己的玩家蓝图类)
--勾选可编辑实例 ,用于接收玩家对象的引用
3.选中文字控件,在右侧「细节」面板,找到内容→文本 属性,点击右侧的绑定 按钮 → 创建绑定
4.此时会自动跳转到图表界面,生成一个绑定函数,在函数中编写逻辑:
--拖入PlayerRef变量,获取其MoveSpeed属性
--右键搜索格式化文本 节点,格式字符串填写 "移动速度:{0}"
--将MoveSpeed的值连入格式化文本的0参数
--将格式化文本的输出连入绑定函数的返回值
原理说明:数据绑定会每帧自动执行绑定函数,更新 UI 控件的属性,对应 C++ 中在 Tick 函数中更新 UI 显示,但 UE 的绑定机制做了性能优化,只有变量变化时才会真正刷新 UI。
步骤 4:将 UI 添加到视口显示
1.打开BP_CubeTest蓝图,找到事件 BeginPlay 节点
2.右键搜索创建控件 节点,类选择WBP_PlayerHUD
3.右键搜索添加到视口 节点,将创建控件的返回值连入添加到视口的目标
4.从创建控件节点的返回值拖出连线,搜索设置 PlayerRef ,将Self(玩家自身)赋值给 UI 的PlayerRef变量
5.点击编译→保存 ,回到主场景点击播放,屏幕左上角就会实时显示玩家的移动速度!
对应 C++ 核心代码实现
cpp
// WBP_PlayerHUD.h
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "WBP_PlayerHUD.generated.h"
class ABP_CubeTest;
UCLASS()
class YOUR_PROJECT_API UWBP_PlayerHUD : public UUserWidget
{
GENERATED_BODY()
public:
// 对应蓝图中的PlayerRef变量
UPROPERTY(BlueprintReadWrite, EditAnywhere)
ABP_CubeTest* PlayerRef;
// 对应蓝图中的文本绑定函数
UFUNCTION(BlueprintCallable)
FText GetSpeedText() const;
protected:
// 对应蓝图中的文本控件
UPROPERTY(meta=(BindWidget))
class UTextBlock* SpeedText;
};
// WBP_PlayerHUD.cpp
#include "WBP_PlayerHUD.h"
#include "BP_CubeTest.h"
FText UWBP_PlayerHUD::GetSpeedText() const
{
if(PlayerRef)
{
return FText::Format(NSLOCTEXT("UMG", "SpeedFormat", "移动速度:{0}"),
FText::AsNumber(PlayerRef->MoveSpeed));
}
return FText::FromString("移动速度:0.0");
}
// BP_CubeTest.cpp 中添加UI到视口
void ABP_CubeTest::BeginPlay()
{
Super::BeginPlay();
if(UWorld* World = GetWorld())
{
UWBP_PlayerHUD* HUD = CreateWidget<UWBP_PlayerHUD>(World, UWBP_PlayerHUD::StaticClass());
if(HUD)
{
HUD->PlayerRef = this;
HUD->AddToViewport();
}
}
}
实操 2:可交互按钮 ------ 实现暂停 / 继续游戏功能
按钮是 UMG 最常用的交互控件,我们将实现:屏幕右上角添加一个暂停按钮,点击后游戏暂停,再次点击继续游戏,掌握按钮的事件绑定与游戏暂停逻辑。
1.打开WBP_PlayerHUD蓝图的设计器,从控制板拖入一个按钮 控件
2.选中按钮控件,在右侧细节面板设置:
--锚点:选择右上角对齐
--位置 → X=-100,Y=50(调整到右上角合适位置)
--大小 → X=120,Y=50
3.在按钮内部拖入一个文本 控件,设置文本为 "暂停",字体大小 20,居中对齐
4.选中按钮控件,在右侧细节面板最下方找到事件 分类,点击OnClicked ,自动生成按钮点击事件节点
5.在图表中编写暂停逻辑:
--创建一个布尔变量bIsPaused,默认值为false,用于记录游戏是否暂停
--添加分支 节点,判断bIsPaused的值
--若为false(未暂停):右键搜索设置游戏暂停 节点,参数设为true,同时将按钮内的文本改为 "继续",并将bIsPaused设为true
--若为true(已暂停):调用设置游戏暂停 节点,参数设为false,将按钮文本改回 "暂停",并将bIsPaused设为false
6.点击编译→保存,运行游戏,点击右上角按钮即可实现暂停 / 继续功能!
实操 3:血量条 UI------ 碰撞障碍物扣血效果
进度条是游戏中最常用的状态显示控件,我们将实现:屏幕左上角添加一个血量条,玩家碰撞障碍物时血量减少,血量为 0 时游戏结束,掌握进度条的使用与 UI 与游戏逻辑的双向通信。
步骤 1:给玩家添加血量属性
1.打开BP_CubeTest蓝图,创建 3 个变量:
--MaxHealth:浮点型,默认值100.0,最大血量,勾选可编辑实例
--CurrentHealth:浮点型,默认值100.0,当前血量
--DamageOnHit:浮点型,默认值20.0,每次碰撞受到的伤害,勾选可编辑实例
2.创建一个事件分发器,命名为OnHealthChanged,添加一个浮点型参数NewHealth,用于血量变化时通知 UI 更新
原理说明:事件分发器是 UE 实现解耦通信的核心,玩家血量变化时只需要触发事件,不需要知道有哪些 UI 在监听,对应 C++ 的多播委托。
步骤 2:实现碰撞扣血逻辑
打开BP_Obstacle蓝图,找到之前的ObstacleHitHandler函数
在销毁障碍物之前,添加逻辑:获取碰撞的玩家对象,调用其受到伤害 的函数
回到BP_CubeTest蓝图,创建一个函数TakeDamage,参数为浮点型Damage:
函数逻辑:CurrentHealth = CurrentHealth - Damage
调用OnHealthChanged事件分发器,将CurrentHealth作为参数传入
添加分支判断:若CurrentHealth <= 0,调用游戏结束逻辑(这里先打印 "游戏结束" 文字提示)
步骤 3:制作血量条 UI 并绑定事件
1.打开WBP_PlayerHUD设计器,从控制板拖入一个进度条 控件
2.选中进度条,在右侧细节面板设置:
--锚点:左上角对齐
--位置 → X=50,Y=100(在移动速度文字下方)
--大小 → X=200,Y=20
--样式 → 填充颜色:设置为红色
3.在图表中,找到Set PlayerRef节点(我们之前在玩家蓝图中调用的)
4.从PlayerRef拖出连线,搜索绑定事件到 OnHealthChanged,将事件与 UI 的更新函数绑定
5.创建一个函数UpdateHealthBar,参数为浮点型NewHealth:
--拖入进度条控件,调用设置百分比节点
--百分比值 = NewHealth / PlayerRef.MaxHealth(将血量值转换为 0-1 的进度值)
6.点击编译→保存,运行游戏,碰撞障碍物时,血量条会实时减少,血量为 0 时打印游戏结束提示!
实操 4:主菜单界面 ------ 开始游戏与退出游戏
主菜单是游戏的入口界面,我们将实现:游戏启动时显示主菜单,点击 "开始游戏" 进入游戏,点击 "退出游戏" 关闭程序,掌握 UI 层级管理与关卡切换逻辑。
1.在内容浏览器中创建一个新的控件蓝图,命名为WBP_MainMenu
2.打开设计器,拖入一个图像 控件作为背景,铺满整个画布
3.拖入一个文本 控件作为标题,设置文本为 "我的第一个 UE 游戏",字体大小 48,居中对齐
4.拖入两个按钮 控件,分别设置文本为 "开始游戏" 和 "退出游戏",垂直居中排列
5.为两个按钮分别绑定OnClicked事件:
--开始游戏按钮:调用移除从父项 节点,关闭主菜单 UI,同时显示玩家 HUD 界面
--退出游戏:右键搜索退出游戏 节点,点击后关闭程序
6.打开项目设置 → 地图与模式 → 游戏默认地图,选择我们当前的关卡
7.打开关卡蓝图(主编辑器顶部菜单栏 → 蓝图 → 打开关卡蓝图)
8.在事件 BeginPlay 节点后,添加逻辑:创建WBP_MainMenu控件并添加到视口,同时隐藏玩家 HUD 界面
9.点击编译→保存,运行游戏,首先会显示主菜单,点击开始游戏进入游戏,点击退出游戏关闭程序!
三、UMG 新手避坑指南(100% 踩坑率总结)
1.UI 在不同分辨率下布局错乱
核心原因是没有正确设置锚点。永远不要用绝对坐标定位 UI,一定要根据 UI 的位置选择对应的锚点预设(左上角、右上角、居中、底部等),保证 UI 在不同分辨率下的相对位置不变。
2.数据绑定不生效
检查 3 点:①绑定的变量是否为可编辑实例;②变量引用是否正确赋值(如 PlayerRef 是否为 null);③绑定函数是否有正确的返回值,且返回值类型与控件属性类型匹配。
3.按钮点击无反应
检查 3 点:①按钮是否被其他控件遮挡(UI 层级是后添加的在上层);②按钮的可点击属性是否勾选;③按钮的OnClicked事件是否正确绑定了逻辑。
4.UI 内存泄漏
不要在 Tick 中频繁创建 UI 控件,UI 创建后如果不再使用,一定要调用移除从父项节点销毁。尤其是动态创建的大量 UI 元素,不销毁会导致内存持续上涨。
5.UI 与游戏逻辑耦合严重
永远不要在 UI 中直接编写游戏逻辑,也不要在游戏逻辑中直接操作 UI 控件。应该使用事件分发器实现解耦通信:游戏逻辑触发事件,UI 监听事件并更新显示;UI 触发事件,游戏逻辑响应事件并执行操作。
四、本篇总结
本篇我们完成了从游戏逻辑到玩家交互界面的跨越,核心收获有 4 点:
1.彻底吃透 UMG UI 系统的核心逻辑,用 C++ 面向对象知识打通了底层原理,理解了 UI 的类继承体系、数据绑定与事件通信机制。
2.掌握了 UMG 的完整开发流程:创建控件蓝图→布局 UI 控件→设置属性与锚点→绑定数据与事件→添加到视口显示。
3.实现了实时状态显示、可交互按钮、血量条、主菜单 4 种游戏中最常用的 UI 效果,形成了 "游戏逻辑→UI 显示→玩家交互→游戏逻辑" 的完整闭环。
4.学会了使用事件分发器实现 UI 与游戏逻辑的解耦通信,掌握了 "高内聚、低耦合" 的开发思想,为后续复杂项目开发打下了基础。
下一篇预告:UE5 零基础入门第五弹:蓝图进阶通信与代码复用,深入讲解事件分发器、蓝图接口、蓝图宏与函数库,彻底解决多蓝图通信难题,同时结合 C++ 的设计模式思想,教你写出优雅可维护的蓝图代码。
核心学习重点与后续规划
本篇必须吃透的核心学习重点
1.UMG 核心开发流程熟练掌握
"创建→布局→绑定→显示" 的完整流程,能独立制作任何基础 UI 界面。重点理解锚点的作用,能解决不同分辨率下的布局问题。
2.数据绑定与事件通信
这是 UMG 的核心,也是新手最容易卡壳的地方。重点理解数据绑定的底层原理,掌握事件分发器的使用方法,养成 "用事件解耦通信" 的习惯,绝对不要在 UI 和游戏逻辑之间直接互相调用。
3.C++ 与 UMG 的联动
发挥你的编程优势,每写一个蓝图 UI 逻辑,都尝试用 C++ 重写一遍。理解UUserWidget基类的作用,掌握 C++ 中创建 UI、绑定事件、更新 UI 的方法,这是大厂实习考察的重点。
4.UI 性能优化意识
养成良好的开发习惯,不要在 Tick 中更新 UI,不要频繁创建销毁 UI,合理使用 UI 缓存。虽然入门阶段性能问题不明显,但良好的习惯会让你在后续复杂项目中少走很多弯路。
