2008年1月26日土曜日

依存性の注入

敵オブジェクトが敵弾オブジェクトを生成する処理をMissileスクリプトで記述することを考える。

その場合、敵スクリプトから、敵弾スクリプトが呼び出される形になるわけだが、ゲーム中にはたくさんの種類の敵と敵弾を登場させる必要があるため、敵1から敵弾1が発射されるパターンや、敵1から敵弾2が発射されるパターンなど、敵スクリプトと敵弾スクリプトを自由に組み合わせて使えると楽になるはずである。
敵スクリプトと敵弾スクリプトの間で依存性があると、スクリプトを使いまわしすることが出来なくなるので、似たようなスクリプトをたくさん書かなくてはならない状況になってしまう。

AスクリプトからBスクリプトを呼び出す場合に、Aスクリプト中にBスクリプトのファイル名を直接記述せずに、外からスクリプト間のバインディングを決定できるような機構があれば、スクリプト間の依存性を無くすことが出来る。

要するに、Seasar2フレームワークのDIコンテナみたいな仕組みを取り込みたいわけである。
ITProのDI解説
WikipediaのDI解説

現状の仕様でも、スクリプト間の依存性がなるべく小さくなるようにMissileスクリプトは設計されている。そのため、スクリプトは単体で実行してテストすることができるようになっている。
この仕様をもう一歩進めて、DI的な仕組みを入れることができれば、スクリプトを「部品」として自由に組み合わせて作っていくことが出来るようになるだろう。

しかし、上記のリンクとかを見てもらえれば分かるが、スクリプターにとってとDIというのは難しい概念だ。そんな概念を知らずとも、自然に使えるような構文を考えないといかんですね。

2008年1月15日火曜日

マップパターンの編集について

マップパターンもスクリプトで書くという方針だったのだが、実際にやってみるとなかなか辛い作業になってしまうことが判明。

やはり、GUIでマップを編集できるツールが必要だなと実感した次第。

マップエディタを1から作るのはそれなりに大変だと思うので、汎用的に使えるものが無いか検索。

Vectorで「マップエディタ」で検索したら、一番上に出てきのが以下のソフトです。

Platinum
http://www.vector.co.jp/soft/win95/game/se231004.html

ちょっと使ってみた感じ、必要な機能がそろっているし、操作性も良い。
編集したマップパターンは、CSV等のテキスト形式やバイナリ形式で出力することができる。

これで作ったマップパターンのファイルを、直接Missileで読み込めるようにするのが本来なら良いと思うのだが、Missileにマップ関連の機能をあれこれ機能を加えるのも大変である。
CSVで出力したマップパターンを、Missileスクリプトにコンバートするようなツールを用意すれば、新たな機能追加無しでいけるので良いと思われる。

例えば、1つのマップチップの縦横のサイズが32ドットで、マップパタンのCSVファイルの内容が以下のような感じの場合、
1, 2, 3
2, 3, 3

コンバータは、以下のようなMissileスクリプト(この場合4つのファイル)を出力すればよい。
// map.sc
x = 0.0; y = 0.0; create("bg1.sc");

x = 32.0; y = 0.0; create("bg2.sc");
x = 64.0; y = 0.0; create("bg3.sc");
x = 0.0; y = 32.0; create("bg2.sc");
x = 32.0; y = 32.0; create("bg3.sc");
x = 64.0; y = 32.0; create("bg3.sc");

// bg1.sc
texture("bg.png", 8, 8);
anim_pos = 0.0;

// bg2.sc
texture("bg.png", 8, 8);
anim_pos = 1.0;

// bg3.sc
texture("bg.png", 8, 8);
anim_pos = 2.0;


こんな感じなら、気合だせばさくっと作れそう。

2008年1月12日土曜日

プログラムが完成しなくなる瞬間

趣味で作っているプログラムが完成しない原因は、やる気の問題であることも多いが、それよりもクリティカルな原因は、コードの複雑化にあると思う。

ある機能を組み込むためには、およそX行のコードを追加する必要があるが、そのためには既存のコードをY行ほど修正しなければならないという状況は多々あることだと思う。

Xが100行だとして、Yが10行程度だったら、問題ない。
XよりYが大きくなった場合、そのプログラムは永遠に完成しなくなるだろう。


Missileフレームワークのソースコードは現在約15000行(内、VisualStudioが自動生成した部分が約5000行)である。
だんだんとプログラムの規模が大きくなるにつれて、機能追加にかかる時間的コストが増加してきた。

自分が、プログラムのコアとなる機能を優先して作り、それ以外の機能を後回しにするのは、後になるほど機能追加が大変になっていく性質があるから、というのもひとつの理由である。

STGっぽいものが何とかMissileスクリプトで記述できるようにようやくなったが、設計段階では気がつかなかった問題を多数抱えた状態になっている今、次のステップに進む前に全体的なリファクタリングを実施しておく必要があると思う。
プログラムの構造をすっきりと整理しておくことで、後々の作業が楽になるはずだ。

リファクタリングの最初の手順として、まずはクラス図を描くところから始めようかと思う。
継承、コンポジション、集約などのクラス間の関係を図にすることで、構造を再確認し、問題がある部分を洗い出すのが目的だ。

何のツールを使ってクラス図を描こうかねえ?
C#のソースから、リバースエンジニアリングでクラス図を自動生成してくれるツールがあればいいんだけれど、ちょっと探した感じだと簡単に使えそうなものは無い感じだった。

ツールの使いかたを覚えるために時間を使うよりは、地道に紙と鉛筆で描いていった方がいいかもな・・・。

2008年1月11日金曜日

MetalBreakerシステム仕様書作成開始

ただいまラフを描いてるわけですが…。

結構悩んでいるのが残機表示の文字。

普通、STGではLIFEだったり書いてあるんですがそれじゃ味気ないと。
何かMetal Breakerらしい表記を考えていたりします。

因みにContinue画面なんかもそれに沿った形にします。
Continue?って感じじゃなくて。

演出的な面で決まっている所も有るのですがせっかく余白があるのでそれを生かした構成にしたいなと。

ただ、あんまり詰め込むとじゃまになるのでその辺のさじ加減がね。
基本的なところはもうできあがってはいるんですが、言葉の使い方というか。

ラフだから、後から文字を変えればいいや、でもいいんですが何となくそこを書いておきたいんですよね。

それと、だいぶMetal Blackから離れてきた感は有ります。
もちろん、その基本的なコンセプトというか世界観は引き継いでいるのですがifの世界の上に縦と言う事もあり気づく人は気づく、っていう演出を多く服みたいなと思っています。

とりあえず演出、ストーリー的な面で見るとMetal Blackの他には斑鳩、クトゥルフ神話が絡んでくる予定です。

まぁ、あくまで演出なので味付けにしか使わないのですが、気付いてくれたらうれしいな。と。
その前に弾幕作らないとね。

2007年12月15日土曜日

例外処理について

例外処理は難しい。

しかし、例外をうまく処理すれば、エラーやバグの原因をすばやく突き止められるはず。

例外はただcatchしておけばよいっていうものでは無く、catchしたあとで誰に宛ててエラーメッセージを表示するかを考慮する必要がありますね。

Missileフレームワークの場合、例外は以下3つに分類されます。
1.ユーザーに宛てる例外
2.スクリプターに宛てる例外
3.プログラマーに宛てる例外

それぞれの名宛人の定義は、
 1は、Missileフレームワークで作られたゲームをプレイしている人
 2は、Missileスクリプトを記述する人
 3は、Missileフレームワークをプログラミングしている人
です。

1は、ユーザーのシステム環境が原因で発生するエラーが主です。
例えば、DirectXのバージョンが古いとか、メモリが足りないなどが考えられます。

2は、スクリプトの記述ミスで発生するエラーです。
例えば、スクリプト中で存在しないファイルを指定した場合などが考えられます。

3は、Missileフレームワークのバグで発生するエラーです。
ぬるぽとかそれ系です。

ApplicationExceptionを継承して、UserExceptionとScriptRuntimeExceptionを用意します。

1は、キャッチして、UserExceptionをthrowします。例外メッセージには、ユーザーが読んで理解できるようなことを入れておきます。

2は、キャッチして、ScriptRuntimeExceptionをthrowします。例外メッセージには、スクリプトのバグの原因を突き止められるような内容を入れておきます。

3は、キャッチしないようにします。その方がVisualStudioでデバックしやすいからです。


そして、UserExceptionとScriptRuntimeExceptionをメイン画面のフォームでキャッチして、例外メッセージをダイアログボックスに表示します。そしてプログラムを終了させます。


こんな感じでいいかと思います。

2007年12月11日火曜日

スコアアタックについて

Missileスクリプトの仕様策定の段階であったのですがスクリプト上簡単にリプレイを作成できるようになっています。
しかし、このリプレイ。
多少の欠点があります。

それは現状バージョンの差異を特定出来ないことです。

リプレイデータはゲーム中でプレイヤーがどのようにキャラを動かしたか、それがMISSILEスクリプトのバーチャルマシン上でどのように反映されるかを保存します。

そのため、スクリプト変更に伴う敵の動きの変更があった場合その動きの差異を拾えきれない事があります。

スクリプト、アップローダ用のスコアを特定する方法はすでに内部で決まってはいるのですが他の同人STGでも過去のリプレイがまともに動く例は少ないようです。
(当たり前といえば当たり前なのですが)

しかし、これを解決したところでステージ構成が違うバージョンでのスコアアタックに意味があるのか。
おそらくは意味がない場合がほとんどなんでしょう。

2007年12月7日金曜日

Missileスクリプトの優位性について2

つづきです。

Misiileスクリプトを使えば、同じことを以下のように記述できます。
main()
{
texture("enemy1.png");
x = 100;
y = 100;

for (;;)
{
vx = 2;
frame(100);
vy = 2;
frame(100);
vx = -2;
frame(100);
vy = -2;
frame(100);
}
}
かなり簡単になりました。
frame命令は、Missileスクリプトの最大の特徴だと思います。
frame命令が実行されるとスクリプトの実行を一時停止して、指定されたフレーム数の間、スクリプトの呼び出し側に処理を返します。
指定されたフレーム数が経過すると、一時停止した所から処理を再開します。

C言語とかの関数の場合は、処理を一時停止して、呼び出し側に戻るっていうことはできません。また、ローカル変数は関数を抜けると開放されてしまうので、キャラクタのコンテキストを関数の外側で保持しておく必要がありました。

Missileスクリプトでは、frame命令で処理を「一時停止」して、その間に他のスクリプトの処理や描画処理などを並列的に実行します。
この仕組みにより、キャラクタのコンテキストをローカル変数に置くことが出来るようになります。また、カウンタなどで時間軸を分割して移動パターンを記述する必要がなくなります。

結果として、直感的に分かりやすいコードになっていると思います。

以下は余談です。
処理を一時停止して、呼び出し側に戻って、その後処理を再開できるルーチンのことを、「コルーチン」と言います。
Lua5.0とかC#2.0にはコルーチンがあります。
でも、C#のコルーチンは、列挙子を記述するための専用構文になっていて汎用的には使えないのが残念です(yield文)。