桌游模拟器(Tabletop Simulator,简称 TTS)作为桌游的通用模拟器,不仅提供了基础的物理模拟,还提供了一个 Lua 脚本运行环境,其在基础的桌游基础上,提供了一些列的操作接口,使得可以完成线下无法做到的自动Setup、拿取/分发物品、终局结算等等电子游戏的功能,简化了玩家操作,让大家更加专注于策略层面。
📢 传统的桌游就是把一堆卡牌和Token拿来移去,好耗时好麻烦,而桌游模拟器就是在传统的桌游上加入脚本好好玩!!!
除了官方的API文档,和桌游模拟器脚本相关的中文资料十分稀少,所以在这里做一些总结,以供大家入门和参考。
适验版本:TTS v14.1.8,Lua 版本 v5.2。
简约开发流程
创建本地游戏:对于想要更改的游戏点击
游戏-保存和载入-保存游戏,输入名称。这一步是对于开发中的游戏建立一个本地存档,否则在更改脚本时会报错。修改游戏:
- 可以通过点击顶部栏的
MOD-脚本来访问游戏内的Lua编辑器,或右键点击对象,选择脚本,然后从上下文菜单中选择 Lua编辑器。 - 在Lua编辑器中,编辑器左侧的标签页允许你在全局脚本和对象脚本之间切换。
- 将右侧的代码全部复制并保存为本地文件(*.lua),使用任意一款代码编辑工具编写代码。
- 代码写好后,复制全部代码粘贴回TTS的代码编辑器,点击
保存并播放按钮提交修改到存档文件,游戏会自动重新加载。保存并播放只会提交你的脚本更改,任何非脚本的更改都会丢失。 - 非脚本的更改需要点击
游戏-保存和载入,对当前保存的游戏点击Option-覆盖进行保存。
- 可以通过点击顶部栏的
- 上传新游戏:在修改完成的游戏中,点击
MOD-上传创意工坊。如果没有上传过就填写上传创意工坊表单,否则填写更新创意工坊表单。
Atom 环境搭建
使用简约开发流程时,需要不断地把代码复制进复制出,十分地麻烦,而搭建一个 Atom 的开发环境,不仅可以自行获取脚本,还有行号、语法高亮、自动补全和现代化的外观等诸多好处。
根据以下指引安装 Atom(试验版本:v1.60.0)和 插件:Release 13.2.0 · Berserk-Games/atom-tabletopsimulator-lua · GitHub
安装后,在 Atom 中点击 Packages 如图所示:

创建本地游戏和上传新游戏的流程不变,只是简化了修改流程。
修改流程
在 TTS 中,进入一个本地游戏。
在 Atom 中,按快捷键
Ctrl+Shift+L,或者点击Packages-Tabletop Simlator-Get Lua Scrits。会直接打开一个名为
Tabletop Simulator Lua的工程,其中包含了当前游戏的所有脚本(.ttslua)和UI(.xml)。双击文件即可对脚本进行修改。
修改完成后,按快捷键
Ctrl+Shift+S,即会触发游戏保存并播放。
Lua 基础
- 仅在函数内使用的变量,一定要加 local 声明:函数中所有没有 local 修饰的变量都默认是全局变量,对于一些通用的变量名来说,会带来诸多非预期的改变。
- 在 Lua 索引值是以 1 为起始,但你也可以指定 0 开始。
- 要计算数组的长度(即数组中元素的个数),你可以使用 # 操作符。
代码片段
初始代码
1 | --[[ Lua code. See documentation: https://api.tabletopsimulator.com/ --]] |
默认的全局脚本,定义了两个事件函数:
onLoad():游戏加载完成后调用 onLoad。onUpdate():每帧调用一次 onUpdate。
获取玩家人数
1 | local seated_players_number = #getSeatedPlayers() |
- getSeatedPlayers():是一个全局函数,返回一个包含已入座玩家颜色字符串的表格(Table)。
获取玩家名称
1 | -- 通过颜色获取玩家名称 |
创建按钮
1 | params1 = { |
按钮需要依附在物体的表面,所以先创建一张小丑牌(使用小丑牌,可以不用增加资产,厚度也比较合适),然后调整按钮的长宽等参数,覆盖下方的卡牌。
createButton(parameters)
return bool,创建一个脚本按钮连接到对象上。脚本按钮是指可以在游戏中点击,触发脚本中的某个函数。
parameters: 包含生成按钮所用信息的 Table。
- parameters.click_function: 点击按钮时执行的函数名称字符串。
- parameters.function_owner: 包含click_function函数的对象。可选,默认为全局。
- parameters.label: 显示在按钮上的文本。可选,默认为空字符串。
- parameters.position: 按钮相对于物体中心出现的位置。可选,默认为 {x=0, y=0, z=0}
- parameters.rotation: 按钮相对于物体旋转的方向。可选,默认为 {x=0, y=0, z=0}
- parameters.scale: 按钮相对于物体缩放的比例。可选,默认为 {x=1, y=1, z=1}
- parameters.width: 按钮相对于物体的宽度。可选,默认为100。
- parameters.height: 按钮相对于物体的高度。可选,默认为100。
- parameters.font_size: 标签字体相对于对象的大小。可选,默认为100。
- parameters.color: 用于可点击按钮的颜色。可选,默认为 {r=1, g=1, b=1}
- parameters.font_color: 标签文字的颜色。可选,默认为 {r=0, g=0, b=0}
- parameters.hover_color: 鼠标悬停时背景的颜色。可选。
- parameters.press_color: 点击后背景颜色。可选。
- parameters.tooltip: 文本弹窗,类似于鼠标悬停时显示对象名称的方式。可选,默认为空字符串。
click_function(obj, player_clicker_color, alt_click)
点击按钮会自动传递以下参数:
- obj: 按钮附着的物体。
- player_clicker_color: 按下按钮的玩家颜色。判断玩家颜色可以针对特定玩家
- alt_click: 如果用了非左键点击按钮,则为 true;左键点击按钮,则为 false。根据左右按键点击可以定义不同的行为。
对象操作
1 | role_card_guid = '752421' |
获取对象信息
1 | card_guid = 'ecc9a9' |
- name 相当于对象类型,有 Card、Bag、PlayerPawn、Deck、Notecard、Custom_Board、Custom_Tile、Custom_Token、Custom_Model 等等。
- getName() 是可编辑的名称。
- getDescription() 是可编辑的描述。
牌堆操作
1 | -- 牌堆的 GUID |
获取某一区域所有的物品
1 | -- 一个简单的计算距离的函数 |
从一堆物品中过滤某一类物品
1 | -- 从一堆物品中过滤某一名称的物品 |
跨对象函数调用
调用某个对象的函数
1 | sub_object_guid = '9f9597' |
在 Global 函数中,调用对象(9f9597)脚本中的函数。
- params:跨对象调用时,只允许传递一个参数,如果想要传递多个参数,需要先封装为一个 Table 类型的变量。
对象脚本中调用全局函数
1 | local workers_number = Global.call("getNumberForSupplemment", params) |
在对象脚本中调用 Global 脚本中的 getNumberForSupplemment 函数。