日記(1012)

<前 次>

斜めマップ

爆ボンのマップを作成するための素材を作っていました。
EDGEでテクスチャを使って塗りつぶす方法を見つけて、というより、きっとあるはずだと信じて探したら本当にあって、その機能のおかげでずいぶんと作成速度が上がりました。
いろいろとマップチップ作成に便利な部品を作ったということも作成速度向上の一因には違いないでしょうが。
まだまだ調整が必要だけどここまでできてます。

1128696113.png

GlobalAllocの途中にて

ふにゃさんステージデータ改訂版。
ステージファイルに埋め込まれたGIF・JPEG画像は読み込めませんでした。
それで必死に原因を探して、原因がOleLoadPictureでエラー-2146827807が発生していることだということを突き止めました。
しかし、そのエラー、ヘルプにも記載されていなければ、どこのヘッダファイルを見てもそのエラーを返すコードもなし。
そこで、エラー番号で検索を掛けてみたら、唯一http://72.14.207.104/search?q=cache:geyhWb4AH8QJ:www.eggheadcafe.com/ng/microsoft.public.vc.atl/post22719515.asp+2146827807&hl=ja&inlang=jaが見つかりました。
ここのフォーラムのやり取りを要約すると、
「OleLoadPictureでエラー-2146827807が発生しているがこの意味が分からない」
「GlobalAllocを使いなされ」
ということだったのですが、実は今回、既にメモリ確保にはGlobalAllocを使っていたんですよね。
それで完全にわけが分からなくなってやけくそになってテンポラリファイルを使って読み込んで、普通のファイルならバッチリ問題なく読めるので一応解決はしました。
しかし、今考えてみると、ステージファイルに埋め込まれた画像というのは、GlobalAllocで確保されたメモリの途中にあるんですよね。必ず。
そして、グローバルメモリオブジェクトを扱うAPIを見てみると、どれもこれもGlobalAllocやGlobalReAllocで得たハンドルを使っているんですよね。
グローバルメモリオブジェクトをバイト配列とみなした途中の変数のアドレスじゃないんですよ。
まあ、分かりやすく言えば、
int *p = malloc(256);
free(&p[1]);
みたいなことをしていたというわけですね。正しく動くわけがありません。
だったら画像を読むときはテンポラリファイルの代わりに、改めて画像データだけを入れるメモリ領域をGlobalAllocで確保してそこにステージに埋め込まれたデータをコピーして使えばよさそうな気がしますがまだ実行していません。

ミディビの開発のため色々調べものをしていました。
CGIに渡すデータは、ヘッダ→空行→データ、という形式。CGIが返すデータとよく似た形式です。
具体的には、Content-Type: multipart/form-data;~だとかContent-Length:~をヘッダに記述して、データ部にMIDIのバイナリデータを書いてやればCGIにMIDIのデータが送られるわけです。
しかしOutputStreamに書き込まれる前にデータサイズが分からないためSocketでサーバに接続して得られたOutputStreamに直接書き込むのは危険そうです。
一応テスト用のOutputStream派生クラスでMidiSystem.writeはOutputStream.write(byte[] b, int off, int len)しか呼ばないことが分かっているので、MIDIデータを一時保存するためのOutputStreamを用意してそこから改めてサーバに接続して得られたOutputStreamに書き込むのがよさそうです。
これを日曜日あたりにプログラムとして書くつもり。

爆ボンやってました。
今度はどきどきモードでドラゴとリバイアを倒しました。
攻略ページのほうの更新はありません。
やっぱりどきどきモードだと強くなっています。

メモリと速度の問題

爆ボンの地下水路の主のページを作りました。
今までの分に加えてわくわくモードとどきどきモードでの正攻法での攻略法も載せています。
あと、タイトルをちょっと改良。「 - 」だと横に伸びすぎるので「>」を区切り文字に使用しました。
今後順次タイトルをこの方式に変更してゆきます。

ふにゃさん、マップの処理が著しく非効率的だったので表示される部分だけを描画するように改良しました。
これで速度的な効率は大幅アップ。
ただでさえ敵味方の相互作用に時間がとられるので、描画ごときに時間をとられている暇は無いのです。
そして、そのついでに、数値計算の都合で少し誤差がおきて背景が1ドットずれるバグを修正しました。
しかし、そのついでにメモリ使用量を見てみると、結構メモリ消費が大きく(もっとも、yaneSDKを使っている時点で10MBは最低使うのですが)、プレイしているうちにどんどん、本当に1面クリアするたびに何MBぐらいの勢いで増えてゆくのです。
そこで私はメモリリークを疑い(何しろメモリリークを起こすようなプログラムを平気で書く人ですから…)、デバッグのためnew/deleteのログを吐き出すようにしてやったのですが、どうもメモリリーク箇所は無い模様でした。
BGMはまあ適当に結構平気で10MBぐらい食いそうだからと考えて、とりあえずBGMを切って勢いはゆるくなったのですが、やはり増えるものは増えるわけで。
実際のところ、新しく敵が登場すると、そのときにプログラム終了まで開放されないメモリを確保するのですが、それにしても多い気が…でもやっぱりメモリリークやらをしていない以上、大きくメモリを食うのはここぐらいのものなので、これが原因なのでしょうか…。
それと、ステージファイルは保存のときにプレイ用と編集用の2つを同時に出力するようにして、標準以外の画像を使うときは画像データそのものも書き込むようにしました(配布時に画像を添付する必要をなくすため)。

裏話の裏話

アクセス解析でパネルγのページへのアクセス状況を見ていました。
するとビックリ!トップページへのアクセスが最多なのは分かりますが、ついで多いのが裏話のページ!!
操作説明よりも多いのには驚きましたよ。
裏話なんて私のような物好きだけが見るページだと思っていたのですがそうでもないのでしょうか…。
それよりも驚きなのは、本体のダウンロードがそれらのページよりもはるかに少ないということ。
検索サイトから直にたどり着く人がいないためトップページからしか来られない(パネルγのページは検索サイトには登録されないようにしてある)ことを考えれば、トップでは本体がバージョンアップしたときにしか直接入れないようになっているため、とりあえずふらりと入ってせっかくなのでダウンロードすることが多いと思っていたのですが、それをも抜いて裏話や操作説明へのアクセスが多いんですよ。
考えられる可能性の一つとして、説明ページを見て面白くなさそうだと判断してダウンロードをやめた人の場合。
これはこれでいいんですよ。
よく確認せずにインストールしてアンインストールの手間を掛けさせるぐらいなら、最初からダウンロードしないほうがよいに決まっています。
ソフト紹介ページは、ソフトの導入を勧めるページであると同時に、引き返すための道でもあると思うのです。
そのためには、やはりよい点だけでなく、悪い点も含めて開示して、納得して使ってもらうのが大事だと思うのです。
フリーウェアを公開して嬉しいことといえば使ってくれた人が満足してくれることだけですから…。
そしてもう一つの可能性は、本体の更新が無くても操作説明や裏話そのものを見るためにわざわざ来てくれる人が多い場合。
ゲームそのものが面白かったからもっとよく知りたいと思う人、裏話を純粋に読み物として読んでくれている人、どちらにせよそれなり以上には楽しんでくれているのでしょう。
これは純粋に嬉しいですね。

constの位置関係

慣れないことをしたため早速失敗しました。
爆ボンマップ作成プログラム、マップチップの演算子をオーバーロードしてsetに入れたときうまく動作するようにする手はずだったんですけど、オーバーロードのやり方を間違えていたためオーバーロードではなく無関係な演算子を定義したということにされ、定義したはずの演算子が定義されていないことにされてしまっていました。
どうも、比較演算子の使用中に比較すべきデータそのものが変更されてしまってはまずい(例えば、a<bという比較をしただけでaやbの値が変更されてしまっては比較の意味がなくなる)ため、データに勝手な変更ができないようconstを付けて作らねばならなかったようです。
しかしそこがそれ、constを書き込み禁止程度にしか理解していない私のこと、constをつける位置を間違えてしまいまして。
bool operator<(const CMapData& rhs) const { return m_Depth<rhs.m_Depth; }
とすべきところをあろうことか
const bool operator<(const CMapData& rhs) { return m_Depth<rhs.m_Depth; }
としてしまっていたのです。
前者は{ return m_Depth<rhs.m_Depth; }を書き込み禁止に、後者はbool型の比較結果を書き込み禁止にしているのですが、boolは値を返した後即座にコピーが作られ、そのコピーのほうが使われるため、constはあってもなくても関係ないんですよね。
でまあ、そこんとこはうまくいったので、今度はエディットに必要不可欠なカーソル位置と選択中のチップデータと範囲選択の範囲を用意。
そしてカーソル表示用のレイヤも作り始めています。

ふにゃさん、ふにゃ第一作のステージを引き継いだり配布用のステージを用意したり、そういうことを手軽に行えるように、と思い、方法を考えてみました。
まず、配布用のステージは現在では編集用のステージを作成した後に「圧縮」を選ぶことで配布用のステージを作成します。
しかし、この方法だと、編集用ファイルと配布用ファイルを別個に用意することになり、テストプレイは編集用、それとは別に配布用、と分けて使うと、ステージ作成者の手元には内容がほぼ同じの2つのステージができることになり、プレイに支障が出ることは必至です。
そこで考えたのですが、保存するときに編集用と配布用の両方を同時に記録して、本体のほうからは配布用=プレイ用のステージしか読み込まないようにすれば、配布用は常に最新だし、テストプレイも楽々、他人のステージは編集ファイルが無いのでプレイはできても編集不可能、というわけで、ディスク容量を考えなければこちらのほうがずっとよい方法なのです。
そして、1からのデータの引き継ぎについては、1のステージメーカーのほうでふにゃさん用の編集ファイルを作れるようにしておき、それをふにゃさんエディタで改めて読み直して保存するとプレイ用ファイルができて引き継ぎ成功ということになるわけです。

セキュリティと大小関係

ミディビで保存ができなかった原因がほぼ特定できました。
http://support.microsoft.com/default.aspx?scid=kb;ja;175622で見てみると、どうも我々のアプレットは信頼されていないため、クライアントであろうとサーバーであろうと関係なく保存できないようです。
お絵描き掲示板はどうなっているのかと見てみると、アプレットから画像ファイルのデータの中身がそっくりそのまま送られてきていて、それをCGI側で保存するようになっていたのですが、これは今回のようなセキュリティの問題だったのかもしれません。
そこで自分もそれに倣ってCGIにデータを渡す方法を考えることにしました。
MidiSystemのwriteメソッドにはFileクラスを渡して直接ファイルに書き出してもらうものともう一つ、OutputStreamの派生クラスを渡して自分で作った出力ルーチンに書き出すものがあって、それを使うと、出力先を自由に変えることができるんですね。
ここで、Socket通信によってデータ受け取りCGIに出力すると、セキュリティに問題の無いCGIから保存ができるようになるということです。
アプレットからCGIにデータを渡す方法などについてはhttp://www.sm.rim.or.jp/~shishido/cgij.htmlなど。

LineConstructorを使っていたら、大きなファイルを開いたときにエラーが起こりました。
機能の足りない部分があってもバグはない自信があったので少しショックだったのですが、原因はどうやらスクロールバーが大きな数に耐えられなかったことが原因だったようです。

今日はチャットにクッキー保存機能をつけました。
本当ならGBBSのプロフィール機能と連動させたかったのですが、それぞれで必須とされている要素が異なり、GBBSのプロフィール自体もまだ発展途上であるため、連携するようにするのは控えておくことにしました。
まあそんなこんなでアルニックはレベル24達成。
目指せレベル30です。

爆ボンのための3Dマップソフトを作成中です。
3Dマップはどの方向にどのようにマップが広がるか分からないし、マップのほとんどの部分は空白状態なので、2Dのときのように二次元配列を用意してそれぞれにマップチップを、というわけにはいかないので、STLのsetに、マップ上の位置とチップ情報を詰め込んだクラスを入れて、順序付けされた配列として扱っています。
なぜ順序付けされた配列にする必要があるかというと、描画段階において確実に奥のチップから表示するためには奥行きでのソート(Zソート)が不可欠であり、マップチップが動かず、視点も動かないことを考えると、描画の時点でソートするより、最初からソートされた配列に順序を狂わさないようにデータを入れてゆけばそのまま最初の要素から順番に描画してゆくことで簡単に描画ができるようになるためです。
しかし、クラスはそのままでは大小関係を見て順序付けすることができないので、operator<だとかoperator>だとかを使って大小関係をつける関数を自作(いや、この場合演算子を定義か)。
なにしろsetにぶち込んで利用するからには値のように扱えるようにしなければならないので、他にもコピーコンストラクタやらoperator=なども考える必要があったりして、結構いい刺激になりました。
もっとも、コピーコンストラクタとoperator=についてはC++が勝手に書いてくれてるので自分で書く必要はないはずですが。

<前 次>