通用Boss血条(Universal Health Bar)是一个用于我的世界网易版的模组,允许开发者为自定义Boss创建独特的血条样式,替代游戏原生的Boss血条显示。
通过服务端系统获取注册对象:
# 获取注册对象
uhbServer = serverApi.GetSystem("lemonUHB", "UHBServer")
registryObj = uhbServer.getRegistryObj()使用链式调用设置血条的各项参数:
registryObj.setBossName("你的Boss名称")\
.setMaskPath("textures/ui/boss_bar_mask")\
.setFillPath("textures/ui/boss_bar_fill")\
.setMaskSize((182, 14))\
.registry()| 方法 | 参数类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
setBossName(name) |
str |
✓ | 无 | 设置Boss的名称(与游戏中显示的名称一致) |
setMaskPath(path) |
str |
✗ | 原版贴图 | 设置血条外层(边框)贴图路径 |
setFillPath(path) |
str |
✗ | 原版贴图 | 设置血条内层(填充)贴图路径 |
setMaskSize(size) |
tuple |
✗ | 原版大小 | 设置外层贴图尺寸,格式:(宽, 高) |
setFillSize(size) |
tuple |
✗ | 与 maskSize 相同 |
设置内层贴图尺寸,格式:(宽, 高) |
setPanelOffset(offset) |
tuple |
✗ | (0, 0) |
设置整个血条面板的偏移,格式:(x, y) |
setMaskOffset(offset) |
tuple |
✗ | (0, 0) |
设置外层贴图的偏移,格式:(x, y) |
setFillOffset(offset) |
tuple |
✗ | (0, 0) |
设置内层贴图的偏移,格式:(x, y) |
setFillColor(color) |
tuple |
✗ | (1.0,1.0,1.0) |
设置内层贴图颜色(色调),格式:(r, g, b) |
setFillOnTop() |
无 | ✗ | False |
设置内层显示在外层之上(用于特殊血条样式) |
setOverwrite() |
无 | ✗ | False |
允许覆盖已存在的同名Boss血条配置 |
- 在资源包的
textures目录下创建你的血条贴图 - 建议准备两张贴图:
- 外层贴图(mask):血条的边框或底色
- 内层贴图(fill):血条的填充部分,会随血量变化
# 示例:为Boss"末影龙"创建血条
import serverApi
# 在合适的时机(需在客户端加载mod完成后)注册血条
def registerCustomBossBar():
uhbServer = serverApi.GetSystem("lemonUHB", "UHBServer")
# 创建并配置注册对象
uhbServer.getRegistryObj()\
.setBossName("末影龙")\
.setMaskPath("textures/ui/custom/dragon_bar_mask")\
.setFillPath("textures/ui/custom/dragon_bar_fill")\
.setMaskSize((182, 14))\
.registry()
# 示例改变原生血条颜色
import serverApi
# 在合适的时机(需在客户端加载mod完成后)注册血条
def registerCustomBossBar():
uhbServer = serverApi.GetSystem("lemonUHB", "UHBServer")
# 创建并配置注册对象 (注意要使用fillOnTop改变顺序)
uhbServer.getRegistryObj() \
.setBossName("末影龙") \
.setFillColor((0.0, 1.0, 0.0)) \
.fillOnTop() \
.registry()- Boss名称必须精确匹配:
setBossName()中设置的名称必须与游戏中Boss显示的名称完全一致 - 贴图路径格式:路径相对于资源包根目录,不需要添加文件扩展名
- 尺寸和偏移:所有尺寸和偏移值使用像素单位
- 注册时机:必须在客户端加载完成后注册,避免同步问题
- 未注册的Boss:未通过此模组注册的Boss会自动显示原生血条
Q: 血条没有显示怎么办?
A: 检查以下几点:
- Boss名称是否与游戏内显示完全一致
- 贴图路径是否正确
- 资源包是否正确安装并启用
Q: 血条位置不对怎么调整?
A: 使用 setPanelOffset() 调整整体位置,使用 setMaskOffset() 和 setFillOffset() 微调细节
Q: 可以为同一个Boss设置多个血条样式吗?
A: 同一个Boss名称只能有一个血条配置,使用 setOverwrite() 可以覆写已经注册的配置
Q: 内层和外层有什么区别?
A: 外层通常是固定的边框或底色,内层是会随血量变化的填充部分
本模组最巧妙的设计在于:每个模组只显示一次自己不管的Boss的原生血条,却每tick隐藏自己管理的Boss的原生血条。这种" 礼让机制"完美解决了多模组血条冲突问题。
假设有两个模组同时安装:
- 模组A:注册了Boss"末影龙"的自定义血条
- 模组B:注册了Boss"凋灵"的自定义血条
当"末影龙"出现时:
模组A的处理逻辑:
1. 检查"末影龙"是否在自己的注册列表 → 是 ✓
2. 每tick执行:隐藏原生血条,显示自定义血条
3. UI数据设置:仅第一次设置
模组B的处理逻辑:
1. 检查"末影龙"是否在自己的注册列表 → 否 ✗
2. 仅执行一次:显示原生血条,隐藏自定义血条
3. 返回,不再处理
结果:模组B只执行了一次"显示原生",而模组A每tick都在"隐藏原生",所以模组A的自定义血条成功显示!
# 每个模组都会执行这段代码
isRegistered = bossName in self.registryData.getBossNames()
# 检查该索引位置的boss名称是否发生变化
cacheKey = "index_{}".format(index)
if cacheKey not in self.bossStateCache or self.bossStateCache[cacheKey]["bossName"] != bossName:
# 名称变化或首次出现,重置缓存
self.bossStateCache[cacheKey] = {
"bossName": bossName,
"isRegistered": isRegistered,
"dataModified": False
}
if not isRegistered:
# 情况1:不是我的Boss,我只显示一次原生血条就不管了
if not self.bossStateCache[cacheKey]["dataModified"]:
vanillaHealthBar.SetVisible(True) # 显示原生(仅一次)
uhbHealthBar.SetVisible(False) # 隐藏自定义(仅一次)
self.bossStateCache[cacheKey]["dataModified"] = True
return # 关键:直接返回,不再每tick处理
# 情况2:是我的Boss,我要持续控制
vanillaHealthBar.SetVisible(False) # 每tick隐藏原生
uhbHealthBar.SetVisible(True) # 每tick显示自定义
# UI数据只设置一次(性能优化)
if not self.bossStateCache[cacheKey]["dataModified"]:
registryData = self.registryData.getRegistryData(bossName)
if not registryData:
return
self._modifyUHBBar(registryData, gridItemPath)
self.bossStateCache[cacheKey]["dataModified"] = True如果未注册的Boss也每tick显示原生血条:
# ❌ 错误做法
if not isRegistered:
vanillaHealthBar.SetVisible(True) # 每tick执行
uhbHealthBar.SetVisible(False)
return问题:模组A和模组B会互相抢夺控制权,谁后执行谁就赢,导致血条闪烁或混乱。
# ✅ 正确做法
if not isRegistered:
if not self.bossStateCache[cacheKey]["dataModified"]:
vanillaHealthBar.SetVisible(True) # 仅执行一次
uhbHealthBar.SetVisible(False)
self.bossStateCache[cacheKey]["dataModified"] = True
return # 礼让:不是我的Boss,我就不再干预优势:
- 模组A注册了"末影龙",每tick隐藏原生 → 主动权
- 模组B没注册"末影龙",只显示一次原生就退出 → 礼让
- 结果:模组A的持续隐藏 > 模组B的一次显示 → 和谐共存