Skip to content

显示板进阶(二):用触发器制作生物血条 ​

返回显示板功能 Wiki

入门篇的进度条只能显示手动填写的数值。这次让它读取生物的生命值:生物掉血,血条就跟着缩短。

**学完你能做出:**一块显示指定生物血量的进度条,并了解如何让血条跟随生物属性变化。

想先看效果,可以直接跳到文末 Lua 实践,按准备步骤挂载后复制代码;想学习触发器,就从第 1 节开始。

1. 先认识“事件”和“动作” ​

触发器就是给地图写规则:什么时候发生什么事。

  • 事件:什么时候执行,例如属性变化的时候。
  • 条件:满足什么要求才继续,例如目标生物仍然存在。
  • 动作:要做什么,例如把血条当前值改成生物现在的生命值。
  • 取值:读出一个数据,例如生物的最大生命值。

这次的规则是:当组件挂载时,获取角色的生命,赋值给进度条;然后在生命变化时,再赋值一次进度条,达到更新血条的效果。

2. 准备生物和进度条 ​

  1. 在练习地图里放置一个可受伤的生物,选择或设置成便于固定观察的状态。

  2. 在它身上挂载显示板组件,并将显示板类型切换为进度条类型。

  3. 按入门篇设置条形进度条:最小值 0,红色进度、深灰背景,进度值样式为“当前值/最大值”。

  4. 空间模式选择屏幕(近大远小),让血条方便从不同方向观察。

  5. 默认显示开启,整体不透明度设为 100,宽高为正数。

    20261021193848_rec_

3. 接上真实血量 ​

在对象上添加一个触发器组件,然后找到当组件装载时,拖出到画布。

然后打开图文信息 → 显示板动作,找到下面这个动作,接下来分别用它设置进度条最大值和当前值:

设置(玩家/玩家组)看到的(对象)的显示板的(数值属性)为(数值)

注意:宽高、空间模式等通用属性可以通过设置对象 → 显示板组件 → 属性调整;本次修改的是进度条独立属性,应使用上面的显示板动作。完整入口对照见 Wiki「三类修改入口」。

最小值继续保持 0。先设置最大值,再设置当前值,避免当前血量被旧的最大值限制。例如生物有 200 点生命上限,血条的最大值也应是 200,而不是一直保留 100。

这里要读的是当前生命值,不是本次受到的伤害。生物从 100 血受到 20 点伤害后,进度条应该显示 80,而不是 20。

“玩家/玩家组”选择所有玩家的参数。如果只选择造成伤害的玩家,其他人就可能看不到本次更新。

image-20261021194505146

20261021194222_rec_

4. 让血条持续刷新 ​

只执行一次,血条之后就不会变化。所以我们在角色的当前生命值或最大生命值变化时,刷新进度条;没有变化时不重复获取,减少不必要的开销。两种属性变化都要覆盖,避免只改变生命上限时,血条仍显示旧的最大值。image-20261021195158390

20261021195021_rec_

5. 试试看:受伤和回血都对吗? ​

进入运行模式,用地图中可用的方式让练习生物受伤、回血。

检查情形应看到的结果
生物满血,生命值为 100/100血条填满,显示 100/100
剩余 75/100约四分之三,显示 75/100
恢复到 90/100血条重新增长,显示 90/100
换成最大生命值 200 的生物读取到的最大值为 200,不固定为 100
第二名玩家加入下一次刷新后看到正确血量
生物被删除或死亡后移除不再读取无效对象,血条自动隐藏

6. 想调整血条位置? ​

可以在编辑模式或运行模式调整偏移。

image-20261021195327221

7. 血条不动时,按这个顺序查 ​

  1. 手动改进度条当前值能不能变化?先确认板子类型和外观设置正确。
  2. 是否读取了当前生命值,而不是伤害数值?
  3. 最大值是否大于最小值,是否先更新了最大值?
  4. 刷新生命值是否在执行,触发器组件是否已经启用?
  5. 玩家参数是否包含当前正在观察的人?

8. Lua 实践:复制脚本,让生物自己带血条 ​

这个 Lua 版本把显示板挂在生物身上,读取同一只生物的生命值。和触发器版一样,启动时先显示一次血量,之后只在当前生命值或最大生命值变化时刷新,不再定时轮询。

8.1 先完成三步准备 ​

  1. 另放一只可受伤的练习生物,给它添加显示板组件。本例会覆盖这块显示板的内容和外观,请用新的练习对象。

    20261020182904_rec_

  2. 新建一个脚本组件,命名为“生物头顶血条”,将下面代码完整替换到脚本编辑器里,保存并挂在同一只生物上。不要挂到世界、UI 资源或之前的独立血条板上。

  3. 进入运行模式。脚本会配置红色血条、头顶偏移和数值显示。让生物受伤或回血,观察血条是否变化。

    20261020183112_rec_

这是一份完成挂载后可直接复制的组件代码,不需要替换占位 ID。它不会自动生成生物,也不会自动造成伤害。若控制台提示未读到生命值或缺少接口,先按提示检查挂载对象和游戏版本。

8.2 完整代码 ​

lua
-- 挂到测试生物上;同一生物需先添加“显示板”组件。
-- 本脚本会把该显示板配置成血条。请在新的练习生物上使用。
local Script = {}

local function validNumber(value)
    return type(value) == "number" and value == value
        and value > -math.huge and value < math.huge
end

function Script:OnStart()
    self.actorId = self:GetGameObjectId()
    if not self:GetComponent("UIBillboard") then
        print("请先给这只生物添加显示板组件,再重新运行。")
        return
    end
    if not DisplayBoard or not DisplayBoard.GenProgressBoardCfg
        or not DisplayBoard.ReplaceByCfg or not DisplayBoard.SetNumberAttr then
        print("当前版本缺少本例使用的显示板接口。")
        return
    end

    local maxHp = Actor:GetAttr(self.actorId, RoleAttr.MaxHp)
    local hp = Actor:GetAttr(self.actorId, RoleAttr.CurHp)
    if not validNumber(maxHp) or maxHp <= 0 or not validNumber(hp) then
        print("没有读到有效生命值,请确认脚本挂在生物上。")
        return
    end

    local cfg = DisplayBoard:GenProgressBoardCfg(
        0, maxHp, math.max(0, math.min(hp, maxHp)),
        0xE84C4C, 0x333333, UIProgressType.Bar,
        UIBarProgressDirection.LeftToRight)
    cfg.width = 2
    cfg.height = 0.3
    cfg.spaceMode = 2 -- 当前实现:近大远小,并朝向镜头
    cfg.relativePosition = Mini.Vec3(0, 2, 0) -- 血条在生物上方 2 米
    cfg.relativeRotation = Mini.Vec3(0, 0, 0)
    cfg.absoluteScale = Mini.Vec3(1, 1, 1)
    cfg.defaultVisible = true
    cfg.backgroundOpacity = 0
    cfg.overallOpacity = 100
    cfg.overallColor = 0xFFFFFF
    cfg.lightMode = 1 -- 当前实现:自发光
    cfg.emissionIntensity = 1
    cfg.progressValueStyle = 1 -- 当前值/最大值
    cfg.progressFontSize = 16
    cfg.progressTextColor = 0xFFFFFF
    cfg.displayDistance = 30
    cfg.responseClick = false

    DisplayBoard:ReplaceByCfg(0, self.actorId, cfg)
    -- 分别监听当前生命值和生命上限,其他属性变化不会触发刷新。
    self:AddEvent(ObjectEvent.ObjectChangeAttr, self.OnCurHpChanged, nil, RoleAttr.CurHp)
    self:AddEvent(ObjectEvent.ObjectChangeAttr, self.OnMaxHpChanged, nil, RoleAttr.MaxHp)
    self.listeningHp = true
end

-- 两种属性变化都重新读取上限和当前值,保证血条范围与数值一致。
function Script:OnCurHpChanged(event)
    self:RefreshHp()
end

function Script:OnMaxHpChanged(event)
    self:RefreshHp()
end

function Script:RefreshHp()
    local maxHp = Actor:GetAttr(self.actorId, RoleAttr.MaxHp)
    local hp = Actor:GetAttr(self.actorId, RoleAttr.CurHp)
    if not validNumber(maxHp) or maxHp <= 0 or not validNumber(hp) then
        return
    end
    hp = math.max(0, math.min(hp, maxHp))
    -- 先改上限,再改当前值;0 在当前实现中用于全局设置。
    DisplayBoard:SetNumberAttr(0, self.actorId,
        DisplayBoardNumAttr.ProgressMaxVal, maxHp)
    DisplayBoard:SetNumberAttr(0, self.actorId,
        DisplayBoardNumAttr.ProgressCurVal, hp)
end

function Script:OnDestroy()
    if self.listeningHp then
        self:RemoveEvent(ObjectEvent.ObjectChangeAttr)
        self.listeningHp = nil
    end
end

return Script

8.3 读懂这几处,就能自己改 ​

代码在做什么可以试着改什么
self:GetGameObjectId()获取挂载这份脚本的生物 ID不需要手填编号
Actor:GetAttr(..., RoleAttr.CurHp)读取当前生命值;MaxHp 读取上限这里读真实数据,不要写成固定 100
GenProgressBoardCfg 与 ReplaceByCfg创建内容配置并应用到生物的显示板进度颜色可以换成 0x33CC66
cfg.width、cfg.height设置板子宽高改成 3 和 0.4,比较大小
Mini.Vec3(0, 2, 0)把板面放在生物上方 2 米大型生物可以把 2 调大
AddEvent(..., nil, RoleAttr.CurHp)只在当前生命值变化时刷新受伤、回血都会触发
AddEvent(..., nil, RoleAttr.MaxHp)单独监听最大生命值变化即使当前生命值没变,也能更新上限
OnDestroy 与 RemoveEvent脚本销毁时移除属性变化监听两种生命属性的监听一起移除

local 表示给当前代码保存一个值;function 定义一段可以执行的逻辑;if ... then ... end 用来判断是否继续执行。这里只需先读懂“获取 ID → 读取生命值并显示 → 监听两种生命属性变化 → 变化时更新显示”这条顺序。事件的第四个参数用于过滤属性,第三个参数 nil 表示不指定优先级,具体用法见组件事件管理。

8.4 检查结果 ​

  • 满血、受伤、回血时,数值与真实生命值一致。
  • 只改变最大生命值,保持当前生命值不变。例如从 75/100 改成 75/200,血条应立即显示 75/200,并缩短到约八分之三。
  • 生物移动时,板面跟着移动;死亡移除时随宿主消失。
  • 再加入一名玩家,检查双方所见数值;超出显示范围再回来检查。
  • 停用旧触发器,避免两套逻辑同时修改同一块板。