Hatena::ブログ(Diary)

senzogawaのNな日々 このページをアンテナに追加 RSSフィード

2004 | 11 | 12 |
2005 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 12 |
2011 | 01 | 03 | 04 | 05 | 06 |
2012 | 01 | 08 | 09 |
2013 | 02 |

2010-01-30

NScripterハンズオン主催者レポート

| NScripterハンズオン主催者レポートを含むブックマーク NScripterハンズオン主催者レポートのブックマークコメント

 あまりイベント主催者側がレポートを書くことって無いような気がしたけど、期待してくださった方々のためにも、せっかくだから書いておく。

 NScripterというよりLuaの話題の方が多いので、まだLuaに触れてない人が見ても面白くないかも。そのうちNScripterからLuaへ移行するための特別講座でもやろうと思う。


導入

 午前中は準備不足のためにあまり大したことはしてない。

 LuaNScripterレイヤーが逆転する話を絡めてSGEについての紹介をして、次に作成物をブロック崩しと決めて、バージョン管理ツールとしてTortoiseHG*1のインストールをして、起動と終了までを作った。


 制作にあたっては、ストーリーの抽出を簡単に実施したあと、ランク付けを行い*2、対象のストーリーについてタスク分割して潰していく、という手法をとった。


 コード的には、package.pathにパスを追加して、requireの対象moduleを暗黙的に取得する記述の参考として君影草工房さんとこのgetkeyプラグインを使おうとしたら、何故かうまく動かず*3、格好悪いところを見せてしまった。


 パス指定は、例えば「import」フォルダ配下に各プラグインラッパーのluaソースファイルをおいた場合、以下のように指定するといい。

package.path = package.path .. ';./import/?.lua'

反復

 再起動が面倒になってきた頃、Luaの強みである動的再読み込みを実装。

 といっても簡易版で、Lua以外のリソース*4を読み込んでいた場合にはNScripter側のリセットもうまく実行する必要がある。

 コードは以下のような記述。実にシンプル。


function reload()
  package.loaded['game'] = nil
  require 'game'
  -- 以下、アプリケーションのステートを最初に戻す。
end

進行

 リセット機能を実装した後は、それなりにスムーズに進行できた。

 やはりちょっとした変更を即時に確認できるというのは強い。

 画像の描画に関しては、透過周りが面倒だったので、テクスチャではなくNScripterおなじみのdraw系命令で実装した。


 LuaからNScripterの命令で描画する際に気を付けることは、座標系で小数になってしまった場合の対処で、math.floorを使って切り捨てるなど、整数に直してから文字列化しないと、命令文として不正な記述になることくらいだろうか。


 あとは描画対象が多いと記述が面倒になるが、描画対象の座標をテーブルに入れておき、テーブルに突っ込んだ描画対象をまとめて描画する仕組みを作ってしまえば、そう面倒でもなくなるため、実際にそうした。

 以下のようなコードだ。

for i,v in ipairs(drawables) do
  NSExec(string.format('drawsp 0,0,255,%d,%d', v.x, v.y))
end

 formatを毎回呼ぶ形になるのでたぶん遅いが、遅さで支障が出るまでは、わかりやすい形を保つのが基本だ。

 そもそもテクスチャ系にすれば高速になるので、大抵の場合、draw系命令のまま速度改善するのは不毛だろう。Lua使うならテクスチャ系の利用をお勧めする。


問答

 途中でpairs関数について質問を受けた。

 確かに単純なカウンタ逓増・逓減ループとの違いを考えるとわかりにくい部分だが、Luaのテーブルに慣れると大したことはない。


 要するに、テーブルの内容について要素を一つずつ走査しているだけなので、pairsとipairsの違いが生じるのは、キーが名前付きか順序かという違いにある*5


 オブジェクト指向では割と類似のものがある記述なので、for-in-doに慣れて損はないけど、pairsとipairsは間違いやすい。気をつけよう。


結末

 時間がなく、リリースについてはあまり話せなかったが、バージョン管理ツールを使っている場合、リリース用に管理を分岐させるのが普通で、TortoiseHGの場合、簡単にやると単にCloneするだけということを解説し、実践。


 実際には単なるCloneだと色々余計な開発用のファイル群も持ってきてしまうため、そこから削除して、リリース用の占有リポジトリを確保する形になる。

 致命的なバグがあった場合には、元の開発用リポジトリで修正して、そこから必要なファイルのみマージをかけたりするが、そこまでやると、今の日本の開発現場でもあまり見ないくらいしっかりした構成管理となるので、個人ではまずやらない。


 最後にマップファイルによりステージを構成する場合、CSVが相性がいいという話をして、CSVは記述内容にもよるが*6Luaだと非常に簡単に読み込めるという実装を見せた。

 時間がなく、ファイルから読むところを話せなかったが、以下のようにするといい。

local data = {}
local comment_char = string.byte(";",1)
for line in io.lines("filename") do
  if line:byte(1) ~= comment_char then
    table.insert(data, assert(loadstring("return {" .. line .. "}"))())
  end
end

 nilがある場合はひねりが必要になるが、大抵は上記のようにloadstringで事足りる。

 行ごとに読まなくても何とでもなる。

 テキストに関しては、どうにかLua文にして読ませるのが色々と楽だ。

まとめ

 参加者の方は割とプログラミングがわかっていたので、少し物足りなかったかもしれない。

 D言語を使ったLuaプラグインの作成方法を話しても面白くなっただろうか。


 あまりプログラミングに近くない分野にいると、困るのは制作の過程を見ることができないという点で、それらを一緒に体験していくというのは割と学習効果が高いのではないかと思う。

 何にしろ、多少は役に立つ情報を渡せたようなので、開催して良かった。

*1MercurialGUI

*2:本来なら大きさを見積もるが、機動性を重視して優先順序だけ決めた

*3:詳細は追ってないが、多分こちらの設定ミス

*4:ここではスプライト番号などNScripter側の情報

*5:もう少しきちんと書くとイテレータ関数の違いだが、詳細はリファレンスのfor文と関数の説明を参照

*6:文字列は''で囲ったりする必要があるなど