示例下载:https://github.com/TanShaoBig/KillBroadcast/raw/refs/heads/main/examples/KillEffectAddonExample.zip
KillBroadcast 提供客户端声明式注册接口。附属包只需要准备纹理、可选音效和一份 Python 定义,不需要修改 KillBroadcast 本体的 Python 或 JSON UI。
当前 API 版本为 1,只支持三类特效:
queue:图标从中心弹出并组成队列,参考“三角洲”。popup:每次击杀直接弹出一张图片,参考“大狗叫”。frame:按行优先顺序播放图集帧,参考“拔罐猫”。
不支持多层组合特效、纸娃娃、自定义 JSON UI 控件或附属包提供的 Python 动画回调。
接入结果
注册成功后,特效会自动出现在 KillBroadcast 的样式切换和布局编辑器中。玩家可以分别调整并持久化:
- 屏幕位置;
- 大小,范围
50%..200%; - 透明度,范围
0%..100%; - 设置页实时预览;
- 恢复附属包开发者提供的默认布局;
- 导入和分享该样式的布局码。
附属包通过 Layout.Default 提供初始值。更新默认值时递增 Layout.Version,KillBroadcast 只迁移仍等于旧默认值的字段;玩家改过的位置、大小或透明度会分别保留。
包结构
推荐一个附属内容包含自己的行为包和资源包:
MyKillEffectBP/
manifest.json
scripts/
client_system.py
MyKillEffectRP/
manifest.json
textures/ui/my_kill_effect/
popup_1.png
sounds/my_kill_effect/
popup_1.ogg
sounds/sound_definitions.json
资源路径和声音事件名必须使用小写 ASCII。纹理字段不写 .png,例如 textures/ui/my_kill_effect/popup_1。声音必须是资源包 sound_definitions.json 中已登记的事件名;KillBroadcast 项目约定击杀音效使用 32 kHz OGG。
资源包声音定义示例:
{
"dy_load_list": [],
"format_version": "1.14.0",
"sound_definitions": {
"sample.kill.popup.1": {
"sounds": [
{
"name": "sounds/my_kill_effect/popup_1",
"stream": false
}
]
}
}
}
注册时机
从附属包客户端系统获取 KillBroadcast 客户端系统,再调用 RegisterKillEffectProvider:
# -*- coding: utf-8 -*-
import mod.client.extraClientApi as clientApi
OWNER_ID = 'sample.kill_effect_pack'
MAX_REGISTER_ATTEMPTS = 40
GameComp = clientApi.GetEngineCompFactory().CreateGame(clientApi.GetLevelId())
def register_kill_effects(attempt=0):
system = clientApi.GetSystem('KillBroadcast', 'KillBroadcastClientSystem')
if not system or not hasattr(system, 'RegisterKillEffectProvider'):
if attempt + 1 < MAX_REGISTER_ATTEMPTS:
GameComp.AddTimer(0.25, lambda: register_kill_effects(attempt + 1))
return False
result = system.RegisterKillEffectProvider({
'ApiVersion': 1,
'OwnerId': OWNER_ID,
'Effects': [POPUP_EFFECT],
})
if not result.get('Success'):
print('[SampleKillEffect] register failed:', result.get('Error'))
return False
return True
建议在附属包收到 LoadClientAddonScriptsAfter 后开始注册。加载顺序不固定,因此应有限重试,每次重新调用 clientApi.GetSystem(...);不要无限轮询。
同一个 OwnerId 再次注册会原子替换该提供方之前的全部定义。任何一个新定义无效或与其他提供方冲突时,整次注册失败,旧定义保持不变。
附属包客户端系统销毁时可以注销:
system = clientApi.GetSystem('KillBroadcast', 'KillBroadcastClientSystem')
if system and hasattr(system, 'UnregisterKillEffectProvider'):
system.UnregisterKillEffectProvider({'OwnerId': OWNER_ID})
公共方法
RegisterKillEffectProvider(payload)
成功返回:
{
'Success': True,
'Revision': 2,
'OwnerId': 'sample.kill_effect_pack',
'EffectIds': ['sample:popup'],
}
失败返回 Success=False、当前 Revision 和可打印的 Error。单个提供方最多注册 32 个特效。
UnregisterKillEffectProvider(payload)
接受 {'OwnerId': 'sample.kill_effect_pack'},返回与注册相同结构。内置示例不可注销。
GetKillEffectRegistryInfo()
返回 API 版本、支持的种类、数量限制、当前修订号和最近一次错误。
GetRegisteredKillEffects(ownerId=None)
返回经过复制的标准化定义。传入 OwnerId 时只返回该提供方的定义,不传则返回全部定义。
通用定义
{
'EffectId': 'sample:popup',
'DisplayName': u'示例弹窗',
'Kind': 'popup',
'Order': 1000,
'Layout': {
'Version': 1,
'BaseSize': [300, 300],
'Default': {
'Center': [0.5, 0.625],
'Size': 100,
'Alpha': 100,
},
},
'Selector': {'Mode': 'combo_clamped'},
'Stages': [],
'Preview': {'KillCount': 5, 'Interval': 3.2},
}
字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
EffectId | 是 | 外部特效必须为全小写 namespace:name,最长 128 字符。 |
DisplayName | 是 | 设置页显示名,最长 80 字符。 |
Kind | 是 | queue、popup 或 frame。 |
Order | 否 | 设置页排序值,默认 1000。 |
Layout.Version | 否 | 开发者默认布局版本,默认 1。默认值变化时必须递增。 |
Layout.BaseSize | 是 | 内容原始宽高和宽高比,范围 1..4096。 |
Layout.Default.Center | 是 | 相对屏幕中心坐标 [x, y],每项范围 0..1。 |
Layout.Default.Size | 是 | 默认大小百分比,范围 50..200。 |
Layout.Default.Alpha | 是 | 默认透明度百分比,范围 0..100。 |
Selector.Mode | 否 | 阶段选择方式,默认 fixed。 |
Stages | 是 | 1 到 32 个阶段。 |
Preview.KillCount | 否 | 每轮设置预览模拟的击杀数,范围 1..32。 |
Preview.Interval | 否 | 设置预览重新播放间隔,范围 0.2..30 秒。 |
Selector.Mode 的行为:
| 值 | 行为 |
|---|---|
fixed | 始终使用第一阶段。 |
headshot | 按阶段的 Headshot 布尔值选择,找不到时回退第一阶段。 |
combo_clamped | 第 N 次连续击杀选择第 N 阶段,超过阶段数后保持最后阶段。 |
random | 每次随机选择一个阶段。 |
所有阶段都支持:
{
'Key': '1',
'Texture': 'textures/ui/my_kill_effect/popup_1',
'Sound': 'sample.kill.popup.1',
'Headshot': False,
}
Texture 必填。Key、Sound 和 Headshot 可选;声音为空时不播放。
队列模式
三角洲内置示例使用 queue:
DELTA_FORCE_EXAMPLE = {
'EffectId': 'sample:delta_force',
'DisplayName': u'三角洲示例',
'Kind': 'queue',
'Layout': {
'Version': 1,
'BaseSize': [960, 70.2],
'Default': {'Center': [0.5143, 0.6584], 'Size': 100, 'Alpha': 100},
},
'Selector': {'Mode': 'headshot'},
'Stages': [
{
'Key': 'normal',
'Texture': 'textures/ui/my_kill_effect/delta_normal',
'Sound': 'sample.kill.delta',
},
{
'Key': 'headshot',
'Texture': 'textures/ui/my_kill_effect/delta_headshot',
'Sound': 'sample.kill.delta.headshot',
'Headshot': True,
},
],
'Queue': {
'MaxVisible': 7,
'ItemSize': 24,
'Spacing': 2,
'EntryDuration': 0.30,
'StartScale': 4.0,
'DismissDuration': 0.30,
'DismissInterval': 0.10,
},
'Text': {
'Main': u'淘汰 {target}',
'Detail': u'普通淘汰',
'HeadshotDetail': u'爆头淘汰',
'HoldDuration': 2.5,
'FadeInDuration': 0.2,
'FadeOutDuration': 0.3,
},
'Preview': {'KillCount': 5, 'Interval': 3.2},
}
队列最多使用 8 个现有图标槽。MaxVisible 范围为 1..8;Text 可省略,{target} 会替换为目标显示名。
弹出模式
大狗叫内置示例使用单图 popup。每次触发只能显示当前阶段的一张图片:
POPUP_EFFECT = {
'EffectId': 'sample:big_dog_bark',
'DisplayName': u'大狗叫示例',
'Kind': 'popup',
'Layout': {
'Version': 1,
'BaseSize': [300, 300],
'Default': {'Center': [0.5, 0.625], 'Size': 100, 'Alpha': 100},
},
'Selector': {'Mode': 'combo_clamped'},
'Stages': [
{
'Texture': 'textures/ui/my_kill_effect/big_dog_bark_1',
'Sound': 'sample.kill.big_dog_bark.1',
},
{
'Texture': 'textures/ui/my_kill_effect/big_dog_bark_2',
'Sound': 'sample.kill.big_dog_bark.2',
},
],
'Animation': {
'Preset': 'pop_shake',
'EntryDuration': 0.22,
'HoldDuration': 2.75,
'FadeOutDuration': 0.30,
'StartScale': 0.68,
'PeakScale': 1.14,
'ShakeDuration': 0.58,
'ShakeDistance': 4.0,
},
'Preview': {'KillCount': 2, 'Interval': 3.2},
}
Animation.Preset 支持 pop、pop_shake、fade、hold。不接受图片层数组或自定义控件。
逐帧模式
拔罐猫内置示例使用 frame。每个连杀阶段可以使用不同图集:
CUPPING_CAT_EXAMPLE = {
'EffectId': 'sample:cupping_cat',
'DisplayName': u'拔罐猫示例',
'Kind': 'frame',
'Layout': {
'Version': 1,
'BaseSize': [192, 192],
'Default': {'Center': [0.5, 0.475], 'Size': 183, 'Alpha': 100},
},
'Selector': {'Mode': 'combo_clamped'},
'Stages': [
{
'Texture': 'textures/ui/my_kill_effect/cupping_cat_1',
'Sound': 'sample.kill.cupping_cat.1',
'FrameSize': [192, 192],
'FrameCount': 13,
'Columns': 4,
'Fps': 12,
},
{
'Texture': 'textures/ui/my_kill_effect/cupping_cat_2',
'Sound': 'sample.kill.cupping_cat.2',
'FrameSize': [192, 192],
'FrameCount': 18,
'Columns': 5,
'Fps': 12,
},
],
'Preview': {'KillCount': 2, 'Interval': 3.7},
}
图集按从左到右、从上到下播放。FrameCount 范围 1..512,Columns 范围 1..64 且不能大于帧数,Fps 范围 1..60。最后一行可以留空白格。
默认值升级
发布过的附属包需要调整默认布局时:
'Layout': {
'Version': 2,
'BaseSize': [300, 300],
'Default': {
'Center': [0.55, 0.60],
'Size': 110,
'Alpha': 90,
},
}
迁移按 Center、Size、Alpha 三个字段独立判断。举例:玩家只把大小改成 150,版本 2 发布后,其位置和透明度会跟随新默认值,大小仍保持 150。只改默认值但不递增 Version 时,已保存配置不会迁移。
布局分享码
内置样式继续使用 TGKE1。外部命名空间样式使用:
KBFX2|sample:popup|0.5,0.625,100,100
接收方必须已经安装并成功注册同一个 EffectId,否则设置页会拒绝导入。
内置参考实现
KillBroadcast 自己也通过同一套注册表声明三种示例,完整定义位于:
behavior_pack_KillBroadcast/KillBroadcastScript/Client/UI_Scripts/KillEffectRegistry.py的BUILTIN_REGISTERED_EFFECTS;delta_force:队列模式;big_dog_bark:单图弹出模式;cupping_cat:逐帧图集模式。
它们与附属包定义经过同一套校验,进入同一播放器,并使用同一套游戏 HUD 和设置预览链路。
排错
不支持的击杀特效注册 API 版本:将ApiVersion设为1。External EffectId must use namespace:name:使用全小写命名空间 ID。EffectId 已被其他附属包注册:更换自己的命名空间,不要复用其他包 ID。- 设置中没有出现:确认注册返回
Success=True,并确认有限重试确实取到了 KillBroadcast 客户端系统。 - 有声音事件但没有声音:检查
sound_definitions.json的键、文件路径、资源包是否启用以及 OGG 是否为 32 kHz。 - 图集错帧:检查
FrameSize、FrameCount、Columns和实际 PNG 排列。 - 新默认值不生效:递增
Layout.Version;玩家已经自定义的字段按设计不会被覆盖。