UE5 零基础入门第四弹:UMG UI 系统入门,从静态界面到逻辑联动


观众老爷们大家好 我是邪修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 缓存。虽然入门阶段性能问题不明显,但良好的习惯会让你在后续复杂项目中少走很多弯路。

相关推荐
CN-Dust2 小时前
【C++】输入cin例题专题
java·c++·算法
薛定猫AI2 小时前
【深度解析】Open Design:用本地优先架构重塑 AI UI 生成工作流
人工智能·ui·架构
智者知已应修善业9 小时前
【51单片机中的打飞机设计】2023-8-25
c++·经验分享·笔记·算法·51单片机
智者知已应修善业12 小时前
【51单片机按键调节占空比3位数码管显示】2023-8-24
c++·经验分享·笔记·算法·51单片机
徐某人..13 小时前
基于i.MX6ULL平台的智能网关系统开发
arm开发·c++·单片机·qt·物联网·学习·arm
无敌秋14 小时前
# C++ 简单工厂模式实战指南
c++·简单工厂模式
cany100014 小时前
C++ -- 模板的声明和定义
开发语言·c++
澈20714 小时前
深耕进阶 Day1:C 与 C++ 核心差异 + C++ 入门基石
c语言·开发语言·c++