在Studio中实现设计

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

实现您的设计 是在Studio中使用内置和自定义UI元素以及触发上下文UI的脚本来创建线框的过程。本教程的这一激动人心的步骤是您可以看到所有设计和辛勤工作的成果汇聚成一套完整的工作流程,准备好供玩家互动。

使用 示例激光标签游戏 .rbxl 文件作为参考,本用户界面设计课程的这一部分向您展示如何将UI规划变为现实,包括以下指导:

  • 从UI资产库中检索资产ID,以便您可以重建示例激光标签游戏的UI组件。
  • 在Studio中直接模拟各种设备,以查看您的UI在不同屏幕和纵横比上的显示效果。
  • 创建 ScreenGuiSurfaceGuiBillboardGui 对象,分别在玩家的屏幕、部件表面和3D空间中显示您的UI。

在您回顾本节中的技术后,您可以将其应用于自己的项目,以制作令人兴奋的UI组件,帮助玩家导航他们在游戏中可以做的事情。

获取资产库

资产库是您可以添加到库存中以便于访问和重用的资产集合。您将在 创作者商店 中为您的项目使用的资产库包括九个2D单个UI元素资产,以及您在本教程这一部分中创建的目标、发射器选择器和玩家信息组件的最终版本。

多重发射器图标
rbxassetid://14309094777
单发射器图标
rbxassetid://14309094641
粉色队伍图标
rbxassetid://14309678581
梯形
rbxassetid://14304828203
倒梯形
rbxassetid://14304827304
绿色队伍图标
rbxassetid://14309678701
渐变
rbxassetid://14304826876
多方向渐变
rbxassetid://14304827147
爆炸按钮图标 - 默认
rbxassetid://18308375067
爆炸按钮图标 - 按下
rbxassetid://18308372597
十字准星图标
rbxassetid://14400935532
命中标记图标
rbxassetid://14401148777
六边形
rbxassetid://14462567943
选择箭头图标
rbxassetid://14309187282
边框渐变
rbxassetid://14309518632

要将资产库从您的库存中导入到您的游戏中:

  1. 将资产库添加到您的库存。

    1. 导航到创作者商店的资产库 详细信息页面
    2. 在右上角,单击 获取模型 按钮。资产库现在在您的库存中,您可以在平台上的任何项目中重用这些资产。
  2. 在Studio中,导航到 主页 选项卡,然后单击 工具箱 按钮。工具箱 窗口打开。

    工具箱在Studio工具栏中突出显示。
  3. 工具箱 窗口中,单击 库存 选项卡。我的模型 排序显示。

  4. 单击下拉菜单,然后选择 我的包 排序。

  5. 单击 最终屏幕UI组件 瓦片,然后在 资源管理器 窗口中选择 已完成组件,然后将它们拖入 StarterGui 服务。您现在可以启用任何最终组件以引用其设计。

模拟设备

Studio的 设备模拟器 允许您测试玩家在各种设备上如何查看和与您的UI互动。这个工具是实现过程中的一个重要部分,因为您在Studio中的视口的纵横比并不一定反映玩家用来访问您游戏的屏幕的纵横比,并且确保您的UI在每个设备上都是可读和可访问的非常重要。

例如,如果您不在一系列屏幕尺寸上测试您的UI,使用大屏幕的玩家可能无法阅读您的文本或辨别您的图标,而使用小屏幕的玩家可能无法看到3D空间,因为您的UI元素占用了显示器上太多的空间。

要在各种屏幕尺寸上模拟您的UI:

  1. 从Studio的 测试 菜单中,切换打开 设备模拟器

  2. 在分辨率下拉菜单中,选择 实际分辨率。这允许您查看您正在模拟的设备上UI元素的真实分辨率。

    设备模拟器设置选项在视口窗口顶部指示。
  3. 在设备下拉菜单中,选择至少一个设备,位于 手机平板桌面控制台 部分。

创建ScreenGui对象

要在每个玩家的屏幕上显示UI元素,您可以在 StarterGui 服务中创建一个 ScreenGui 对象。ScreenGui 对象是屏幕上UI的主要容器,StarterGui 服务会在玩家进入游戏时将其内容复制到每个玩家的 PlayerGui 容器中。

您可以创建多个 ScreenGui 对象,以便在游戏过程中有条理地组织和显示UI元素的分组。例如,示例激光标签游戏包括五个单独的 ScreenGui 对象,这些对象最初是禁用的,直到玩家在游戏的主要 用户流程 中满足不同的条件:

  • HUDGui - 在玩家在回合中活跃时显示有关游戏的关键信息,例如目标和每个队伍的总分。
  • PickABlasterGui - 在玩家开始或重新加入回合时显示所有发射器选择。
  • ForceFieldGui - 在玩家选择发射器时显示六边形网格,并在他们暂时无敌时显示。
  • OutStateGui - 当玩家被标记出局时,在屏幕周围显示黑色边框。
  • RoundResultsGui - 在屏幕顶部显示黑色覆盖层,显示哪个队伍赢得了回合的信息。

在您创建 ScreenGui 对象后,您可以根据每个容器的目的创建和自定义其子 GuiObjects。为了演示,在接下来的部分中,您将学习如何实现玩家在示例激光标签游戏中需要知道的 三类信息 的UI元素。您可以调整过程的任何部分以满足您自己游戏的规格

要创建 ScreenGui 对象:

  1. 资源管理器 窗口中,将鼠标悬停在 StarterGui 服务上,然后单击 图标。上下文菜单显示。

  2. 插入一个 ScreenGui

  3. 根据其子UI元素的上下文重命名 ScreenGui

  4. 对于每个需要在每个玩家的屏幕上显示的UI元素分组,重复此过程。

目标UI

遵循 线框您的布局 中的视觉层次最佳实践,本节教您如何实现与游戏目标相关的所有屏幕UI元素。这个UI元素分组位于屏幕的顶部,因为目标和每个队伍的分数在赢得游戏的过程中具有最重要的意义。

例如,示例提供了一个目标UI组件,玩家可以参考以了解他们在回合中需要做什么才能成功。当玩家标记出敌队成员并获得积分时,该组件会跟踪每个队伍的得分与标题提示中的总体目标。有关所有客户端和服务器脚本的高级审查,这些脚本共同工作以跟踪积分,请参见 跟踪积分 在游戏玩法脚本课程中。

要在示例 激光标签 游戏中准确重建目标UI:

  1. 为整个组件创建一个容器。

    1. HUDGui ScreenGui 对象中插入一个 Frame

      1. 资源管理器 窗口中,导航到 StarterGui 服务。
      2. 将鼠标悬停在其子 HUDGui 对象上,然后单击 ⊕ 图标。上下文菜单显示。
      3. 从上下文菜单中插入一个 Frame
    2. 选择新 Frame,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 0,以将框架的原点设置在其顶部中间(从框架的左侧到右侧50%,从框架的顶部到底部0%)。
      2. BackgroundTransparency 设置为 1,以使框架的背景完全透明。
      3. Position 设置为 {0.5, 0},{0.03, 0},以将框架设置在屏幕的顶部中间附近(从屏幕的左侧到右侧50%,从屏幕的顶部到底部3%以留出一点缓冲)。
      4. Size 设置为 {0.5, 0},{0.13, 0},以使框架的元素占据屏幕顶部的大部分区域,以吸引玩家的注意(水平50%,垂直13%)。
      5. Name 设置为 Objective
    3. 可选
      Objective 中插入一个 UIAspectRatioConstraint,以确保标签的纵横比在任何玩家的屏幕尺寸下保持不变。示例将其 UIAspectRatioConstraint.AspectRatio 属性设置为 7

  2. 为目标的提示对象创建一个容器。

    1. Objective 中插入一个 Frame

    2. 选择新 Frame,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 0,以将框架的原点设置在其顶部中间(从框架的左侧到右侧50%,从框架的顶部到底部0%)。
      2. BackgroundTransparency 设置为 1,以使框架的背景完全透明。
      3. Position 设置为 {0.5, 0},{0, 0},以将框架设置在容器的中间(从父框架的左侧到右侧50%,从父框架的顶部到底部0%)。
      4. Size 设置为 {1, 0},{0.67, 0},以使选择UI组件占据容器的上半部分(父框架的100%水平和67%垂直)。
      5. Name 设置为 ObjectiveDisplay
  3. 创建标题元素。

    1. ObjectiveDisplay 中插入一个 ImageLabel

    2. 选择 ImageLabel,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 1,以将标签的原点设置在其底部中间(从标签的左侧到右侧50%,从标签的顶部到底部100%)。
      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
      3. LayoutOrder 设置为 -1
      4. Position 设置为 {0.5, 0},{0.34, 0},以将标签设置在框架的顶部上中间附近(从父框架的左侧到右侧50%,从父框架的顶部到底部34%)。
      5. Size 设置为 {0.46, 0},{0.34, 0},以将提示区域宽度扩展到几乎占据框架的一半(父框架的46%水平和34%垂直)。
      6. Name 设置为 Header。
      7. Image 设置为 rbxassetid://14304828123 以显示梯形。
      8. ImageTransparency 设置为 0.15,以使标题半透明。
    3. 可选
      ImageLabel 中插入一个 UIAspectRatioConstraint,以确保标签的纵横比在任何玩家的屏幕尺寸下保持不变。示例将其 UIAspectRatioConstraint.AspectRatio 属性设置为 13.781

    4. Header 中插入一个 TextLabel 以显示标题。

    5. 选择新标签,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5,以将新标签的原点设置在其中间(从标签的左侧到右侧50%,从标签的顶部到底部50%)。
      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
      3. Position 设置为 {0.5, 0},{0.5, 0},以将标签移动到其父标签的中间(从父标签的左侧到右侧50%,从父标签的顶部到底部50%)。
      4. Size 设置为 {0.62, 0},{0.55, 0},以将文本空间宽度扩展到超过父标签的一半(父标签的62%水平和55%垂直)。
      5. Name 设置为 HeaderTextLabel
      6. FontFace 设置为 Montserrat 以符合未来主义的美学。
      7. Weight 设置为 Medium 以加粗字体。
      8. Text 设置为 目标
      9. 启用 TextScaled
  4. 创建提示元素。

    1. ObjectiveDisplay 中插入一个 ImageLabel

    2. 选择 ImageLabel,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 1,以将标签的原点设置在其底部中间(从标签的左侧到右侧50%,从标签的顶部到底部100%)。
      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
      3. Position 设置为 {0.5, 0},{1, 0},以将标签移动到其父框架的底部中间(从父框架的左侧到右侧50%,从父框架的顶部到底部100%)。
      4. Size 设置为 {0.89, 0},{0.66, 0},以将文本空间宽度扩展到几乎占据父框架的全部宽度(父框架的89%水平和66%垂直)。
      5. Name 设置为 Body
      6. Image 设置为 rbxassetid://14304827265 以显示倒梯形。
      7. ImageColor3 设置为 0, 0, 0 以将图像着色为黑色。
      8. ImageTransparency 设置为 0.3,以使标题半透明。
    3. 可选
      ImageLabel 中插入一个 UIAspectRatioConstraint,以确保标签的纵横比在任何玩家的屏幕尺寸下保持不变。示例将其 UIAspectRatioConstraint.AspectRatio 属性设置为 13.781

    4. Body 中插入一个 TextLabel 以显示提示。

    5. 选择新标签,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5,以将新标签的原点设置在其中间(从标签的左侧到右侧50%,从标签的顶部到底部50%)。
      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
      3. Position 设置为 {0.5, 0},{0.5, 0},以将标签移动到其父标签的中间(从父标签的左侧到右侧50%,从父标签的顶部到底部50%)。
      4. Size 设置为 {0.85, 0},{0.39, 0},以将文本空间宽度扩展到超过父标签的一半(父标签的85%水平和39%垂直)。
      5. Name 设置为 BodyTextLabel
      6. FontFace 设置为 Montserrat 以符合未来主义的美学。
      7. Weight 设置为 Medium 以加粗字体。
      8. TextColor3 设置为 255, 255, 255 以使文本在黑色背景上为白色。
      9. Text 设置为 标记对方玩家以获得积分!第一个达到 %d 分的队伍获胜。
      10. 启用 TextScaled
  5. 为目标的队伍计数器创建一个容器。

    1. Objective 中插入一个 Frame

    2. 选择新 Frame,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 1,以将标签的原点设置在其底部中间(从框架的左侧到右侧50%,从框架的顶部到底部100%)。
      2. BackgroundTransparency 设置为 1,以使框架的背景完全透明。
      3. Position 设置为 {0.5, 0},{1, 0},以将框架设置在容器的底部中间(从父框架的左侧到右侧50%,从父框架的顶部到底部100%)。
      4. Size 设置为 {0.44, 0},{0.27, 0},以使选择UI组件占据容器的左半部分(父框架的44%水平和27%垂直)。
      5. Name 设置为 TeamPointCounter
  6. 为队伍计数器创建填充。

    1. 在步骤5的框架中插入一个 UIListLayout 对象。
    2. 选择 UIListLayout 对象,然后在 属性 窗口中,
      1. Padding 设置为 0.025, 0,以在未来的队伍计数器之间提供空间。
      2. FillDirection 设置为 Horizontal,以便每个队伍计数器并排显示。
      3. HorizontalAlignment 设置为 Center,以便每个队伍计数器彼此对齐到中间。
  7. 创建绿色队伍计数器元素。

    1. TeamPointCounter 中插入一个 ImageLabel

    2. 选择 ImageLabel,然后在 属性 窗口中,

      1. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
      2. Position 设置为 {0.5, 0},{1, 0},以将标签移动到其父框架的底部中间(从父框架的左侧到右侧50%,从父框架的顶部到底部100%)。
      3. Size 设置为 {0.5, 0},{1, 0},以将标签宽度扩展到父框架的一半(父框架的50%水平和100%垂直)。
      4. Name 设置为 TeamACounter
      5. Image 设置为 rbxassetid://14304826831 以显示方向渐变。
      6. ImageColor3 设置为 88, 218, 171 以将图像着色为薄荷绿色。
    3. 配置一个自定义属性以跟踪该标签是针对绿色队伍的。

      1. 属性 窗口中,导航到 属性 部分,然后单击加号图标。弹出对话框显示。
      2. 名称 字段中输入 teamColor
      3. 类型 下拉菜单中选择 BrickColor
      4. 单击 保存 按钮。
      5. 将新的 teamColor 属性设置为 Mint
    4. TeamACounter 中插入一个 TextLabel 以显示提示。

    5. 选择新标签,然后在 属性 窗口中,

      1. AnchorPoint 设置为 1, 0.5,以将新标签的原点设置在其右中间(从标签的左侧到右侧100%,从标签的顶部到底部50%)。
      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
      3. Position 设置为 {0.95, 0},{0.5, 0},以将标签移动到其父标签的右侧(从父标签的左侧到右侧95%,从父标签的顶部到底部50%)。
      4. Size 设置为 {0.85, 0},{0.39, 0},以将文本空间宽度扩展到超过父标签的一半(父标签的85%水平和39%垂直)。
      5. FontFace 设置为 Montserrat 以符合未来主义的美学。
      6. Weight 设置为 Bold 以加粗字体。
      7. TextColor3 设置为 255, 255, 255 以使文本在黑色背景上为白色。
      8. Text 设置为 -
      9. 启用 TextScaled
      10. TextXAlignment 设置为 Right
    6. TextLabel 中插入一个 UIStroke 对象,然后在 属性 窗口中,将 Color 设置为 8, 78, 52 以用深绿色轮廓描边。

  8. 创建粉色队伍计数器元素。

    1. 复制 TeamAICounter 及其子元素。

    2. 选择复制的 TeamACounter,然后在 属性 窗口中,

      1. Name 设置为 TeamBCounter
      2. Image 设置为 rbxassetid://14305849451 以显示相反方向的方向渐变。
      3. ImageColor3 设置为 255, 170, 255 以将图像着色为肉色粉红色。
      4. teamColor 属性设置为 Carnation Pink
    3. 选择复制的 TextLabel 子元素,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0, 0.5,以将新标签的原点设置在其左中间(从标签的左侧到右侧0%,从标签的顶部到底部50%)。
      2. Position 设置为 {0.05, 0},{0.5, 0},以将标签移动到其父标签的左侧(从父标签的左侧到右侧5%,从父标签的顶部到底部50%)。
      3. TextXAlignment 设置为 Left
    4. 选择复制的 UIStroke 子元素,然后在 属性 窗口中,将 Color 设置为 158, 18, 94 以用深粉色轮廓描边。

  9. 参考以下 ReplicatedStorage 脚本,这些脚本在示例 激光标签 场所文件中以编程方式更新目标提示并跟踪队伍积分。

以下脚本需要一组模块脚本,这些脚本共同工作以设置主要的抬头显示(HUD),包括 setObjectivestartSyncingTeamPoints。在玩家加入回合并选择其发射器后,此脚本确保所有HUD UI元素根据玩家的状态、设备和队伍状态适当地显示。

local Players = game:GetService("Players")
local setPlayerPortrait = require(script.setPlayerPortrait)
local setPlayerName = require(script.setPlayerName)
local startSyncingTeamColor = require(script.startSyncingTeamColor)
local setObjective = require(script.setObjective)
local setupTouchButtonAsync = require(script.setupTouchButtonAsync)
local startSyncingTeamPoints = require(script.startSyncingTeamPoints)
local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled)
local setupHitmarker = require(script.setupHitmarker)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("HUDGui")
setPlayerPortrait(gui)
setPlayerName(gui)
startSyncingTeamColor(gui)
setObjective(gui)
startSyncingTeamPoints(gui)
disableMouseWhileGuiEnabled(gui)
setupHitmarker(gui)
setupTouchButtonAsync(gui)

发射器UI

遵循 线框您的布局 中的视觉层次最佳实践,本节教您如何实现与玩家发射器相关的所有屏幕UI元素。这个UI元素分组占据了屏幕中心附近的大部分屏幕空间,因为它作为焦点吸引玩家的注意力,参与3D空间中的动作,并且在游戏中具有最重要的意义。

十字准星

十字准星是一个UI元素,告知玩家他们在发射武器时将要产生影响的位置。这个UI元素是第一人称射击游戏的一个重要游戏要求,因为玩家需要能够准确瞄准他们的发射器并标记敌队成员。

与大多数其他第一人称射击游戏一样,示例激光标签游戏将十字准星放置在屏幕的中心,以便玩家在其化身在3D空间中移动时有一个静态的焦点。除了减少晕动症,这种放置方式还允许十字准星在整体环境中可感知,同时又能融入其中。

要在示例 激光标签 游戏中准确重建十字准星:

  1. HUDGui ScreenGui 对象中插入一个 ImageLabel

    1. 资源管理器 窗口中,导航到 StarterGui 服务。

    2. 将鼠标悬停在其子 HUDGui 对象上,然后单击 图标。上下文菜单显示。

    3. 从上下文菜单中插入一个 ImageLabel

  2. 选择新 ImageLabel,然后在 属性 窗口中,

    1. Image 设置为 rbxassetid://14400935446
    2. AnchorPoint 设置为 0.5, 0.5,以将标签的原点设置在标签的中间(从标签的左侧到右侧50%,从标签的顶部到底部50%)。
    3. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
    4. Name 设置为 Crosshair
    5. Position 设置为 {0.5,0},{0.5,0},以将标签设置在屏幕的中间。
    6. ScaleType 设置为 Fit,以便图像适合其容器,并且在各种屏幕尺寸上不会拉伸。
  3. 可选
    Crosshair 中插入一个 UIAspectRatioConstraint,以确保标签的纵横比在任何玩家的屏幕尺寸下保持不变。示例将其 UIAspectRatioConstraint.AspectRatio 属性设置为 0.895

命中标记

命中标记是一个UI元素,仅在发射与敌队的另一名玩家产生影响时显示。与十字准星一样,这个UI元素是第一人称射击游戏的一个重要游戏要求,因为它提供了玩家成功标记对手时的视觉反馈。

要在示例 激光标签 游戏中准确重建命中标记:

  1. Crosshair ImageLabel 对象中插入一个 ImageLabel

    1. 资源管理器 窗口中,导航到 StarterGui 服务。

    2. 将鼠标悬停在其子 Crosshair 对象上,然后单击 图标。上下文菜单显示。

    3. 从上下文菜单中插入一个 ImageLabel

  2. 选择新 ImageLabel,然后在 属性 窗口中,

    1. Image 设置为 rbxassetid://14401148736 以显示矩形命中标记图标。
    2. AnchorPoint 设置为 0.5, 0.5,以将标签的原点设置在标签的中间。
    3. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
    4. Position 设置为 {0.5,0},{0.5,0},以将标签设置在屏幕的中间。
    5. Name 设置为 Hitmarker
    6. Size 设置为 {0.6, 0},{0.06, 0},以减少十字准星中间矩形的大小。
    7. ImageTransparency 设置为 1,以使命中标记完全透明。以下步骤中的脚本在玩家的发射与敌队的另一名玩家产生影响时将透明度恢复为0。
  3. 参考以下 ReplicatedStorage 脚本,这些脚本在示例 激光标签 场所文件中以编程方式显示命中标记,当发射与敌队的玩家产生影响时。

以下脚本需要一组模块脚本,这些脚本共同工作以设置主要的抬头显示(HUD),包括 setupHitmarker。在玩家加入回合并选择其发射器后,此脚本确保所有HUD UI元素根据玩家的状态、设备和队伍状态适当地显示。

local Players = game:GetService("Players")
local setPlayerPortrait = require(script.setPlayerPortrait)
local setPlayerName = require(script.setPlayerName)
local startSyncingTeamColor = require(script.startSyncingTeamColor)
local setObjective = require(script.setObjective)
local setupTouchButtonAsync = require(script.setupTouchButtonAsync)
local startSyncingTeamPoints = require(script.startSyncingTeamPoints)
local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled)
local setupHitmarker = require(script.setupHitmarker)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("HUDGui")
setPlayerPortrait(gui)
setPlayerName(gui)
startSyncingTeamColor(gui)
setObjective(gui)
startSyncingTeamPoints(gui)
disableMouseWhileGuiEnabled(gui)
setupHitmarker(gui)
setupTouchButtonAsync(gui)

发射器选择器

发射器选择器是一个UI组件,玩家在加入或重新加入回合之前使用它来选择其发射器类型。示例激光标签游戏提供两种类型的发射器:一种产生多个光束,具有宽的水平扩散,另一种产生单个光束。玩家选择的发射器类型会影响他们在回合中的策略,使这个UI组件成为整体游戏的重要工作流程。

以下步骤详细说明如何为不同的UI元素分组创建多个容器、带有提示的标题、导航和选择按钮,以及发射器按钮预制件。整体组件的脚本逻辑根据表示每种发射器类型的 Configuration 实例填充不同的视觉特征到发射器按钮预制件中。

这种设置允许您为更多的发射器类型创建额外的 Configuration 实例,这些实例会自动在发射器选择器中正确显示,而无需在 StarterGui.PickABlasterGui 中创建单独的按钮。

要在示例 激光标签 游戏中准确重建发射器选择器:

  1. 为整个组件创建一个容器。

    1. PickABlaster ScreenGui 对象中插入一个 Frame

      1. 资源管理器 窗口中,导航到 StarterGui 服务。
      2. 将鼠标悬停在其子 PickABlaster 对象上,然后单击 图标。上下文菜单显示。
      3. 从上下文菜单中插入一个 Frame
    2. 选择新框架,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 1,以将框架的原点设置在其底部中间(从框架的左侧到右侧50%,从框架的顶部到底部100%)。
      2. BackgroundTransparency 设置为 1,以使框架的背景完全透明。
      3. Position 设置为 {0.5, 0},{0.9, 0},以将框架设置在屏幕的底部中间附近(从屏幕的左侧到右侧50%,从屏幕的顶部到底部92.4%)。
      4. Size 设置为 {0.8, 0},{0.25, 0},以使发射器选择器的UI组件占据屏幕的大部分区域,以吸引玩家的注意(水平80%,垂直25%)。
      5. Name 设置为 Component
    3. 可选
      Component 中插入一个 UIAspectRatioConstraint,以确保框架及其子UI元素的纵横比在任何玩家的屏幕尺寸下保持不变。示例将其 UIAspectRatioConstraint.AspectRatio 属性设置为 5

  2. 创建一个容器以容纳UI元素分组。

    1. Component 中插入一个 Frame

    2. 选择新框架,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5,以将框架的原点设置在其中间(从框架的左侧到右侧50%,从框架的顶部到底部50%)。
      2. BackgroundTransparency 设置为 1,以使框架的背景完全透明。
      3. Position 设置为 {0.5, 0},{0.375, 0},以将框架设置在容器的顶部中间附近(从父框架的左侧到右侧50%,从父框架的顶部到底部37.5%)。
      4. Size 设置为 {1, 0},{0.75, 0},以使选择UI组件占据容器的3/4(父框架的100%水平和75%垂直)。
      5. Name 设置为 SelectionFrame
  3. 为发射器选择器创建提示。

    1. SelectionFrame 中插入一个 ImageLabel

    2. 选择新标签,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 1,以将标签的原点设置在其底部中间(从标签的左侧到右侧50%,从标签的顶部到底部100%)。
      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
      3. LayoutOrder 设置为 -1
      4. Position 设置为 {0.5, 0},{0.22, 0},以将标签设置在框架的顶部上中间附近(从父框架的左侧到右侧50%,从父框架的顶部到底部22%)。
      5. Size 设置为 {0.45, 0},{0.22, 0},以将提示区域宽度扩展到几乎占据框架的一半(父框架的45%水平和22%垂直)。
      6. Name 设置为 Header
      7. Image 设置为 rbxassetid://14304828123 以显示梯形。
      8. ImageTransparency 设置为 0.15,以使标题半透明。
    3. 可选
      在标签中插入一个 UIAspectRatioConstraint,以确保标签的纵横比在任何玩家的屏幕尺寸下保持不变。示例将其 UIAspectRatioConstraint.AspectRatio 属性设置为 13.78

    4. Header 中插入一个 TextLabel 以显示提示。

    5. 选择新标签,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5,以将新标签的原点设置在其中间(从标签的左侧到右侧50%,从标签的顶部到底部50%)。
      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
      3. Position 设置为 {0.5, 0},{0.5, 0},以将标签移动到其父标签的中间(从父标签的左侧到右侧50%,从父标签的顶部到底部50%)。
      4. Size 设置为 {0.6, 0},{0.55, 0},以将文本空间宽度扩展到超过父标签的一半(父标签的60%水平和55%垂直)。
      5. Name 设置为 HeaderTextLabel
      6. FontFace 设置为 Montserrat 以符合未来主义的美学。
      7. Weight 设置为 Medium 以加粗字体。
      8. Text 设置为 选择一个发射器
      9. 启用 TextScaled
  4. 创建用于发射器按钮容器和选择箭头的容器。

    1. SelectionFrame 中插入一个 ImageLabel

    2. 选择新标签,然后在 属性 窗口中,

      1. 删除默认 Image 值。
      2. AnchorPoint 设置为 0.5, 1,以将标签的原点设置在其底部中间(从标签的左侧到右侧50%,从标签的顶部到底部100%)。
      3. BackgroundColor 设置为 0, 0, 0,以使标签为黑色。
      4. BackgroundTransparency 设置为 0.3,以将标签的不透明度降低30%,并与游戏中的所有黑色UI元素匹配。
      5. Position 设置为 {0.5, 0},{1, 0},以将标签设置在框架的底部中间(从父框架的左侧到右侧50%,从父框架的顶部到底部100%)。
      6. Size 设置为 {1, 0},{0.77, 0},以将标签区域宽度扩展到提示下方的空间(父框架的100%水平和77%垂直)。
    3. 圆角容器的角。

      1. 在标签中插入一个 UICorner 对象。
      2. 选择新角对象,然后在 属性 窗口中,将 CornerRadius 设置为 0.075, 0 以圆角。
  5. 创建用于发射器按钮的容器。

    1. 在步骤4的标签中插入一个 Frame

    2. 选择新框架,然后在 属性 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5,以将新框架的原点设置在其中间(从框架的左侧到右侧50%,从框架的顶部到底部50%)。
      2. BackgroundTransparency 设置为 1,以使框架的背景完全透明。
      3. Position 设置为 {0.5, 0},{0.5, 0},以将框架设置在其父标签的中间(从父标签的左侧到右侧50%,从父标签的顶部到底部50%)。
      4. Size 设置为 {0.85, 0},{0.77, 0},以将框架区域宽度扩展到大部分标签(父标签的85%水平和77%垂直)。
      5. Name 设置为 Container
  6. 为所有未来的发射器按钮创建填充。

    1. 在步骤5的框架中插入一个 UIListLayout 对象。
    2. 选择新布局对象,然后在 属性 窗口中,
      1. Padding 设置为 0.035, 0,以在所有未来按钮之间提供空间。
      2. FillDirection 设置为 Horizontal,以便每个按钮并排显示。
      3. HorizontalAlignmentVerticalAlignment 都设置为 Center,以便每个按钮彼此对齐到中间。
  7. 创建左侧导航按钮。

    1. 在步骤4的 ImageLabel 中插入一个 ImageButton 对象。

    2. 选择新按钮,然后在 属性 窗口中,

      1. 删除默认 Image 值。
      2. AnchorPoint 设置为 0, 0.5,以将新按钮的原点设置在其左中间(从按钮的左侧到右侧0%,从按钮的顶部到底部50%)。
      3. BackgroundTransparency 设置为 0.15,以在悬停时提供视觉反馈,表明按钮是可选择的。
      4. Position 设置为 {0.02, 0},{0.5, 0},以在按钮的左侧提供填充(从父标签的左侧到右侧2%,从父标签的顶部到底部50%)。1. 将 Size 设置为 {0.04, 0},{0.33, 0} 使选择按钮比发射器按钮小得多(在父框架的水平和垂直方向上分别为 4% 和 33%)。
  8. Name 设置为 NavigationButtonLeft

  9. 圆角按钮。

    1. 在按钮中插入一个 UICorner 对象。
    2. 选择新的角对象,然后在 Properties 窗口中,将 CornerRadius 设置为 0.1, 0 以圆化角。
  10. 在按钮中插入一个 ImageLabel 对象。

  11. 选择新的标签,然后在 Properties 窗口中,

    1. AnchorPoint 设置为 0.5, 0.5 以将新标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。
    2. Position 设置为 {0.45, 0},{0.5, 0} 以将标签设置在其父按钮的中间附近(从父按钮的左侧到右侧 45%,从父按钮的顶部到底部 50%)。这个值不在中间,因为箭头在 {0.5, 0},{0.5, 0} 的按钮中 视觉上 看起来并不在中间。
    3. Size 设置为 {0.8, 0},{0.8, 0} 以扩展标签区域以适应提示下方的空间(在父框架的水平和垂直方向上分别为 80%)。
    4. BackgroundTransparency 设置为 1 以使图像的背景完全透明。
    5. Image 设置为 rbxassetid://14309187238
    6. ScaleType 设置为 Fit
  12. 创建右侧导航按钮。

    1. 复制 NavigationButtonLeft

    2. 选择复制的按钮,然后在 Properties 窗口中,

      1. AnchorPoint 设置为 1, 0.5 以将新按钮的原点设置在其右中间(从按钮的左侧到右侧 100%,从按钮的顶部到底部 50%)。
      2. Position 设置为 {0.98, 0},{0.5, 0} 以在按钮的父容器右侧提供填充(从父标签的左侧到右侧 98%,从父标签的顶部到底部 50%)。
      3. Name 设置为 NavigationButtonRight
    3. 选择其 ImageLabel 子对象。

      1. Rotation 设置为 180 以翻转图像。
      2. Position 设置为 {0.55, 0},{0.5, 0} 以将标签设置在其父按钮的中间附近(从父按钮的左侧到右侧 55%,从父按钮的顶部到底部 50%)。这个值不在中间,因为箭头在 {0.5, 0},{0.5, 0} 的按钮中 视觉上 看起来并不在中间。
  13. 创建 SELECT 按钮。

    1. Component 中插入一个 ImageButton。请注意,这个过程使选择按钮与 SelectionFrame 分开,以便您可以在组件的主要部分与选择按钮之间添加填充。

    2. 选择新按钮,然后在 Properties 窗口中,

      1. 删除默认的 Image 值。
      2. AnchorPoint 设置为 0.5, 1 以将新按钮的原点设置在其底部中间(从按钮的左侧到右侧 50%,从按钮的顶部到底部 100%)。
      3. BackgroundTransparency 设置为 0.15 以在悬停时提供视觉反馈,表明按钮是可选择的。
      4. Position 设置为 {0.5, 0},{0.99, 0} 以将按钮设置在其容器的底部中间附近(从父框架的左侧到右侧 50%,从父框架的顶部到底部 99%)。
      5. Size 设置为 {0.17, 0},{0.18, 0} 以使按钮在发射器按钮下方延长(在父框架的水平和垂直方向上分别为 17% 和 18%)。
      6. Name 设置为 SelectButton
    3. 圆角按钮。

      1. 在按钮中插入一个 UICorner 对象。
      2. 选择新的角对象,然后在 Properties 窗口中,将 CornerRadius 设置为 0.2, 0 以圆化角。
    4. 在按钮中插入一个 TextLabel 对象,以便您可以显示行动呼吁。

    5. 选择新的标签,然后在 Properties 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5 以将新标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。
      2. BackgroundTransparency 设置为 1 以使标签的背景完全透明。
      3. Position 设置为 {0.5, 0},{0.5, 0} 以将标签移动到按钮的中间(从父按钮的左侧到右侧 50%,从父按钮的顶部到底部 50%)。
      4. Size 设置为 {0.9, 0},{0.55, 0} 以将文本空间扩展到几乎占据父标签的全部宽度(在父标签的水平和垂直方向上分别为 90% 和 55%)。
      5. Name 设置为 SelectTextLabel
      6. FontFace 设置为 Montserrat 以符合未来主义的美学。
      7. Weight 设置为 Medium 以加粗字体。
      8. Text 设置为 SELECT
      9. 启用 TextScaled
  14. 创建一个发射器按钮预制件。

    1. ReplicatedStorage 服务中,创建一个文件夹结构以组织您的 UI 对象。示例使用一个 Instances 文件夹和一个子 Guis 文件夹。
    2. Guis 文件夹中插入一个 ImageButton 对象。
    3. 选择新按钮,然后在 Properties 窗口中,
      1. 删除默认的 Image 值。
      2. AnchorPoint 设置为 0.5, 0.5 以将新按钮的原点设置在其中心(从按钮的左侧到右侧 50%,从按钮的顶部到底部 50%)。
      3. BackgroundTransparency 设置为 0.65 以提供视觉反馈,表明按钮不在焦点中。第 12 步中的脚本在按钮处于焦点时提供程序化视觉反馈。
      4. LayoutOrder 设置为 2
      5. Name 设置为 BlasterButtonPrefab
      6. Size 设置为 {0.8, 0},{0.8, 0}
      7. ImageTransparency 设置为 1 以使图像完全透明。
    4. BlasterButtonPrefab 中插入一个 UIAspectRatioConstraint 以确保按钮的纵横比在组件中保持不变,无论玩家的屏幕大小如何。
    5. 圆角按钮。
      1. BlasterButtonPrefab 中插入一个 UICorner 对象。
      2. 选择 UICorner,然后在 Properties 窗口中,将 CornerRadius 设置为 0.05, 0 以圆化角。
    6. BlasterButtonPrefab 中插入一个 ImageLabel
    7. 选择新的标签,然后在 Properties 窗口中,
      1. 删除 Image 属性中的占位符资产 ID。第 7 步中的脚本会以编程方式将玩家的头像插入图像标签中。
      2. AnchorPoint 设置为 0.5, 0.5 以将新标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。
      3. BackgroundTransparency 设置为 1 以使标签的背景完全透明。
      4. Position 设置为 {0.52, 0},{0.497, 0} 以将标签设置在其父按钮的中间附近(从父按钮的左侧到右侧 52%,从父按钮的顶部到底部 49.7%)。这个值不在中间,因为发射器在 {0.5, 0},{0.5, 0} 的按钮中 视觉上 看起来并不在中间。
      5. Size 设置为 {1.20, 0},{0.9, 0} 以扩展标签区域超出按钮(在父按钮的水平和垂直方向上分别为 120% 和 90%)。
      6. ScaleType 设置为 Fit
  15. 参考以下 ReplicatedStorage 脚本,在示例 Laser Tag 场所文件中以编程方式显示每个发射器的按钮,当玩家选择一个不在焦点中的按钮时缩放按钮,并将玩家的发射器选择附加到他们的头像。

以下脚本需要一组协同工作的模块脚本来创建发射器选择器。当玩家加入游戏或在健康值降至零后重新生成回合时,此脚本激活所有发射器选择器的 UI 元素,直到玩家做出选择。

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local GuiAttribute = require(ReplicatedStorage.GuiAttribute)
local setupBlasterButtons = require(script.setupBlasterButtons)
local connectResetSelectionOnEnabled = require(script.connectResetSelectionOnEnabled)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("PickABlasterGui")
setupBlasterButtons(gui)
connectResetSelectionOnEnabled(gui)
gui:SetAttribute(GuiAttribute.selectedIndex, 1)

爆炸按钮

爆炸按钮是一个 UI 组件,玩家用它来在通过移动设备或平板电脑访问游戏时发射他们的发射器。示例激光标签游戏使用一个带有十字准星和爆炸图标的发射按钮,以在没有文本的情况下传达按钮的功能。

要在示例 Laser Tag 游戏中准确重建爆炸按钮:

  1. HUDGui ScreenGui 对象中插入一个 ImageButton

    1. Explorer 窗口中,导航到 StarterGui 服务。

    2. 将鼠标悬停在其子 HUDGui 对象上,然后单击 图标。上下文菜单显示。

    3. 从上下文菜单中插入一个 ImageButton

  2. 在视口中,将按钮移动到玩家的拇指自然休息的位置,以便您可以直观地感知按钮在玩家设备上的外观,然后在 Properties 窗口中,

    1. Image 设置为 rbxassetid://18308375035 以显示爆炸按钮图标。
    2. PressedImage 设置为 rbxassetid://18308372558 以在玩家按下按钮时显示爆炸按钮图标的反转版本。
    3. BackgroundTransparency 设置为 1 以使标签的背景完全透明。
    4. Name 设置为 BlastButton
    5. ScaleType 设置为 Fit 以使图像适合其容器,并且在各种屏幕尺寸上不会拉伸。
    6. ImageTransparency 设置为 0.3 以降低标签的透明度,使其与游戏中的所有黑色 UI 元素匹配。
  3. BlastButton 中插入一个 UIAspectRatioConstraint 以确保按钮的纵横比在玩家的屏幕大小下保持不变。

  4. 参考以下 ReplicatedStorage 脚本,在示例 Laser Tag 场所文件中以编程方式显示发射按钮,当玩家在接受触控的设备上使用触控输入时。

以下脚本需要一组模块脚本协同工作,以设置主要的抬头显示 (HUD),包括 setupTouchButtonAsync。在玩家加入回合并选择他们的发射器后,此脚本确保所有 HUD UI 元素根据玩家的状态、设备和团队状态适当地显示。

local Players = game:GetService("Players")
local setPlayerPortrait = require(script.setPlayerPortrait)
local setPlayerName = require(script.setPlayerName)
local startSyncingTeamColor = require(script.startSyncingTeamColor)
local setObjective = require(script.setObjective)
local setupTouchButtonAsync = require(script.setupTouchButtonAsync)
local startSyncingTeamPoints = require(script.startSyncingTeamPoints)
local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled)
local setupHitmarker = require(script.setupHitmarker)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("HUDGui")
setPlayerPortrait(gui)
setPlayerName(gui)
startSyncingTeamColor(gui)
setObjective(gui)
startSyncingTeamPoints(gui)
disableMouseWhileGuiEnabled(gui)
setupHitmarker(gui)
setupTouchButtonAsync(gui)

玩家 UI

遵循 Wireframe Your Layouts 中的视觉层次最佳实践,本节教您如何实现与玩家状态相关的所有屏幕 UI 元素。这组 UI 元素位于屏幕的两侧,因为玩家可以在不分散注意力于游戏玩法的情况下理解这些外围信息。

玩家指示器

玩家指示器是一个 UI 组件,玩家可以参考它以快速判断他们属于哪个团队,尤其是在他们生成到团队的生成区时。示例激光标签游戏提供了两个版本的玩家指示器,具体取决于玩家是 绿色 还是 粉色 团队。

绿色团队
粉色团队

遵循 Select a Color Theme 的指导,两种版本的玩家指示器将团队颜色与独特、简单的图标结合在一起,细节最少,以便在小屏幕上保持可读性。提供两种形式的视觉反馈很重要,因为它有助于保持设计对色盲玩家的可访问性。

要在示例 Laser Tag 游戏中准确重建玩家指示器组件:

  1. HUDGui ScreenGui 对象中插入一个 Frame

    1. Explorer 窗口中,导航到 StarterGui 服务。
    2. 将鼠标悬停在其子 HUDGui 对象上,然后单击 ⊕ 图标。上下文菜单显示。
    3. 从上下文菜单中插入一个 Frame
  2. 选择新的 Frame,然后在 Properties 窗口中,

    1. AnchorPoint 设置为 0, 1 以将框架的原点设置在其底部中间(从框架的左侧到右侧 0%,从框架的顶部到底部 100%)。

    2. BackgroundTransparency 设置为 1 以使标签的背景完全透明。

    3. Name 设置为 PlayerDisplay

    4. Position 设置为 {0.02, 0},{0.97, 0} 以将框架设置在屏幕的左下角附近。

    5. Size 设置为 {0.23, 0},{0.08, 0} 以缩短和加宽框架。

    6. 启用 ClipsDescendants 以修剪超出框架的子 GuiObjects。

  3. 创建多边形形状。

    1. PlayerDisplay 中插入一个 ImageLabel

    2. 选择新的标签,然后在 Properties 窗口中,

      1. Image 设置为 rbxassetid://14304828123 以显示梯形图标。
      2. AnchorPoint 设置为 1, 1 以将标签的原点设置在其底部右侧(从标签的左侧到右侧 100%,从标签的顶部到底部 100%)。
      3. BackgroundTransparency 设置为 1 以使标签的背景完全透明。
      4. Name 设置为 Block
      5. Position 设置为 {1,0},{1,0} 以将标签设置在框架的右侧。
      6. Size 设置为 {1.858, 0},{0.581, 0} 以将标签加宽超出框架,并缩短到框架长度的一半多一点。
      7. ImageTransparency 设置为 0.15 以使标签稍微透明。
      8. ScaleType 设置为 Fit 以使图像适合其容器,并且在各种屏幕尺寸上不会拉伸。
      1. Block 中插入一个 UIAspectRatioConstraint 以确保标签及其子 UI 元素的纵横比在玩家的屏幕大小下保持不变。
      2. 选择新的约束,然后在 Properties 窗口中,将 AspectRatio 设置为 13.78
  4. 创建玩家头像的框。

    1. PlayerDisplay 中插入一个 ImageLabel

    2. 选择新的标签,然后在 Properties 窗口中,

      1. 删除 Image 属性中的占位符资产 ID。第 7 步中的脚本会以编程方式将玩家的头像插入图像标签中。
      2. AnchorPoint 设置为 0, 1 以将标签的原点设置在其左下角(从标签的左侧到右侧 0%,从标签的顶部到底部 100%)。
      3. BackgroundColor3 设置为 0, 0, 0 以将标签的背景颜色设置为黑色。
      4. BackgroundTransparency 设置为 0.3 以将标签的透明度降低 30%,并与游戏中的所有黑色 UI 元素匹配。
      5. Name 设置为 PlayerPortrait
      6. Position 设置为 {0.11, 0},{1, 0} 以将标签设置在多边形形状的左侧。
      7. Size 设置为 {0.23, 0},{1, 0} 以缩小标签。
      8. ImageTransparency 设置为 0.15 以使标签稍微透明。
      9. ScaleType 设置为 Fit 以使图像适合其容器,并且在各种屏幕尺寸上不会拉伸。
      1. PlayerPortrait 中插入一个 UIAspectRatioConstraint 以确保标签及其子 UI 元素的纵横比在玩家的屏幕大小下保持不变。
      2. PlayerPortrait 中插入一个 UICorner,然后在 Properties 窗口中,将 CornerRadius 设置为 0.05, 0 以稍微圆化角。
  5. 创建玩家名称的文本标签。

    1. PlayerDisplay 中插入一个 TextLabel 对象。

    2. 选择新的标签,然后在 Properties 窗口中,

      1. AnchorPoint 设置为 0, 0.5 以将新按钮的原点设置在其左中间(从按钮的左侧到右侧 0%,从按钮的顶部到底部 50%)。
      2. BackgroundTransparency 设置为 1 以使标签的背景完全透明。
      3. Name 设置为 PlayerNameTextLabel
      4. Position 设置为 {0.35, 0},{0.72, 0} 以将标签设置在其容器的右侧(从父标签的左侧到右侧 35%,从父标签的顶部到底部 72%)。
      5. Size 设置为 {0.52, 0},{0.3, 0} 以便文本可以占据多边形形状区域的大部分(在父框架的水平和垂直方向上分别为 52% 和 30%)。
      6. FontFace 设置为 Montserrat 以符合未来主义的美学。
      7. Weight 设置为 Bold 以加粗字体。
      8. 删除 Text 属性中的占位符文本。第 7 步中的脚本会以编程方式将玩家的名称插入文本标签中。
      9. 启用 TextScaled
      10. TextXAlignment 设置为 Left
  6. 创建显示在玩家头像左侧的团队图标和颜色。

    1. PlayerDisplay 中插入一个 Folder,然后将其重命名为 TeamIcons

    2. 创建 绿色 团队图标和颜色。

      1. TeamIcons 中插入一个 ImageLabel
      2. 选择新的标签,然后在 Properties 窗口中,
        1. AnchorPoint 设置为 0, 1 以将标签的原点设置在其左下角(从标签的左侧到右侧 0%,从标签的顶部到底部 100%)。
        2. BackgroundColor3 设置为 88, 218, 171 以将标签的背景颜色设置为薄荷绿色。
        3. Name 设置为 TeamAIcon
        4. Position 设置为 {0, 0},{1, 0} 以将标签设置在框架的左侧。
        5. Size 设置为 {0.135, 0},{0.58, 0} 以将标签缩小到玩家头像的左侧。
        6. ImageTransparency 设置为 1 以使标签透明。
      1. 配置一个自定义属性以跟踪此标签是用于绿色团队的。此步骤对于第 7 步中的脚本非常重要。
        1. Properties 窗口中,导航到 Attributes 部分,然后单击加号图标。弹出对话框显示。
        2. Name 字段中,输入 teamColor
        3. Type 下拉菜单中,选择 BrickColor
        4. 单击 Save 按钮。
        5. 将新的 teamColor 属性设置为 Mint
      2. TeamAIcon 中插入一个 UIAspectRatioConstraint 以确保标签及其子 UI 元素的纵横比在玩家的屏幕大小下保持不变。
      3. 创建图标。
        1. TeamAIcon 中插入一个 ImageLabel
        2. 选择新的标签,然后在 Properties 窗口中,
          1. Image 设置为 rbxassetid://14309678670 以显示绿色团队图标。
          2. AnchorPoint 设置为 0.5, 0.5 以将标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。
          3. BackgroundTransparency 设置为 1 以使标签的背景完全透明。
          4. Name 设置为 Icon
          5. Position 设置为 {0.5, 0},{0.5, 0} 以将标签设置在其父标签的中间。
          6. Size 设置为 {0.7, 0},{0.6, 0} 以缩小标签。
          7. ScaleType 设置为 Fit 以使图像适合其容器,并且在各种屏幕尺寸上不会拉伸。
    3. 创建 粉色 团队图标和颜色。

      1. 复制 TeamAIcon 及其子对象。
      2. 选择复制的 TeamAIcon,然后在 Properties 窗口中,
        1. BackgroundColor3 设置为 255, 170, 255 以将标签的背景颜色设置为肉色粉。
        2. Name 设置为 TeamBIcon
        3. teamColor 属性设置为 Carnation Pink
        4. 选择 TeamBIcon 的复制的 Icon 子对象,然后在 Properties 窗口中,将 Image 设置为 rbxassetid://14309678549 以显示粉色团队图标。
  7. 参考以下 ReplicatedStorage 脚本,在示例 Laser Tag 场所文件中以编程方式显示玩家指示器,使用适当的团队颜色和图标,同时玩家在回合中处于活动状态。

以下脚本需要一组模块脚本协同工作,以设置主要的抬头显示 (HUD),包括 startSyncingTeamColorsetPlayerNamesetPlayerPortrait。在玩家加入回合并选择他们的发射器后,此脚本确保所有 HUD UI 元素根据玩家的状态、设备和团队状态适当地显示。

local Players = game:GetService("Players")
local setPlayerPortrait = require(script.setPlayerPortrait)
local setPlayerName = require(script.setPlayerName)
local startSyncingTeamColor = require(script.startSyncingTeamColor)
local setObjective = require(script.setObjective)
local setupTouchButtonAsync = require(script.setupTouchButtonAsync)
local startSyncingTeamPoints = require(script.startSyncingTeamPoints)
local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled)
local setupHitmarker = require(script.setupHitmarker)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("HUDGui")
setPlayerPortrait(gui)
setPlayerName(gui)
startSyncingTeamColor(gui)
setObjective(gui)
startSyncingTeamPoints(gui)
disableMouseWhileGuiEnabled(gui)
setupHitmarker(gui)

力场屏幕

力场屏幕是一个 UI 元素,覆盖在视口上,以通知玩家他们在加入或重新加入回合时免受敌方团队火力的影响。遵循 Choose an Art Style 中的图标美学指南,示例激光标签游戏利用半透明的六边形图案来象征力场。这个设计决策不仅强化了游戏中所有 UI 的未来主义艺术风格,而且还在没有任何文本或额外指导的情况下传达了玩家的状态。

要在示例 Laser Tag 游戏中准确重建力场屏幕:

  1. ForceFieldGui ScreenGui 对象中插入一个 ImageLabel

    1. Explorer 窗口中,导航到 StarterGui 服务。

    2. 将鼠标悬停在其子 ForceFieldGui 对象上,然后单击 图标。上下文菜单显示。

    3. 从上下文菜单中插入一个 ImageLabel

  2. 选择新的标签,然后在 Properties 窗口中,

    1. Image 设置为 rbxassetid://14462567888

    2. BackgroundTransparency 设置为 0.8 以使力场半透明。

    3. Size 设置为 {1, 0},{1, 0} 以使图像填充整个屏幕(在父 ScreenGui 的水平和垂直方向上均为 100%)。

    4. ScaleType 设置为 Tile 以使六边形在整个屏幕上平铺。

    5. TileSize 设置为 {0, 104},{0, 180}

  3. 在标签中插入一个 UIGradient 对象。

  4. 选择新的渐变对象,然后在 Properties 窗口中,

    1. Color 设置为一个颜色序列,从蓝色开始,变为白色,然后再变为蓝色。

      1. Color 设置为 120, 192, 250 以将浅蓝色应用于所有六边形。

      2. 单击 Color 属性,然后单击 按钮。颜色序列弹出窗口显示。

        颜色序列底部轴上的每个三角形都是一个关键点,确定该属性在图像的左侧到右侧的颜色值。

      3. 单击并拖动颜色序列,直到达到 Time 值为 0.05,然后单击 Color 旁边的小方块以打开 Colors 弹出窗口。

      4. 选择明亮的白色,然后关闭弹出窗口。

      5. 单击并拖动颜色序列,直到达到 Time 值为 0.95,然后再次打开 Colors 弹出窗口,选择之前相同的白色。

    2. Rotation 设置为 225 以使颜色序列的蓝色部分显示在左上角和右下角。

    3. Transparency 设置为一个数字序列,使力场看起来像在闪烁。

      1. 单击 Transparency 属性,然后单击 按钮。数字序列弹出窗口显示。数字序列开头和结尾的每个方块都是一个关键点,确定该属性在图像的左侧到右侧的透明度值。

      2. 在数字序列中设置以下时间和值属性:

      • Time = 0Value = 0.25
      • Time = .101Value = 0.875
      • Time = .183Value = 0
      • Time = .3Value = 1
      • Time = .7Value = 1
      • Time = 1Value = 0.9
  5. 复制第 2 步中的 ImageLabel

  6. 选择复制标签中的 UIGradient 对象,然后在 Properties 窗口中,

    1. Rotation 设置为 -45 以翻转图像,使其几乎在 Y 轴上相互镜像。

    2. 修改 Transparency 以使闪烁看起来更自然。

      1. 单击 Transparency 属性,然后单击 按钮。数字序列弹出窗口显示。
      2. 选择第三个关键帧,然后单击 Delete 按钮。
  7. 参考以下 ReplicatedStorage 脚本,在示例 Laser Tag 场所文件中以编程方式显示力场屏幕,当玩家加入或重新加入回合时。

以下 ReplicatedStorage.ForceFieldClientVisuals 客户端脚本用 StarterGui.ForceFieldGui 替代默认的 ForceField 视觉效果。当玩家加载到游戏中并在 SpawnLocation 上生成,且其 Duration 属性大于 0 时,所有游戏的默认行为是为他们的头像提供一个保护性的蓝色光球,暂时防止他们失去生命值。

该脚本首先监听 ForceField 被添加到角色时的事件,禁用默认的第一人称力场视觉效果,然后启用 ForceFieldGui ScreenGui 对象。请注意,这并不会影响玩家在观察其他玩家重新生成时的第三人称视觉效果。

第一人称力场视觉效果包括屏幕周围的未来派六边形网格。
第一人称力场视觉效果
第三人称力场视觉效果包括在玩家生成到游戏时周围的蓝色闪烁光球。
第三人称力场视觉效果
local Players = game:GetService("Players")
local localPlayer = Players.LocalPlayer
local function onCharacterAddedAsync(character: Model)
local forceField = character:WaitForChild("ForceField", 3)
if not forceField then
-- 如果玩家在禁用 ForceField 的生成点生成
return
end
forceField.Visible = false
localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = true
forceField.Destroying:Wait()
localPlayer.PlayerGui.ForceFieldGui.Enabled = false
end
if localPlayer.Character then
onCharacterAddedAsync(localPlayer.Character)
end
localPlayer.CharacterAdded:Connect(onCharacterAddedAsync)

重生屏幕

重生屏幕是一个 UI 元素,它使视口变暗,以通知玩家他们已被标记出局,并且服务器正在将他们重新生成回他们的生成区域。这个 UI 元素很重要,因为它给玩家时间处理他们已被标记出局的事实,并在重新加入活动回合之前策划他们的下一步行动。

有关示例激光标记游戏中自定义重生行为的更多信息,请参见 Respawn Characters 来自游戏玩法脚本课程。

要在示例 Laser Tag 游戏中准确重建重生屏幕:

  1. 创建中心信息横幅。

    1. OutStateGui ScreenGui 对象中插入一个 ImageLabel

      1. Explorer 窗口中,导航到 StarterGui 服务。

      2. 将鼠标悬停在其子 OutStateGui 对象上,然后单击 图标。上下文菜单显示。

      3. 从上下文菜单中插入一个 ImageLabel

    2. 选择新标签,然后在 Properties 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5,以将新按钮的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。

      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。

      3. Position 设置为 {0.5, 0},{0.5, 0},以将标签设置在其容器的中间(从父 ScreenGui 的左侧到右侧 50%,从父 ScreenGui 的顶部到底部 50%)。

      4. Size 设置为 {0.48, 0},{0.06, 0},以加宽标签(父 ScreenGui 的 48% 水平和 6% 垂直)。

      5. Name 设置为 Block

      6. Image 设置为 rbxassetid://14304827265,以使图像成为梯形。

      7. ImageColor 设置为 0,0,0,以使梯形为黑色。

      8. ImageTransparency 设置为 0.3,以将标签的不透明度降低 30%,并与游戏中的所有黑色 UI 元素匹配。

    3. Block 中插入一个 UIAspectRatioConstraint,以确保标签及其子 UI 元素的纵横比在玩家的屏幕大小变化时保持不变。

    4. 选择新约束,然后在 Properties 窗口中,将 AspectRatio 设置为 13.78

    5. Block 中插入一个 TextLabel 以显示信息文本。

    6. 选择新标签,然后在 Properties 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5,以将标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。

      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。

      3. Position 设置为 {0.5, 0},{0.5, 0},以将标签设置在其父标签的中间(从父标签的左侧到右侧 50%,从父标签的顶部到底部 50%)。

      4. Size 设置为 {.85, 0},{0.55, 0},以便文本可以占据大部分梯形区域(父标签的 85% 水平和 55% 垂直)。

      5. Name 设置为 BodyTextLabel

      6. FontFace 设置为 Montserrat,以符合未来派美学。

      7. Weight 设置为 Bold,以加粗字体。

      8. Text 设置为 Respawning…

      9. TextColor3 设置为 255, 255, 255,以使文本为白色。

      10. 启用 TextScaled

  2. 创建标题。

    1. Block 中插入一个 ImageLabel

    2. 选择新标签,然后在 Properties 窗口中,

      1. AnchorPoint 设置为 0.5, 1,以将标签的原点设置在其底部中间(从标签的左侧到右侧 50%,从标签的顶部到底部 100%)。

      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。

      3. Position 设置为 {0.5, 0},{0, 0},以将标签设置在其父标签的顶部中间(从父标签的左侧到右侧 50%,从父标签的顶部到底部 0%)。

      4. Size 设置为 {0.46, 0},{0.56, 0},以加宽标签(父标签的 46% 水平和 56% 垂直)。

      5. Name 设置为 Header

      6. Image 设置为 rbxassetid://14304826985,以使图像成为多方向渐变。

      7. ImageColor 设置为 245, 46, 46,以使渐变为红色,表示玩家在被标记出局时暂时处于非活动状态。

    3. Header 中插入一个 TextLabel 以显示信息文本。

    4. 选择新标签,然后在 Properties 窗口中,

      1. AnchorPoint 设置为 0.5, 0.5,以将标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。

      2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。

      3. Position 设置为 {0.5, 0},{0.5, 0},以将标签设置在其父标签的中间(从父标签的左侧到右侧 50%,从父标签的顶部到底部 50%)。

      4. Size 设置为 {.85, 0},{0.55, 0},以便文本可以占据大部分渐变区域(父标签的 85% 水平和 55% 垂直)。

      5. Name 设置为 HeaderTextLabel

      6. FontFace 设置为 Montserrat,以符合未来派美学。

      7. Weight 设置为 Black,以加粗字体。

      8. Text 设置为 TAGGED - YOU'RE OUT!

      9. TextColor3 设置为 255, 255, 255,以使文本为白色。

      10. 启用 TextScaled

  3. 创建屏幕边缘的渐变。

    1. OutStateGui 中插入一个 ImageLabel
    2. 选择新标签,然后在 Properties 窗口中,
      1. AnchorPoint 设置为 0.5, 0.5,以将标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。
      2. BackgroundColor3 设置为 0,0,0,以将标签的背景颜色设置为黑色。
      3. BackgroundTransparency 设置为 0.5,以使标签的背景半透明。
      4. Position 设置为 {0.5, 0},{0.5, 0},以将标签设置在其容器的中间(从父 ScreenGui 的左侧到右侧 50%,从父 ScreenGui 的顶部到底部 50%)。
      5. Size 设置为 {1, 0},{1, 0},以将标签扩展到整个屏幕(父 ScreenGui 的 100% 水平和 100% 垂直)。
      6. ZIndex 设置为 -1,以在其他 UI 元素后面显示渐变。
      7. Name 设置为 Header
      8. Image 设置为 rbxassetid://14309518613,以使图像成为边框渐变。
      9. ImageTransparency 设置为 0.1,以使渐变稍微透明。
  4. 在示例 Laser Tag 场所文件中引用以下 ReplicatedStorage 脚本,该脚本在玩家的生命值降至零时以编程方式显示重生屏幕,并且他们正在重新生成回他们的团队生成区域。

以下 ReplicatedStorage.PlayerStateHandler 客户端脚本包含根据 playerState 属性触发不同类型行为的函数。所有事件响应在此脚本中逻辑上分组在一起,因为它们需要类似的行为来启用或禁用玩家控制、相机移动以及哪个 UI 层可见。

当玩家的生命值降至零时,他们的 playerState 变为 TaggedOut,这会触发 onTaggedOut() 函数。onTaggedOut() 立即触发以下行为:

  • 玩家无法在竞技场中移动。
  • 玩家无法移动他们的相机。
  • 玩家无法使用他们的光束枪。
  • StarterGui.OutStateGui 独占启用。

当玩家重生时,他们的 playerState 变为 SelectingBlaster,这会触发 onSelectingBlaster() 函数。onSelectingBlaster() 然后独占启用 StarterGui.PickABlasterGui,这会自动禁用重生屏幕。有关这些条件的更多信息,请参见 Handle Client State 来自游戏玩法脚本课程。

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local PlayerState = require(ReplicatedStorage.PlayerState)
local PlayerAttribute = require(ReplicatedStorage.PlayerAttribute)
local BlasterState = require(ReplicatedStorage.Blaster.BlasterState)
local togglePlayerMovement = require(script.togglePlayerMovement)
local togglePlayerCamera = require(script.togglePlayerCamera)
local scheduleDestroyForceField = require(ReplicatedStorage.scheduleDestroyForceField)
local localPlayer = Players.LocalPlayer
local playerGui = localPlayer.PlayerGui
local guiLayers = {
playerGui:WaitForChild("HUDGui"),
playerGui:WaitForChild("OutStateGui"),
playerGui:WaitForChild("PickABlasterGui"),
}
-- 禁用所有 UI 层,除了给定的例外
local function setGuiExclusivelyEnabled(enabledGui: ScreenGui?)
-- guiLayers 包含应独占设置的 gui 列表。
for _, screenGui in guiLayers do
screenGui.Enabled = screenGui == enabledGui
end
end
local function onSelectingBlaster()
-- 启用相机,以便玩家在选择光束枪时可以四处查看
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
-- 在选择光束枪时禁用光束枪
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
local function onPlaying()
-- 在选择光束枪后启用玩家移动
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
-- 在游戏中启用光束枪
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
-- 当玩家开始游戏时调度销毁力场逻辑
scheduleDestroyForceField()
end
local function onTaggedOut()
-- 被标记出局时禁用控制
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- 被标记出局时禁用光束枪
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
local function onInLobby()
-- 在大厅中启用控制
togglePlayerMovement(true)
togglePlayerCamera(true)
-- 在大厅中隐藏所有 HUD
setGuiExclusivelyEnabled(nil)
-- 在大厅中禁用光束枪
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
local function onPlayerStateChanged(newPlayerState: string)
if newPlayerState == PlayerState.SelectingBlaster then
onSelectingBlaster()
elseif newPlayerState == PlayerState.Playing then
onPlaying()
elseif newPlayerState == PlayerState.TaggedOut then
onTaggedOut()
elseif newPlayerState == PlayerState.InLobby then
onInLobby()
else
warn(`无效的玩家状态 ({newPlayerState})`)
end
end
-- 如果设置了初始玩家状态,则处理初始玩家状态
local initialPlayerState = localPlayer:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(initialPlayerState)
local function updateFromPlayerState()
onPlayerStateChanged(localPlayer:GetAttribute(PlayerAttribute.playerState))
end
-- 处理未来的玩家状态更新
localPlayer:GetAttributeChangedSignal(PlayerAttribute.playerState):Connect(updateFromPlayerState)
-- 确保在重生后仍然应用更改
localPlayer.CharacterAdded:Connect(updateFromPlayerState)

创建 SurfaceGui 对象

要在 3D 空间中显示 UI 在部件表面上,并响应 每个单独玩家 的脚本逻辑,您可以将 SurfaceGui 对象父级设置为您希望在 ReplicatedStorage 服务中显示 UI 的部件。此技术确保您的 UI 及其脚本逻辑可供服务器和每个玩家的客户端使用。

SurfaceGui 对象包含所有在 3D 空间中显示在部件表面上的 GuiObjects。示例激光标记游戏仅包含一个 SurfaceGui 对象的实例:显示在每个玩家光束枪上方的冷却计。此对象需要为每个玩家编写脚本逻辑,因为它会积极响应每个玩家的输入,并提供他们何时可以再次发射光束枪的视觉反馈。

要创建 SurfaceGui 对象:

  1. Explorer 窗口中,将鼠标悬停在 ReplicatedStorage 服务上,然后单击 图标。上下文菜单显示。
  2. 从上下文菜单中插入一个 Part 对象。
  3. 在部件中插入一个 ScreenGui 对象。
  4. 根据其子 UI 元素的上下文重命名 SurfaceGui
  5. 对于您需要在 3D 空间中显示在部件表面上的每个 UI 元素,重复此过程。

冷却计

冷却计是一个 UI 组件,通知玩家在能够再次发射光束枪之前需要等待多长时间。这一小段暂停防止玩家能够像点击或按下按钮一样快速发射,这对于激光标记游戏的玩法是不现实的。

要在示例 Laser Tag 游戏中准确重建冷却计:

  1. 创建一个部件来容纳您的 SurfaceGui 对象。

    1. Explorer 窗口中,将鼠标悬停在 Workspace 上,然后单击 ⊕ 图标。上下文菜单显示。
    2. 从上下文菜单中插入一个 block 部件。这是部件的临时位置,以便您可以在每个步骤中可视化更改。
  2. 将部件定位并定向到玩家角色持有光束枪的位置,然后在 Properties 窗口中,

    1. Transparency 设置为 1,以使部件完全透明。

    2. Name 设置为 CooldownBarPrefab

    3. Size 设置为 0.169, 0.027, 2.537,以将部件缩放到大约光束枪长度的大小。

    4. 禁用 CanCollideCanQuery

  3. CooldownBarPrefab 中插入一个 SurfaceGui

  4. 选择新的 SurfaceGui,然后在 Properties 窗口中,

    1. Face 设置为 Top,以便 UI 向上显示。

    2. LightInfluenceMaxDistance 设置为 0

    3. PixelsPerStud 设置为 200

  5. 创建黑色条。

    1. SurfaceGui 中插入一个 ImageLabel

    2. 选择新标签,然后在 Properties 窗口中,

      1. 删除默认的 Image 值。
      2. AnchorPoint 设置为 0.5, 0.5,以将标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。
      3. BackgroundColor3 设置为 0,0,0,以将标签的背景颜色设置为黑色。
      4. BackgroundTransparency 设置为 0.4,以使标签的背景半透明。
      5. Position 设置为 {0.5, 0},{0.5, 0},以将标签设置在其容器的中间(从父 SurfaceGui 的左侧到右侧 50%,从父 SurfaceGui 的顶部到底部 50%)。
      6. Size 设置为 {1, 0},{1, 0},以将标签扩展到整个部件(100% 水平和 100% 垂直)。
      7. Name 设置为 Container
  6. 圆角容器。

    1. Container 中插入一个 UICorner 对象。

    2. 选择 UICorner,然后在 Properties 窗口中,将 CornerRadius 设置为 0.15, 0,以稍微圆角。

  7. 创建红色条。

    1. Container 中插入一个 ImageLabel
    2. 选择新标签,然后在 Properties 窗口中,
      1. 删除默认的 Image 值。
      2. AnchorPoint 设置为 1, 0.5,以将标签的原点设置在其右中间(从标签的左侧到右侧 100%,从标签的顶部到底部 50%)。
      3. BackgroundColor3 设置为 172, 13, 13,以将标签的背景颜色设置为深红色。
      4. BackgroundTransparency 设置为 0.2,以使标签的背景稍微透明。
      5. Name 设置为 Bar
      6. Position 设置为 {1, 0},{0.5, 0},以将标签设置在其容器的右中间(从父标签的左侧到右侧 100%,从父标签的顶部到底部 50%)。
      7. Size 设置为 {0, 0},{1, 0},以将标签延长到其父标签的顶部(在父标签中水平 0% 和垂直 100%)。此步骤对于脚本中发生的 tweening 行为也很有益。
  8. 圆角标签。

    1. Bar 中插入一个 UICorner 对象。
    2. 选择 UICorner,然后在 Properties 窗口中,将 CornerRadius 设置为 0.15, 0,以稍微圆角。
  9. CooldownBarPrefab 移动到 ReplicatedStorage

    1. 创建一个文件夹结构来组织您的 UI 对象。示例使用一个 Instances 文件夹,里面有一个子文件夹 Guis

    2. CooldownBarPrefab 移动到 Guis

  10. 在示例 Laser Tag 场所文件中引用以下 ReplicatedStorage 脚本,该脚本以编程方式将冷却计附加到玩家的光束枪,然后在玩家发射光束枪后动画化红色条。

以下 ReplicatedStorage.FirstPersonBlasterVisuals 客户端脚本处理玩家第一人称光束枪的所有视觉逻辑。它需要一组模块脚本,这些脚本协同工作以设置更符合激光标记游戏玩法的光束枪视觉效果,包括 FirstPersonBlasterVisuals.addCooldownBarFirstPersonBlasterVisuals.runCooldownBarEffect

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local BlastData = require(ReplicatedStorage.Blaster.BlastData)
local PlayerAttribute = require(ReplicatedStorage.PlayerAttribute)
local PlayerState = require(ReplicatedStorage.PlayerState)
local getBlasterConfig = require(ReplicatedStorage.Blaster.getBlasterConfig)
local runBlastVisuals = require(script.runBlastVisuals)
local setupAnimations = require(script.setupAnimations)
local addCooldownBar = require(script.addCooldownBar)
local runCooldownBarEffect = require(script.runCooldownBarEffect)
local laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEvent
local RIG_OFFSET_FROM_CAMERA = CFrame.new(2, -2, -3) * CFrame.Angles(math.rad(0.25), math.rad(95.25), 0)
local localPlayer = Players.LocalPlayer
local currentCamera = Workspace.CurrentCamera
local rigModel = nil
local cooldownBar = nil
local animations = {}
local function addFirstPersonVisuals()
local blasterConfig = getBlasterConfig()
-- 添加第一人称 rig
rigModel = blasterConfig.RigModel:Clone()
rigModel.Parent = Workspace
-- 添加冷却条
cooldownBar = addCooldownBar(rigModel.PrimaryPart.CooldownBarAttachment)
animations = setupAnimations(blasterConfig, rigModel)
end
local function removeFirstPersonVisuals()
for _, animation in animations do
animation:Stop()
animation:Destroy()
animation = nil
end
if rigModel then
-- 这也会销毁冷却条,因为它是父级到 rig
rigModel:Destroy()
rigModel = nil
end
end
-- 当发生爆炸时运行第一人称视觉效果
laserBlastedBindableEvent.Event:Connect(function(blastData: BlastData.Type)
runBlastVisuals(rigModel.PrimaryPart.TipAttachment, blastData, animations.blastAnimation)
runCooldownBarEffect(cooldownBar)
end)
-- 如果存在,则将 rig 绑定到相机
RunService.RenderStepped:Connect(function()
if rigModel then
-- 更新 rig 的 CFrame,相对于相机的位置和 RIG_OFFSET_FROM_CAMERA
rigModel:PivotTo(currentCamera.CFrame * RIG_OFFSET_FROM_CAMERA)
end
end)
-- 当光束枪类型在游戏中更改时处理视觉变化
localPlayer:GetAttributeChangedSignal(PlayerAttribute.blasterType):Connect(function()
local playerState = localPlayer:GetAttribute(PlayerAttribute.playerState)
if playerState == PlayerState.Playing then
removeFirstPersonVisuals()
addFirstPersonVisuals()
end
end)
-- 当玩家状态更改时处理视觉变化
localPlayer:GetAttributeChangedSignal(PlayerAttribute.playerState):Connect(function()
local newPlayerState = localPlayer:GetAttribute(PlayerAttribute.playerState)
-- 在选择光束枪或在大厅时移除视觉效果
if newPlayerState == PlayerState.SelectingBlaster or newPlayerState == PlayerState.InLobby then
removeFirstPersonVisuals()
-- 当玩家完成选择光束枪时添加视觉效果。
elseif newPlayerState == PlayerState.Playing then
addFirstPersonVisuals()
end
end)

创建 BillboardGui 对象

为了在 3D 空间中显示 UI 元素,这些元素响应脚本逻辑并始终面向每个玩家的相机,无论他们的视角如何,例如玩家名称或地图标记,您可以创建一个 BillboardGui 对象,作为 BasePartAttachment 的子对象,这些对象存在于 3D 空间中。

示例激光标记游戏包含两个单独的 BillboardGui 对象,位于 ReplicatedStorage 服务中:

  • OtherPlayerIndicatorGuiPrefab - 在每个玩家的头上显示粉色或绿色圆圈,当他们在回合中处于活动状态时。
  • TaggedOutIndicatorGuiPrefab - 在玩家被标记出局时显示在他们的头上。

在您创建 BillboardGui 对象后,您可以根据每个容器的目的创建和自定义其子 GuiObjects。为了演示,在接下来的部分中,您将学习如何在示例激光标记游戏中实现这两种指示器类型的 UI 元素。您可以调整过程的任何部分以满足您自己游戏的规格

要创建 BillboardGui 对象:

  1. Explorer 窗口中,将鼠标悬停在 BasePartAttachment 上,然后单击 图标。上下文菜单显示。
  2. 从上下文菜单中插入一个 BillboardGui 对象。
  3. 根据其子 UI 元素的上下文重命名 BillboardGui
  4. 对于您需要在玩家头上上下文显示的每个 UI 元素,重复此过程。

团队指示器

团队指示器是一个 UI 元素,通知玩家回合中其他玩家属于哪个团队,以便他们可以轻松区分盟友和敌方团队成员。这条信息很重要,因为第一人称射击游戏的玩法要求玩家在战斗区域内快速做出战略决策,以便在标记出局后迅速移动到下一个目标,以免在同一位置停留太久而变得脆弱。

要在示例 Laser Tag 游戏中准确重建团队指示器:

  1. 在临时 rig 中插入一个 BillboardGui 对象。

    1. 从工具栏的 HomeAvatar 选项卡中,单击 Character

    2. 从可用选项中选择。示例使用 R15 rig 类型、女性身体形状和 Rthro 头像。rig 在 3D 视口和 Explorer 窗口中显示,名称为 Rig

    3. Explorer 窗口中,导航到 rig 的子 Head 网格,然后单击 图标。上下文菜单显示。

    4. 从上下文菜单中插入一个 BillboardGui

  2. 选择新的 BillboardGui,然后在 Properties 窗口中,

    1. LightInfluence 设置为 0,以防止环境光影响指示器的颜色。

    2. Name 设置为 OtherPlayerIndicatorPrefab

    3. Size 设置为 {0, 10},{0, 10},以使标签显著变小。

    4. StudsOffsetWorldSpace 设置为 0, 4, 0,以将其定位在 rig 的头上方。

  3. OtherPlayerIndicatorPrefab 中插入一个 Frame 对象。

  4. 选择新框架,然后在 Properties 窗口中,

    1. AnchorPoint 设置为 0.5, 0.5,以将框架的原点设置在其中心(从框架的左侧到右侧 50%,从框架的顶部到底部 50%)。

    2. BackgroundColor3 设置为 255, 3, 0,以将框架的背景颜色设置为红色作为占位符颜色。

    3. Position 设置为 {0.5, 0},{0.5, 0},以将框架设置在其容器的中间(从父 BillboardGui 的左侧到右侧 50%,从父 BillboardGui 的顶部到底部 50%)。

    4. Size 设置为 {1, -2},{1, -2},以将框架缩短到 BillboardGui 的表面区域。

  5. Frame 中插入一个 UICorner 对象,以完全圆角。

  6. Frame 中插入一个 UIStroke 对象,以勾勒指示器的圆圈。

  7. OtherPlayerIndicatorPrefab 移动到 ReplicatedStorage

  8. 在示例 Laser Tag 1A 场所文件中引用以下 ReplicatedStorage 脚本,该脚本以编程方式显示每个玩家在活动回合中的团队指示器,除非他们在敌方团队并被遮挡。

以下 ReplicatedStorage.OtherPlayerIndicatorGuiSetup 脚本在玩家生成到竞技场进行活动回合时运行。它通过调用 addIndicatorToCharacter() 函数附加团队指示器,该函数定位到参与回合的每个玩家角色的 Head 对象。如果他们还没有团队指示器,脚本将克隆并将 otherPlayerIndicatorPrefab UI 添加到角色的 Head,并将团队指示器颜色设置为他们的团队颜色。

如果其他玩家在同一团队中,团队指示器始终显示,即使他们在 3D 空间中隐藏在物体后面;如果其他玩家在敌方团队中,团队指示器仅在 3D 空间中没有物体遮挡时显示。

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local localPlayer = Players.LocalPlayer
local otherPlayerIndicatorPrefab = ReplicatedStorage.Instances.Guis.OtherPlayerIndicatorPrefab
local characterSpawnConnectionsByPlayer: { [Player]: RBXScriptConnection } = {}
local playerAddedConnection: RBXScriptConnection?
local function removeIndicatorFromPlayer(player: Player)
if not player.Character then
return
end
local head = player.Character:WaitForChild("Head", 3)
if not head then
return
end
local gui = head:FindFirstChild(otherPlayerIndicatorPrefab.Name)
if gui then
gui:Destroy()
end
end
local function addIndicatorToCharacter(otherCharacter: Model?)
local otherPlayer = Players:GetPlayerFromCharacter(otherCharacter)
if not otherPlayer then
return
end
task.spawn(function()
local otherHead = otherCharacter:WaitForChild("Head", 3)
if not otherHead then
return
end
-- 仅将指示器添加到参与回合的玩家
if not otherPlayer.Team then
return
end
-- 避免添加重复指示器,仅在不存在时创建新的
local gui = otherHead:FindFirstChild(otherPlayerIndicatorPrefab.Name)
if not gui then
gui = otherPlayerIndicatorPrefab:Clone()
gui.Frame.BackgroundColor3 = otherPlayer.TeamColor.Color
gui.Parent = otherHead
end
-- 仅当玩家是友好的时,指示器始终在上方
local isFriendly = otherPlayer.Team == localPlayer.Team
gui.AlwaysOnTop = isFriendly
end)
end
local function addIndicatorWhenCharacterSpawns(player: Player)
if characterSpawnConnectionsByPlayer[player] then
return
end
local connection = player.CharacterAdded:Connect(addIndicatorToCharacter)
characterSpawnConnectionsByPlayer[player] = connection
end
local function stopSyncingIndicators()
for _, connection in characterSpawnConnectionsByPlayer do
connection:Disconnect()
end
table.clear(characterSpawnConnectionsByPlayer)
if playerAddedConnection then
playerAddedConnection:Disconnect()
playerAddedConnection = nil
end
for _, player in Players:GetPlayers() do
removeIndicatorFromPlayer(player)
end
end
local function addIndicatorToPlayer(player: Player)
if player == localPlayer then
return
end
addIndicatorToCharacter(player.Character)
addIndicatorWhenCharacterSpawns(player)
end
local function startSyncingIndicators()
for _, player in Players:GetPlayers() do
addIndicatorToPlayer(player)
end
if not playerAddedConnection then
playerAddedConnection = Players.PlayerAdded:Connect(addIndicatorToPlayer)
end
end
local function onLocalTeamChanged()
local localTeam = localPlayer.Team
if localTeam then
startSyncingIndicators()
else
stopSyncingIndicators()
end
end
localPlayer:GetPropertyChangedSignal("Team"):Connect(onLocalTeamChanged)
onLocalTeamChanged()

被标记出局指示器

被标记出局指示器是一个 UI 元素,通知玩家其他玩家在回合中不再处于活动状态,并正在重新生成回他们的生成区域。这个信息很重要,因为第一人称射击游戏的玩法要求玩家在标记出局后迅速移动到下一个目标,以免在竞技场中停留太久而变得脆弱。

要在示例 Laser Tag 游戏中准确重建被标记出局指示器:

  1. 在临时 rig 中插入一个 BillboardGui 对象,以便您可以在每个步骤中可视化更改。

    1. 从工具栏的 HomeAvatar 选项卡中,单击 Character

    2. ​从可用选项中选择。示例使用 R15 rig 类型、男性身体形状和 Rthro 头像。rig 在 3D 视口和 Explorer 窗口中显示,名称为 Rig

    3. Explorer 窗口中,导航到 rig 的子 Head 网格,然后单击 图标。上下文菜单显示。

    4. 从上下文菜单中插入一个 BillboardGui

  2. 选择新的 BillboardGui,然后在 Properties 窗口中,

    1. LightInfluence 设置为 0,以防止环境光影响指示器的颜色。

    2. Name 设置为 TaggedOutIndicatorGuiPrefab

    3. Size 设置为 {3, 0},{0.5, 0},以加宽标签空间。

    4. StudsOffset 设置为 0, 3.25, 0,以将其定位在玩家的头上方。

  3. TaggedOutIndicatorGuiPrefab 中插入一个 ImageLabel 对象。

  4. 选择新标签,然后在 Properties 窗口中,

    1. AnchorPoint 设置为 0.5, 0.5,以将标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。
    2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。
    3. Name 设置为 Frame
    4. Position 设置为 {0.5, 0},{0.5, 0},以将标签设置在其容器的中间(从父 BillboardGui 的左侧到右侧 50%,从父 BillboardGui 的顶部到底部 50%)。
    5. Size 设置为 {1, 0},{1, 0},以将标签扩展到整个 BillboardGui(100% 水平和 100% 垂直)。
    6. Image 设置为 rbxassetid://14304826985,以使图像成为多方向渐变。
    7. ImageColor 设置为 245, 46, 46,以将标签着色为红色。
  5. Frame 中插入一个 TextLabel 对象。

  6. 选择新标签,然后在 Properties 窗口中,

    1. AnchorPoint 设置为 0.5, 0.5,以将标签的原点设置在其中心(从标签的左侧到右侧 50%,从标签的顶部到底部 50%)。

    2. BackgroundTransparency 设置为 1,以使标签的背景完全透明。

    3. Name 设置为 BodyTextLabel

    4. Position 设置为 {0.5, 0},{0.5, 0},以将标签设置在其容器的中间(从父标签的左侧到右侧 50%,从父标签的顶部到底部 50%)。

    5. Size 设置为 {0.85, 0},{0.7, 0},以便文本可以占据大部分渐变区域(85% 水平和 70% 垂直)。

    6. FontFace 设置为 Montserrat,以符合未来派美学。

    7. Weight 设置为 Bold,以加粗字体。

    8. Text 设置为 TAGGED

    9. TextColor3 设置为 255, 255, 255,以使文本为白色。

    10. 启用 TextScaled

  7. TaggedOutIndicatorGuiPrefab 移动到 ReplicatedStorage

  8. 在示例 Laser Tag 1A 场所文件中引用以下 ServerScriptService 脚本,该脚本以编程方式在玩家重新生成回他们的团队生成区域时显示被标记出局指示器。

以下 ServerScriptService.SetupHumanoid 服务器脚本在玩家加载游戏时立即运行。它确保每当玩家的角色被添加到数据模型时,都会使用他们的 Humanoid 调用 setupHumanoidAsync

local Players = game:GetService("Players")
local setupHumanoidAsync = require(script.setupHumanoidAsync)
local function onCharacterAdded(player: Player, character: Model)
local humanoid = character:WaitForChild("Humanoid")
setupHumanoidAsync(player, humanoid)
end
local function onPlayerAdded(player: Player)
-- 如果玩家已经有角色,则调用 onCharacterAdded
if player.Character then
onCharacterAdded(player, player.Character)
end
-- 对于此玩家的所有未来角色生成调用 onCharacterAdded
player.CharacterAdded:Connect(function(character: Model)
onCharacterAdded(player, character)
end)
end
-- 对于游戏中已经存在的任何玩家调用 onPlayerAdded
for _, player in Players:GetPlayers() do
onPlayerAdded(player)
end
-- 对于所有未来的玩家调用 onPlayerAdded
Players.PlayerAdded:Connect(onPlayerAdded)

恭喜您完成用户界面设计课程!现在您已经获得了创建艺术风格、线框布局和从头到尾在 Studio 中实现设计的经验,您可以通过新的 UI 和功能扩展您的项目,或遵循其他教程课程,例如 Gameplay Scripting Curriculum,该课程教您有关示例激光标记游戏的一般组织和关键实现细节。祝您创作愉快!

©2026 Roblox Corporation、Roblox、Roblox 标志及 Powering Imagination 是我们在美国及其他国家或地区的注册与未注册商标。