NPTwinsお外で開発 / Pnetium-Mって速い / delegate便利


 昨晩は帰りにマックに篭って開発してたらえらいすすんだ。
 余計なものがないのがよいのだと思われる。家に帰っちゃうとダラダラしちゃうしねー


 開発はThinkPadX40でやってるけど、XGA解像度でもどうにかなるようだ。さすがにUXGAと比べると一度に表示できる行数は減ってしまうけど。
 逆にいまのIDEって広い画面活かしづらいと思う。ソースを二つ横に並べて見比べながら作業するとか面倒(できなくはないけど)。IDEのメインウィンドウから分離できたらいいのに。


 開発環境はVS2008だけど、X40でも十分な性能で特にストレスは感じない。メモリー使用量はトータルで600Mbyteなので余裕。
 ま、あんまり無理はかけないようにしてやってるわけだけど。それでも、これに加えてPhotoShop起動してドット絵を描くくらいの余裕はあるなー
 バッテリーは4時間持った。


 それにしても、X40は思いのほか性能がある。
 SDLでの描画はグラフィックカードアクセラレーション効かないので、ほとんどCPUで描画しているのだけど、Pentium4 3.06GHzのデスクトップ機より速かったりする。
 NPTwinsのタイトル画面は、VGA解像度全画面にわたってα付の1枚絵を表示していて何気に負荷が高い。
 X40(Pentium-M 1.3Ghz)では60FPSでる(以前はなぜか40FPS弱しかでなかったけど、最近なぜか60FPSでるようになった)のに対し、Pentium4 3.06GHzのデスクトップ機は40PFS強しか出ない。
 デスクトップ機はグラフィックカードも挿しているのに・・・
 逆にCPU描画メインの場合は、チップセット内蔵ビデオの方が性能出るのかな?




 CPU自体の性能でいうと、Pnetium4に比べPentium-Mは、クロックあたりの性能が1.5倍くらいあるという話だけど、もっと差がありそう。
 
 
 Pentium-M 1.3Ghz(ThinkpAdX40)はPentium4 3.06GHz(デスクトップ)より速いのか?精通演算は速いようだなあ。SDLでの描画はPentium-Mのほうが速い。ここまで速いとは・・・
 たとえは、このブログ記事(Pentium M ベンチマーク)では、整数演算はクロックあたり2.4倍も速かった。
 Super PIの結果も同様なので、整数演算にかぎってはPentium-Mのほうが無茶苦茶速いようだ。
 1.3GhzのPentium-Mは、3.12GHzのPenitum4相当かも。
※ SSEの活用しだいではまた結果は変わってくるのかもしれないけど、SDLってSSEつかってたっけ?


 これをみるとThinkPadX40もまだまだいけそうなので大事に使っていきたいと思います。
 X40のHDDは、日立形コネクタの1.8インチで換装できるHDDが非常に限られていたのですが、最近このタイプのSSDが発売されました→日立型の1.8インチSSDが発売に
 みんな待ち焦がれていたのか相当な人気のようで、すぐ売り切れてしまったようです。
 32GBが19,800円、64GBが34,800円です。
 これに換装したらかなり快適になるだろうなー
 でも、34,800円だすんだったら、VAIO type P購入にあてたほうがいいかなー(悩



 ・・・などと、次次世代のCore i7がとっくに出荷されてるのにこんなこと考えてたりして。




● さて昨晩の開発の成果は・・・



2009/02/03 ver.0.14
・トレース挙動変更
 ・TYPE-W:ワイヤーの長さを変更(100→125pixel)。
 ・TYPE-W:ショット時は摩擦係数を高くした。(目的の位置に止めやすくした)
・ステージ開始時にステージ番号を表示
・自機表示上下関係修正:コントロール機が上に表示されるように修正
・プログラム内部での変更(まだゲーム中の見た目はかわりません)
 ・ステージ管理クラスを作りステージ定義部分を分離した
 ・敵キャラ初期値設定ヘルパークラスを完成
 ・敵ショットタイミング、ショット内容をdelegate化。敵キャラ個体ごとの柔軟な挙動設定を可能にした
 ・敵編隊をグループ化可能にした。編隊全滅ボーナス用


 こんな感じ。
 あんまりゲーム内容はかわってませんが、内部構造を結構いじりました。
 特に敵の出現パターン設定部分が大幅に変更されています。



【出現キャラを全部あらかじめ生成】
 これはもともと富豪的プログラミングです。
 1ステージに出現する敵キャラは、すべてステージ開始時に生成して出現タイミングをキーにして、SortedListにつっこんであります。
 ゲーム中はゲームの進行カウンターにあわせて、SortedListから敵キャラオブジェクトを取得して、ゲーム中で動作するListに加えていきます。
 この方法だと、ゲーム進行しながらタイミングみて敵を出現させるとか、並列動作のための面倒がなくてよいです。複数の編隊は重なったタイミングで出現させるときも、順番に記述してよけばよいです。
 デメリットは一度に非常に大量のオブジェクトが生成されるのでメモリーとか食いまくりですが。グラフィックに比べると微々たるものかな。いまのところ、変なタイミングでGCが発生したことはないです。



【楽チンパラメーター生成】
 今回の改良では、敵キャラの出現時のパラメータ設定のためのサポートクラスを用意しました。
 以前は、Initメソッドなどで必要なパラメーターをまとめて設定していました。


ActorEnemy act = new ActorEnemy();
act.Init(m_game, 1, i % 2, 2,
new Vector(m_game.GetRandom(200)+40 + (i%2)*200, -50, 0),
new Vector(0, 1, 0),
new Vector(0, 0.01, 0),
-1,
60 * 1, 60 * 4 , 3.0f,
null);
 しかしこれでは、不要なパラメーターもかく必要がありますし、(IDEでのサポートがあるにせよ)なんのパラメーターかわかりづらいです。


 そこで、サポートクラスを間にかませて、以下のように記述できるようにしました。


act.m_aps.Pat*1;
 ※ラムダ式を直接記述することも出来ます


 ショットの内容も従来は弾速のみの設定でしたが、以下のようにロジックを記述できます。
 例) .Shot( new FireFuncs(i+1).FireAimPlayer );


 また、ショットタイミングとショット内容のセットを複数設定するここともできます。


.ShotAlgo( (ActorEnemy acte) => { if (new IsShotFuncs(30, 90, i * 5 + 5).IsShotInterval(act)) new FireFuncs(i + 1).FireAimPlayer(act); })
.ShotAlgo( (ActorEnemy acte) => { if (new IsShotFuncs(100, 130, 5).IsShotInterval(act)) new FireFuncs(-i-1).FireAimPlayer(act); });
 上記は、タイミング(カウント30〜90、カウント100〜130)によって2種類の弾発射ロジックを設定しています。
 これを並べていけば、ボス敵のような複雑なショット定義も可能になります。



/

 このようにdelegateを利用することで、オブジェクトの振る舞いを一部だけ変更することが簡単にできます。
 クラスを派生することでも似たようなことはできますが、柔軟性は段違いです。
 delegateではさまざまなロジックを用意しておき、必要に応じた組み合わせでキャラオブジェクトに組み込めます。複数組み込むことすら可能です。
 また、特殊なロジックはその場でラムダ式で記述してもよいです。



 シューティングゲームでは、キャラごとに微妙な挙動の違いをつけたい場面が多くありますが、このようなプログラムとdelegateはかなり相性がよいです。
 まだ自分も一部しか使いこなせていませんが、もっと活用していきたいと思います。




● 最新版のダウンロードはこちら (ver.0.14)


以下のURLからダウンロードできます。
http://www.dokokano.net/~fslasht/warehouse/programs/NPTwins_latest.zip


※ 上記URLからは常に最新のバージョンがダウンロードできます。(ver.0.14を指定してダウンロードする場合は→こちら



現状1面道中まで実装。
HELLモードでもクリアできることを確認しました。
自機切り替えを活用して弾幕を回避すれば可能です。
よかったらチャレンジしてみてください。
(今夜あたり動画を撮ってみます)

*1:int)CHR.TRACER).NP(i % 2).Life(2).Pos(m_game.GetRandom(200) + 40 + (i % 2) * 200, -50).Vect(0, 1).VectVect(0f, 0.01f).ShotTiming(60 * 1, 60 * 4).Shot(CHR.SHOT1, 3.0f);

 m_apsがサポートクラスのインスタンスです。Pat()やLife()などのメソッドでパラメータを設定できます。  ポイントは、各設定メソッドの戻り値は、m_apsを指しているため、つなげて設定メソッドを呼べることです。これは楽チン。  スタックがいっぱい必要な気がしますが富豪なので気にしません。 【編隊出現はdelegateを利用】 従来は、敵オブジェクトを生成して、順にAddEnemyStartメソッドで敵リストに追加していきました。
for (int i = 0; i < 40; i++) { ActorEnemy act = new ActorEnemy(); act.Init(m_game, 1, i % 2, 2, new Vector(m_game.GetRandom(200)+40 + (i%2)*200, -50, 0), new Vector(0, 1, 0), new Vector(0, 0.01, 0), -1, 60 * 1, 60 * 4 , 3.0f, null); AddEnemyStart(nStart + i * 10, act); }
改良版では、AddEnemyGorupStartメソッドに敵生成delegateメソッドで渡しています。以下の例ではラムダ式で記述しています。
AddEnemyGorupStart(nStart, 40, 10, (ActorEnemy act, int i) => { act.m_aps.Pat((int)CHR.TRACER).NP(i % 2).Life(2).Pos(m_game.GetRandom(200) + 40 + (i % 2) * 200, -50).Vect(0, 1).VectVect(0f, 0.01f).ShotTiming(60 * 1, 60 * 4).Shot(CHR.SHOT1, 3.0f); });
【敵ショット設定】  敵のショットタイミングは、従来は開始カウント、ショット間隔を設定しての固定ロジックで判定していました。  例).ShotTiming(30,90,i*5+5);  この部分に、delegateで独自ロジックを渡せるようにしました。  例).ShotTiming(new IsShotFuncs(100, 130, 5).IsShotInterval(act