跳至主要内容

Lua 腳本:入門指南

本頁將教你為 FreeMinecraftModels 道具撰寫第一個 Lua 腳本,從一個空白檔案一路到一個可運作的互動式道具。讀完之後,你將理解鉤子、上下文、Prop API 以及每個道具腳本檔案的通用架構。

熟悉基礎知識後,請繼續閱讀配套頁面:

  • Prop API -- context.propcontext.eventcontext.world 和其他上下文 API
  • 範例與模式 -- 可供學習和改寫的完整可運行腳本
  • 疑難排解 -- 常見錯誤、除錯技巧和 QC 檢查清單
實驗性功能

Lua 道具腳本目前為實驗性功能。隨著 FreeMinecraftModels 的演進,鉤子名稱、輔助方法和行為仍可能發生變化,因此在正式伺服器上使用前請仔細測試。

與 EliteMobs Lua 的關係

FreeMinecraftModels 與 EliteMobs 共享同一個 Lua 引擎(Magmacore)。如果你已經在為 EliteMobs 撰寫 Lua 能力,核心概念——鉤子、contextapi_version、排程器、區域和沙盒——是相同的。差異在於:

  • 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每個伺服器 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

以下是可用功能的摘要。完整詳情請參見 Prop 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 -- 世界互動:粒子、聲音、方塊查詢、閃電、附近實體。完整方法列表請參見 Prop API

  • context.zones -- 建立和監視空間區域(球體、圓柱體、長方體)。完整方法列表請參見 Prop 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、排程器邏輯或區域。

這個順序能大幅簡化除錯,因為每次只改變一件事。


預製腳本

FMM 附帶兩個預製的 Lua 腳本:

  • invulnerable.lua -- 取消左鍵點擊傷害事件,使道具不可摧毀。這是最簡單實用的道具腳本。
  • pickupable.lua -- 讓玩家透過攻擊道具三次來撿起它。每次攻擊都會在道具上播放受傷動畫,第三次攻擊時道具會被移除並掉落其放置物品供玩家撿取。

更多範例請參見範例與模式頁面。


Lua 沙盒

道具腳本在與 EliteMobs 相同的沙盒化 LuaJ 環境中執行。沙盒限制完全相同。已移除的全域變數和可用標準函式庫函式的完整列表請參見 EliteMobs Hooks & Lifecycle 頁面。

摘要:

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

print 會輸出到伺服器主控台,但建議使用 context.log:info(msg) 進行輸出。日誌訊息會以腳本檔名為前綴,便於追蹤是哪個腳本產生的訊息。


下一步

  • Prop API -- context.propcontext.eventcontext.worldcontext.zonescontext.scheduler 的完整參考
  • 範例與模式 -- 附帶詳解的完整可運行腳本
  • 疑難排解 -- 常見問題、除錯技巧和 QC 檢查清單

如果你同時在撰寫 EliteMobs 的 Lua 能力,共享 API(context.worldcontext.zonescontext.schedulercontext.statecontext.log)的運作方式完全相同。Boss 特有的 API 請參見 EliteMobs Lua 文件