Ming the Tutorial(日本語訳)

日本語訳 :Syunsuke Fujiwara <webmaster@petsounds.org>

URL:http://www.opaque.net/ming/


1. Introduction


Mingって何?

MingはSWF(Flash)ムービーを欠くことのできるオープンソース(LGPL)のCライブラリだ.
それは,PHP,Python,Rubyなどのスクリプト言語のラッパー群でもある.

Mingは,Flash4のほぼすべての特徴をサポートしてる.
shapes, gradients, bitmaps (pngs and jpegs), morphs ("shape tweens"), text, buttons, actions, sprites ("movie clips"), streaming mp3 audio, and color transforms.
ただ唯一ないのが,サウンドイベントだ.

また,MingはPNGのアニメの一種であるMNGとはまったく関係がないんだ.
MingとMNG両者とも,Webアニメーションにおいて使用され,”ミング”と発音されるけど,その一つだけが,うるさいFlashムービーを作成するために使われる.
そんなわけで,実際のところ,名前コンテストを開くつもりで,優勝者には,5枚入り1/4インチフロッピーディスクの箱を一つか,6つの8088マザーボードを
手にすることができだろう.


PHPはすでにSWF関数をもってなかったっけ?

オーケイ,もちろんだ.でも,現在PHPで利用できるSWF関数は改良が必要であると感じたんだ.これらの関数を使うのに必要なライブラリはクローズドソースになっていて,
数種のプラットホームでしかBuildできない.Windowsもその中には入っていない.加えて,libswfはSWFフォーマットのファイルを意味あるものにするために,
かなり緻密で,内部的な知識が必要だ.

だから,お願いしたい..質問しないでくれ,いいかい?


Ming Tutorial

このチュートリアルは,Mingを使った簡単なFlashアニメーションの作り方や,いくつかの先進的な特徴についてのヒントを教えてくれるだろう..
それから,あなたが挫折し,部屋の明かりを消してしまう前に,Mingを使って,なにか有用でためになるようなことをするつもりだ.

1. Introduction
2. Basic Drawing Commands
3. Fill Styles
4. Animation
5. Text
6. An example!


2. Basic Drawing Commands


[Drawing 描画]

Flashムービーにおける基本的なオブジェクトは,shapeだ.または,Mingで言えば,SWFShape.
PHPにおいては,簡単な表現で,SWFShapeのインスタンスを作ればいい.

new SWFShape();

Mingライブラリは純粋にC言語でかかれているけれども,オブジェクト指向環境で使われるようデザインされている.;
それゆえ,PHPでは,SWFShapeオブジェクトを通してMingの描画関数にアクセスする.400unitesの赤い四角を20unitesのライン幅を持って両サイドに描画するためには,

$s = new SWFShape();
$s->setLine(20, 0xff, 0, 0);
$s->drawLineTo(400, 0);
$s->drawLineTo(400, 400);
$s->drawLineTo(0, 400);
$s->drawLineTo(0, 0);

まず,単位について:Flashの仕様では,1ピクセルにつき,20unitesとなっている.だから,上記のコードによって作成された四角は
20ピクセルの四角で,線は1ピクセルということになる.しかしながら,この尺度は,どこにも強制されない.;つまり,実際に描かれるスケールは
ムービーファイルをHTMLに埋め込む際に指定するサイズと,ムービーの内部でリストされるフレームサイズにのみ依存する.(詳細は後ほど)
もしかしたら,1unitesのラインで20unitesの四角形を書いてしまうかもしれない.いずれにしろ,もし,ムービーをその縮尺で描けば,同じ四角形を
描くことが出来る.

さて,一般的な「線」は4つのパラメータである,両端のX,Y座標を持つ一方で,drawLineto メソッドが二つの引数しか取らないことに
気づいたかもしれない.そしておそらく,賢いあなたは,つまりMingで描画するのはpen-basedであり,Shapeオブジェクトは
描画面に,仮想的にPenの場所の跡を保持している,そして描画コマンドは暗に,Penの場所を参照し,Penの場所に影響を与える...
と結論付けたろう.これのことは,drawLineToが,絶対座標の変わりに相対座標を使うdrawLine関数と関係があることを知れば,
ずっと明らかになるはずだ.つまり,上記の描画関数は以下と置き換えることが可能だ.

$s->drawLine( 400, 0 );
$s->drawLine( 0, 400 );
$s->drawLine(-400, 0);
$s->drawLine(0, -400);


すると同じ赤い四角が得られる.

setLineメソッドは4つ(または5つ)の引数を取る: ライン幅,ラインの色の構成要素として,赤,緑,青(オプションとしてアルファ値)を取る.
ラインスタイルをセットする際,それは続くすべての描画コマンドに適用され,それは,再びsetLineでラインスタイルを変更するまで適用される.
ラインスタイルを持たないと(ところで,それはShapesのデフォルトである),ライン幅がゼロにセットされるだけだ.

最後に,二つの有用な描画コマンドを紹介しよう.

$s->movePenTo(x)

これは線を書くことなしに座標(X,Y)にペンを移動させる(movePenはこれの相対的座標を扱ういとこだ).

一方で,

$s->drawCurveTo(cx, cy, ax, ay);

は,シンプルな二次ベジエ曲線を,現在のpenの場所から(ax,ay)まで,(cx,cy)をコントロールポイントとして使って描く.
予想されるように,drawCurveは同じ事をするが,現在のPen位置として相対座標を使う.


Fill Styles

これはMingにおいてもっとも混乱させるものかもしれない.なのでいくつかの説明を先にしておくことにする.
SWFファイルはrenderするのが,コンパクトで簡単なようデザインされている.これはつまりファイルのなかのShapesの表現の最適化が,
Flashプレイヤーの中でなされるのではなくて,前もって,なされているということを意味する.,そして,Shapesが
人間のへぼい頭脳にはすぐには理解できないような感じで書かれているということだ.

特に,Shapeを定義する際には,描いているラインのどちらの側に,何が埋め込まれるのかを,プレーヤーに教えてやらなければならない.
この理由...(自由にこのパラグラフを飛ばしてもらっても構わない,テストじゃないんだから)
は,Flashプレイヤーがあなたの作ったShapeを走査線上に描くからだ.それ(Player?)は,Shape定義におけるそれぞれのエッジを横切ると,
それは,どんなFill Styleがライン上に描写されれるのかを知るために,左側,と右側のFill Recordを見る.
もしエッジが南というよりも北のほうに向いていれば,走査線は,ラインの上を右から左へ横切るように,エッジの右側の方に動く,
だから,それは,エッジの右側のFill Styleで埋めはじめる.もしエッジがより南向きであったなら,左側のFill Styleを使うことになるだろう.

あなたは,Shapeの左サイドと右サイド のfillを setRightFill と setLeftFill メソッドでセットすることになる.
これらは,setLineと同じように動き,続くすべてのエッジに,再びFill Styleを変更するまで適用される.
setRightFill とsetLeftFillは一つの引数,SWFFillオブジェクトを取る.


・SWFFill

SWFFillはSolidなfillや,グラディエーション,そしてBitmapを含む.けれどもSWFFillのインスタンスを直接作る代わりに,SWFShapeクラスに
SWFShapeのaddFillメソッドを使って,インスタンスを作成する.この裏に潜むロジックは, Fill がShapeの文脈でのみ意味を持つ
ということだが,短くその理由を見ていこう.addFillは様様なFlavor; あなたが作れるどのタイプのFill,Solid colorや,ビットマップ,
そしてグラディエーションにおいて出てくる.

$f = $s->addFill(r, g, b [,a]) ;

特定の,赤,緑,青(オプションでAlpha)コンポーネントを使って,ソリッドカラーを作る.
さあ,真っ赤な四角を書いてみよう.

$s = new SWFShape();
$f = $s->addFill(r, g, b [,a]);
$s->setRightFill($f);
$s->movePenTo(-200, -200);
$s->drawLine(400, 0);
$s->drawLine(0, 400);
$s->drawLine(-400, 0);
$s->drawLine(0, -400);


ここでは,setRightFillを使った.なぜなら,四角の輪郭を時計回りに書いたためである.
よって,四角の内部は,右サイドのエッジにある.もし,四角を反時計回りに書いたなら,setLeftFillメソッドを使っただろう.


・SWFBitmap

Shapeにビットマップを加えるためには,,,,:

$f = $s->addFill(bitmap [, flags]);

ここで,bitmap はSWFBitmapオブジェクトで,以下によって作られる.

$b = new SWFBitmap(filename, [, maskname]);

ここで,filename はjpegまたはdblファイルの名前である."dbl"はMingがより簡単に扱えるために"png2dbl"ツールでmungedされたPNGである.
(このMungingは,時々出食わすことになるだろうMingの"特徴"だ.このような余分なステップを踏んでもらうのは,みんなが,mingを使うために
libpngのような様様なサポートライブラリを必要としないようにというのが唯一の理由だ.いつか,autoconfなどにこの面倒を追い出してもらって,
みんながPNGや,GIF,TTFsを簡単な関数呼び出しで使えるようになると思う).
"png2dbl"プログラムはMingのソースパッケージに含まれている.今の時点ではMingはベースライン圧縮されたJpegファイルしか変換できない
ことも覚えておいて欲しい.ベースライン最適化や,プログレッシブスキャンのjpegファイルは動かない.

オプションのmaskname引数は,"msk"ファイルを指定するもので,それは,jpeg bitmapにαチャネルを加えるものだ.
mskファイルは"gif2mask"で作られ,これもMingのソースパッケージの中に含まれる.入力のGifファイルはjpegと同じ寸法で無いといけない.
出てくるビットマップは,マスクイメージが白のときところは,不明瞭な感じで,グレーなら半透明,黒ならば全体的に透明になる.
つまりは,マスクファイルのピクセル値はjpegイメージピクセルのαレベルとして使用される.

flags引数はSWFFILL_CLIPPED_BITMAP か,SWFFILL_TILED_BITMAP, のどちらかで,ビットマップがclipped(これがデフォルト,フラグ引数の無いとき)
またはtiled どちらであるかを示している.

SWFBitmapはイメージの寸法を取り出すメソッドも提供している.

$b->getWidth();
$b->getHeight();


それは,みんながビットマップと同じ長方形を書くのを可能にしてくれて,実際かなり使える.


・SWFGradient

グラデーション...すでによくご存知のように,それは色と色の滑らかな移り変わりのことだ.Flashでは,グラデーションは
直線的か,放射状が可能で,8つの色まで入れることが出来,透明なのも可能だ.グラデーションFillを作るためには,まず
SWFGradientオブジェクトを作らないといけない:

$g = new SWFGradient();

それから,それにいくつかの登録をおこなう.

$g->addEntry(radio, r, g, b [, a]);

ここで,ratioは0-1の寸法の数で,r,g,b(ついでにa)は,グラデーションにおける特定の場所における,
赤,緑,青(そしてα)コンポーネントである.それらを増加比率の順序で加えないといけない.

例えば,ここに黒から白への簡単なグラデーションのやり方がある.

$g = new SWFGradient();
$g-> addEntry(0,0,0,0);
$g-> addEntry(1.0, 0xff,0xff,0xff);

最後に,そのグラデーションを自分のShapeに加えればよい.

$f = $s->addFill(gradient [, flags]);

ここで,gradientは出来たてほやほやのSWFGradientと,SWFFILL_RADIAL_GRADIENT(放射状のもの:びっくり!)かSWFFILL_LINEAR_GRADIENT(退屈な直線)どちらかのフラグである.
もしフラグ引数が指定されていなければ,あなたのグラディエーションはデフォルトで直線で,退屈なものだ.

・Transforming your fill(fill を変形させる)


もったいぶった新しいグラデーションと,ビットマップfillはデフォルトの場所ではあまり面白みが無い.
われわれは親切に,次のメソッドを提供している.

$f->moveTo(x,y);

fill を座標(x,y)に移動する.

$f->rotateTo(deg);

fill をdeg度だけ回転させ,また,

$f->scaleTo(xscale [, yscale]);

は,fillの寸法をx方向にxscaleにセットし,y方向にyscaleだけセットする.
もし,一つの引数さえ与えられていれば,両方の寸法は,xscaleに合わせられる.

最後に...


$f->skewXTo(s);
$f->skewYTo(s);

は,fillをx,y軸において,s だけ曲げる.sが0のときは曲がらないし,sが1.0のときは45度の角度で曲げられる.


Animation

さて,Shapeを定義したところで,きっとそれを見たくなるだろう.そのためには描くためのキャンバスが必要だ.
そのキャンバスが,SWFMovie()様である.

$m = new SWFMovie();

これは,新しいSWFMovieオブジェクトを作り,アニメーションの基礎である.

背景色を設定するためには,

$m->setBackground(r, g, b);

を使い,
フレームレート(1秒間のフレーム数)を以下のように設定する.

$m->setRate(rate);


そしてムービーのサイズをセットするには,

$m->setDimension(width, height);


を用いる.


私たちが言っているのはScaling(比率)のことだ.:HTMLの中に書かれた表示サイズが,実際には大きさを決定する.
ここでのmovie dimensions 設定はshapesが描かれるスケールを決定する.もしmovieサイズを3200×2400にして,それを
HTMLに320×240で埋め込んだら,Shapesは描画コマンドで指定された10分の1のサイズになるだろう.

Shapeは,以下のようにインスタンスを作成するまでは,movieに描かれない.

$i = $m->add($s);

このメソッドは,movieの中にShapeのインスタンスを作成し,ハンドルを返し,それを通してインスタンスの見かけを変えることが出来る.
実際,同じShapeをお望みだけ(もちろん常識の範囲内で)movieに加えることが出来,戻ってきたハンドルをそれぞれ別々に参照するために
使うことが出来る.


Mingを使ってFlashムービーを作ることはストップモーションカメラを使うのに似ている:キャンバスにShapesをおき,それらを好きなように配置し,
そしてその配置を記憶するためにシャッターボタンを押し,次のフレームに進む.(Shapeの)配置,シャッター,配置,シャッター, ad nauseum.
Mingのシャッターを開放するボタンは,

$m->nextFrame();

"配置"のステップは,インスタンスハンドル(それは,SWFMovieの add メソッドで作られるよ,覚えてる?)によって与えられたメソッドを呼び出すことによってなされる.
これらのメソッドは,それぞれが,現在のポジションや,rotationや,歪み...に関して相対的なバージョンであることを除けば,SWFFillで見た移動メソッド
と同じである.例えば,

$i->moveTo(x,y);

これは,オブジェクトを座標(x,y)に動かす,一方で,

$i->move(x, y);

は,オブジェクトをヴェクトル(x,y)だけずらす.メソッドの残りは以下のようなものである:

$i->rotateTo(deg);
$i->rotate(deg);
$i->scaleTo(xscale [, yscale]);
$i->scale(xscale [, yscale]);
$i->skewXTo(s);
$i->skewX(s);
$i->skewYTo(s);
$i->skewY(s);

みなさんが,fillを移動させるセクションを読んでいて,これらのメソッドが何をするかを理解できていると信じている.
また,飽きたら,表示するリストからオブジェクトを削除してしまうことも出来る.

$m->remove($i);

[Color transforms]

私たちが出来るもう一つ楽しいことは,オブジェクトの色を変えられることだ:

$i->addColor(r, g, b [, a]);
$i->multColor(r, g, b [, a]);

最初のメソッドは一定の値を,表示オブジェクトの赤,緑,青(オプションでα)チャンネルに加える.
二番目のは,与えられた尺度値によって言われたチャンネルを掛け合わせる.

これらの値は,負でありうるということをふれておく; 青いチャンネルを反転するためには,以下のようになる.

$i->addColor(0,0,0xff);
$i->multColor(1.0, 1.0, -1.0);

これらのメソッドは,累積しないことも述べておこう.だから...

$i->addColor(0xff, 0, 0);
$i->addColor(0, 0xff, 0);

これらは,与えられた色をGreenにする.けっして黄色になるわけではない.


[Finally]

全部終わったら,Mingに,Movieをブラウザに描画するよう言ってやればいい.

header('Content-type: application/x-shockwave-flash');
$m->output();

ヘッダーコマンドはWebサーバをして,クライアントブラウザーに,これが,Flashムービーだということを教えるものだ.

ちょっとした例として,前に作った赤い四角をスピンさせてみよう!


$m = new SWFMovie();
$m->setDimension(800, 600);

$i = $m->add($s);
$i->moveTo(400, 300);

for($j=0; $j<5; ++$j)
{
$m->nextFrame();
$i->rotate(15);
}

$m->nextFrame();

header('Content-type: application/x-shockwave-flash');
$m->output();

最初のフレームが90度の回転と同じなので,ここではかなりCleverに,アニメーションをほんの75度だけ回した後,最初にループバックしている.
かしこいなあ,実に.


Text


まず,フォントをロードする:

$f = new SWFFont(filename);

ここで,ファイル名は,fdbファイルのことを言う.(そう,それは特別なファイルフォーマットで,Mingのmakefdbユーティリティーを使ってSWF Flash Templateから作ることが出来る)
SWFFontオブジェクトはいくつか,フォントメトリック情報を与えてくれる.

$f->getAscent();
$f->getDescent();
$f->getLeading();
$f->getWidth(string);

それぞれのメソッドは,要求されたメトリックを,1024までのフォント高までの尺度で,数値として取り出してくれる.
もしあなたがテキストを1024の高さで表示していなければ,高さ/1024で,返り値を掛け算する必要があるだろう.

平均的なtext-fuは,以下のような感じだろう:

$t = new SWFText();
$t->setFont($f);
$t->setHeight(240);
$t->setColor(0xff,0,0);
$t->moveTo(10,250);
$t->addString("no, please! not my toes!");

SWFText をSWFMovie の add メソッドでムービーに追加し,戻りハンドルで,ちょうどShapeのように,動かせばいい.


My Big Example

 

時の夜明け以来,人間は,測りきれないもの...宇宙における人間の位置を規定するものは何か,白熱する年齢の問題...を長く考えてきた.:
いったい,"slashdot headline scroller"のFlashインプルメンテ-ションがあるのだろうか?見よ!時間が近づいてきた.祈るものは
答えを得られるだろう.

最初に,ちょっと見てみよう.かなりイケてないかい? 本当のことは,言わないでおくれ...

ここにそれがどのように動くのかを示そう.まず,slashdot.orgをオーバーロードするのを避けるために,自分のPCにslashdot.xmlのコピーを持っておこう.
;もしファイルが30分かそこら,古いものであれば,別のコピーを取ってくることが出来る.

$s = stat("slashdot.xml");

if(!$s || $s[9] < time()-1800) // idx 9 is last modification time
{
$page = file("http://slashdot.org/slashdot.xml");
$page = implode("", $page);

fwrite(fopen("slashdot.xml","w"), $page);
}
else
{
$page = file("slashdot.xml");
$page = implode("", $page);
}


さて,ここでURLとヘッドラインを切り出すために手軽な正規表現を使おう.

# borrowed from http://www.wiredstart.com

preg_match_all("/<title\>(.+?)<\/title\>\s+<url\>(.+?)<\/url\>/i",
$page, $news, PREG_PATTERN_ORDER);
$i = 0;

while(list(,$match) = each($news[1]))
{
$url[$i] = $news[2][$i];
$match = str_replace('&amp;','&',$match);
$match = str_replace('&lt;','<',$match);
$match = str_replace('&gt;','>',$match);
$match = str_replace('<i>','',$match);
$match = str_replace('</i>','',$match);

$title[$i] = $match;
++$i;
}

$count = $i;

PHPのhtmlentities関数の逆ではないようなので,普通のテキストを使って,より一般的なentitiesに置き換えることができる.
さあ,SWFMovieファイルを作り,フォントを設定しよう.

$m = new SWFMovie();
$m->setDimension(1200,60);
$m->setBackground(0, 0x66, 0x66); // minty slashdot green!
$m->setRate(24.0);

$f = new SWFFont('Techno.fdb');


・SWFButton
あなたも私も,そろそろ疲れてきた頃だろうと思うので,ボタンについて重要なことだけ話しておく.ボタンは,それら1)Up Shape -ボタンの一般的な見栄え- ,
; 2)Over Shape, マウスがボタンの上に動いたときに表示されるもの;3)Down Shape,ユーザがボタンをクリックしたときに表示されるもの,
そして,最後に4)クリックできるポイント;それは決して表示はされないが,ボタンのクリック出来るエリアを定義する.
以上と関連した多くのShapeを持つことが出来る.

ボタンは,

$b = new SWFButton();

で作ることが出来,ShapeをボタンStateを持って追加することが出来る.

$b->addShape($s, SWFBUTTON_HIT | SWFBUTTON_DOWN | SWFBUTTON_OVER | SWFBUTTON_UP);

これは,同じShapeをすべての「状態」に加えることに注意して欲しい.つまり,クリックできるエリアはShapeが描かれるエリアと同じで,
ボタンは,そのShapeにマウスオーバーや,クリックしたとき同じように見え,全く面白みは無い.われわれの例にあるボタンは
monkey-bucketsな楽しみはないが,分離したクリックエリアを作ってみよう.

// make a hit region for the button

$hit = new SWFShape();
$hit->setRightFill($hit->addFill(0,0,0));
$hit->movePenTo(-600, -30);
$hit->drawLine(1200, 0);
$hit->drawLine(0, 60);
$hit->drawLine(-1200, 0);
$hit->drawLine(0, -60);

これがなければ,クリックエリアはヘッドラインテキストだけになり,ちょっと変だ.さあ,それらボタンを搾り出そう.

for($i=0; $i<$count; ++$i)
{
$t = new SWFText();
$t->setFont($f);
$t->setHeight(40);
$t->setColor(0,0,0);
$t->moveTo(-$f->getWidth($title[$i])*(40/1024)/2, 20);
$t->addString($title[$i]);

$b[$i] = new SWFButton();
$b[$i]->addShape($hit, SWFBUTTON_HIT);
$b[$i]->addShape($t, SWFBUTTON_OVER | SWFBUTTON_UP | SWFBUTTON_DOWN);
$b[$i]->addAction(new SWFAction("getURL('$url[$i]','popup');"), SWFBUTTON_MOUSEUP);
}

今までに出てきていない別のオブジェクトタイプ,SWFActionがある.そろそろ終わりに近づいてきているので,Flashが,バイトコードエンジンを持ち,それによって
多くの膨らんだりするダイナミックなアクションが可能になり,そしてまた,MingがCに似たスクリプトコードを,Flashの
アクションバイトコードにコンパイルする,ということ以外は,深くはふれないことにする.

上のgetURLアクションはブラウザーに与えられたURLを読み込ませる働きをする..詳細はMingのWebサイトを見て欲しい.
これで,ボタンを作ることが出来たので,それらをアニメーションしてみよう.これら下の二つのファイルは,ヘッドライン間の様様な移行
を演じる関数を含んでいる.

include('infuncs.php');
include('outfuncs.php');

たとえば,infuncs.phpからの次の関数は,完全な透明から,完全な非透明までフェードしながら左側からボタンをスライドさせる.

function slideleftin($movie, $shape)
{
$i = $movie->add($shape);

for($j=0; $j<=20; ++$j)
{
$i->moveTo(600-($j-20)*($j-20), 30);
$i->multColor(1.0, 1.0, 1.0, $j/20);
$movie->nextFrame();
}

return $i;
}

infuncs.phpのすべての関数は,movieとShapeを引数として取り,MovieのなかにおけるShapeのインスタンスを戻り値として返す.
outfile.phpの関数は,movie,Shape,in-functionによって作られたインスタンスを引数に取り,値が返る時,キャンバスが空であることを
保証する.

配列,$infuncsと$outfuncsでリストされる関数は,まったくメインのPHPコードをいじること無しに,ただ,関数を作り,適切な配列の中にそれをリストすることによって,新しい移動
を追加することができる.これがアニメーションを動かす最後のちょっとしたコードだ.

for($n=0; $n<4; ++$n)
{
for($i=0; $i<$count; ++$i)
{
$infunc = $infuncs[rand(0, count($infuncs)-1)];
$instance = $infunc($m, $b[$i]);

for($j=0; $j<60; ++$j)
$m->nextFrame();

$outfunc = $outfuncs[rand(0, count($outfuncs)-1)];
$outfunc($m, $b[$i], $instance);
}
}


すこしバラエティーを持たすためにそれを4回ループしているが,それは,ただ一度大抵いくつかの移動効果をミスするからである.
ここにソースコードがある.

そしてUrs Gehrigにありが当を言いたい.彼は,rssファイルもいまや扱っている!わっほー!


[A tearful farewell]

さて,一緒にいる時間も終わりに近づいてきた.わたしは,手骨のトンネルトラウマ病棟に戻ることにしよう.そうすれば,
あなたはこれからの3時間をSlashdotのユーザーコメントを読むことに費やすことが出来るだろう.でもその前にMingについてもっと
よく知るために,また,コードをダウンロードして多くの例をみて,メーリングリストに参加するために,http://www.opaque.net/ming/
を訪れて欲しいが,あれ,コードのダウンロードについてはふれたっけ?


brought to you by Twende.jp