LPAI 独立网页游戏 AI 社区指南

表现与音频

客户端视觉特效

用目标、会话、更新模式和清理规则安全实现短期视觉反馈。

表现与音频

特效可以失败,游戏结果不能跟着改变

客户端特效用于飘字、高亮、抖动、遮罩和跟手提示。它读取已经结算的结果并增强反馈,不拥有属性、对象或剧情状态。

# 两层脚本

外层是普通 Worker 脚本,可使用 playerobjectstools;内层是传给客户端宿主的字符串,只使用注入的 apipayloadtargettargetssessionpointer

tools.runClientEffect(`
  const current = api.getTarget();
  if (!current) return;
  current.setStyle({ filter: "brightness(1.15)" });
`, {
  target: "confirm-control-id",
  label: "confirm-highlight"
});

内层按原生 JavaScript 执行,不做关键字扫描或浏览器全局遮蔽。即使技术上能访问浏览器能力,也不要用它绕过 tools、读取私密数据或自行请求平台接口。需要的运行时值先在外层计算,再通过 payload 传入。

# 公开生命周期 API

方法 作用
tools.runClientEffect(script, options?) 创建或更新特效会话
tools.disposeClientEffect(label) 清理指定会话
tools.clearClientEffects() 清理当前页面全部特效

options 的核心字段:

字段 作用
payload 传入纯数据
target 控件 ID 或 { itemId, objectId }
targets 多个目标
label 可替换、更新和清理的稳定会话名
mode replaceupdate
waitMs 脚本完成后的额外保留时间
timeoutMs 宿主中断脚本前的最大执行时间
pointer 显式可序列化指针快照;通常从挂载点继承

目标不存在时应安静结束,不得因此改变规则结算。

# 完整内层 api 参考

方法 返回/责任
api.getTarget() 第一个注入目标,缺失时为 null
api.getTargets() 全部注入目标句柄的副本
api.resolveTarget(input?) 严格解析一个控件/对象目标
api.resolveTargets(inputs?) 解析多个目标;省略参数时返回注入目标
api.createNode(options?) 创建受跟踪 DOM 节点句柄
api.createText(text, options?) 创建受跟踪文本节点句柄
api.upsertNode(key, options?) 创建或更新稳定 key 节点
api.upsertText(key, text, options?) 创建或更新稳定 key 文本节点
api.wait(ms?) 受跟踪延迟;会话销毁时 reject
api.animate(handle, keyframes, options?) 受跟踪 Web Animation Promise
api.remove(node) 节点存在时移除
api.cleanup(reason?) 销毁当前特效会话
api.getViewport() 返回 { width, height, scrollX, scrollY }
api.log(...args) 带会话标签的客户端特效日志

target 是第一个注入目标,targets 是完整列表。runtime 提供 sessionIdlabelnow()viewport()session 是客户端特效/音频共用的页面前端脚本 session,不是 Worker 的 tools.session,也不会写入存档。

# 目标与节点句柄

目标句柄是不拥有 DOM 的已渲染控件引用:

目标方法/属性 用途
kindidsource 句柄身份和原始目标输入
exists() 目标当前是否仍存在
getBounds() 矩形以及 centerX / centerY
animate(keyframes, options?) 运行动画并跟踪恢复
setStyle(style) 应用会话跟踪的临时内联样式
addClass(className) 添加清理时移除的 class

节点句柄拥有当前会话创建的特效 DOM:

节点方法/属性 用途
kindid 句柄身份
mount(parent?) / append(child) 挂到根、目标或另一个节点
clearChildren() 清理受跟踪子内容
setText(value) / setHTML(value) 替换内容;HTML 必须由作者控制
setStyle(style) 修改内联样式
addClass(name) / removeClass(name) 修改 class
setAttribute(name, value) 设置允许的属性
place(placement) 相对目标或视口坐标定位
animate(keyframes, options?) 运行受跟踪动画
getBounds() 读取当前矩形
remove() 移除节点和跟踪器

节点 options 支持 tagtexthtmlclassNameattrsstyleparentmountplacementmotion。定位支持 targetx/y、偏移、目标锚点、自身锚点和 trackTarget。输入必须由作者控制:setHTML 不能用来渲染玩家、AI 或联网返回的 HTML。

# replace 与 update

模式 行为 用途
replace 同 label 新请求销毁旧会话 飘字、闪烁、抖动、一次性动画
update 复用会话和 keyed 节点 跟手提示、拖拽预览、连续尺寸/位置

update 必须有稳定 label,内层使用 api.upsertNode(key, options)api.upsertText(key, text, options)。相同 label 与 key 会 patch 现有 DOM。

const point = tools.kwargs.pointer;
const label = `pointer-tip-` + tools.kwargs.itemID;
if (!point) return;

tools.runClientEffect(`
  api.upsertText("tip", payload.text, {
    style: {
      padding: "4px 8px",
      background: "rgba(15,23,42,.9)",
      color: "#fff",
      pointerEvents: "none"
    },
    placement: {
      x: payload.x, y: payload.y,
      offsetX: 12, offsetY: -10,
      selfX: 0, selfY: 1
    },
    motion: { durationMs: 80, easing: "linear", position: true }
  });
`, {
  mode: "update",
  label,
  payload: { text: "查看详情", x: point.clientX, y: point.clientY }
});

对应的离开脚本调用 tools.disposeClientEffect(label)。高频更新中不要 await api.wait、等待动画或自己循环补帧。

# 目标与对象行

  • 普通控件:target: "control-id"
  • 对象行:target: { itemId: "control-id", objectId: obj.getID() }
  • 多目标:targets: ["a", "b"]
  • 指针挂载点不传 target 时,运行时通常注入真实触发目标。

对象列表重排后仍按对象 ID 命中,不能使用数组下标。

# 纯 AI 提示词契约

说明触发挂载点、目标 ID、持续时间、是否跟随指针、稳定 label、页面切换与目标消失时的清理。要求 AI 先查询控件和对象上下文,再生成外层与内层脚本。

# 验收

测试目标存在/不存在、重复触发、快速移动、对象行重排、离开清理、页面切换、脚本异常和低性能设备。删除全部特效后,游戏状态仍应完全正确。

下一步:交互特效脚本