一个 HUD 或 抬头显示 是一组在游戏过程中始终可见或可访问的 UI 元素,如得分显示、生命计量器和菜单按钮。包括 HUD 对于大多数体验至关重要,因为它显示了帮助玩家在游戏目标中取得成功的信息。
一个常见的 HUD 元素是带有左侧图标的生命计量器,可以改编为计时条、进度条或类似的形式。

以 Hazardous Space Station 为起点,以 UI 基础知识 - HUD 计量器 为完成的参考,本教程演示:
- 设置和使用 设备模拟器 在多个模拟屏幕上测试您的设计。
- 使用 StarterGui 作为设计和存储容器。
- 如何在内置 Roblox 控件和设备凹口/岛屿(例如现代手机的相机凹口)周围位置/调整 UI 元素大小。
- 如何用自己的计量器替代默认的 Roblox 生命计量器,并将其连接到角色的生命值。
- 如何动画化生命计量器的中心部分并将其颜色设置为五个颜色渐变关键点(红色、橙色、黄色、青柠色、绿色)之间。
启用设备模拟器
Roblox 本质上是 跨平台 的,玩家可以在手机或平板电脑上发现并加入体验,然后再在其 PC 或主机上继续他们的进度。移动设备(手机和平板电脑)拥有最少的屏幕空间,因此确保您的 UI 元素能够适应较小的屏幕,并且对玩家清晰可见是非常重要的。
测试跨平台 UI 设计的最佳方式是 Studio 的 设备模拟器。这个工具提供了预设的设备选择,还允许您添加自定义的预设。
在 Studio 中打开 Hazardous Space Station 模板。
从 Studio 的 测试 菜单中,切换启用 设备模拟器。
从主视口上方的栏中,选择一个手机模拟,例如 iPhone X 或 Samsung Galaxy A51。然后,将视图大小设置为 适应窗口 以最大化利用 Studio 中的空间。

创建屏幕容器
ScreenGui 容器用于保存要在玩家屏幕上显示的 UI 对象(在本教程中,即整个生命计量器)。要将 ScreenGui 及其子对象显示给每个加入体验的玩家,请将它放置在 StarterGui 容器内。当玩家加入并且他们的角色首次生成时,ScreenGui 及其内容会克隆到该玩家的 PlayerGui 容器中,位于 Players 容器内。

要插入一个空的 ScreenGui:
在 资源管理器 窗口中,找到 StarterGui 容器。

将鼠标悬停在容器上,点击 ⊕ 按钮,插入一个 ScreenGui。

将新容器重命名为 HUDContainer 以反映其用途。

利用安全区域
现代手机充分利用整个屏幕,但通常包括凹口、切口和其他占用屏幕空间的元素。每个 Roblox 体验还包含 顶部条控件,供快速访问主菜单、聊天、排行榜等。

为了确保玩家可以不受阻碍地查看和访问所有 UI,Roblox 提供了 ScreenInsets 属性,控制 ScreenGui 内容的 安全区域。您在 ScreenGui 中放置的每个 UI 对象都是相对于内边距边界的位置。

虽然 CoreUISafeInsets 的默认设置确保所有 UI 对象远离 Roblox UI 和设备切口,但是 DeviceSafeInsets 可能是利用有限屏幕空间的更好选择,如下所示。

在 资源管理器 窗口中,选择 HUDContainer。

在 属性 窗口中,将 ScreenInsets 属性设置为 DeviceSafeInsets。

设置边缘内边距
将 ScreenInsets 设置为 DeviceSafeInsets 后,内容现在可以直接延伸到屏幕的物理顶部边缘。然而,少量的 内边距 可以帮助将生命计量器(以及容器中的其他对象)稍微推离屏幕边缘,以便外观更加干净,并防止被裁剪。

给 UI 容器应用内边距的一种方法是插入一个 UIPadding 修饰符:
在 HUDContainer 中插入一个 UIPadding 修饰符。

选择新建的 UIPadding 对象,为容器的所有边缘输入值 0, 16(PaddingBottom,PaddingLeft,PaddingRight,PaddingTop)。这样将在容器周围应用 16 像素的内边距,无论屏幕的分辨率如何。

构建生命计量器
配置 屏幕容器 后,您可以开始使用 Roblox UI 对象,例如 框架 和 图像标签 来构建生命计量器。

创建父框架
类似于 Figma 和 Photoshop 等设计应用程序,Roblox 中的 Frame 作为其他 UI 对象的容器。对于本教程,整个生命计量器将包含在一个单一的父框架中,使其在各种 HUD 布局中 reposition 更加简单。
将新的框架实例重命名为 MeterBar。

定位框架
在 Roblox 中,UI 对象的位置由一组 UDim2 坐标表示,其中包含 X 和 Y 轴的 Scale 和 Offset 值:


要将 UI 对象放置在屏幕容器的右上角,Scale 是最佳选择,因为 X 值为 1(100%)表示容器的右边缘,而不管屏幕的物理像素大小如何。类似地,Y Scale 值为 0(0%)表示容器的顶部边缘。

此外,您还需要为父框架设置右上角的 锚点,以定义其原点。可接受的值在 0 和 1 之间,相对于对象的大小,因此锚点值 1, 0 将框架的锚点放在其右上角。

在 资源管理器 窗口中,选择您之前插入的 MeterBar 框架。

为 AnchorPoint 属性输入 1, 0。框架现在应位于 设备安全区 的右上角,由于 内边距,与边缘略微缩进。

调整框架大小
与位置一样,UI 对象的 Size 由一组 UDim2 坐标表示,其中包含 X 和 Y 轴的 Scale 和 Offset 值。
默认情况下,新框架的大小为 {0, 100},{0, 100},意味着宽度 (X) 和高度 (Y) 为 100 像素。虽然严格的像素值在某些情况下是有用的,但许多 UI 元素在多个屏幕上设置为 百分比 时,更能响应性地缩放。
选择 MeterBar 框架,访问 属性 窗口并导航至 Size 属性。
输入值 0.35, 0, 0.05, 0 以设置 35% 宽和 5% 高的百分比大小,没有像素偏移。

样式框架
默认情况下,Frames 确实是实心白色的。最终的生命计量器应该有一个更暗且略微不透明的填充,并且有一个深色的轮廓,以便在明亮和黑暗背景上都能更好地突出显示。

选择 MeterBar 框架,为 BackgroundColor3 属性输入 0。Studio 将自动将其转换为 RGB 值 [0, 0, 0]。
为 BackgroundTransparency 属性输入 0.75。在 Roblox 中,透明度范围从 0(完全不透明)到 1(完全透明),因此 0.75 等于在其他应用程序(如 Figma 或 Photoshop)中 25% 的不透明度。

为了最终确定计量器框架的样式,您可以将角落圆滑化以形成“药丸”形状,而不是尖锐的矩形。
在 MeterBar 框架中插入一个 UICorner 实例。

选择新的 UICorner,将 CornerRadius 设置为 0.5, 0。使用 比例 值 0.5(50%)而不是像素值对于计量条特别方便,因为这确保了无论容器变得多高或多宽,都能做到完全圆滑的曲线。

创建内部填充
现在生命计量器的容纳框架已完成,您可以添加一个 内部填充 部分来表示角色的可变生命值。由于它只需一个实心填充区域,因此在父框架内部插入一个子 Frame 是合适的。

在 MeterBar 框架中插入一个子 Frame。
将新框架实例重命名为 InnerFill。

选择 InnerFill,设置以下属性:
- AnchorPoint = 0, 0.5(左边缘和垂直中心)
- Position = 0, 0, 0.5, 0
- Size = 1, 0, 1, 0
由于框架的子对象是相对于其父对象定位和大小,因此使用比例使得内部框架填满父框架的全部宽度和高度,从父框架的左边缘开始。

为了与父框架的“药丸”形状相匹配,将额外一个 UICorner 插入 InnerFill。

选择新的 UICorner 修饰符,将其 CornerRadius 属性设置为 0.5, 0 以匹配父 MeterBar 框架的“药丸”形状。

为了更好地表示满表表示良好的健康,选择 InnerFill,并将其 BackgroundColor3 属性设置为 [0, 225, 50](在下一步中,您会 编写脚本 使此颜色根据实际生命变化而变化)。

添加图标
为了更清晰地指示计量器的用途,您可以在左侧添加一个 图像标签,在这种情况下是一个红色心形图标,通常象征生命或健康。

在 MeterBar 框架中插入一个 ImageLabel。该对象允许您应用已经作为贴花上传到 Roblox 的 2D 图像资产。
将新的标签实例重命名为 Icon。

选择 Icon,将其 ZIndex 属性设置为 2。虽然新插入的 UI 对象总是会位于之前插入对象的前面,但这个改变确保图标总是显示在计量器框架元素的前面。

为了确保图标的 ImageLabel 始终保持 1:1 的宽高比,插入一个 UIAspectRatioConstraint。虽然这个 约束 具有可自定义的属性来控制宽高比,但是您可以保持其默认值不变。

选择 Icon,通过更改以下属性来完成外观和位置:
- AnchorPoint = 0.5, 0.5(中心锚点)
- BackgroundTransparency = 1(100% 透明)
- Position = 0, 0, 0.5, 0(计量器左侧和垂直中心)

限制大小
虽然 比例高度 为 0.05(5%)在现代手机屏幕和宽高比为 16:9 或更宽的游戏监视器上看起来不错,但在平板屏幕和旧款手机上,计量器可能看起来有点太高。您可以通过模拟 iPad 第 7 代 这样的平板来检查这一点,使用 设备模拟器。


为了保持计量器条在较宽屏幕之间的高度更一致,您可以使用 UISizeConstraint 来限制最大像素高度。
在 MeterBar 框架中插入一个 UISizeConstraint。

选择新的约束,将 MaxSize 属性设置为 inf, 20,以限制其高度为 20 像素,同时不限制宽度。

现在,计量器条在较宽和较高的屏幕之间保持更一致的高度。

替换默认生命计量器
Roblox 体验包括一个默认生命计量器,当角色受伤时会变得可见。如果您保持默认计量器可见,它将会重复并可能与自定义计量器重叠。

禁用默认计量器
为了禁用默认生命计量器,您需要使用 LocalScript 在 StarterPlayerScripts 中调用 StarterGui:SetCoreGuiEnabled()。
在 资源管理器 窗口中,展开 StarterPlayer 容器并定位其中的 StarterPlayerScripts 容器。

在容器中插入一个新的 LocalScript ,并将其重命名为 HideDefaultHealthMeter 以描述其目的。StarterPlayerScripts 中的脚本在本地玩家加入体验时会自动运行,使其成为运行一个永久隐藏默认计量器的脚本的理想容器。

当您插入一个新脚本时,它会自动在新脚本编辑器标签中打开(如果没有,请双击 资源管理器 窗口中的脚本)。
在 HideDefaultHealthMeter 脚本中粘贴以下代码:
HideDefaultHealthMeterlocal StarterGui = game:GetService("StarterGui")-- 隐藏默认生命计量器StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Health, false)代码说明行 目的 1 获取对核心 服务 StarterGui 的引用,该服务表示您创建自定义生命计量器的同一容器,其内容克隆到每个加入体验的玩家的 PlayerGui 容器中。 4 调用该服务的 SetCoreGuiEnabled() 方法并指示禁用默认生命计量器 (false)。
如果您现在进行游戏测试并受伤,您会注意到默认计量器被禁用并隐藏(您将在下一部分中脚本化自定义计量器以 反映生命变化)。

监听生命变化
所有默认的 Roblox 角色模型都包含一个 Humanoid 类,该类为角色提供特殊行为和功能,例如设置其行走/奔跑速度和管理其生命值。 Health 在服务器上 复制 到每个玩家的客户端,您可以检测这些变化以更新自定义生命计量器的大小和颜色。
在 资源管理器 窗口中,找到 StarterCharacterScripts 容器,位于 StarterPlayer 内。

在容器中插入一个新的 LocalScript ,并将其重命名为 UpdateCustomMeter 以描述其目的。StarterCharacterScripts 中的脚本在玩家的角色每次生成时会自动运行,使其成为一个在每次重生时完全重置生命计量器的脚本的理想容器。

在 UpdateCustomMeter 脚本的编辑窗口中,粘贴以下代码:
UpdateCustomMeterlocal Players = game:GetService("Players")-- 本地玩家、角色和人形的引用local player = Players.LocalPlayerlocal character = player.Characterlocal humanoid = character:WaitForChild("Humanoid")-- 计量器条内部框架的引用local playerGui = player:WaitForChild("PlayerGui")local meterBarInner = playerGui.HUDContainer.MeterBar.InnerFill-- 渐变序列颜色(红色、橙色、黄色、青柠色、绿色)local gradient = {Color3.fromRGB(225, 50, 0),Color3.fromRGB(255, 100, 0),Color3.fromRGB(255, 200, 0),Color3.fromRGB(150, 225, 0),Color3.fromRGB(0, 225, 50)}-- 获取渐变序列中各分数点的颜色local function getColorFromSequence(fraction: number): Color3-- 每种渐变颜色定义一个部分的开始和/或结束local numSections = #gradient - 1-- 每一部分代表 1 的一部分local sectionSize = 1 / numSections-- 确定请求的分数落入哪个部分local sectionStartIndex = 1 + math.clamp(fraction, 0, 1) // sectionSize-- 获取部分开始和结束的颜色local sectionColorStart = gradient[sectionStartIndex]local sectionColorEnd = gradient[sectionStartIndex + 1] or sectionColorStart-- 在该部分内将分数标准化为 0 到 1 的数字local fractionOfSection = math.clamp(fraction, 0, 1) % sectionSize / sectionSize-- 根据标准化的分数在开始和结束之间插值return sectionColorStart:Lerp(sectionColorEnd, fractionOfSection)endlocal function onHealthChanged()-- 计算新生命值为最大生命值的百分比local healthFraction = math.max(0, humanoid.Health / humanoid.MaxHealth)-- 将计量器条大小/颜色设置为新的目标meterBarInner.Size = UDim2.new(healthFraction, 0, 1, 0)meterBarInner.BackgroundColor3 = getColorFromSequence(healthFraction)end-- 监听人形生命值变化humanoid.HealthChanged:Connect(onHealthChanged)-- 最初设置(或重置)计量器条大小/颜色为当前生命值onHealthChanged()代码说明行 目的 4‑6 获取对本地 Player、其 Character 模型和其中的 Humanoid 类的引用。 9‑10 获取对计量器的 InnerFill 对象的引用,该对象必须在角色生命值变化时进行调整大小和重新着色。 13‑19 声明五种颜色(红色、橙色、黄色、青柠色、绿色)的数组,以在各个点重新着色计量器;例如,满血时绿,50% 时黄,0% 时红,或在关键点之间的任何分数时的混合色。 22‑41 辅助函数,用于返回任意渐变颜色关键点之间的颜色混合。 43‑50 处理生命值变化的函数。这里计算新的生命值为角色的 MaxHealth 的百分比,并将 InnerFill 的大小调整为该比例,并将其重新着色为 getColorFromSequence() 函数返回的颜色。 53 主要的 事件 连接,检测由服务器复制的 Health 变化,并调用 onHealthChanged() 函数。 56 初始(在角色生成或重生时)调用 onHealthChanged() 函数,以调整 InnerFill 的大小和颜色为正确的百分比。通常这将是完整的宽度和绿色。
如果您现在进行游戏测试,您会注意到当角色受伤时,自定义计量器会正确更新大小和颜色:
动画化计量器条
为了给自定义计量器增加一层额外的精致效果,您可以通过 插值 动画化生命变化,逐渐改变计量器条的大小和颜色,持续时间为半秒钟。
访问之前编辑的 UpdateCustomMeter 脚本的脚本编辑器标签。
选择所有行(CtrlA 或 ⌘A),然后用以下代码粘贴覆盖它们(CtrlV 或 ⌘V):
UpdateCustomMeterlocal Players = game:GetService("Players")local TweenService = game:GetService("TweenService")-- 本地玩家、角色和人形的引用local player = Players.LocalPlayerlocal character = player.Characterlocal humanoid = character:WaitForChild("Humanoid")-- 插值属性local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out)-- 计量器条内部框架的引用local playerGui = player:WaitForChild("PlayerGui")local meterBarInner = playerGui.HUDContainer.MeterBar.InnerFill-- 渐变序列颜色(红色、橙色、黄色、青柠色、绿色)local gradient = {Color3.fromRGB(225, 50, 0),Color3.fromRGB(255, 100, 0),Color3.fromRGB(255, 200, 0),Color3.fromRGB(150, 225, 0),Color3.fromRGB(0, 225, 50)}-- 获取渐变序列中各分数点的颜色local function getColorFromSequence(fraction: number): Color3-- 每种渐变颜色定义一个部分的开始和/或结束local numSections = #gradient - 1-- 每一部分代表 1 的一部分local sectionSize = 1 / numSections-- 确定请求的分数落入哪个部分local sectionStartIndex = 1 + math.clamp(fraction, 0, 1) // sectionSize-- 获取部分开始和结束的颜色local sectionColorStart = gradient[sectionStartIndex]local sectionColorEnd = gradient[sectionStartIndex + 1] or sectionColorStart-- 在该部分内将分数标准化为 0 到 1 的数字local fractionOfSection = math.clamp(fraction, 0, 1) % sectionSize / sectionSize-- 根据标准化的分数在开始和结束之间插值return sectionColorStart:Lerp(sectionColorEnd, fractionOfSection)endlocal function onHealthChanged()-- 计算新生命值为最大生命值的百分比local healthFraction = math.max(0, humanoid.Health / humanoid.MaxHealth)-- 将计量器条插值到新的大小/颜色目标local tweenGoal = {Size = UDim2.new(healthFraction, 0, 1, 0),BackgroundColor3 = getColorFromSequence(healthFraction)}local meterBarTween = TweenService:Create(meterBarInner, tweenInfo, tweenGoal)meterBarTween:Play()end-- 监听人形生命值变化humanoid.HealthChanged:Connect(onHealthChanged)-- 最初设置(或重置)计量器条大小/颜色为当前生命值onHealthChanged()主要添加/更改行 目的 2 获取对 TweenService 的引用,以在脚本中实现插值功能。 10 创建一个 TweenInfo 构造函数,定义预期插值的持续时间、插值样式和 插值方向。 52‑57 与 前一个版本 中简单设置计量器条的大小和颜色不同,声明一个 tweenGoal 表,包含目标大小/颜色,创建一个新插值使用 tweenInfo 和 tweenGoal 参数,并播放新的插值。
如果您现在进行游戏测试,您会注意到自定义计量器在每次生命值变化时都会进行插值:
添加伤害效果
默认生命计量器系统会在角色受伤时屏幕边缘上出现短暂细微的红色光泽。通过 禁用默认计量器,此效果将被移除,但您可以用自己的实现替换它。
访问之前编辑的 UpdateCustomMeter 脚本的脚本编辑器标签。
选择所有行并用以下代码覆盖它们:
UpdateCustomMeterlocal Workspace = game:GetService("Workspace")local Players = game:GetService("Players")local TweenService = game:GetService("TweenService")-- 本地玩家、角色和人形的引用local player = Players.LocalPlayerlocal character = player.Characterlocal humanoid = character:WaitForChild("Humanoid")-- 插值属性local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out)-- 用于存储/缓存角色生命值的变量local cachedHealth = humanoid.Health / humanoid.MaxHealth-- 获取(或创建新的)颜色校正效果,在玩家相机内部local colorCorrection = Workspace.CurrentCamera:FindFirstChildWhichIsA("ColorCorrectionEffect") or Instance.new("ColorCorrectionEffect", Workspace.CurrentCamera)colorCorrection.Name = "DamageColorEffect"-- 计量器条内部框架的引用local playerGui = player:WaitForChild("PlayerGui")local meterBarInner = playerGui.HUDContainer.MeterBar.InnerFill-- 渐变序列颜色(红色、橙色、黄色、青柠色、绿色)local gradient = {Color3.fromRGB(225, 50, 0),Color3.fromRGB(255, 100, 0),Color3.fromRGB(255, 200, 0),Color3.fromRGB(150, 225, 0),Color3.fromRGB(0, 225, 50)}-- 获取渐变序列中各分数点的颜色local function getColorFromSequence(fraction: number): Color3-- 每种渐变颜色定义一个部分的开始和/或结束local numSections = #gradient - 1-- 每一部分代表 1 的一部分local sectionSize = 1 / numSections-- 确定请求的分数落入哪个部分local sectionStartIndex = 1 + math.clamp(fraction, 0, 1) // sectionSize-- 获取部分开始和结束的颜色local sectionColorStart = gradient[sectionStartIndex]local sectionColorEnd = gradient[sectionStartIndex + 1] or sectionColorStart-- 在该部分内将分数标准化为 0 到 1 的数字local fractionOfSection = math.clamp(fraction, 0, 1) % sectionSize / sectionSize-- 根据标准化的分数在开始和结束之间插值return sectionColorStart:Lerp(sectionColorEnd, fractionOfSection)endlocal function onHealthChanged()-- 计算新生命值为最大生命值的百分比local healthFraction = math.max(0, humanoid.Health / humanoid.MaxHealth)-- 将计量器条插值到新的大小/颜色目标local tweenGoal = {Size = UDim2.new(healthFraction, 0, 1, 0),BackgroundColor3 = getColorFromSequence(healthFraction)}local meterBarTween = TweenService:Create(meterBarInner, tweenInfo, tweenGoal)meterBarTween:Play()-- 如果新生命值低于缓存生命值,则显示伤害效果if healthFraction < cachedHealth then-- 缓存新生命值cachedHealth = healthFraction-- 设置颜色校正为红色作为初始色调,然后进入缓冲colorCorrection.TintColor = Color3.fromRGB(255, 25, 25)colorCorrection.Saturation = 2.5-- 将色调插值回白色(中性,无色变化)local colorCorrectionTweenGoal = {TintColor = Color3.fromRGB(255, 255, 255),Saturation = 0}local colorCorrectionTween = TweenService:Create(colorCorrection, tweenInfo, colorCorrectionTweenGoal)colorCorrectionTween:Play()endend-- 监听人形生命值变化humanoid.HealthChanged:Connect(onHealthChanged)-- 最初设置(或重置)计量器条大小/颜色为当前生命值onHealthChanged()主要添加/更改行 目的 14 设置一个占位符引用(cachedHealth),用于跟踪角色生命值在变化之间的数量,以便比较变化是否降低(受伤)。 17‑18 在初始角色生成时,在玩家当前的 Camera 内部创建一个新的 ColorCorrectionEffect,或者在后来的重生中获取同一实例的引用。通过将此 后处理效果 作为玩家相机的父级,它只会应用于他们的本地屏幕,而不会影响服务器上的每个玩家屏幕。 68‑83 首先执行条件检查,以确认生命变化低于 cachedHealth 值,表示受伤;如果是,则将 cachedHealth 设置为新值。接下来,将 ColorCorrectionEffect 色调设置为 [255, 25, 25](红色)并提高饱和度,然后将色调插值回默认的中性白色([255, 255, 255])并取消饱和度。
如果您现在进行游戏测试,您会注意到每当角色受伤时,屏幕会短暂地闪烁为红色:



