LPAI AI 游戏编辑器文档

特效系统

交互特效脚本

特效系统 让控件在移入、移出、移动、长按时出现视觉反馈 交互特效脚本对应 4 个位置:`js_enter`、`js_leave`、`js_move`、`js_long`。它们最适合做移入高亮、悬浮提示、跟手提示、长按反馈这类交互表现。 # 先记住这 4 个入口 脚本 什么时候触发 js_enter 鼠标移入,

特效系统

让控件在移入、移出、移动、长按时出现视觉反馈

交互特效脚本对应 4 个位置:`js_enter`、`js_leave`、`js_move`、`js_long`。它们最适合做移入高亮、悬浮提示、跟手提示、长按反馈这类交互表现。

# 先记住这 4 个入口

脚本 什么时候触发
js_enter 鼠标移入,或手机手指按下时
js_leave 鼠标移出,或手机手指松开 / 取消时
js_move 鼠标移动,或手指按住移动时
js_long 鼠标主键按住,或手机手指持续按住,达到长按阈值时

# 1. 这类脚本最适合做什么

# js_enter

  • 鼠标移入高亮
  • 显示悬浮提示
  • 按钮轻微放大

# js_leave

  • 关闭悬浮提示
  • 恢复默认样式
  • 清理 hover 特效

# js_move

  • 跟手提示
  • 光标附近文字
  • 局部聚光

# js_long

  • 长按出现菜单
  • 长按预览详情
  • 长按触发一段强调特效或提示

# 2. 交互脚本本身更适合只做“特效入口”

交互脚本更常见的用法是:

  • 读当前上下文
  • tools.runClientEffect(...)
  • tools.disposeClientEffect(...)
  • tools.clearClientEffects()

真正的结算和状态变化,通常会放在别的脚本位置:

  • 扣金币
  • 发奖励
  • 写剧情日志
  • 创建对象
  • 发 AI 请求

# 3. 这里最容易搞混的一点

在手机上,这 4 个入口不是互斥关系:

  • 鼠标移入通常会先触发 js_enter
  • 手机按下也会先触发 js_enter
  • 按住移动时会触发 js_move
  • 持续按住达到阈值后再触发 js_long
  • 最后松开或取消时触发 js_leave

在交互脚本本身里,能直接用的是:

  • player
  • objects
  • tools

而这些变量:

  • api
  • target
  • targets
  • pointer

只在 tools.runClientEffect("...") 里面那段特效脚本里可用。

所以这是错的:

const target = api.getTarget();

可以这样写:

tools.runClientEffect(`
  const target = api.getTarget();
  if (!target) return;
`);

# 4. 当前脚本附带的信息

交互脚本里,常见会直接用这些值:

  • tools.kwargs.itemID
  • tools.kwargs.object
  • tools.kwargs.objID
  • tools.kwargs.effectTargetId

例如:

let obj = tools.kwargs.object;
let itemID = tools.kwargs.itemID;

# 5. 常见写法:进入创建,离开销毁

# js_enter

tools.runClientEffect(`
  const current = api.getTarget();
  if (!current) return;

  current.setStyle({
    outline: "2px solid rgba(255,255,255,0.65)",
    filter: "brightness(1.08)"
  });
`, {
  label: `hover-outline-${tools.kwargs.itemID}`
});

# js_leave

tools.disposeClientEffect(`hover-outline-${tools.kwargs.itemID}`);

这套写法很常见,因为:

  • 进入时只负责开效果
  • 离开时只负责收效果
  • label 固定,不容易堆积

# 6. js_move 里要重视 label

因为 js_move 触发频率很高。
如果每次移动都新开一个没有固定 label 的效果,同类特效就会越积越多。

# 跟手提示例子

tools.runClientEffect(`
  api.createText(payload.text || "查看详情", {
    style: {
      padding: "4px 8px",
      borderRadius: "999px",
      background: "rgba(15,23,42,0.88)",
      color: "#fff",
      fontSize: "12px"
    }
  }).place({
    x: pointer?.clientX || 0,
    y: pointer?.clientY || 0,
    offsetX: 14,
    offsetY: 14
  });
`, {
  payload: { text: "查看详情" },
  label: `pointer-tip-${tools.kwargs.itemID}`
});

# js_leave

tools.disposeClientEffect(`pointer-tip-${tools.kwargs.itemID}`);

# 7. js_long 更适合做“延迟确认”型反馈

js_longjs_enter 的区别是:

  • js_enter 更适合立即亮起
  • js_long 更适合“按住一会儿才出现”的反馈

例如长按后出现一个环形描边:

tools.runClientEffect(`
  const current = api.getTarget();
  if (!current) return;

  current.setStyle({
    outline: "2px solid rgba(250,204,21,0.95)",
    boxShadow: "0 0 0 6px rgba(250,204,21,0.18)"
  });
`, {
  label: `long-press-highlight-${tools.kwargs.itemID}`
});

结束长按态时,通常还是在 js_leave 里统一收掉:

tools.disposeClientEffect(`long-press-highlight-${tools.kwargs.itemID}`);

# 8. 什么时候不用手动传 target

在交互脚本里,如果你不传 target,运行时通常会自动绑定当前真实触发的那个目标。

所以很多移入高亮、悬浮提示,直接省略 target 就够了。

# 9. 什么时候适合自己传 target

如果你想高亮的不是当前控件,而是别的控件,可以自己传:

tools.runClientEffect(`
  const current = api.getTarget();
  if (!current) return;
  current.setStyle({ filter: "brightness(1.08)" });
`, {
  target: "主按钮",
  label: "main-btn-highlight"
});

如果要一次命中多个目标,可以用 targets

# 10. 对象控件里更适合直接用当前对象

如果这个控件本身绑定了对象类,交互脚本里通常会直接用:

let obj = tools.kwargs.object;
if (!obj) return;

tools.runClientEffect(`
  const current = api.getTarget();
  if (!current) return;
  current.setStyle({ filter: "brightness(1.08)" });
`, {
  label: `hover-${obj.getID()}`
});

# 使用时要注意的几件事

  • 交互脚本本身更像特效入口。
  • api / target / pointer 只在内层特效脚本里可用。
  • 高频 js_move 通常会配一个稳定的 label
  • js_long 更适合放延迟出现的反馈,不要和 js_enter 做成完全重复的一套效果。
  • js_enter 创建,js_leave 销毁,是最常见的组合。

# 下一步

如果你还没看过基础特效入口,可以先看 客户端特效系统