MEXSLTをspark projectにコミットしてみた
明日ってのは、確かに地球の自転周期から言えば、24時間かもしれないがね
日光・鬼怒川の自転周期から言えばもっと長いのだよ。
すみません。
週末、鬼怒川に温泉入りに言っててすっかりブログが更新できませんでした。
で、一週間が過ぎましたが、ついにspark projectに自分の作ったライブラリをコミットしましたよ!
MEXSLT
このライブラリは、Flexユーザー向けのXML解析サポートライブラリとでも言いましょうか。
XSLTっていう、XMLを別のXMLやらHTMLやらに変換するマークアップ言語があるんですが、それを参考にして
FlashでXMLを読み込んで、それを自分で定義したクラスに変換したりできるものを作りました。
サンプルとして、こんなXMLを読み込むとします。
ちなみに、このXMLはなんちゃって個人情報です。
<?xml version="1.0" encoding="UTF-8"?>
<records>
<record>
<name>宇多田 信輔</name>
<ruby>うただ しんすけ</ruby>
<mail>utada_shinsuke@example.com</mail>
<sex>男</sex>
<age>65</age>
<birthday>1944/1/1</birthday>
<married>既婚</married>
<bloodtype>A型</bloodtype>
<prefecture>埼玉県</prefecture>
<curry>ぶっかけ・せき止め派</curry>
</record>
<record>
<name>竹下 未來</name>
<ruby>たけした みらい</ruby>
<mail>takeshita_mirai@example.com</mail>
<sex>男</sex>
<age>24</age>
<birthday>1984/10/12</birthday>
<married>既婚</married>
<bloodtype>A型</bloodtype>
<prefecture>新潟県</prefecture>
<curry>ぶっかけ・せき止め派</curry>
</record>
<record>
<name>北村 翔太</name>
<ruby>きたむら しょうた</ruby>
<mail>kitamura_shouta@example.com</mail>
<sex>男</sex>
<age>37</age>
<birthday>1972/1/8</birthday>
<married>既婚</married>
<bloodtype>O型</bloodtype>
<prefecture>神奈川県</prefecture>
<curry>手前ルー・せき止め派</curry>
</record>
...
</records>
このXMLを読み込んで、自分で定義したクラスにそれぞれの値を設定したりする作業って割とよくあると思うんですよ。
そこらへんを、MXMLを使って簡単に書けたらいいなという目論見で作ったライブラリの使用例がこちら。
(例1)
-
<mex:MEXSLT id="parser" output="array">
-
<mex:xslt>
-
<mex:Template match="records/record">
-
<mex:New method="push" product="{DummyData}">
-
<mex:ValueOf select="name" target="name" />
-
<mex:ValueOf select="ruby" target="ruby" />
-
<mex:ValueOf select="mail" target="mail" />
-
<mex:ValueOf select="sex" target="sex" />
-
<mex:ValueOf select="age" target="age" />
-
<mex:ValueOf select="birthday" target="setBirthday" method="invoke" />
-
<mex:ValueOf select="married" target="setMarried" method="invoke" />
-
<mex:ValueOf select="bloodtype" target="bloodType" />
-
<mex:ValueOf select="prefecture" target="prefecture" />
-
<mex:ValueOf select="curry" target="curry" />
-
</mex:New>
-
</mex:Template>
-
</mex:xslt>
-
</mex:MEXSLT>
mexってのは名前空間です。あらかじめ、xmlns:mex="http://da.rkome.me/2009/mexslt"というようにmexという名前空間を設定しておいてください。
例えば、<Application>の直下でこのXMLパーサの定義を行うなら、<Application xmlns:mex="http://da.rkome.me/2009/mexslt">という感じで。
まず、一番上のMEXSLTタグ。これは、XMLパーサの定義です。
output属性には、このパーサがどのような形式を返すかを指定します。arrayかobjectのどちらかです。
そして、このパーサがどのようにXMLを解析するのかを<mex:xslt>ノード以下に書きます。
ここからは、xsltプロパティの設定の仕方。
まず、Templateという大枠の定義を書きます。
match属性に、渡されたXMLのどのタグを元にインスタンスを生成するかをXPathで指定します。
このサンプルの場合は、recordsタグの子要素のrecordタグ以下の値を一つのクラスにまとめます。
一応、クラス定義はこんな感じ。
-
package me.rkome.da.sample.model
-
{
-
public class DummyData
-
{
-
public function DummyData()
-
{
-
}
-
-
public var name:String;
-
public var ruby:String;
-
public var mail:String;
-
public var sex:String;
-
public var age:Number;
-
public var birthday:Date;
-
public var married:Boolean;
-
public var bloodType:String;
-
public var prefecture:String;
-
public var curry:String;
-
-
public function setBirthday(dateString:String):void
-
{
-
var array:Array = dateString.split("/");
-
birthday = new Date(parseInt(array[0]), parseInt(array[1]), parseInt(array[2]));
-
}
-
-
public function setMarried(marriedString:String):void
-
{
-
married = (marriedString == "既婚");
-
}
-
-
public function toString():String
-
{
-
return "名前: " + this.name + "n" +
-
"よみ: " + this.ruby + "n" +
-
"メール: " + this.mail + "n" +
-
"性別: " + this.sex + "n" +
-
"歳: " + (this.sex == "女" ? this.age - 2 : this.age) + "n" +
-
"生年月日: " + this.birthday.toDateString() + "n" +
-
"結婚: " + (this.sex == "女" && !this.married && 30 <= this.age ? "募集中" : this.married) + "n" +
-
"血液型: " + this.bloodType + "n" +
-
"出身: " + this.prefecture + "n" +
-
"カレーの食べ方: " + this.curry;
-
}
-
}
-
}
このクラスへと変えるために、まずNewタグというのをTemplateタグの直下に書きました。
これは、match属性でマッチしたXMLからクラスを生成する時に使用するタグです。
method属性には生成したインスタンスを出力オブジェクトに対してどうするかを指定します。
サンプルの場合、出力オブジェクトはarrayなのでここではpushを指定しています。
例えば、出力オブジェクトがobjectだった場合、Newタグのtarget属性に設定先プロパティ名、method属性にassignを指定するとtarget属性で指定したプロパティに生成したインスタンスを代入します。
product属性には、生成するインスタンスのクラス名を設定します。{}で囲んでいるのは何故かというと、product属性の値はClass型だからです。
で、Newタグの直下にずらりと書いてあるのがValueOfタグで、これはNewタグで生成したクラスに値を設定するためのタグです。
特別なクラスを生成しないで、単にString型の配列を作りたいときは、Templateタグの直下にValueOfオブジェクトを書けばOKです。
例えば、このなんちゃって個人情報の名前の配列を受け取りたい場合はこんな感じ。
(例2)
-
<mex:MEXSLT id="parser" output="array">
-
<mex:xslt>
-
<mex:Template name="nameList" match="records/record">
-
<mex:ValueOf select="name" method="push" />
-
</mex:Template>
-
</mex:xslt>
-
</mex:MEXSLT>
さて、ValueOf タグの属性の説明。
select属性にTemplateタグで指定したXMLに対して、XPathでタグを指定します。
この例2の場合は、Templateタグのmatch属性にrecords/recordがあり、ValueOfタグのselect属性にnameがあるため、recordsタグの子要素のrecordタグの子要素のnameタグの値を指定していることになります。
method属性にはassign、push、invokeを指定できます。assignは代入、pushは配列に追加、invokeはtarget属性で指定した名前の関数をselect属性で指定した値を引数として呼び出します。
例1でinvokeを指定しているValueOfタグのtarget属性にはsetBirthdayとかsetMarriedといった関数の名前が書いてあります。
この二つはどちらもString型を引数に取る関数で、内部ではそれぞれ値をDate型やBoolean型に変換しています。
method属性のデフォルト値はassignなので、プロパティにselect属性で指定した値を代入する場合はmethod属性をいちいち設定しなくてもassignが設定されています。
この例には出てきませんが、ValueOfタグにはもう二つ重要な属性があります。
一つは、defaultValue属性です。select属性で指定した値がXML文書の中に記述されてなかった場合に設定する値を書くと、この属性で指定した値が設定されます。
もう一つは、required属性です。これもselect属性で指定した値がなかった場合の動作で、required属性をtrueに設定するとMEXSLTErrorをスローするようになります。デフォルトはfalseになっているので、必須のものにだけrequired属性をtrueに設定してください。
最後に、このように定義したMEXSLTクラスを実際に使う時です。
-
var result:Array = parser.parse(new XML(xml));
-
for each (var data
ummyData in result) -
{
-
println(data.toString());
-
println("================================");
-
}
parse関数でXMLを指定すれば、解析して戻り値として結果を返します。
required属性をtrueに設定した場合など、エラーがスローされる可能性がある場合はtry~catchでMEXSLTErrorをcatchするようにしてください。
また、複数のXMLパーサの定義を行いたい場合は、Templateをいくつも定義してTemplateタグのname属性に一意の名前をつけてcallTemplate関数でその名前を指定することにより、解析することが出来ます。
-
var names:Array = parser.callTemplate("nameList", new XML(xml));
-
for each (var name:String in names)
-
{
-
println(name);
-
println("================================");
-
}
導入方法
導入は、一番簡単なのはMEXSLT.swcをライブラリパスとしてFlexプロジェクトに追加します。
MEXSLT.swcはas3/MEXSLT/MEXSLT/binにあります。
次に、名前空間を書いてください。
Flexプロジェクトを作ると、大抵Applicationタグがルートになるはずなので、mx名前空間の次にでもmex名前空間に以下のURLで設定してください。
xmlns:mex="http://da.rkome.me/2009/mexslt"
これでMEXSLTを使えるようになるはずです。
もし分からなかったら、気軽に僕にメールを下さい。このブログにコメントして頂いても結構です。
ある程度サポート致します。
と言うわけで、鬼怒川から帰ってきたDarkOmemeはウラシマ効果によって、もう眠いわけです。普段なら3時まで平気で起きているのに、今日はまだ2時だというのに半分寝てます。
ではでは、おやすみなさい。
ゲームプログラミングのためのフレームワークについて本気出して考えてみた3
どうも、DarkOmemeです。
引き続きゲームプログラミングフレームワークについての記事です。
その前に、このゲームプログラミングフレームワークの名前が決まったので発表したいと思います。
「ME Framework」 です。
MEはMXML Extemporeの略です。
Extemporeってのは即興、即席、即座という意味です。
つまり『MXMLで即座に作れる!』という意味をもってME Frameworkという名前にしました。
実は、DarkOmemeのme(目)というダブルミーニングだという話も・・・。
--
昨日の続きです。
昨日説明したように、このME Frameworkはプレイヤーから入力があると、それをCommandという形でコマンドキューにpushします。
このコマンドキューをバックグラウンドで動作しているSingletonなクラスのCommandManagerがEnterframeイベントで逐次一つずつキューからコマンドを読み取って、実行しています。
まあ、このバックグラウンドの仕組みはあまり利用者が意識しなくてもいいようにしておきたいですね。
--
さて、今日の話は【MXMLで何を記述するか?】です。
僕がFlexのMXMLに感じている特徴としては、静的な情報を表現するのに向いている、ということです。
MXMLで一応、処理のフロー(?)も書くこともできるんですが、なんとなくいまいちだと思います。
(たとえば、Effectを定義する場合、回転→ブラー+フェードみたいな処理の流れは書ける)
やっぱり、もっと常に一定な情報を記述できた方がいいのではないかと考えます。
イメージとしてはHTML。
基本的な枠組みはHTMLで記述して、動的な処理についてはJavaScriptに任せてしまう、という思想。
今回考えているフレームワークもこんなHTMLのような感じで記述することでコードの見通しを良くしようというアイディアに基いています。
現在考えている&実装しているのは、以下のものが記述できます。
- 画面状態
- 入力受付状態
- 読み込みデータファイルリスト
- レイヤー構造
- UI
もっと他にも記述できるものはあると思いますが、とりあえず見通しがついているものはこの5つです。
実際のソースコードはこんな感じで記述できたらいいなあと思っています。
SampleGame.mxml
-
<?xml version="1.0" encoding="utf-8"?>
-
<me:GameApplication xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:me="me.rkome.da.front.*" layout="absolute" backgroundColor="#000000" width="640" height="480" currentState="SenarioPhase" xmlns:planes="me.rkome.da.planes.*" xmlns:io="me.rkome.da.io.*" xmlns:commands="me.rkome.da.commands.*" xmlns:data="me.rkome.da.data.*" xmlns:layer="me.rkome.da.layer.*">
-
<me:states>
-
<mx:State name="SenarioPhase">
-
<mx:AddChild position="lastChild">
-
<planes:Plane id="senario" currentInputState="{test}">
-
<planes:inputStates>
-
<io:InputState id="test">
-
<io:horizontal>
-
<commands:TraceCommandA traceText="A" />
-
</io:horizontal>
-
<io:vertical>
-
<commands:TraceCommandB />
-
</io:vertical>
-
<io:keys>
-
<io:Key code="Z">
-
<commands:ReloadCommand />
-
</io:Key>
-
</io:keys>
-
</io:InputState>
-
</planes:inputStates>
-
<planes:layers>
-
<layer:LayerList>
-
<layer:Layer id="system" />
-
<layer:Layer id="foreground" />
-
<layer:Layer id="message" />
-
<layer:Layer id="character" />
-
<layer:Layer id="background" />
-
</layer:LayerList>
-
</planes:layers>
-
</planes:Plane>
-
</mx:AddChild>
-
</mx:State>
-
</me:states>
-
<data:LoadList id="_list" complete="onLoad()">
-
<data:FileInstance id="bitmap" source="images/darkomeme.png" type="image" />
-
</data:LoadList>
-
</me:GameApplication>
一つずつ説明します。
1.画面状態(Planeタグ)
これはME Frameworkで最も大きな枠組みです。
このタグを一つの画面と見立てて、これらを親コンテナにaddしたりremoveする事で、画面が切り替わります。
ME Frameworkは基本的に画面単位で状態を記述します。
ゲーム、例えばRPGにはタイトル画面・オプション画面・ゲーム画面・戦闘画面・エンディング画面・スタッフロール画面という風に画面によってゲームのルールが変わります。
これはそれらを一つの単位として扱うためのタグです。
また、このタグの要素として、他の静的な情報をタグで記述していきます。
2.入力受付状態(InputStateタグ)
これは、入力の状態を記述するタグです。
PlaneのinputStatesプロパティに直接、複数の入力受付状態を記述することができます。
これはつまり、キーボード入力と入力によってコマンドキューに追加されるキューの紐付けを行います。
SampleGame.mxmlのtestというInputStateの場合、左右キーを押すとTraceCommandAコマンドが発行され、上下キーを押すとTraceCommandBコマンドが発行され、Zキーを押すとReloadCommandコマンドが発行されるという事が記述されています。
方向キーについては、horizontal,verticalの他に上下左右を現すmoveと、それぞれ一つのキーに対応したup,down,left,rightがあります。
それ以外のキーについてはkeys以下にKeyタグを使って記述します。使えるキーはA-Z、0-9、Enter、Space、BackSpaceのみです。
もちろん、他にも対応させてもいいんですが、ゲームにそこまで多くのキー入力は必要ないと思って対応してません。
Shiftくらいは対応してもいいかもしれません。
3.読み込みデータファイルリスト(LoadListタグ)
これはPlaneタグの要素として記述する必要はありません。
場合によっては別のファイルに書いておいてもいいかもしれません。
何かと言うと、これはゲームを表現するのに必要な外部読み込みデータのリストをひとまとめにしておけるタグです。
実装では内部的にBulkLoaderというライブラリを使っており、type属性で指定している値は、BulkLoaderで使用する値そのものです。
SampleGame.mxmlの_listはloadという関数を持っているので、任意のタイミングで_list.load()と呼び出すことで、このタグで記述されたファイルリストのsourceから逐次ダウンロードなどしてデータを読み込みます。
読み込みが完了するとonLoad()という関数が呼ばれるように設定してあります。
これは、画面毎に決まった素材をロードする時に、このように記述しておくことで、一覧性がかなり向上するんじゃないかと思います。
デフォルトのロード画面をPlaneクラスにあらかじめ持たせておくようにフレームワークを設計すれば、このLoadListをPlaneに渡すだけで、画面遷移が起こった時点でロード画面を自動的に表示してロードが完了すると、Planeクラスに記述された動的なスクリプトが実行されるようにすることも可能だと考えています。
うーん、素敵だ。
ロード画面は多少カスタムできれば、別にいちいちPlaneタグを使って用意する程の画面じゃないよね?
4.レイヤー構造(LayerListタグ)
これは、レイヤーの階層を定義するタグです。
※ちなみに、ここからの話は絵に描いた脳内彼女です。
恐らく、あまり動的にゲームの表示レイヤーというものは変化しないはずなので、こんな感じで定義します。
もちろん、あらかじめ表示されるものが決まっている場合は、Layerタグの子要素としてオブジェクトを配置することもできるようにします。
実際は、ここで定義されたレイヤーのidを使って、それぞれのレイヤーに動的にオブジェクトを配置していく事になると思います。
上に書いてあるものが最も上(手前)に表示され、下に書いてあるものが最も下(奥)に表示されます。
それぞれのレイヤーに呼びかけて、SampelGame.mxmlで言うと、characterレイヤーだけ子オブジェクトを全部removeするなんて事もできるようにする予定です。
レイヤー間の移動など、レイヤー全体を通して行う処理に関してはLayerListクラスが担当することになると思います。
5.UI
これは、正直まだあまり考えていません。
よく使うようなUIをFlexのカスタムコンポーネントとして作っておいて、レイアウトなんかをデザインビューなんかで確認できれば最高だなあと思っているんですが、Flex Builderのデザインビュー機能はこれがなかなか曲者です。
どれくらい曲者かと言うと、簡単に言えば、ナナフシという虫から生命を取ったようなくらいです。
棒切れです。
カスタムコンポーネント開発においては、全然まだ使えるレベルじゃないという噂です。(実際使いこなしている方をお見受けしたことはありません
なので、これは結構長い間保留になるかと思います。
まあ、作るのが確定しているものとしては、とりあえずメッセージボックスとゲージコンポーネントくらいでしょうか。
メッセージボックスは文章を流れる様に表示する、ストーリー記述には必須のコンポーネントです。
ゲージコンポーネントはプログレスバーみたいに数値を棒グラフ状に表現できるコンポーネントとして用意します。
ここら辺はFlexのSkin機能を使って、簡単にデザインを変更できるようにしておきたいなあ。
--
と、長く書いてきましたが、以上が現段階で考えている&実装しているME Frameworkです。
イメージ掴めましたか?
ゲーム開発で是非とも使ってみたい、使ってみたいとは思わないがこんな機能が欲しい、物申す!などなんでもいいのでご意見をお聞かせ下さい。
一応、このゲームフレームワークが完成したら、spark projectにコミットしようかと考えています。
まだまだ、構想としては色々考えていますが、時間の関係などで今日はとりあえずここまでです。
SampelGame.mxmlで記述されたプログラムは近々このブログにアップする予定です。
そんな感じで、DarkOmemeが本気出してゲームプログラミングフレームワークについて考えてみた、はこれにて。
以降はME Frameworkの記事として適当にやったことや、構想などを書いて行ければとか思ってます。
--
あー、疲れた。
もう寝よう。
あれ、3:46 amだって。。明日も明日として仕事なのに、お前は一体なにをや(ry
sparkしてきた
ということで。
spark project 勉強会 #5に行って来ました。
発表者・参加者の方、どうもお疲れ様でした。
今回、僕は初参加でした。懇親会では、色々と新鮮なお話が聞けて、個人的には大満足です。
話をするのはすごく好きなのですが、人付き合いというものをイマイチ良くわかっていない自分がいるので、ご迷惑おかけしてたらどうしよう・・
それにしても、とてもレベルの高い方ばかりで、驚愕です。
それも、若い方ばかり。
なんという・・。とても、刺激されました。
僕も、いつかsparkにコミットしたいな~とか思った次第です。
さて、本日、名刺交換をして頂きました。こちらのブログご覧になれているでしょうか?
→の自己紹介文は妄言です。深く考えずにお付き合い頂けると幸いにございます。
しかしながら、それでも深くお聞きになりたい場合は、是非色々聞いてください。
このブログ、実は、自宅サーバ上にあるため、この冬は特に食器洗浄機と電子レンジのコラボレーションをもって落ちます。
なんとも素晴らしいサーバですので、時々繋がらない事があるかと思いますが、なるほど・・・ブレーカーだな、と思うようにするとDarkOmemeの生活観が垣間見えるかと思います。
そうでした。
懇親会では、ハンドルネームを言わなかったんでした。
DarkOmemeです。
実は小学生の頃からずっと使っているペンネームであり、ハンドルネームなので、僕の素性を知っている方からすれば、ほぼ本名と同義です。
そして、僕が企画・運営・開発しております、ひまころじーですが。
こちらのブログにてアナウンスなど行っているのが現状です。
ご感想、もし頂けるようであれば、こちらの方へ書き込んでいただくか
d[at]rkome.me
までメールを頂ければ幸いです。
この、ドメイン。気に入っているのです。
さて、ほぼ、拡張自己紹介記事になってしまいましたが。
ちょっと、ひまころじーの事について、書きます。
今週のノルマ
とりあえず、今週。
データの読み込みを完全にBulkLoaderに移行するように書き換えよう…。
〆
今日のブログ。執筆中のBGMは初音ミクの消失でした。
ということで、明日健康診断なのにアルコール摂取の上、夜更かしです。
なので、寝ます。もう、遅いと思うけど・・。