こんにちは。


 おかげさまで、制作中のゲームのシステムは8割以上完成してきました。



 さて、今日は戦闘処理への複線として、ルーレット選択を実装しましょう。英語では"roulette wheel selection"といいます。



 ルーレット選択自体に余り馴染みのない方のために説明すると、ルーレット選択はそれぞれに比重がある複数項目のうちからランダムに一つを選び出す方法で、概念的には賭博場にあるあの玉を転がすルーレット(roulette wheel)を想像していただければ良いです。このとき、ルーレットの枠の大きさは、項目それぞれの比重にあたります。



 ルーレット選択は遺伝的アルゴリズム(GA)等にもよく使われる方法で、ゲームを制作する際には非常に有用です。


 では、関数の実装に入ります。


public int rouletteChoice(int[] rate){
  int max = 0;
  for(int i = 0;i < rate.length ;i++ )
    max += rate[i];
  int temp = rand.nextInt(max);
  for(int i = 0;i < rate.length ;i++ ){
    temp -= rate[i];
  if(temp < 0)
    return i;
  }

  System.out.println("予期しないエラー@ルーレット選択");
  return -1;
}


 実装自体は非常に簡単です。引数rate[]はそれぞれの起こる「比重」であり、合計が100であると分かりやすいですが、100である必要はありません。randはjava.util.Randomクラスのオブジェクトです。




 では、使用例を挙げてみましょう。今、明日の天気が晴れ50%、曇り20%、雨30%であるとします。このとき引数たるrateを


int[] tommorow_rate = {50,20,30};


として初期化し、


int result = rouletteChoice(tommorow_rate);


とします。resultが0となる確率は50%、1、2となる確率はそれぞれ20、30%であることが分かると思います。前述の通り、int[] tommorow_rate = {5,2,3};としても挙動は変わりません。



 実装の際考慮した点としては、値の評価ですかね。簡単のために合計が100である場合を挙げると、確率50に該当するケースは乱数が0~49のときです(50個)。すなわち、乱数<50の場合に該当します。乱数=<50の場合ではないということです。




 この関数を用意すれば、乱数発生は格段に楽になると思います。次からは戦闘について簡単に書いていきたいと思います。







前回から大分期間が空いてしまいましたが、こんばんは。


詰まっていたアイテムも、どうにか仕様が固まったので、その過程を晒して見たいと思います。


本来、アイテムのように「一度設定したパラメータが変化しない」ようなデータは、オブジェクトをコピーするのではなく間接的に参照した方が効率が良いです。(対して、戦闘時にHPが変化する敵は、オブジェクトのcloneが出来た方が良いと思います。同種の敵が同時に二匹出現したとき、HPの値を共有していては困りますからね。)





一つ目の分岐点となったのは「使用アイテムと装備アイテムのクラスを分けるか」という点でした。結論から言って、メモリ量節約のため私はクラスを分けたのですが、ここで分けなかった場合は数キロバイトのメモリを無駄にする反面、いくつかの問題は解消していたと思います。





さて、私は装備アイテムを使用アイテムの子クラスにし、所持アイテムを親クラスの参照変数配列で表現することにしました。しばらく平穏に過ぎましたが、次の問題「セーブ」に思い当たりました。セーブ時には持っているアイテムを数字で管理できることが望ましいので、私は参照の方法を参照変数の配列からint配列に変更しました。





ここでクラスの分割が裏目に出ます。単なる整数では、普通のアイテムの配列と装備アイテムの配列のどちらを参照していいか分かりません。単純に連結したのでは、普通のアイテムのデータ数によって装備アイテムの添え字が変わってしまいます。





ここで、二つの考えが思いつきました。一つは、両方を同一の配列に突っ込み、定義ファイルでは使用アイテムと装備アイテムが混在することを許すという方法です。もう一方は、あらかじめ使用アイテムのデータ数の限界値を決め、装備アイテムを表す値はその数値に添え字を足すという方法です。アイテムを表す数字が限界値以上だったらその数値を引いて装備アイテムとみなすということになります。





前者の方が実装が楽そうで「スマート」に見えましたが、結局私は後者を採用しました。前者にするならば、そもそもクラスを分けたメリットが余り発揮されないことと、後者は型変換が必要ないことが主な理由です。


どちらが優れているかはまだ答えは出ていませんが、逆の方が優れていたらすぐにでも移行しようとは考えています。


こんばんは。


私用で少し間が空いてしまいました。


//そもそもここ見てる人いるんだろうか…とか不安になってきますが


MIDPは十分に将来性のあるプラットフォームだと思いますので、開発を続けていきましょう!


さて、今日も前回の続きでイベントの処理です。


今やスクリプトの導入によるロジックとデータの分離は割と頻繁に採用されていると思いますが、スクリプト内においてもデータは開始時に読み込んでおいた方がいい場合は多いです。


特にマップデータ・グラフィックはマップ及びイベントと密接に関わってきますので、スクリプトファイル読み込み時にまとめて読み込んでおいた方が”読み込み忘れ”が起き難いです。可読性が上がりデバッグもしやすいですしね。


データ定義といっても、スクリプト読みこみの段階で「キーワード検索→逐次行処理」を繰り返すだけで簡単に実装できます。新しい関数等を導入する必要はありません(関数にまとめた方が見やすいとは思いますが)。


では、実際の逐次行処理についてです。基本は文字と数字の組み合わせなので、String.equals関数とInteger.parseInt関数で地道に処理していくだけです。問題として起きうるのは、スペルミス、変換ミス、引数の個数を間違えたことによるnull参照くらいでしょうか。


一例としては、こんな感じです。


while(reading){

  String[] temp = qf.readLine();

  if(temp[0].equals("window")){

    waiting_lines = split(temp[1],"<br>");

    adjustDisplayLines();

    dialogwindow = true;

    reading = false;

    keywait = KEY_FIRE;

  }

  else if(temp[0].equals("EndDif")){

    reading = false;

  }

……


更にパフォーマンスを向上させるには、コンパイル前の処理で、命令と数字の1:1変換を行うのもありかもしれません。その方が望ましいです…まだそこまではしていませんが^^;