メインコンテンツまでスキップ

Lua Scripting: はじめに

このページでは、FreeMinecraftModels の prop 用の最初の Lua script を、空のファイルから動作するインタラクティブな prop まで段階的に書く方法を説明します。最後まで読めば、hook、context、Prop API、そして prop script ファイルの一般的な構成を理解できるようになります。

基本を理解したら、以下の関連ページに進んでください:

実験的機能

Lua prop script は現在実験的です。hook 名、ヘルパーメソッド、動作は FreeMinecraftModels の進化に伴い変更される可能性があります。本番サーバーで使用する前に十分にテストしてください。

EliteMobs Lua との関係

FreeMinecraftModels は EliteMobs と同じ Lua エンジン(Magmacore)を共有しています。既に EliteMobs の Lua パワーを書いている場合、基本概念 -- hook、contextapi_version、scheduler、ゾーン、sandbox -- は同一です。違いは以下の通りです:

  • EliteMobs の script はボス上で実行され、on_boss_damaged_by_playeron_enter_combat などの hook があります。
  • FMM の script は prop 上で実行され、on_right_clickon_left_clickon_projectile_hit などの hook があります。

context.worldcontext.zonescontext.schedulercontext.statecontext.log API は両方の plugin で同一です。このページでは FMM prop に固有の内容のみを扱います。


Prop Script とは

Prop script は plugins/FreeMinecraftModels/scripts/ フォルダにある独立した .lua ファイルです。モデルファイルと同じ場所にある YAML config ファイルから参照され、prop がワールドにスポーンされるたびに実行されます。

Prop Script が得意なこと

Prop script は以下のような場合に力を発揮します:

  • プレイヤーのクリックに反応するインタラクティブな prop(ドア、レバー、ボタン)
  • プレイヤーに壊されない無敵の装飾用 prop
  • プレイヤーがエリアに入ったり出たりしたことを検知する近接トリガー
  • インタラクションやタイマーでアニメーションを再生するアニメーション付き prop
  • クリックや接近時にサウンドを再生する音声付き prop
  • 静的な装飾を超えるロジックを必要とする prop の動作全般

prop が純粋に装飾的でインタラクションが不要な場合、script は必要ありません。


このページの対象読者

このページは3種類の読者を対象に書かれています:

  • EliteMobs の Lua scripting を既に知っていて、FMM 固有の hook と API を学びたい人
  • Lua scripting が初めてで、prop 用の完全で正確な名前のリファレンスが必要な人
  • AI を使って prop script を作成しており、AI が架空のものを作り出した場合に見分けるための十分な詳細が必要な人

有用な prop script を書く前に本格的な Lua 開発者になる必要はありません。ほとんどの実用的な prop script に必要なのは以下だけです:

  • 返却テーブルに有効な hook を配置する方法
  • 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 は名前付きキーを持つオブジェクトや、返却する script 定義にテーブルを使用します:

return {
api_version = 1,

on_spawn = function(context)
end
}

コメント

-- を使ってメモを書きます:

-- Cancel the damage event so the prop cannot be broken
context.event.cancel()
ヒント

より詳しい Lua 入門については、EliteMobs はじめに ページを参照してください。構文の基本は同一です。


ファイルの配置場所

Script ファイル

.lua ファイルは中央の scripts フォルダに配置します:

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

FMM は起動時に plugins/FreeMinecraftModels/scripts/ 内のすべての .lua ファイルを検出します。

モデルファイルと Config ファイル

各モデルファイルは同じディレクトリに .yml config ファイルを持つことができます:

plugins/
FreeMinecraftModels/
models/
torch_01.fmmodel
torch_01.yml <-- torch_01 の script config
scripts/
invulnerable.lua <-- torch_01.yml から参照

.yml config がモデルとその script を接続します。


Config ファイルのフォーマット

モデルファイルの横に置く YAML config ファイルには2つのフィールドがあります:

isEnabled: true
scripts:
- invulnerable.lua
フィールドデフォルト備考
isEnabledbooleantrueこの prop の script が有効かどうか
scripts文字列のリスト[]scripts/ フォルダ内の .lua ファイル名

同じ prop に複数の script を添付できます。各 script はそれぞれ独立したインスタンスです。

Config の遅延生成

prop がスポーンし、対応する .yml ファイルが存在しない場合、FMM は isEnabled: true と空の scripts: リストを持つデフォルト config ファイルを自動的に作成します。これは非同期で行われるため、最初のスポーン時には script が適用されません -- config が作成され、script ファイル名を追加してから有効になります。

つまり:

  1. モデルファイルを models/ に配置する
  2. prop を一度スポーンさせる(FMM が .yml を自動作成)
  3. 生成された .yml を編集して script ファイル名を追加する
  4. prop を再スポーンまたはサーバーをリロードする(script が有効になる)

Hook リファレンス

すべての Lua prop script ファイルはテーブルを返します。そのテーブルの各キー(api_versionpriority を除く)は、以下にリストされた hook のいずれかでなければなりません。ランタイムは対応するゲームイベントが発生するたびに、一致する関数を呼び出します。

Hook発火タイミング備考
on_spawnprop がワールドにスポーンされた時script がバインドされた時に一度実行
on_game_tickサーバーティックごとに一度(50 ms)script がこの hook を定義している場合のみ有効
on_destroyprop がワールドから削除された時クリーンアップ hook
on_left_clickプレイヤーが prop を左クリック(攻撃)した時context.event はダメージイベント
on_right_clickプレイヤーが prop を右クリックした時context.event はインタラクションイベント
on_projectile_hit飛び道具が prop に当たった時context.event は飛び道具ヒットイベント
on_zone_enterプレイヤーが監視ゾーンに入った時ゾーン監視の設定が必要
on_zone_leaveプレイヤーが監視ゾーンから出た時ゾーン監視の設定が必要

最小ファイル規約

すべての Lua prop script はテーブルを return しなければなりません。

必須およびオプションのトップレベルフィールド

フィールド必須備考
api_versionはいNumber現在は 1 でなければなりません
priorityいいえNumber実行優先度。値が小さいほど先に実行。デフォルトは 0
サポートされる hook キーいいえFunctionHook リファレンスに記載された正確な hook 名のいずれかを使用

バリデーションルール

  • ファイルはテーブルを返さなければなりません
  • api_version は必須で、現在は 1 でなければなりません。
  • priority は存在する場合、数値でなければなりません。
  • 追加のトップレベルキーはすべてサポートされている hook 名でなければなりません。
  • 各 hook キーは関数を指していなければなりません。
  • 不明なトップレベルキーは拒否されます。

ヘルパー関数やローカル定数は最後の returnに配置し、返却テーブルの中には入れないでください。


最初の動作する Prop Script をゆっくり構築

ステップ1の前に:Config の設定

  1. モデルファイル(例:my_prop.fmmodel)を plugins/FreeMinecraftModels/models/ に配置する
  2. prop を一度スポーンさせて .yml config を生成する
  3. plugins/FreeMinecraftModels/scripts/first_test.lua に script ファイルを作成する
  4. plugins/FreeMinecraftModels/models/my_prop.yml を編集する:
isEnabled: true
scripts:
- first_test.lua
  1. prop を再スポーンまたはサーバーをリロードする

ステップ1:ファイルを読み込ませる

return {
api_version = 1,

on_spawn = function(context)
end
}

コンソールにエラーなく読み込まれれば、以下が証明されます:

  • ファイルが有効な Lua である
  • FMM が scripts/ フォルダで見つけた
  • config が正しく参照している
  • 返却テーブルの形状が正しい

ステップ2:Prop に目に見える動作をさせる

return {
api_version = 1,

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

サーバーコンソールを確認してください。ログメッセージが表示されれば、hook が発火しています。

ステップ3:プレイヤーのクリックに反応する

return {
api_version = 1,

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

ゲーム内で prop を右クリックしてください。コンソールにメッセージが表示されれば、クリック hook が動作しています。

ステップ4:ダメージをキャンセルして Prop を無敵にする

return {
api_version = 1,

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

これはプリメイドの invulnerable.lua script で使用されているパターンです。ダメージイベントをキャンセルすることで、prop のバッキング armor stand が破壊されないようにします。

ステップ5:クリック時にアニメーションを再生する

return {
api_version = 1,

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

これは prop モデルで "open" アニメーションをブレンドありループなしで再生します。


context とは

すべての hook 関数は context という引数を1つ受け取ります。これは何かが起きるたびに FMM が渡してくれるツールボックスのようなもので、prop、ワールド、ゾーンなどと対話するために必要なすべてが含まれています。

on_right_click = function(context)
-- context.prop = クリックされた prop
-- context.event = クリックイベント(キャンセル可能)
-- context.world = パーティクル、サウンド、ブロッククエリ用ツール
-- context.state = 独自の永続ストレージ
-- context.log = コンソールログ
-- context.scheduler = 遅延タスクと繰り返しタスク
-- context.zones = 空間ゾーンの作成と監視
end

context は自分で作成するものではありません -- FMM が作成して hook に渡します。

備考

context は hook の呼び出しごとに新しく作成されますが、context.state例外で、prop の生存期間全体を通じて持続します。つまり、context.state にデータを保存し、後で別の hook で読み取ることができます。


主要な context API

利用可能な機能の概要です。完全な詳細は Prop API を参照してください。

  • context.prop -- Prop エンティティ。model_idcurrent_locationplay_animation()stop_animation() を提供します。

  • context.event -- この hook をトリガーした Bukkit イベント。on_left_clickon_right_clickon_projectile_hit で利用可能。cancel()uncancel()is_cancelled を提供します。イベントのない hook(on_spawnon_game_tick など)では nil です。

  • context.state -- Prop の生存期間中持続するプレーンな Lua テーブル。フラグ、トグル状態、タスク ID など、hook 間で記憶しておく必要があるものを保存するために使用します。

  • 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") -- 同じこと

すべてのドキュメントでは一貫して : を使用しています。


コピー&ペースト用テンプレート

最小の有効な Prop Script

return {
api_version = 1,

on_spawn = function(context)
end
}

無敵 Prop テンプレート

return {
api_version = 1,

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

インタラクティブ Prop テンプレート

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
}

最初の実践ワークフロー

新しい prop script を作成する際は、この順序で進めてください:

  1. .lua ファイルを作成し、on_spawn を動作させる。
  2. script ファイル名を prop の .yml config に追加する。
  3. 実際に使いたい hook(例:on_right_click)に変更する。
  4. アニメーションやエフェクトの前に、まずログメッセージを追加する。
  5. 実際のエフェクト(アニメーション、サウンド、パーティクル)を1つ追加する。
  6. その後に初めて、ヘルパー、state、scheduler ロジック、ゾーンを追加する。

この順序はデバッグを大幅に容易にします。なぜなら、一度に1つのことしか変わらないからです。


プリメイド Script

FMM には1つのプリメイド Lua script が付属しています:

  • invulnerable.lua -- 左クリックのダメージイベントをキャンセルし、prop を破壊不能にします。これは最もシンプルで実用的な prop script です。

その他の例は サンプルとパターン ページで見つけられます。


Lua Sandbox

Prop script は EliteMobs と同じサンドボックス化された LuaJ 環境内で実行されます。sandbox の制限は同一です。削除されたグローバル変数と利用可能な標準ライブラリ関数の完全なリストについては、EliteMobs Hooks & Lifecycle ページを参照してください。

要約:

  • 削除済み: debugdofileioloadloadfileluajavamoduleospackagerequire
  • 利用可能: すべての math.*string.*table.* 関数、pairsipairstypetostringtonumberpcallprint など
ヒント

print はサーバーコンソールに出力しますが、出力には context.log:info(msg) を推奨します。ログメッセージには script ファイル名がプレフィックスとして付くため、どの script がメッセージを生成したかを追跡しやすくなります。


次のステップ

EliteMobs の Lua パワーも書いている場合、共有 API(context.worldcontext.zonescontext.schedulercontext.statecontext.log)は同じように動作します。ボス固有の API については EliteMobs Lua ドキュメント を参照してください。