跳到主要内容

Lua 脚本:入门指南

本页将教您编写第一个 FreeMinecraftModels 道具的 Lua 脚本,从空文件一直到一个可工作的交互式道具。读完之后,您将理解钩子、context、道具 API 以及每个道具脚本文件的通用结构。

熟悉基础知识后,请继续阅读配套页面:

  • 道具 API -- context.propcontext.eventcontext.world 和其他 context API
  • 示例与模式 -- 可供学习和改编的完整可运行脚本
  • 故障排除 -- 常见错误、调试技巧和 QC 检查清单
实验性功能

Lua 道具脚本目前为实验性功能。随着 FreeMinecraftModels 的演进,钩子名称、辅助方法和行为仍可能发生变化,因此在生产服务器上使用前请仔细测试。

与 EliteMobs Lua 的关系

FreeMinecraftModels 与 EliteMobs 共享同一个 Lua 引擎(Magmacore)。如果您已经在为 EliteMobs 编写 Lua 技能,核心概念——钩子、contextapi_version、scheduler、区域和沙盒——是相同的。区别在于:

  • EliteMobs 的脚本运行在 Boss 上,拥有 on_boss_damaged_by_playeron_enter_combat 等钩子。
  • FMM 的脚本运行在道具上,拥有 on_right_clickon_left_clickon_projectile_hit 等钩子。

context.worldcontext.zonescontext.schedulercontext.statecontext.log API 在两个插件中完全相同。本页仅涵盖 FMM 道具特有的内容。


什么是道具脚本

道具脚本是位于 plugins/FreeMinecraftModels/scripts/ 文件夹中的独立 .lua 文件。它们通过模型文件旁边的 YAML 配置文件引用,并在道具被生成到世界中时运行。

道具脚本擅长什么

道具脚本在以下场景中表现出色:

  • 响应玩家点击的交互式道具(门、拉杆、按钮)
  • 玩家无法破坏的无敌装饰道具
  • 检测玩家进入或离开区域的接近触发器
  • 在交互或定时器触发时播放动画的道具
  • 被点击或靠近时播放声音的道具
  • 任何需要超越静态装饰逻辑的道具行为

如果您的道具纯粹是装饰性的且不需要交互,则不需要脚本。


本页的目标读者

本页面面向三类读者:

  • 已经了解 EliteMobs Lua 脚本,想学习 FMM 特有钩子和 API 的人
  • Lua 脚本新手,需要完整且名称精确的道具参考的人
  • 使用 AI 起草道具脚本,需要足够细节来辨别 AI 是否编造了虚假内容的人

需要在编写有用的道具脚本之前成为一名完整的 Lua 开发者。对于大多数实用的道具脚本,您真正需要的只是:

  • 如何在返回的表中放置有效的钩子
  • 如何从 context 读取值
  • 如何用 if ... then return end 提前退出
  • 如何精确调用几个辅助方法

Lua 快速入门

如果您完全没接触过 Lua,以下是您需要的最少语法。

变量

使用 local 存储一个值:

local animation_name = "open"
local sound_volume = 1.0

函数

函数是可复用的逻辑块:

local function log_click(context)
context.log:info("Prop was clicked!")
end

if 检查

当某件事只应在某些情况下发生时使用 if

if context.event == nil then
return
end

nil

nil 表示"无值"。您会经常检查它:

if context.event ~= nil then
context.event.cancel()
end

Lua 使用表来表示具有命名键的对象以及返回的脚本定义:

return {
api_version = 1,

on_spawn = function(context)
end
}

注释

使用 -- 添加注释:

-- Cancel the damage event so the prop cannot be broken
context.event.cancel()
提示

如需更详细的 Lua 入门,请参阅 EliteMobs 入门指南 页面。语法基础完全相同。


文件存放位置

脚本文件

.lua 文件放在中央 scripts 文件夹中:

plugins/
FreeMinecraftModels/
scripts/
invulnerable.lua
interactive_door.lua
proximity_sound.lua

FMM 在启动时会发现 plugins/FreeMinecraftModels/scripts/ 中的所有 .lua 文件。

模型文件和配置文件

每个模型文件可以在同一目录中有一个对应的 .yml 配置文件:

plugins/
FreeMinecraftModels/
models/
torch_01.fmmodel
torch_01.yml <-- torch_01 的脚本配置
scripts/
invulnerable.lua <-- 被 torch_01.yml 引用

.yml 配置是连接模型与其脚本的桥梁。


配置文件格式

模型文件旁边的 YAML 配置文件有两个字段:

isEnabled: true
scripts:
- invulnerable.lua
字段类型默认值说明
isEnabledbooleantrue此道具的脚本是否激活
scripts字符串列表[]scripts/ 文件夹中 .lua 脚本的文件名

您可以将多个脚本附加到同一个道具。每个脚本都是独立的实例。

配置延迟生成

当道具生成且没有对应的 .yml 文件时,FMM 会自动创建一个默认配置文件,包含 isEnabled: true 和空的 scripts: 列表。这是异步进行的,因此道具在首次生成时不会有脚本——只有在配置创建后并编辑添加脚本文件名之后才会生效。

这意味着:

  1. 将模型文件放入 models/
  2. 生成道具一次(FMM 自动创建 .yml
  3. 编辑生成的 .yml 添加您的脚本文件名
  4. 重新生成道具或重载服务器(脚本现在已激活)

钩子参考

每个 Lua 道具脚本文件返回一个表。该表中的每个键(除了 api_versionpriority)必须是下面列出的钩子之一。运行时在对应的游戏事件触发时调用匹配的函数。

钩子触发时机说明
on_spawn道具被生成到世界中脚本绑定时运行一次
on_game_tick每个服务器刻一次(50 毫秒)仅在脚本定义了此钩子时激活
on_destroy道具从世界中移除清理钩子
on_left_click玩家左键点击(攻击)道具context.event 是伤害事件
on_right_click玩家右键点击道具context.event 是交互事件
on_projectile_hit投射物击中道具context.event 是投射物命中事件
on_zone_enter玩家进入监视区域需要先设置区域监视
on_zone_leave玩家离开监视区域需要先设置区域监视

最小文件契约

每个 Lua 道具脚本必须 return 一个表。

必填和可选的顶层字段

字段必填类型说明
api_versionNumber目前必须为 1
priorityNumber执行优先级。较小的值先执行。默认为 0
支持的钩子键Function必须使用钩子参考中列出的精确钩子名称之一

验证规则

  • 文件必须返回一个表。
  • api_version 为必填,目前必须为 1
  • priority 如果存在必须为数字。
  • 每个额外的顶层键必须是受支持的钩子名称。
  • 每个钩子键必须指向一个函数。
  • 未知的顶层键会被拒绝。

辅助函数和局部常量应放在最后的 return 上方,而不是在返回的表内部。


一步步构建您的第一个可工作的道具脚本

步骤 1 之前:设置配置

  1. 将模型文件(例如 my_prop.fmmodel)放入 plugins/FreeMinecraftModels/models/
  2. 生成道具一次以生成 .yml 配置
  3. plugins/FreeMinecraftModels/scripts/first_test.lua 创建您的脚本文件
  4. 编辑 plugins/FreeMinecraftModels/models/my_prop.yml
isEnabled: true
scripts:
- first_test.lua
  1. 重新生成道具或重载服务器

步骤 1:让文件加载

return {
api_version = 1,

on_spawn = function(context)
end
}

如果这在控制台中无错误地加载,您证明了:

  • 文件是有效的 Lua
  • FMM 在 scripts/ 文件夹中找到了它
  • 配置正确引用了它
  • 返回表的结构正确

步骤 2:让道具做一件可见的事

return {
api_version = 1,

on_spawn = function(context)
context.log:info("Prop script loaded for: " .. (context.prop.model_id or "unknown"))
end
}

检查服务器控制台。如果您看到日志消息,说明您的钩子正在触发。

步骤 3:响应玩家点击

return {
api_version = 1,

on_right_click = function(context)
context.log:info("Prop was right-clicked!")
end
}

在游戏中右键点击道具。如果控制台显示消息,说明点击钩子正在工作。

步骤 4:取消伤害使道具无敌

return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

这是预制 invulnerable.lua 脚本使用的模式。它取消伤害事件,使道具底层的盔甲架无法被摧毁。

步骤 5:点击时播放动画

return {
api_version = 1,

on_right_click = function(context)
context.prop:play_animation("open", true, false)
end
}

这会在道具模型上播放 "open" 动画,带有混合且不循环。


什么是 context

每个钩子函数接收一个名为 context 的参数。把它想象成一个工具箱,FMM 在每次发生事件时交给您——它包含了您与道具、世界、区域等交互所需的一切。

on_right_click = function(context)
-- context.prop = 被点击的道具
-- context.event = 点击事件(可以取消)
-- context.world = 粒子、声音、方块查询工具
-- context.state = 您自己的持久存储
-- context.log = 控制台日志
-- context.scheduler = 延迟和重复任务
-- context.zones = 空间区域的创建和监视
end

您不需要自己创建 context——FMM 会创建它并传递给您的钩子。

信息

context 在每次钩子调用时都会重新创建,除了 context.state,它在道具的整个生命周期中持续存在。这意味着您可以将数据存储在 context.state 中,稍后在不同的钩子中读取。


关键 context API

以下是可用功能的摘要。完整详情请参见 道具 API

  • context.prop -- 道具实体。提供 model_idcurrent_locationplay_animation()stop_animation()

  • context.event -- 触发此钩子的 Bukkit 事件。在 on_left_clickon_right_clickon_projectile_hit 中可用。提供 cancel()uncancel()is_cancelled。在没有事件的钩子中(如 on_spawnon_game_tick)为 nil

  • context.state -- 在道具生命周期内持续存在的普通 Lua 表。用于存储标志、切换状态、任务 ID 以及需要在钩子之间记住的任何内容。

  • context.log -- 通过 log:info(msg)log:warn(msg)log:error(msg) 进行控制台日志记录。

  • context.scheduler -- 通过 scheduler:run_later(ticks, callback)scheduler:run_repeating(delay, interval, callback) 实现延迟和重复任务。回调接收全新的 context。通过 scheduler:cancel(taskId) 取消任务。

  • context.world -- 世界交互:粒子、声音、方块查询、闪电、附近实体。完整方法列表请参见 道具 API

  • context.zones -- 创建和监视空间区域(球体、圆柱体、长方体)。完整方法列表请参见 道具 API


方法语法:: vs .

在 Lua 中,object:method(arg)object.method(object, arg) 的简写。FMM API 接受两种形式:

context.log:info("hello")
context.log.info("hello") -- 相同效果

所有文档统一使用 :


即拷即用的模板

最小的有效道具脚本

return {
api_version = 1,

on_spawn = function(context)
end
}

无敌道具模板

return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

交互式道具模板

return {
api_version = 1,

on_spawn = function(context)
context.state.is_active = false
end,

on_right_click = function(context)
context.state.is_active = not context.state.is_active

if context.state.is_active then
context.prop:play_animation("activate", true, true)
else
context.prop:stop_animation()
end
end
}

较大的文件布局

local ANIMATION_NAME = "idle"

local function do_something(context)
context.log:info("Doing something!")
end

return {
api_version = 1,
priority = 0,

on_spawn = function(context)
context.state.task_id = nil
end,

on_right_click = function(context)
do_something(context)
end,

on_destroy = function(context)
if context.state.task_id ~= nil then
context.scheduler:cancel(context.state.task_id)
end
end
}

第一个实际工作流程

构建全新的道具脚本时,请按以下顺序进行:

  1. 创建 .lua 文件并让 on_spawn 正常工作。
  2. 将脚本文件名添加到道具的 .yml 配置中。
  3. 切换到您实际需要的钩子(例如 on_right_click)。
  4. 先添加一条日志消息,然后再添加动画或特效。
  5. 添加一个真实的特效(动画、声音、粒子)。
  6. 只有在此之后,才添加辅助函数、state、scheduler 逻辑或区域。

这个顺序能极大简化调试,因为每次只改变一件事。


预制脚本

FMM 附带两个预制的 Lua 脚本:

  • invulnerable.lua -- 取消左键点击伤害事件,使道具不可摧毁。这是最简单实用的道具脚本。
  • pickupable.lua -- 允许玩家通过敲击道具三次来拾取它。每次敲击都会在道具上播放受伤动画,第三次敲击时道具会被移除并掉落放置物品供玩家拾取。

更多示例请参见示例与模式页面。


Lua 沙盒

道具脚本在与 EliteMobs 相同的沙盒化 LuaJ 环境中运行。沙盒限制完全相同。已移除的全局变量和可用标准库函数的完整列表请参见 EliteMobs Hooks & Lifecycle 页面。

概要:

  • 已移除: debugdofileioloadloadfileluajavamoduleospackagerequire
  • 可用: 所有 math.*string.*table.* 函数、pairsipairstypetostringtonumberpcallprint
提示

print 会输出到服务器控制台,但建议使用 context.log:info(msg) 进行输出。日志消息会以脚本文件名为前缀,便于追踪是哪个脚本产生的消息。


下一步

  • 道具 API -- context.propcontext.eventcontext.worldcontext.zonescontext.scheduler 的完整参考
  • 示例与模式 -- 附带详解的完整可运行脚本
  • 故障排除 -- 常见问题、调试技巧和 QC 检查清单

如果您同时在编写 EliteMobs 的 Lua 技能,共享 API(context.worldcontext.zonescontext.schedulercontext.statecontext.log)的工作方式完全相同。Boss 特有的 API 请参见 EliteMobs Lua 文档