Lua について

Last-modified: 2017-06-08 (木) 21:15:03

WMLを使っていると時々、Luaという言葉を目にすることがあると思います。
それほど複雑で無いアドオンを作っているうちはLuaについて気にする必要はありませんが、慣れてくるとWMLの機能に不満が出てくるかもしれません。そんな時に解決法になるかもしれないのがLuaコードです。WMLとは構文も違いますが、よりフレキシブルなイベントの作成が可能なようです。この記事がLuaとは何か理解するための助けになればいいのですが・・・
このトピックの元記事は本家wikiの「LuaWML」です。ここでは記事の冒頭の部分しか訳していませんが、本記事ではLuaで使用できるコマンドの詳しい情報が網羅されています。
原文で読みたい方はこちら

 
 

LuaWMLの概要

The [lua] tag
このタグはActionWMLの一部であり、[event]内やActionWMLを使用できる場所で使用できます。 これは、Lua 5.2言語でアクションを書くことを可能にします。
このタグを[scenario] ScenarioWMLの中に入れることも可能です。これらのタグは、シナリオプリロードイベントが発生する前であっても、luaエンジンのロード時に即座に実行されます。

 

(バージョン1.13.0 以降のみ)[lua]は[era]と[modification]でも使用できるようになりました。 [lua]タグは[era]や[modification]の中で[event]のように使われたときに[scenario]/[multiplayer]にコピーされます。

 

タグはコードキーのみをサポートします。これはLuaスクリプトを含む文字列です。 Luaは引用符と{と}の記号を使用するので、プリプロセッサがマクロ展開とトークン化を実行するのを防ぐため、スクリプトをより強力な引用符で囲むことをお勧めします。

[lua]
   code = << wesnoth.message "Hello World!" >>
[/lua]
 

Luaカーネルはコマンドモードからもアクセスできます:

:lua local u = wesnoth.get_units({ id = "Konrad" })[1]; u.moves = 5
 

[args]サブタグを使用して、可変のローカル変数 "..."を介してWMLオブジェクトをスクリプトに渡すことができます。

[lua]
   code = << local t = ...; wesnoth.message(tostring(t.text)) >>
   [args]
       text = _ "Hello world!"
   [/args]
[/lua]
 
 

Global environment (グローバル環境)
シナリオのすべてのLuaスクリプトは、同じ global environment(別名Lua状態)を共有します。 たとえば、イベントで定義された関数は、その後に発生するすべてのイベントで使用できます。

[event]
   name = preload
   first_time_only = no
   [lua]
       code = <<
           function narrator(t)
               -- Behave like the [message] tag.
               wesnoth.fire("message",
                 { speaker = "narrator", message = t.sentence })
           end
       >>
   [/lua]
[/event]
[event]
   name = turn 1
   [lua]
       code = << narrator(...) >>
       [args]
           sentence = _ "Hello world!"
       [/args]
   [/lua]
   [lua]
       code = << narrator(...) >>
       [args]
           sentence = _ "How are you today?"
       [/args]
   [/lua]
[/event]
 

上記の例では、冗長構造がマクロの背後に隠れる可能性があります。 むしろ、単純に新しいWMLタグを定義する方が良いかもしれません。

[event]
   name = preload
   first_time_only = no
   [lua]
       code = <<
           -- The function is now placed in the wesnoth.wml_actions table
           -- The tag is [narrator], same as the function name
           function wesnoth.wml_actions.narrator(t)
               -- Behave like the [message] tag.
               wesnoth.fire("message",
                 { speaker = "narrator", message = t.sentence })
           end
       >>
   [/lua]
[/event]
[event]
   name = turn 1
   [narrator]
       sentence = _ "Hello world!"
   [/narrator]
   [narrator]
       sentence = _ "How are you today?"
   [/narrator]
[/event]
 

グローバル環境はセーブ/ロードサイクルにわたって保存されません。 したがって、グローバル環境に値を格納することは、一般的には悪い考えです。
グローバル変数(関数定義を含む)を割り当てる唯一のタイミングは[シナリオ]直下の[lua]ブロックに直接、またはプリロードイベントの中です。 それはこのイベントは常に実行されるためです。したがって、その時点で定義されたヘルパー関数は、後のすべてのスクリプトで使用できるようになります。

 

グローバル環境には、basic(名前なし)、string、table、および math のモジュールが含まれています。wesnothモジュールも利用可能で、C ++エンジンへのアクセスを提供します。
さらに、osライブラリからのクロック、日付、時刻、およびdifftime関数(マルチプレイヤーおよびリプレイセーフではないことに留意してください)、バッグライブラリからのトレースバックも利用できます。

 

スクリプトの開始時にある可変のローカル変数 ...(3つの点)は、WMLデータを表す代理テーブルです。 このテーブルは[lua]タグの[args]サブタグの内容です(存在する場合)。

 
 

Examples

次のWMLイベントは、Wesnothのチュートリアルから取り上げたものです。 これは、LuaスクリプトがWesnothにどのように埋め込まれているかを示す例として役立ちます。 このイベントは、WML変数target_hexに設定されていないタイルに、サイド1のユニット(つまり、ユーザーによって制御されるヒーロー)が移動するたびに発生します。

 
# General catch for them moving to the wrong place.
[event]
   name=moveto
   first_time_only=no
   [allow_undo][/allow_undo]
   [filter]
       side=1
   [/filter]
   [if]
       [variable]
           name=target_hex.is_set
           equals=yes
       [/variable]
       [then]
           [if]
               [variable]
                   name=x1
                   equals=$target_hex.x
               [/variable]
               [variable]
                   name=y1
                   equals=$target_hex.y
               [/variable]
               [then]
               [/then]
               [else]
                   [redraw][/redraw]
                   [message]
                       speaker=narrator
                       message=_ "*Oops!
You moved to the wrong place! After this message, you can press 'u' to undo, then try again." +
                       _ "
*Left click or press spacebar to continue..."
                   [/message]
               [/else]
           [/if]
       [/then]
   [/if]
[/event]
 

同じアクションを実行するLuaスクリプトを以下に示します。

[event]
   name=moveto
   first_time_only=no
   [allow_undo][/allow_undo]
   [filter]
       side=1
   [/filter]
   [lua]
       code = <<
           local event_data = wesnoth.current.event_context
           if V.target_hex.is_set and
              (event_data.x1 ~= V.target_hex.x or event_data.y1 ~= V.target_hex.y)
           then
               W.redraw()
               narrator_says(_ "*Oops!\nYou moved to the wrong place! After this message, you can press 'u' to undo, then try again.")
           end
       >>
   [/lua]
[/event]
 

以下は、Luaコードのより詳細な説明です。 まずは最初の行

local event_data = wesnoth.current.event_context
 

イベントデータをevent_dataローカル変数に格納します。 これはmovetoイベントであるため、event_dataテーブルには、x1フィールドとy1フィールドにユニットの宛先が含まれています。
次の2行は、テスト

if V.target_hex.is_set and
   (event_data.x1 ~= V.target_hex.x or event_data.y1 ~= V.target_hex.y)
 

変数V.target_hexがイベントパラメータと一致するかどうかを判定します。 Vはローカル変数ではないため、グローバル環境から取得されます。 通常、グローバル環境の変数は永続的ではないので(リロード時に失われます)データの格納には使用しないでください。 通常の永続Wml変数に簡単にアクセスできるように、グローバル変数Vは、次のプリロードイベントによってWML変数の記憶域にマップされます。

[event]
   name=preload
   first_time_only=no
   [lua]
       code = <<
           H = wesnoth.require "lua/helper.lua"
           -- skipping some other initializations
           -- ...
           V = H.set_wml_var_metatable {}
       >>
   [/lua]
[/event]
 

Vをリダイレクトするプレリュードがなければ、条件文が書かれます。

if wesnoth.get_variable("target_hex.is_set") and
   (event_data.x1 ~= wesnoth.get_variable("target_hex.x") or event_data.y1 ~= wesnoth.get_variable("target_hex.y")
 

条件の本体は[redraw]アクションを実行します。

W.redraw()
 

ここでも、この短い構文は、WをWMLアクションを実行するためのプロキシーにするプレリュードの行によって可能になります。

W = H.set_wml_action_metatable {}
 

このショートカットがなければ、最初のステートメントが書かれます。

wesnoth.fire("redraw")
 

最後に、次のコードでスクリプトはメッセージを表示します。

narrator_says(_ "*Oops!\nYou moved to the wrong place! After this message, you can press 'u' to undo, then try again.")
 

narrator_says関数も、プレリュードで定義されています。その背後にある構造はチュートリアルで何度も出現するためです。 単純なWMLでは、代わりにマクロが使用されていました。 関数の定義は次のとおりです。

function narrator_says(m)
   W.message { speaker="narrator",
               message = m .. _ "\n*Left click or press spacebar to continue..." }
end
 

この関数は[message]アクションを起動し、通常の2つのフィールドを含むWMLオブジェクトを渡します。 2番目のフィールドは、関数の引数を別の文字列と連結することによって初期化されます。 両方の文字列の先頭に_記号が付いています。 (_は単なる単なる関数であり、キーワードではないことに注意してください)。これもまた、プレリュードの特定の行によって可能になります。

_ = wesnoth.textdomain "wesnoth-tutorial"
 

Interface to the engine and helper functions(エンジンとヘルパー関数へのインタフェース)

ゲームエンジンの機能は、wesnothグローバルテーブルに含まれる機能を通じて利用可能です。 これらの関数の中には、プロキシテーブルを返すものがあります。 "read-only"とマークされたフィールドへの書き込みは無視されます。 __cfgフィールドはプレーンなテーブルを返します。 特に、書き込みは元のオブジェクトを変更せず、読み込みはダンプが実行された時点からの値を返します。
いくつかのヘルパー関数は、lua / helper.luaライブラリによって提供されています。 wesnoth.requireを使用してライブラリをロードするときに返されるテーブルの中に格納されます。

helper = wesnoth.require "lua/helper.lua"
 

さらに詳しい情報

このトピックの元記事は本家wikiの「LuaWML」です。ここでは記事の冒頭の部分しか訳していませんが、本記事ではLuaで使用できるコマンドの詳しい情報が網羅されています。
原文で読みたい方はこちら

 

前のページへ戻る