Luaスクリプティング:行動プログラム
行動プログラムは、モブのネイティブAIをLuaのビヘイビアに置き換え、モブがどこへ移動し、何を見て、どのエンティティをターゲットにし、いつ攻撃するかを決めます。EliteMobsはこれらのプログラムを、MagmaCoreのMindランタイムを通じてMinecraftのネイティブBrainシステム上で実行します。
行動プログラムはLuaパワーではありません。Luaパワーはon_boss_damaged_by_playerなどのボスイベントに反応しますが、行動プログラムは毎tick実行され、モブの移動制御を所有します。ボスは両方を使えます。ビヘイビアはon_mind_actionを通じて、ボスのパワーにアクションの実行を依頼できます。
ボスの作成で説明しているとおり、カスタムボスファイル、またはエンティティタイプのモブプロパティファイルでbehaviorを設定します。behavior: nativeはバニラのAIを維持します。サーバーのバージョンがネイティブMindに対応していない場合、EliteMobsは起動時にNative Mind interface is unavailable on this Minecraft version.をログに出力します。その場合、プログラムを選択したカスタムボスはスポーンせず、Cannot spawn <boss file>: ...がログに出力されます。behaviorに存在しないプログラムや無効なプログラムを指定したボスは、どのバージョンでも同じように失敗します。
ファイル
行動ファイルはplugins/EliteMobs/behaviors/に置きます。EliteMobsは、次の同梱ファイルが存在しない場合に書き出します。
plugins/
EliteMobs/
behaviors/
basic_melee.lua
modules/
target.lua
pursuit.lua
melee.lua
wander.lua
modulesという名前のフォルダ内にある.luaファイルはモジュールです。それ以外の.luaファイルはすべてプログラムです。- ボスは
behaviors/からの相対パスでプログラムを参照します。例:basic_melee.luaやguards/patrol_guard.lua。 - EliteMobsはMindサービスの起動時に行動ファイルを読み込みます。検証に失敗したファイルはスキップされ、コンソールに理由とともに
Could not load behavior <file>またはCould not load behavior module <file>が出力されます。 - プログラムとモジュールのIDは、
elitemobs:behavior/basic_meleeのようにelitemobs名前空間を使います。IDは小文字で、a-z、0-9、.、_、-を使え、コロンの後には/も使えます。2つのプログラムが同じIDを共有することはできず、各モジュールIDは1つのファイルでのみ宣言してください。 - 1つの名前空間に含められるモジュールは最大256個です。モジュールが列挙できる依存関係は最大32個、プログラムが解決できるモジュールは最大64個で、各ソースファイルは1,000,000文字までに制限されます。
他のLuaスクリプトと同じサンドボックスのルールが適用されます。Luaサンドボックスを参照してください。プログラムを実行する各モブは独自のLua環境を持つため、ファイルローカルの変数はモブ間で共有されません。
プログラムファイル
プログラムファイルはai.program { ... }を返します。
return ai.program {
id = 'elitemobs:behavior/basic_melee', revision = 1,
modules = {
'elitemobs:behavior/target', 'elitemobs:behavior/pursuit',
'elitemobs:behavior/melee', 'elitemobs:behavior/wander'
},
budget = {
callback_micros = 2000, entity_micros = 4000, server_micros = 5000,
max_callbacks = 20, max_instructions = 12000, max_action_requests = 2
}
}
| フィールド | 型 | 備考 |
|---|---|---|
id | string | 必須。名前空間付きのID。behaviors/内のファイルではelitemobs:...です。 |
revision | 正の整数 | 必須。 |
modules | 文字列の配列 | 任意。メモリ、センサー、ビヘイビアをプログラムに加えるモジュールのID。依存関係は、それを必要とするモジュールより先に読み込まれます。重複と依存関係の循環は拒否されます。 |
memories、sensors、behaviors | テーブル | 任意。プログラムはモジュールと同じ形式で独自のものを宣言できます。 |
budget | テーブル | 任意のソフトなスケジューリング制限。バジェットを参照してください。 |
runaway | テーブル | 任意のコールバックごとのハードな制限。バジェットを参照してください。 |
モジュールファイル
モジュールファイルはai.module { ... }を返し、再利用できるメモリ、センサー、ビヘイビアをまとめます。
| フィールド | 型 | 備考 |
|---|---|---|
id | string | 必須。名前空間付きのモジュールID。 |
revision | 正の整数 | 必須。 |
dependencies | 文字列の配列 | 任意。このモジュールが必要とする他のモジュールのID。 |
memories | テーブル | 任意。メモリを参照してください。 |
sensors | 配列 | 任意。ai.sensor { ... }のエントリー。 |
behaviors | 配列 | 任意。ai.behavior { ... }のエントリー。 |
sensors、behaviors、modules、dependenciesは、欠番や名前付きキーのない単純な配列である必要があります。センサー、ビヘイビア、メモリの名前は、プログラム内のすべてのモジュールを通じて一意である必要があります。
メモリ
メモリは、1体のモブについてセンサーとビヘイビアが共有する型付きの値です。それぞれ名前を付けて宣言します。
memories = {
candidate = { type = 'uuid', persistent = false },
next_attack = 'integer'
}
| 型 | Luaの値 |
|---|---|
string | 文字列 |
boolean | 真偽値 |
integer | 整数 |
number | 数値 |
uuid | UUID文字列 |
position | { world = 'world', x = 0, y = 64, z = 0 } |
コロンを含まない名前は、プログラムの名前空間に置かれます。persistentのデフォルトはfalseで、trueにするとそのメモリはMindの保存状態とともにシリアライズされる対象になります。メモリの読み書きにはc.memory:get(name)、c.memory:set(name, value, ttl_ticks)、c.memory:forget(name)、c.memory:contains(name)を使います。setの3番目の引数は省略可能で、指定するとその値はそのtick数の後に期限切れになります。宣言していない名前を使うとエラーになります。
センサー
センサーは情報を収集し、通常はメモリに格納します。センサーはビヘイビアより先に実行されます。
ai.sensor {
id = 'elitemobs:behavior/find_target', interval = 20,
sense = function(c)
local target = c.perception:nearest_player(35)
if target then c.memory:set('candidate', target.uuid, 21)
else c.memory:forget('candidate') end
end
}
| フィールド | 型 | デフォルト | 備考 |
|---|---|---|---|
id | string | 必須。 | |
interval | 正の整数 | 1 | 実行間隔のtick数。 |
sense | function | 必須。Mindコンテキストを受け取ります。 |
センサーはc.actuatorを使えません。センサーからアクチュエーターのメソッドを呼び出すとエラーになります。
ビヘイビア
ビヘイビアは、宣言したコントロールを保持している間、モブに対して動作します。
ai.behavior {
id = 'elitemobs:behavior/attack', priority = 10,
controls = { ai.controls.attack },
can_start = function(c) return c.perception:current_target() ~= nil end,
can_continue = function(c) return c.perception:current_target() ~= nil end,
tick = function(c)
local target = c.perception:current_target()
if target then c.actuator:attack(target) end
end
}
| フィールド | 型 | デフォルト | 備考 |
|---|---|---|---|
id | string | 必須。 | |
priority | integer | 0 | 数値が小さいほど優先されます。 |
controls | array | なし | このビヘイビアが実行中に確保するコントロール。 |
can_start | function | 常にtrue | trueまたはfalseを返す必要があります。 |
can_continue | function | can_startと同じ | trueまたはfalseを返す必要があります。 |
start | function | なし | ビヘイビアの開始時に1回実行されます。 |
tick | function | 必須。ビヘイビアの実行中、毎tick実行されます。 | |
stop | function | なし | function(c, reason)。ビヘイビアの停止時に実行されます。 |
ライフサイクル
- 停止中、ビヘイビアは
can_startを確認します。trueが返り、宣言したすべてのコントロールを確保できると、startが実行されます。 - 実行中は開始したtickも含めて毎tick、まず
can_continueが実行されます。falseが返るとビヘイビアは理由completedで停止し、それ以外の場合はtickが実行されます。 stop(c, reason)は、completed、preempted、program_replaced、entity_removed、callback_failed、handle_closedのいずれかを受け取ります。停止するとビヘイビアのコントロールが解放され、保持していた移動、ターゲット、攻撃が止まります。
can_continue、start、tickでLuaエラーが発生すると、ビヘイビアはcallback_failedで停止します。can_startやcan_continueから真偽値以外を返すとエラーになります。失敗ごとに、コンソールにMind <program> callback <callback> failed with ...が出力されます。
コントロール
| コントロール | 許可される操作 |
|---|---|
ai.controls.move | c.actuator:move_to(...)、c.actuator:stop_moving() |
ai.controls.look | c.actuator:look_at(...) |
ai.controls.jump | c.actuator:jump() |
ai.controls.target | c.actuator:set_target(...)、c.actuator:clear_target() |
ai.controls.attack | c.actuator:attack(...) |
ai.controls.action | c.actions:request(...) |
ai.controls.use_item | 予約済み。Luaのアクチュエーターにはアイテム用のメソッドがありません。 |
各コントロールは、一度に1つの実行中ビヘイビアだけが保持します。priorityの数値が小さいビヘイビアは、数値が大きいビヘイビアからコントロールを奪い、奪われた側は理由preemptedで停止します。数値が小さいビヘイビアが保持しているコントロールは奪えません。優先度が同じ場合は、idがアルファベット順で先になるビヘイビアがコントロールを得ます。コントロールを保持せずにアクチュエーターのメソッドを呼び出すとエラーになります。c.actuator:stop_all()は、そのビヘイビアが保持しているものだけを停止します。
同梱モジュールでは、ターゲット選択が優先度5、近接攻撃が10、追跡が20、待機中の徘徊が50を使います。そのため、ターゲットが存在するとすぐに追跡が徘徊から移動を奪います。
ボスのアクションの要求
ai.controls.actionを保持しているビヘイビアは、c.actions:request(identifier, payload)を呼び出せます。EliteMobsはこの要求を、on_mind_actionを通じてボスのLuaパワーに送ります。識別子は'elitemobs:slam'のような、128文字以内の小文字の名前空間付きキーである必要があります。ペイロードに含められるエントリーは最大16個で、キーは64文字以内の小文字、文字列の値は256文字までに制限されます。無効な識別子やペイロードはエラーになります。呼び出しはaccepted、deferred、rejectedのいずれかを返し、1tick内でプログラムのmax_action_requestsを超えた要求はdeferredを返します。ペイロードの形式はMindアクションのコンテキストを参照してください。
バジェット
budgetはソフトなスケジューリング制限を設定します。あるtickでモブがいずれかの制限に達すると、Mindランタイムは次のtickまでそのモブの残りのコールバックをスキップします。すでに実行中のコールバックは必ず最後まで実行されます。
| キー | デフォルト | 備考 |
|---|---|---|
callback_micros | 2000 | これより長く実行されたコールバックがあると、そのtickのモブのコールバックは終了します。 |
entity_micros | 4000 | モブ1体・1tickあたりのコールバックの合計時間。 |
server_micros | 20000 | すべてのMindプログラムの合計使用時間がこの値に達すると、そのtickのモブのコールバックは停止します。 |
max_callbacks | 128 | モブ1体・1tickあたりのコールバック数。 |
max_action_requests | 8 | モブ1体・1tickあたりのアクション要求数。最大64。 |
すべての値は正である必要があり、callback_microsはentity_microsを、entity_microsはserver_microsを超えられません。
runawayは、1つのコールバックを中断するハードな制限を設定します。
| キー | デフォルト | 備考 |
|---|---|---|
cpu_micros | 50000 | コールバックごとのスレッドCPU時間。 |
max_instructions | 50000 | コールバックごとのLua命令数。 |
同梱のbasic_melee.luaのように、budgetでもmax_instructionsを指定できます。budgetとrunawayのどちらか一方で宣言し、両方には書かないでください。runawayの制限を超えたコールバックは、他のコールバックエラーと同じように失敗します。
次のステップ
- ボスの作成:behavior -- ボスのプログラムを選択する
- Lua APIリファレンス:Mindプログラムのコンテキスト --
c.memory、c.perception、c.actuator、c.actionsのすべてのメソッド - フックとライフサイクル --
on_mind_actionとその他のLuaパワーフック
