LPAI AI 游戏编辑器文档

脚本系统

tools 对象

脚本系统 用 tools 连接弹窗、场景、AI、地图、特效和音效 `tools` 负责把脚本和别的功能接起来。弹窗、输入、场景跳转、聊天消息、AI、地图、画面特效和前端音效,都会在这里用到。 # 最常见的用途 弹出提示或输入框 让玩家做选择 先把文案翻译成当前语言 跳转场景 发送聊天消息 读写本地数据 请求

脚本系统

用 tools 连接弹窗、场景、AI、地图、特效和音效

`tools` 负责把脚本和别的功能接起来。弹窗、输入、场景跳转、聊天消息、AI、地图、画面特效和前端音效,都会在这里用到。

# 最常见的用途

  • 弹出提示或输入框
  • 让玩家做选择
  • 先把文案翻译成当前语言
  • 跳转场景
  • 发送聊天消息
  • 读写本地数据
  • 请求 AI 内容
  • 控制地图移动
  • 触发画面特效
  • 触发前端音效

# 先记住这一点

很多 tools 方法不会立刻给结果。
脚本里常见写法是:

tools.inputText("输入名字").then((value) => {
  if (!value) return;
  player.setString("玩家名字", value);
});

# 还有一个现在很重要的约定

如果一段文本最后是给玩家看的,优先先过一遍 tools.tr(...)

tools.showToast(tools.tr("购买成功"), "success");

tools.tr(text, params?) 的规则是:

  • 直接把原文当成项目 i18n 的 key
  • 返回当前语言下对应的文本
  • 如果当前语言还没配这条翻译,就回退显示原文

带变量时可以这样写:

tools.showToast(
  tools.tr("你获得了{count}个苹果", { count: 3 }),
  "success"
);

这对下面这些内容尤其重要:

  • showToast / showNotify
  • showDialog
  • choose / chooseObjects
  • inputText / inputNumber
  • postMessage
  • 动态字符串脚本 的返回值

如果你现在先写死中文,以后想做多语言,通常就得回头逐个重改。直接从一开始写成 tools.tr(...) 会省很多事。

# 1. 提示和弹窗

# 中间提示

tools.showToast(tools.tr("购买成功"), "success");

# 顶部提示

tools.showNotify(tools.tr("新的任务已开启"));

# 确认弹窗

tools.showDialog(tools.tr("确认"), tools.tr("奖励已经发放"));

# 关闭当前控件弹窗

if (player.getPro("任务已完成") > 0) {
  tools.closeCurrentDialog();
}

tools.closeCurrentDialog() 用于关闭当前控件所在的最上层 dialog/subdialog 弹窗。没有控件弹窗时不会报错,也不会影响脚本继续执行。

它不能关闭 tools.showDialogtools.choosetools.inputTexttools.inputNumber 创建的交互弹窗;这些弹窗仍通过确认、取消、点击遮罩或按 Esc 结束。

# 2. 选择和输入

# 选项选择

tools.choose(tools.tr("请选择奖励"), tools.tr("从下面选一个"), {
  optionNames: [
    tools.tr("金币"),
    tools.tr("体力"),
    tools.tr("经验")
  ],
  optionValues: ["gold", "stamina", "exp"]
}).then((result) => {
  if (!result?.value) return;
  player.setString("本次选择", result.value);
});

# 文本输入

tools.inputText(tools.tr("输入昵称"), "", {
  placeholder: tools.tr("请输入昵称")
}).then((value) => {
  if (!value) return;
  player.setString("玩家昵称", value);
});

# 数字输入

tools.inputNumber(tools.tr("输入数量"), "", {
  min: 1,
  max: 99,
  defaultValue: 1
}).then((value) => {
  if (value == null) return;
  player.setString("购买数量", String(value));
});

# 对象选择

let objectList = objects.getObjects("NPC");

tools.chooseObjects(tools.tr("选择对象"), tools.tr("请选择一位角色"), {
  objectList
}).then((selectedID) => {
  if (!selectedID) return;
  player.setString("当前伙伴", String(selectedID));
});

# 3. 场景和聊天消息

# 跳转场景

tools.gotoScene("主城");
tools.gotoScene("战斗场景");

# 发送消息

tools.postMessage(tools.tr("你获得了新的线索"));
tools.postMessage(tools.tr("服务器公告"), "server");

# 清空消息

tools.clearMessage("private");
tools.clearMessage("all");

# 4. 本地数据和临时数据

# 读取和写入本地数据

tools.setLocalData("last_name", "小明").then(() => {
  tools.showToast(tools.tr("已保存"), "success");
});

tools.getLocalData("last_name").then((value) => {
  if (!value) return;
  player.setString("上次昵称", value);
});

这适合保存本机上的一些小数据。

# 当前运行中的临时数据

tools.session.lastSelectedNPC = "张三";

tools.setTempData("current_page", "商店");
let page = tools.getTempData("current_page");

这类数据只适合当前运行过程里的临时记录。

# 5. 延迟执行

脚本里如果要延迟执行,请用 tools.delay(...)

tools.delay(() => {
  tools.showToast(tools.tr("时间到了"));
}, 5);

这里的第二个参数是游戏帧数,不是秒数。
运行时里不能用 setTimeoutsetIntervalclearTimeoutclearInterval

# 6. 联网限制

脚本不允许发起任意外部 HTTP 请求,也不提供直接读写云端 JSON 数据的通用工具。

# 7. AI 相关

# 请求一段 AI 文本

tools.requestAIText("总结玩家刚才的行动").then((text) => {
  player.setString("战斗摘要", text);
});

# 让 AI 改属性或对象

tools.requestAIPropertyChanges("根据剧情给玩家增加奖励");
tools.requestAIObjectChanges("生成一位新的旅行商人");

# 控制 AI 智能体

tools.sendAIAgentInput("向导", "继续剧情");
tools.chooseAIAgentOption("向导", 0);
tools.undoAIAgentTurn("向导");
tools.resetAIAgentSession("向导");

# 8. 地图相关

tools.getMapGrid("主地图");
tools.jumpMapGrid("主地图", "城门");
tools.moveMapDirection("主地图", "right");
tools.canMoveMapGrid("主地图", "商店");
tools.resetMap("主地图");

这些方法适合做:

  • 地图跳转
  • 方向移动
  • 检查某个格子能不能过去

# 9. 画面特效

tools.runClientEffect(`
  api.createText(payload.text, {
    style: {
      color: "#fff",
      fontSize: "18px"
    }
  }).place({
    target: api.getTarget(),
    offsetY: -20
  });
`, {
  payload: { text: "-10" },
  target: "攻击按钮",
  label: "damage-text"
});

配套清理方法:

tools.disposeClientEffect("damage-text");
tools.clearClientEffects();

这很适合做:

  • 飘字
  • 高亮
  • 抖动
  • 跟手提示
  • 悬浮提示

# 10. 前端音效

tools.runClientAudio(`
  await audio.resume();
  const synth = audio.createSynth({
    oscillator: { type: "triangle" },
    envelope: { attack: 0.005, decay: 0.08, sustain: 0.02, release: 0.12 }
  });
  synth.setVolume(-10);
  synth.triggerAttackRelease("C5", "16n");
`, {
  label: "ui_click_confirm"
});

配套清理方法:

tools.disposeClientAudio("ui_click_confirm");
tools.clearClientAudio();

这很适合做:

  • 点击音
  • 命中音
  • hover 音
  • 程序化短旋律
  • 循环背景音乐

使用时先记住两点:

  • 音符要写完整音高,例如 C4F#4Bb3
  • 同一时刻要发多个音时,优先用 createPolySynth(),不要把单音 createSynth() 当和弦器

更完整的写法可以继续看 客户端音效系统

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

有些脚本里会直接带上当前上下文,可以这样拿:

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

最常用的是:

  • tools.kwargs.object:当前对象行对应的对象
  • tools.kwargs.objID:当前对象 ID

# 12. 一个常见例子

下面这个例子会先弹输入框,再把结果写到动态字符串里:

tools.inputText("输入留言").then((value) => {
  if (!value) return;

  player.setString("玩家留言", value);
  tools.showToast("已保存", "success");
});

# 使用时要注意的几件事

  • 很多 tools 方法不会立刻返回结果,要用 then(...) 接结果。
  • 延迟执行请用 tools.delay(...)
  • setTimeoutsetInterval 在运行时里不能用。
  • 对象行脚本里,通常会直接用 tools.kwargs.object
  • runClientEffect 适合做临时视觉效果,不适合拿来保存长期状态。
  • runClientAudio 适合做前端声音表现;循环或背景音乐要重视 label 和清理时机。

# 下一步

如果你还没看过脚本挂载位置,可以继续看后面的脚本系统章节。