Lua言語
一般的にゲームの挙動やパラメータなどは通常はプログラム内に埋め込みますが、もしこれらの変更があった場合は修正後にプログラムを再コンパイルする必要があり、値を変更するだけでも無駄な時間や手間がかかってしまいます。
また数値ぐらいであれば設定ファイルなどで対応は出来ますが、計算式などの挙動は普通は設定ファイルに入れることは出来ません。
※式は一定で係数を変えるぐらいなら可能ですが、計算式自体をそもそも別なものに置き換えるということは出来ません
これらを可能にしたのがLuaであり、例えばゲームのパラメータを定義したい場合はLua内にパラメータに対する値を記述したり、プログラム側から何らかの処理を行わせたい場合は、スクリプト内の関数を呼び出したりといったことが出来ます。
さらにLuaではパラメータとして単に固定値を入れるだけではなく、計算式や関数を使って値を入れることが出来たり、また関数を呼び出す時にパラメータを引数として渡し、ゲーム側でそのパラメータを使って処理を行ったり、逆にゲーム側が持っている値をLuaに渡すことで、Lua側でそれに対応した処理を行ったりと言ったことが出来ます。
なお、Luaは上から下までの行を順番に実行していくという単純なスクリプト言語ではなく、CやBasicなどの一般的なプログラミング言語のように自分で関数を作ってこれを呼び出すことも出来ます。
このため同じような処理は関数にしておき、必要なときに呼び出して使用するといった使い方も可能で、さらにLuaにはスタックというの概念があるため、ある関数の中でまた更に同じ関数を呼び出すといった、再起呼び出しという複雑な処理も可能です。
そして一番重要な点は、Luaは通常のテキストファイルなのでWindows標準のメモ帳などで編集することが出来ます。
このため、誰でもすぐにLua言語を使ってプログラムを作ること出来るという利点があります。
※charatbeatHDXはLua内の文字列は全てUTF8で処理されているため、Luaを保存する場合は出来るだけBOM付きのUTF8としてください。
一応SJISにも対応はしているため、よく分からなければ普通に保存するだけでも動作はするはずです。
ちなみに以下は雑談ですが、まずLuaとはポルトガル語で「月」という意味だそうです。
詳しくは
Wikiにありますが、Luaはセガやバンダイナムコ、スクエアエニックスなどの大手ゲームメーカーにも採用されている有名な組み込み型の言語です。
また変数に型の無いスクリプト言語の中で最速な言語だそうで、しかもMITライセンスということで表記さえあれば特に使用に制限が無く商用として使うことも可能なので、ウチの会社でも既に中枢に組み込まれていたりして、I/O制御や挙動エンジンなどで大活躍しています。(まぁそのLuaエンジンはこのcharatbeatHDXから派生してたりするのだけれどもw)
ところで、2030年から小学校でプログラミングの授業が必修となりますが、学校によってはLua言語もこのプログラミング授業に採用されるかもしれません。
実は今仕事で学校関連のプログラムコンテンツを作っていたりして、今のところはまだ言語というよりはそもそもプログラムというのはどういうもので、どういった感じで処理が流れていくのかだけを教えるような感じなのですが、そこから4年後には今度はプログラム言語を使って実際に何か作ってみるといった流れになる気がします。(教科書はだいたい4年ごとに改変される)
もしくは学校に新しくプログラミング部みたいものが出来て、その中でやりたい人はもっと高度なものをやっていったり、さらに小学生対象のプログラミング大会なんてのもやり始めるかもしれません。(実は自分は高校生の時に学校経由でプログラムコンテストに応募したことがあったりw結果は何も来なかったけどねwww)
まぁ何が言いたいのかというと、これからは小学生でもプログラムが分かるようになるから負けてられないよねって話w
ちなみに今一番困っているのはプログラムを教えられる先生がいないというwww
Luaのバージョン
charatbeatHDXでは
Luaバージョン5.2.2が使用されています。
このバージョンで使用出来る関数は全て実行は出来ますが、一部の関数は意図した動作を行わない可能性があるため、詳細はhdx関数の各ページを確認してください。
またcharatbeatHDXに組み込まれているLuaエンジンには、プログラムしやすいように少し拡張が行われております。(下記参照)
Lua言語の詳細は以下のリファレンスマニュアルを参照してください。
http://milkpot.sakura.ne.jp/lua/lua52_manual_ja.html
※Luaで使用可能な全ての関数の詳細が載っています
文法
LuaはC言語やJavaScript、VisualBasicの記述方法を合わせたような文法になっています。しかし基本的な記述方法はどれも同じであるため、Luaのルールさえ分かってしまえば他の言語で記述できるようなプログラムやアルゴリズムはそのまま使用出来ます。
Luaはプログラムの区切りを空白や改行などで自動的に解釈しますが、C言語のように「;(セミコロン)」で明示的に区切ることも出来ます。このあたりは自分で見やすい方法で記述してください。
charatbeatHDXに組み込まれているLuaは、C言語やVisualBasicに慣れている人のために、ある程度その言語と同じ記述方法が使用可能なようにちょっとだけ拡張されています。(詳しくは以下を参照)
コメント
Lua上でコメントを記述するには、「(ハイフン2個)」によりそれから改行までを無視する書き方と、「」と「」によるブロックコメントの2種類があります。
これ以外にこのゲームではC言語風のコメント「」と、「」「」によるブロックコメントが記述可能になっています。これらは単に「」と「」、「」に置き換えているだけなので、それ以外のスクリプトには一切影響はありません。
例)
a = a + 1; … aに1を足すのをコメント化
b = b - 1; … bに1を引くのをC言語風にコメント化
… cに1を足す部分とdに1を引く部分をまとめてコメント化
c = c + 1;
d = d - 1;
… eに1を足す部分とfに1を引く部分をまとめてC言語風にコメント化
e = e + 1;
f = f - 1;
数値
Luaには整数値と小数値の区別はありません。しかし計算時には常に小数値として扱われるため、明示的に整数(小数切捨て)とするためにはmath.floor()関数を使用する必要があります。
Lua上で数値を定義する場合は10進数と16進数が使用出来ます。先頭に0xが無ければ10進数、0xを付けると16進数となりますが、これらの値はLua内部では小数値として演算処理されます。なお、演算後に整数値として参照する場合、Luaは32bitの整数値として変換しますが、この時32bitの値より大きな値は正しい値に変換できません。
マイナス値を表すには「-」を指定しますが、プラス値を表す「+」は足し算の意味となるため、プラス値はそのままの数値を記述する必要があります。
※Lua5.2.2の64bit版にて何故か0xFFFFFFFFが正しく処理出来なかったため、charatbeatHDXに組み込まれているLuaライブラリではこれを修正しています。このため、色の指定などでARGB形式の32bit値を直接16進数で表すことが出来ます。
例)
a = 100; … 値を代入(内部的には小数値の100.000となっている)
b = -100.1234; … 値を代入
c = 0x12345678; … 16進数で値を代入(内部的には305419896.000となっている)
d = a * b; … 掛け算(dは-10012.34となる)
color = 0xFFFF8000; … アルファ1.0、赤1.0、緑0.5、青0.0の色を定義
変数
Luaの変数は他の言語のように任意の変数名に対して値を入れることが出来ます。しかしLuaには型という概念はなく(内部的にはあるがスクリプト上は無いに等しい)、変数には文字列や数値などを自由に入れることが出来ます。なお計算などで参照される場合、文字列だった場合は数値に変換出来るものは数値となって計算に使用され、逆に数値を文字列として使用する場合は自動的に文字列に変換されて処理されます。
変数に値を入れる場合、特に明示しない場合はグローバル変数となります。Luaでは関数内しか使用しないローカル変数も定義することが出来ます。この場合は変数名の最初に「local」と定義することで、明示的にローカル変数を定義することが出来ます。
関数で同じ名前の変数を使用する場合、グローバル変数として定義していると値が共有されるため、意図しない値が入っている可能性があります。このため不具合が発生しないように、明示的にローカル変数として定義しておくことをお勧めします。
例)
グローバル変数
g_mTitleName = "テスト";
g_iCount = 1000;
ローカル変数
function Func()
local count = 2000;
end
配列
Luaの配列は全て連想配列で、連想配列とはつまりキーとなる文字に対する値が入るという感じの参照方式となります。
このため配列数という概念は無く、JavaScriptと同じで異なる名前で何個でも登録することが出来ます。
値を入れたり参照する際はC言語やVisualBasicのように「<変数名>.<文字列>」とドットで繋げて文字列を指定する方法と、「変数名["<文字列>"]」と配列名で指定する方法があり、前者のドットで繋げる方法では文字列の最初に数値が使用できないといった制限があります。
注意点として他の言語と違い
Luaの配列番号は1から始まります。
なお、これは定義の段階でデータを指定した場合の話で、実は自分で値を入れていく場合は0から指定することが出来ます。
最初からデータを指定した配列にアクセスするには「変数名[<配列番号>]」とダブルクォーテーション無しで配列番号を指定することで可能ですが、定義時にデータが入っている配列は1から始まっているため、「変数名[0]」を指定してしまうと値が入っていないことになるので気をつける必要があります。
例)
パターン1
data = {}; … 空の配列定義
data.name = "名前"; … "name"という名前に文字列を代入
data.count = 100; … "count"という名前に数値を代入
data["name2"] = "名前"; … "name2"という名前に文字列を代入
data["count2"] = 200; … "count2"という名前に数値を代入
パターン2
data = {}; … 空の配列定義
data[0] = 100; … インデックス0に100を入れる
data[1] = 200; … インデックス1に200を入れる
※自分で配列番号を指定して値を入れる場合はdata[0]も可
(実は内部的には文字列として扱われるためdata[-1]という配列も作れる)
パターン3
data = { "名前1","名前2","名前3" }; … データ込みの配列定義
trace( data[1] ); … 配列の先頭を参照
trace( data[2] ); … 配列の2つ目を参照
trace( data[3] ); … 配列の最後を参照
※初期データの入った配列は1からのためdata[0]はNG
ループ
Luaでは他の言語にもあるforやwhileのようなループ処理が行えます。ループの終わりは必ずendで閉じている必要があります。また、途中でループから抜けるにはbreakを指定します。
これらの挙動は他の言語と多少挙動が異なるため詳しくはLuaの専門書などで確認してください。
※forで使用したiはfor内でしか使用できない(通常の変数ではない)など
例)
for i=0,9 do
a = a + 1
end
while a>0 do
a = a - 1
end
分岐
Luaでは他の言語と同じくifによる条件分岐が可能です。ifは必ずendで閉じている必要があります。また入れ子構造にしたりすることも可能です。
Luaでは「elseif」と「else if」はスペースの有無で意味が変わります。以下のようにブロック的に見た場合、まったく別な処理を行ってしまいます。
パターン1(スペース無し)
「elseif」の場合はそれぞれ別々に処理
if a==1 then
<aが1の場合の処理>
elseif a==2 then
<aが2の場合の処理>
elseif a==3 then
<aが3の場合の処理>
end
パターン2(スペースあり)
「else if」の場合はelseは最初のifの条件が偽だった場合に全て適用されるものとなり、ifはその中でのもう1つの条件文となってしまう
if a==1 then
<aが1の場合の処理>
else if a==2 then
<aが2の場合の処理>
end …「if a==2」はもう1つのif文となるためendが必要
end
整理すると…
if a==1 then
<aが1の場合の処理>
else … elseとifは別物
<aが1以外の場合の処理>
if a==2 then
<aが2の場合の処理>
end …「if a==2」はもう1つのif文となるためendが必要
end
条件演算子はLuaで使用出来るものの他に、C言語風の「!=」とVisualBasic風の「<>」が使用可能です。
a==b |
aとbが等しければtrue |
a~=b
a!=b
a<>b |
aとbが等しくなければtrue |
a>b |
aがbより大きければtrue |
a<b |
aがbより小さければtrue |
a>=b |
aがb以上ならばtrue |
a<=b |
aがb以下ならばtrue |
さらに複数の条件を繋げる場合の論理演算子にて、C言語風の「&&」「||」が使用可能です。
条件1 and 条件2
条件1 && 条件2 |
条件1と条件2がどちらも満たしているならtrue |
条件1 or 条件2
条件1 || 条件2 |
条件1と条件2のどちらかが満たされているならtrue |
not 条件 |
条件が満たされていなければtrue
※条件~=trueでもよい |
サブルーチン
Luaではユーザー関数を作成してこれを呼び出すことが出来ます。また、関数内でさらに自分自身を呼び出す再起処理も出来るので、かなり複雑な処理を実装することが出来ます。
function UserFunc( param )
trace( "ユーザー関数が呼ばれたにょ! : "..param );
end
function OnRunGame( upd_frm )
UserFunc( "引数も渡せるにょ!" ); … UserFunc関数を引数付きでコール
end