*いんとろだくしょん [#n2cd725f]
**第0章の目的 [#xa707968]
第0章はLuaを使用してモデルを動かすために何をどうすればいいか大雑把に示すことを目的としています。つまり、''詳細だったり正確だったりする説明は、Luaの文法説明を担当する第1章に丸投げです。''適宜リンクを張りますので、不明な点やより詳しい情報が欲しい場合はそちらを参照してください。
本章は、「Keyブロックを使った『無制御モデル』は作れるけど、Luaは使えない」という人を想定して記述しています。逆に言うと、''「モデルのつくりかたがわかりません!」''という人は残念ながら対象外です。RigidChipsWiki(%%http://f42.aaa.livedoor.jp/~nemucat/pukiwiki14/pukiwiki.php %%http://www4.atwiki.jp/rigidchips/pages/16.html )に''チュートリアル/きほんけいがあるので、そちらの方を読んでください。''ドキュメント作成側の事情を正直に語れば、「変数って何ですか!わかりません!」といった問いはあまりにも根源的であるが故に逆に答え辛く、ドキュメント製作者の能力を超えるものがあるので、ここでは既存のドキュメントを参照してくれ!という態度をとります。
また、本章ではRigidChipsのモデルファイル(*.rcdまたは*.txt)をテキストエディタを用いて編集することを前提とします。プログラミング用の高機能なテキストエディタが望ましいですが、''ぶっちゃけWindows付属のメモ帳で十分です。''RigidChipsDesigner(RCD)やRigidChipsModeler(RCM)といったモデルエディタは便利なのですが、Val変数を所掌するValブロックやモデルの構造を記述するBodyブロックとの対応関係を考える視座を導入したいので、今回は生のテキストを扱うことにします。ついでに、モデルエディタ個別の操作説明とか書くのが面倒です。こっちが本音ですね。
以上の内容をまとめます。
-本格的な文法説明は他をあたれ
-簡単なモデルは既に作れる人が対象
-モデルファイルはメモ帳で開こう
**RigidChipsDocument(*.rcd)の書式にみるLuaブロックの位置付け [#t31ecf63]
以下に実際のrcdファイルの全文を示します。
長いですが、とりあえずここで見るべきものはさほど多くありません。大雑把に読み飛ばしましょう。
#codeprettify{{
//BasicCar
Val
{
Brake(default=0,min=0,max=80)
HBrake(default=0,min=0,max=100)
Handle(default=0,min=-20,max=20)
Engine(default=0,min=-2500,max=2500)
}
Key
{
// 0:Engine(step=-500)
// 1:Engine(step=500)
// 2:Handle(step=-0.5)
// 3:Handle(step=0.5)
// 7:Brake(step=30),HBrake(step=20)
// 8:HBrake(step=20)
}
Body {
Core(){
N:Chip(){
N:Rudder(angle=Handle){
W:Frame(){
W:Wheel(angle=90,brake=Brake){
}
}
E:Frame(){
E:Wheel(angle=90,brake=Brake){
}
}
}
}
S:Chip(){
W:Frame(){
W:Wheel(angle=90,power=Engine,brake=HBrake){
}
}
E:Frame(){
E:Wheel(angle=90,power=-Engine,brake=HBrake){
}
}
}
}
}
Script
{
print 0,"Welcome to Rigid-Chips World."
print 1," FPS=",_FPS()," Chips=",_CHIPS()," Weight=",_WEIGHT()
print 2," Width=",_WIDTH()," Height=",_HEIGHT()
print 3," Faces=",_FACE()
print 4," Vel=",_VEL()
print 5," R=",_RED(32,32)
print 6," G=",_GREEN(32,32)
print 7," B=",_BLUE(32,32)
}
Lua
{
function main ()
out( 0,"Welcome to Rigid-Chips World." )
out( 1," FPS=",_FPS()," Chips=",_CHIPS()," Weight=",_WEIGHT() )
out( 2," Width=",_WIDTH()," Height=",_HEIGHT() )
out( 3," Faces=",_FACE() )
out( 4," Vel=",math.sqrt(_VX()^2+_VY()^2+_VZ()^2) ) --_VEL()はそういえばLuaにないので、めどいよぬるぽ
out( 5," R=",_RED(32,32) )
out( 6," G=",_GREEN(32,32) )
out( 7," B=",_BLUE(32,32) )
if _KEY(0)>0 then
ENGINE = ENGINE - 500
elseif _KEY(1)>0 then
ENGINE = ENGINE + 500
else
ENGINE = 0
end
if _KEY(2)>0 then
HANDLE = HANDLE - 0.5
elseif _KEY(3)>0 then
HANDLE = HANDLE + 0.5
else
if HANDLE > 0 then
HANDLE = math.max(0,HANDLE-5)
else
HANDLE = math.min(0,HANDLE+5)
end
end
if _KEY(8) then
HBRAKE = HBRAKE + 20
end
if _KEY(7)>0 then
BRAKE = BRAKE + 30
HBRAKE = HBRAKE + 20
else
BRAKE = 0
if not (_KEY(8)>0) then
HBRAKE = 0
end
end
end
}
}}
まず、最初のほうにある Val{ なんとかかんとか } という塊がいわゆる''Valブロック''と呼ばれるものです。モデルエディタから変数を定義している方はあまり馴染みがないかもしれませんが、この位置にこんな感じで記述されています。
Valブロックの役割はモデルの動作に必要な変数を定義することにあります。逆に言うと、Valブロックで変数を定義しないと、動くモデルは作れません。これは当然のことのように思われるかもしれませんが、Luaを使用する場合には重要なことです。なぜならば、Luaブロックでは、Luaブロック内でのみ通用する変数(''Lua変数'')を定義することができますが、それらではWheelを回したり、Jetを噴射したりすることはできないからです。Luaスクリプトによってモデルを動かそうとするときは、必ずValブロックで定義した変数(Lua変数との対比から''Val変数''とも呼ばれる)を使用しなければなりません。
次に目に付くのがKey{ なんとかかんとか }という塊です。これを''Keyブロック''と呼びます。ScriptやLuaを用いない「無制御モデル」の場合、この部分でキー操作の設定を行います。
上述のサンプルコードはデフォルトモデルであるBasicを改造したものなので、コメントアウトによって無効化された記述が残っていますが、このブロックの機能は''例外なく全てLua側で代替可能''なので、通常、中には何も書く必要はありません。
その次に目につくのが、Body{ なんとかかんとか }という塊です。これを''Bodyブロック''と呼びます。モデルの構造や形状を定義している部分で、まさにモデルエディタなどで弄り回している部分になります。
ここで注意すべき点は、Chipの名前指定の動作です。
#codeprettify{{
Body {
Core(name=CORE) { }
}
}}
このように記述すると、名前をつけたChipの番号を示す変数が定義されます。上記の例ではCoreチップの番号である0を示す変数COREが作成されます。Valブロックで定義したものではありませんが、このChip名変数も便宜的にVal変数に含めて取り扱います。
さらに次には、Script{ なんとかかんとか }という塊が存在します。これは、RigidChipsにLuaの実行環境が搭載される前から存在している、RigidChips組み込みScriptを記述するためのスペースで、''Scriptブロック''と呼ばれます。
Luaブロックと同様にモデルをプログラムによって制御するために利用されますが、Luaの方が高機能であるため、現代においてもはや積極的に利用する実益はありません。上述のサンプルコードのように、ScriptブロックとLuaブロックを併記した場合には、Luaブロックのみが実行されることを把握しておけば足りるでしょう。
最後に存在するLua{ なんとかかんとか }という塊が、これからモデルの制御のために弄り回す''Luaブロック''です。{ と } の間にLuaのコードを記述します。
以上をまとめるとこんな感じになります。
-Val変数はしっかり定義しよう
-Keyブロックは今やいらない子
-Bodyブロックはモデルエディタにでも任せとけ
-Scriptブロックもいらない子
-Lua{ この中にLuaのコードを書くんだ! }
**Luaってどう書くの? [#i51c2c86]
問い 最速のレースカーに必要なLuaを教えてください!
答え 知らねぇよ
自分の思う通りにモデルを動かすために、どんなLuaプログラムを書けばいいのかというのは、基本的に''自分で考えて自分で決めるべき問題''です。普遍的一般的な正解が存在するわけではありませんから、他人は何も言えないのが現実です。
しかし、Luaが正常に動作するためには''守らなければならないルール''があります。いわゆる、''Luaの文法''です。このルールを無視すると、およそ貴方の書いたLuaプログラムは正常に動きません。RigidChipsの窓の上の方に赤いエラー文字列が出てきて、モデルは思い通りに動くどころか、全く動かない状態になるでしょう。
そういうわけで、最初の目標は''エラーの赤文字が出ないように正しいLuaプログラムを書く''ということになります。最速の車だか超絶性能の曲芸飛行機だか知りませんが、そんなものは後回しです。幸いなことに、エラーを出さないだけなら、実は簡単に書けてしまうので、以下にエラーの起こらないLuaブロックの例を示します。
#codeprettify{{
Lua{
function main()
end
}
}}
テキストエディタで、モデルのBodyブロックの次に以上の4行を追加すると、とりあえずLuaプログラムが動きます。もっとも、このサンプルを動かしてみるとちょっと不安な気持ちになるかもしれません。というのも、これは''何もしないプログラム''なので、4行を追加してみても何も起こらないからです。
そういうことで、最低限動いていることがわかるように、何か書いておきましょう。
#codeprettify{{
Lua{
function main()
out(0,"Hello World !")
end
}
}}
Luaブロックを以上のように書き換えると、画面の左上に Hello World ! という文字が表示されるはずです。表示されない場合は、RigidChipsのメニューにあるHelpからShow Script Messageが有効になっているか、同じくメニューにあるRegulationからUsable Scriptが有効になっているか確認してください。有効であることを確認しても文字が表示されない場合は、貴方のミスなので反省してください(編集しているファイルとRCで読み込んでいるファイルが実は別物だったとか、そういうどうしようもないミスはよくあることなので注意だ!)。
さて、この''function main() 〜 end''というやつが非常に大切です。理屈なんかどうでもいいので、とりあえず書いておいてください。こういう「真面目に説明すると難しすぎるから、とりあえず文句を言わずにこう書いとけ」というものを''おまじない''と表現しますが、function main() 〜 end というのは、まさしくそのおまじないに相当します。
その正体はRigidChipsによって毎フレーム呼び出される特別な名前の関数なのですが、その辺の理屈を理解していてもいなくても''書けば動く''ので、大人しくそういうものだということで納得してください。
以上の内容をまとめるとこんな感じです。
-難しいことを考えるのはエラーの起こらないプログラムを書けるようになってから
-御託はいいから、とりあえず、function main() endは必ず書いておけ
**インデントとコメントの薦め [#tde52bf1]
プログラマの格言に''「プログラムは思い通りに動くんじゃない、書いた通りに動くんだ」''というものがありますが、実際、どんな風に書こうとも、文法に適合している限り、Luaプログラムは書いた通りに動きます。(思い通りには動きません)
簡単なプログラムを書いているうちはいいのですが、段々Luaに慣れてきて複雑なプログラムを書くようになると、「何処に何のコードが書いてあるのかわからない><」という事態に直面します。自分の書いたコードなら、日の浅いうちは大丈夫だったりしますが、放置してから時間が経つと、「このコードわけわかんねぇ><」という事態が頻発します。
さて、そんなカオス状態に陥ったコードのサンプルを見てみましょう。
#codeprettify{{
divide_path = function (str)
local t = {}
local count = 0
for s in string.gfind(str,"(%w+)%.?") do
count = count+1
t[count] = s
end
return t,count
end
assert_path = function (str)
local t,c = divide_path(str)
if c<=0 then return false, nil, nil end
if c==1 then return true, _G, _G[t[1]], t[c] end
local tmp
local parent = _G
for n=1, c-1 do
tmp = parent[ t[n] ]
if object_type(tmp) ~= "namespace" then return false, parent, nil else parent = tmp end
end
tmp = parent[ t[c] ]
return true, parent, tmp, t[c]
end
}}
実に読み辛いです。何故読み辛いかというと、コードの記述に意味のまとまりが反映されていないからです。Luaの構文解析プログラムにとっては何の問題もないのですが、コードを読む人間にとっては深刻な問題です。
そこで、''インデント''というものをつけてみます。インデントとは、コードの行の左端につける空白のことです。百聞は一見に如かずとかいうので、とりあえずやっちゃってみましょう。
#codeprettify{{
divide_path = function (str)
local t = {}
local count = 0
for s in string.gfind(str,"(%w+)%.?") do
count = count+1
t[count] = s
end
return t,count
end
assert_path = function (str)
local t,c = divide_path(str)
if c<=0 then return false, nil, nil end
if c==1 then return true, _G, _G[t[1]], t[c] end
local tmp
local parent = _G
for n=1, c-1 do
tmp = parent[ t[n] ]
if object_type(tmp) ~= "namespace" then
return false, parent, nil
else
parent = tmp
end
end
tmp = parent[ t[c] ]
return true, parent, tmp, t[c]
end
}}
インデントをつけると、こんな風に左の空白の量で何となく意味の纏まりが見えるようになります。人間がコードを読んだとき、どれだけ意味がとりやすいかという指標を''可読性''などと表現しますが、一般的には可読性の高いコードほど不具合が発生した際の対処が容易になります。面倒だからとインデントをつけないと、後々酷い目に遭うので、しっかりとつけておくことをお勧めします。
なお、インデントに使う空白文字についてはちょっとした派閥抗争があります。ここではTab文字を利用しましたが、Tab文字は環境やエディタによって見え方が変わってしまうことから、Tab文字を半角スペース4文字で代用すべきとする主張も存在するようです。ドキュメント製作者としては''全角スペースさえ使わなければそれでいい''と思うので、暇ならばその種の論争を参照して適宜自分の態度を決してください。
さて、インデントによって幾分意味付けは明瞭になりましたが、それでもこのコードには何だかよくわからない部分が多々残っています。そこで、「この部分では一体何をしているのか」という情報をコード内に記述することにします。
#codeprettify{{
--与えられたパス文字列を分割する
divide_path = function (str)
local t = {}
local count = 0
for s in string.gfind(str,"(%w+)%.?") do
count = count+1
t[count] = s
end
return t,count --分割したパス文字列を含むテーブル, 分割数
end
--与えられたパス文字列を検証し、該当する名前空間を返す
assert_path = function (str)
local t,c = divide_path(str)
if c<=0 then return false, nil, nil end --分割数0なら失敗
if c==1 then return true, _G, _G[t[1]], t[c] end --分割数1なら検証の必要なし
--分割数2以上の場合、階層構造を検証する
local tmp
local parent = _G
for n=1, c-1 do
tmp = parent[ t[n] ]
--もし名前空間でないものが含まれていたら失敗
if object_type(tmp) ~= "namespace" then
return false, parent, nil
else
parent = tmp
end
end
tmp = parent[ t[c] ] --該当する名前空間が存在しない場合、nilで構わない
return true, parent, tmp, t[c]
end
}}
正直、あまりわかり易くなった気がしないけど気にしたら負けだ!
このような、コード中に書き加える説明文を''コメント''といいます。上述の例では、--から改行までが全てコメントになります。コメントはLuaプログラムとして実行されることはありません。そのおかげで色々と自由に書けます。コメントがあるとコードを読み解く助けになるので、できる範囲で簡潔にコメントを残しておくことをお勧めします。
コメントはプログラムとして実行されないことを以下の例で確認してみましょう。Luaブロックを丸ごと示します。
#codeprettify{{
Lua{
function main()
--out(0,"Hello World !")
out(1,"Hello alternative World !")
end
}
}}
実行すると、Hello World ! ではなく、Hello alternative World ! と表示されます。--をつけたり取っ払ったりして、挙動を確かめてみてください。
余談になりますが、このようにコードの一部をコメントにして動作しなくすることを、''コメントアウト''と言います。簡単に無効化したり、元に戻したりできるので、一時的に一部のコードを無効化したい時に便利です。いちいち書き直したりする手間が省けます。
以上の内容をまとめるとこんな感じです。
-プログラムは書いた通りにしか動かない
-インデントをつけて人間に優しい記述を心がけるべき
-適切なコメントを残しておくと、もっと人間に優しい
-コメントはプログラムとして実行されることはない