「Bomber丸World」に関する日記(10)

<前 次>

BGMなのである

Bomber丸WorldのBGM、デンノウセントウできました。
基本パターンだけですけど開発室にバトル3としてアップされています。
ザコ戦音楽のうち、コネコベーダーなど、メカ・電気系の敵に使われる曲で、ピコピコ音のみから成り立っており、ことあるごとにビギューンとかズギューンといった風な効果音が入ったりします。
この効果音の入れ方も既に考え付いており、効果音はBGMのボリューム調整だけで作るつもりです。

そうそう、マザー3、3章まで終了しました。
64版にあった戦闘のテーマ5、ついに出ましたよ。
「オバケのためのエチュード」というタイトルで、オソヘ城の絵画のモンスターで使われていました。
ちなみに曲はそのままではなく、サウンドバトルシステムとの関係なのか、部分的に変化しているところもあるみたいです。

和風風のタクト

大神やってました。
和風風のタクトですな。いろんな意味で。
オープニングからいきなり剣を抜き取るというゼルダの伝説恒例の名シーンで始まり、登場キャラクターの足の短さ、壷を割るとアイテムが出るところ、トゥーンレンダリング、ややうるさい助言などなど、随所に似ている部分があるんですよ。
もちろんゲームとしては全くの別物で、同じところよりかは違うところのほうが圧倒的に多いんですよ。
だから言い換えるならば特別似ているところが探しやすいんです。

そうそう、Bomber丸WorldのBGM作ってます。
今日はザコ戦闘曲を作っているのですが、実はザコ戦の音楽にも数種類あり、しかもそのそれぞれにまた異なるアレンジがあったりします。
今あるザコ戦闘曲は、「わきあいあい」「山の音楽家」「デンノウセントウ」「本当の力」の4通りです。

グランチルダがボールになる

弟がバンジョーとカズーイの大冒険をクリアしてました。
最後のグランチルダは強かったはずなんだけどチイトの体力自動回復がダメージを受ける勢いをはるかに上回っていたためほぼ体力全快で勝利。
グランチルダはかなり残酷な最期を遂げました。

物理量解析プログラムの解析には5つのレベルがあります。
レベル0:完全に確定した分
レベル1:確定した小数
レベル2:確定した整数
レベル3:千の位まで確定した整数
レベル4:未解析の連続した数値
レベルは基本的に解析が進めば下がるって最終的には0になります。
アルゴリズムが完成すれば公開してみようと思います。

Bomber丸WorldのBGMの変化パターンをいくつかに分類しようと考えています。
一つ一つにプログラムを組もうかとも思ったのですがデータと辻褄を合わせるのが結構大変そうだしめちゃくちゃ特別なことをするのではなく簡単なものを組み合わせるだけなので簡単なプログラムを組んでデータを渡すという方式にしたいのですよ。
サウンドテストを作るときにもプログラムとデータを分けたほうが都合がいいし。
フェード:指定の音量に徐々に近付く。目標値と時間を指定する。
プログラムチェンジ:音色を変更する。音色番号を指定。
即時切替:BGMを直ちに別の曲に変更する。BGM番号を指定する。
遷移:現在のBGMが終わった後に別の曲をかける。BGM番号を指定する。
効果音楽:指定の曲を現在のBGMに重ねる。ME番号を指定する。

レッドマウンテン

爆ボンのコンテンツ、レッドマウンテンが完成しました。
他もまあ適当にアトランダムに更新されてます。
そうそう、カスタムパーツも記載しました。
最終的にはあれにパーツの取れるステージのページへのリンクをつけるつもりです。
うちの爆ボンコンテンツはマニアックなデータベース兼完璧な攻略ガイドになる予定です!

Bomber丸Worldの開発室のBGMのページを必要なリソースのページに改装しました。
つまり、画像も含めて書いているということです。

サンプルのコンパイル結果

そういうことで中間コードを書いてみました。
とりあえず今のアルゴリズムとしては、計算式を見つけたら何はともあれ計算結果を一時変数に入れる、配列に対しては要素に直接値を代入するか要素を変数に代入するだけしかしない(変数名、インデックス、値で3つになるため)、命令を呼び出すときはパラメータをリストで渡して結果を一時変数で受ける、などなど、変換規則も考えつつ書いてみました。

# 例えば、イベントとか
msg("ハローワールド!\nこんにちは世界である!!");
&eee(select("それがどうした", "なるほど", "げこげこ"));

sub eee {
talk("みふみん", "あなたは".$_[0]."とおっしゃるのですね。");
if ($_[0] eq "げこげこ") {
for ($i=0; $i<10; $i++) {
$p = $i*($p+1);
msg($_[0].$p);
}
}
}

$param0[0] = "ハローワールド!\nこんにちは世界である!!";
$temp0 = msg @param0;
$param1[0] = "それがどうした";
$param1[1] = "なるほど";
$param1[2] = "げこげこ";
$temp1 = select @param1;
$param2[0] = $temp1;
@_ = @param2;
gosub eee;
$temp2 = $return;
goto LABEL0;
label eee;
$param2[0] = "みふみん";
$temp3 = "あなたは";
$temp4 = $_[0];
$temp5 = $temp3 . $temp4;
$temp6 = $temp5 . "とおっしゃるのですね。";
$param2[1] = $temp6;
$temp7 = talk @param2;
$temp8 = $_[0];
$temp9 = $temp8 eq "げこげこ";
if (!$temp9) goto LABEL1;
$i = 0;
label LABEL3;
$temp10 = $i < 10;
if (!$temp10) goto LABEL2;
$temp11 = $p + 1;
$temp12 = $i * $temp12;
$p = $temp12;
$temp13 = $_[0];
$temp14 = $temp13 . $p;
$param3[0] = $temp14;
$temp15 = msg @param3;
$i++;
goto LABEL3;
label LABEL2;
label LABEL1;
$return = undef;
return;
label LABEL0;

pseudoPerlコンパイラ

昨日の。
やっぱり中間言語レベルまではコンパイルしたほうがいいかな。
などと思ってます。
だって、逐次解釈とはいったって、式の中に命令が入っていたり、特定の命令で実行する場所が色々な場所に飛んだりして、今どこの何を実行中なのかがある程度プログラムからわからないとうまく動かないんですよ。
例えば2つの関数の結果を受け取って一つの価を返す関数。
f(g(x), h(y))
みたいなのね。
BASICのような逐次解釈じゃ難しいの。
最低限gとhの実行結果を持っておく変数が必要なわけ。
ああなんだかわからなくなってきた。
3番地コードで示される中間コードにコンパイルするの。
Add X Y Z
といったら
X = Y + Z;
と解釈するみたいに。
ん~。
ちょっとサンプルプログラムを手動コンパイルして雰囲気をつかんできますわ。

pseudoPerl

Bomber丸Worldのイベントや戦闘時のキャラクターの行動等はPerlに似たスクリプトで作ることにしました。
なぜPerlっぽいのにしたのか。
第一なぜ既存のプログラミング言語と似たようなものを使うことにしたのか。
完全オリジナルじゃいけなかったのか。
まずやりたいことを考えてみます。
1.計算式を使いたい。
2.イベントでも戦闘でも同じ感覚で記述したい。
3.決まりきった処理は関数のようにして呼び出したい。
4.変数や配列を使いたい。
5.変数は明示的に宣言しなくても使えるようにしたい。
6.数値も文字列も深く気にせず使いたい。
7.長い行は複数行に分けて書きたい。
8.変数と関数の区別を容易に付けたい。
9...きりがない。
もちろん、Perlを完全に実装するなんて無茶なことはしません。
例えば、便利そうでも実装が面倒な連想配列は実装しませんし、数値は解析の容易な整数のみです。
変数の省略も原則として認めませんし、標準関数はほぼ全て新しいものに置き換えられて、ローカル変数なども基本的にありません。
また、Perlは実行時にコンパイルして中間コードを生成し、高速化を図りますが、全体的なスピードが要求されず、多くのスクリプトが次々呼び出されるRPGではインタープリタ式のほうが適切だと思うので今回はコンパイラではなくインタープリタとして作ります。
一応言語仕様そのものは汎用性のあるものに作るつもりなので、出来上がったインタープリタは今後公開することがあるかもしれません。

ビットフィールドなんて知らなんだ

プログラミングコーナーのビット操作のところにビットフィールドのことを追記しました。
構造体やクラスの中限定で任意のビット長をもつ変数が使えるというのです。
ただ、ビットフィールドは構造体等の32ビット境界があるのでうまく変数をつめて書かないと帰って無駄が大きくなることもあり、また、配列が使えないという欠点もあったりします。

Bomber丸Worldでは、行動アルゴリズムをクラス化し、さらにGoToを実装しました。
GoToという行動タイプは今までになかったものですが、これは目的地をマップ座標で指定して、その場所へ向かうという行動を取ります。
簡単に言えば、人を追いかけるFollowの場所版ということです。
この行動は、まず目的地を最高点にし、他の場所に目的地に行くのにかかる歩数分だけ低くした点数をつけ、自分は最も点数の高くなる方向へ向かうというアルゴリズムになっています。
これ、まず全ての場所を最低点で初期化し、次に目的地を最高点にして、その場所を中心とした回り4箇所に点数をつけ、次は今点数をつけた周りに点数をつけ、更に次も同じようにして点数をつけ、自分の位置にたどり着くまでこれを繰り返します。
ただ、これを馬鹿正直に全部の場所を調べて点数をつけていたら検査する歩数の3乗のオーダーで処理回数が増え、高々32歩でたどり着く場所に1万回以上の処理が必要になったりするのでうまいこと処理を省くために調べる範囲を減らしたり調べるまでもないマスを読み飛ばしたりしています。

キャラを動かす続き

今日は押すアルゴリズムの改良と行動タイプ"Random"を実装しました。

押すアルゴリズムは、昨日作ったものだとキャラが沢山いたとき再帰呼び出しで無制限に次のキャラに動くよう促し続けると、袋小路などで「お前がどけ」「そっちこそ邪魔だ」の無限ループが発生し、フリーズすることがあったので、再帰呼び出しに制限を設け、一定回数以内の処理で動けなければその方向には移動できないということで解決しておきました。
一度これを呼び出し回数ではなく再帰の深さで判定するようにしていたのですがそうすると処理回数が4深さのオーダーで増えて、飛び出し回数で制限したときと同じ1024回で打ち切りにできるようにするには押せるキャラクターは高々5人程度になってしまい、人ごみをかき分けて進むようなネタが使えなくなってしまうことがわかったので、元の呼び出し回数式に戻しました。
再帰の深さを使ったバージョンは確実に動く人数を制限できるため、打ち切り深さを1から始めて順に深くしていけば動く人数を最小限に抑えることが可能だったはずなのですが、ちょっと袋小路の中に30人ほど人がいたら処理回数は10億回程度になりますからね、とてもじゃないが使い物になりません。
一方処理回数で打ち切る方式は適当に次々呼び出してどこかで道が開けた時点で処理終了になるのでどんなに遠回りだったとしても呼び出した経路上にいた人全員が動き出し、一人を動かすつもりが百人以上ごそっと動き出すこともしばしばです(もちろん数百人が密集していた場合だけですけどね)。
この方式だと呼び出し回数が確実に1024以下になるため、CPU負荷はそれほどかかりません。
まれに処理回数が1024に達してしまうほどの遠回りをして打ち切られることもありますが、その場合はそのフレームでは移動を諦めて、次のフレームで改めで最初から処理を開始すれば、動けるなら今度こそ高い確率で動けるでしょうし、動けなければ高々1024回の処理の後に何もしないだけです。

で、行動タイプ"Random"は、次の動きを決めるフレームでは上下左右に動くか、あるいは何もしないか、をランダムで決めます。
しかし今考えると、RPGの場合、話しかけられたら止まらなければいけません。
"Random"にしたキャラクターは結構せわしなく動くので一時的に行動タイプを"Still"などにして止めなきゃならないんですが(キャラは行動タイプを与えると勝手に動くので外部から制御するには行動タイプを変更するしかない)、そのあとはまた本来のキャラの動きに戻す必要があるので、行動タイプを変更するときは前の行動タイプを、関連する変数と共にスタックにつんでおかなければいけません。
つまり、動作とデータをひとまとめにして扱う必要があるのです。
これって…オブジェクト指向にする理由に一致するよね?
というわけで行動決定アルゴリズムはクラスにすること決定。
数が多いので少し面倒くさそうですが。

ダウンロードページのギャラリーに開発中の画像を公開し始めました。

キャラを動かす

今日はキャラが動かせるようになりました。
方向を与えてやれば、その方向が移動であるか、その方向に誰かいないかを判定して、移動可能であればその方向に移動します。
そして、動こうとした方向に誰かいた場合、そのキャラに動くよう促して、動いてくれた場合はもうその場所にキャラはいないので自分も移動します。
それから、行動アルゴリズムのうち、User、つまりユーザーの操作に反応して動くタイプを作るため、キー入力クラスも作りました。
現在は画像表示を作っているので今のところ仮のものですが。
今日の画像は、白いのを操作して、他の二人を押していっているところです。

1141656285.png

マップの話の続きの続き

で、マップ表示ができるようになって調子に乗ってキャラも表示できるようにしちゃいました。
キャラ部分がまた作りこみが甘いうちからテスト実行なんかしちゃってエラー続出。
自分で考えた仕様を自分で把握しきれていないというのが最悪でした。
思い出したおかげでなんとか形になったものの、次のステップに進む前に一度キャラ部分の再点検が必要そうです。

1141569988.png

マップの話の続き

さて、ようやくマップ表示できそうなところまでこぎつけました。
今日作ったのは、マップチップ管理の読み込み部分。
とりあえず読み込んでいないものは随時読み込んで、終了時にまとめてメモリ解放しています。
この先マップチップ管理には必要なくなった画像を随時解放していく部分を作るのですが最後に一気に解放する処理があるので今のところ優先順位を落としています。
そしてマップの地形データの読み込み。
マップチップ番号とチップを置く高さが入っています。
しかしまだこれでもマップは表示できる段階ではありません。
早くマップぐらい表示したいんですが。

Bomber丸Worldのマップの話

今日はマップ表示でもやったろかと思ってたんですがマップデータを用意する前に考えなくちゃいけないことがいろいろあったため作れませんでした。
まずマップチップが用意されてない。
まだテスト段階なので素材はテスト用マップチップでいいのですがそれを使えるようにするためのデータの形式が決まっていなかったのです。
で、それを考えていると今度は内部的に3Dで処理している座標の扱いがまだ決まっていないためにマップの高さ情報が決められないということに気付いたので今度は座標系をきっちり決めることになったんですよ。
で、まあ、決めた結果はいつものようにダウンロードページの開発室においてあります。

モンキーウォーク

ミディビ更新しました。
できること自体はそんなに変わっていません。

Bomber丸Worldのキャラの動きについて考えてます。
動きタイプをFollowにすると基本的に特定の人物についていくんですが、実は人によって歩調が違ってましてね、つまり、相手の動きが速いと取り残されることがあるということです。
それから更に踏み込んで考えたのが、バルーンモンキー。
バルーンモンキーって知ってますか?マザー2の。
ジェフの後についてくるキャラクターなんですが、ほっとくとしばらくその辺をうろうろしていて、ある程度距離を置くと思い出したように駆けつけてくるんです。
そのようにしてつかず離れず追いかけてくるのがバルーンモンキー。
これが結構面白そうなのでやってみたいのです。
アルゴリズムは案外簡単です。
ある距離以上に離れるまではRandomで歩かせておいて、離れたらFollowモードに内部的に切り替えるのです。
あと、前に言ったかどうか知りませんが、相手を押すというシステムも作っています。
押すリスクも加えて追いかけるプログラムを書くのは少し難しそうな気がしますがそう難しくもなさそうなのでまあできそうな気がします。(どっちだ

キャラ作ってます

Bomber丸Worldのマップシステム構築中です。
しかしやっぱり路線変更してキャラの動きのほうに走っています。
結局のところキャラが実際にする動きは行動アルゴリズムに関わらず上下左右に動くかその場で止まっているかのどれかであって、共通の部分はマップとはあまり関係ないので先に作れそうな気がしたのです。
行動アルゴリズム上どうしてもマップに依存する部分があるのでそこはまた後から作らなきゃいけないのですが。

マップ構造

Bomber丸Worldのキャラの動きはマップ構造に関係する部分があるのでマップのほうを先に作ることにしました。
しかしBomber丸Worldのマップは一つのマップにつき画像が一つだけという制限もないしアニメーションもするのでどのようにしてメモリ効率よく複数のマップチップを読み込ませるかでちょっと悩んでます。
とにかく画像が大きいと考えて、今は画像以外のデータと画像データを切り離して管理して、画像データは必要なときのみ読み込み、それ以外(現状では画像を読み込むためのデータと高さのみ)は常にメモリ上に置いておくという手法にしています。
やろうと思えば「それ以外のデータ」も適宜ファイルから読み込むようにできるのでしょうがあまり意味がないような気がします。

そうそう、ClipCaptorというのを作ってアップしました。
クリップボードに画像をコピーずると0.2秒後にファイルに書き出してクリップボードをクリアするというプログラムです。
たぶんベクターなどで探せば見つかるのでしょうが、探すより作ったほうが早いぐらいのものだったのでちゃちゃっと作ってみました。

キャラクター定義ファイル

Bomber丸Worldのキャラクター定義ファイルを作り始めました。
じゃあ、例を出してみます。

Name "クロ"
Animation "Black.and"
StopDelay 10
WalkDelay 10
Type Follow "White"

プログラム側ではまだ対応していません。
このファイルの書き方とか、ダウンロードのページにアップしてます。
ひそかにキャラごとに移動速度が違ったりします。

一文字追加も時として大変な作業なのですよ

2月1日の日記に書いていたBomber丸Worldのアニメーション機能の問題ですが、データとしては4方向使えるようにしつつも、必要ない場所では3方向はデータを用意しない方法で、使用するメモリを押さえることができそうです。
ただそれだけであれば2月1日の時点でわかっていたのですが、問題はそれを生真面目にやろうとすると全てのアニメーションデータで方向を明記しなくちゃならなくて、それが結構な数になるということでした。
それを解決するために、省略時に自動的に指定される方向を決めるコマンドを作ってみました。
あ、でもマップチップとかはほとんどアニメーションしないからあまり意味はないのか。
まあいいや。気休め程度に。

クリップボードに保管

ミディビのデータ保護機能、とにかくJavaの外にデータを退避させておかなければJavaごと落ちたときに復旧ができないのです。
もちろんJavaが落ちるタイミングがわかるわけじゃないので常にどこかにデータのバックアップを取っておくことになります。
サーバー上に保存するというのは回線を切って作業できなかったりサーバー負荷が大きくなることなどから非現実的ですし、クライアント側のコンピュータにファイルとして保存するのもセキュリティの関係でできません。
そもそもバックアップがあったからといってどうやって復旧すればよいのでしょう。
画像のようにスクリーンショットが取れるわけじゃないのです。
そこで考えたのが、MIFUMIDIA上で復元可能な曲データを文字として表示し、コピーさせるというもの。
実際には面倒くさくてコピーなんてあんまりしないでしょうから、定期的に勝手にクリップボードにデータを転送します。
そうなるとユーザーがクリップボードを使えなくなるという弊害が出てくるのでデフォルトで自動バックアップは使わないようにして、「定期的にクリップボードに保存」にチェックを入れれば自動バックアップが働くようにします。
もちろんデフォルト設定はOFFです。
また、経験的にMIDIデータは同じ音が何度も続いたりして非常に冗長なので圧縮をかけてデータ量を減らします。
まともな曲を真面目に作るならどうしても10KB以上になってしまうことが多くて、その10KB分の文字というのが結構な量になったりするので圧縮が必要になるのです。
しかしそうすると高速圧縮アルゴリズムが必要になりますね。
ちなみに、この文字データをサーバー側のファイルに保存すれば、サーバーに一時保存してから後で再開するということもできるようになります。

2月1日、Bomber丸Worldのマップにかかるメモリを計算するときにさりげなく4をかけたりしていましたが、これは元々半キャラ分の大きさを当たり判定の単位にしようと考えていたからだったのですが、こうすると、二つのマップチップにキャラがまたがったときにどちらのマップチップを優先するかで困ってしまうため、やっぱり当たり判定等もマップチップの大きさとあわせることにしました。
本当は見た目に動きが不自然になるので嫌だったんですが遊びにくくなるよりはマシです。
実はこの方法、謎の男の歩き方にも悪影響を及ぼすのですが、しかしこうすることでメモリ使用量が単純計算で4分の1になるわけですから逆に言えば同じメモリで広いマップを作れるようになるので、やはりマップ優先で作ることにします。

蟲使い、蟲捨てた。

Bomber丸Worldのアニメーション機能がエラーを起こしていたので直しました。
原因は機械的な置換ではすまないところを機械的に置換してしまったことにあったようです。

MIFUMIDIA、特定の環境で灰色画面になることがあるようです。
おそらくJavaのバージョンの違うゆえのものだろうけど今確認できる状況にないんですよね…。
だから想像で改善していくしかありません。
それと空MIDIを送信できてしまう症状。
これも改善しなきゃいけません。
早いうちに突っ込まれてよかった。
本格始動してからだとカッコ悪いことこの上ない。

そーいえばテレビでナウシカ見ました。
アニメのほうだと登場人物があんまり死ななくて安心して見てられます。
原作ではユパ様もテトもカイも死んじゃうんだもんね。
クロトワさんもかなりズタズタにされちゃいます、本当に物理的に。
ナウシカは蟲使いに女神様と呼ばれてたり。

<前 次>