2012年2月17日金曜日

2D迷路データとその解を自動生成する(3):迷路データからのマップデータ生成

ひきつづき、「どんな迷路を、どのようにつくるか」について、述べていきます。

(1)大きな構造の生成、(2)実際のMapに与えるdata生成、(3)細部の構造の生成、という 3段階のうち、「(2)実際のMapに与えるdata生成」についてを解説します。

以下、Worldというクラスを定義し、そのコンストラクタ内で一つ前のエントリで生成した迷路データをもとにして、enchant.jsのMapインスタンスに与える前景の地図データ・背景の地図データを作成するというプログラムを示していきます。

このWorldのインスタンスから、 「主人公キャラクタ開始位置」「地形衝突判定データ」「マップ前景データ」「マップ背景データ」などの それぞれの値が得られるものとします。

ここでは、Worldの実装として、 単純な「壁の最小の厚さと、道の最小の幅とが、基本的には同じ」であるような迷路マップを作ることを考え、 コンストラクタには、次のような引数を与えることにします。

  • mapWidth マップ全体の横幅のセル数
  • mapHeight マップ全体の高さのセル数
  • mapCellUnitSize 1セルがマップチップ何個分であるのかを表す個数
  • block_id 「岩」(通行不可能セル)を表すマップチップの値
  • wall_id 「壁」(通行可能セル)を表すマップチップの値
  • empty_id 「道」(通行可能セル)を表すマップチップの値

ようするに、迷路データのそれぞれのセルを、mapCellUnitSizeの分だけ単純に整数倍して引き伸ばした内容を迷路マップとして作っていこうというものです。 マップ全体の横幅・高さのセル数であるmapWidth, mapHeightを引数とし、迷路データの幅・高さのセル数については、内部的に算出するものとします。 また、地形の基本マップチップである「岩」「壁」「道」を表す値として、block_id, wall_id, empty_idの3種類の値を与えることにします。

この部分のプログラムは、特に複雑なアルゴリズムを用いているわけではなく、とってもかんたんです。

// 世界クラスの定義
var World = enchant.Class.create({
    /**
      @param {Number} mapWidth マップ全体の横幅のセル数
      @param {Number} mapHeight マップ全体の高さのセル数
      @param {Number} mapCellUnitSize 1セルがマップチップ何個分であるのかを表す個数
      @param {Number} block_id 「岩」(通行不可能セル)を表すマップチップの値
      @param {Number} wall_id 「壁」(通行可能セル)を表すマップチップの値
      @param {Number} empty_id 「道」(通行可能セル)を表すマップチップの値
    */
    initialize: function(mapWidth, mapHeight, mapCellUnitSize, mazeComplexity,
                                    block_id, wall_id, empty_id){

            //mazeの幅・高さを算出
     var mazeWidth = Math.floor(mapWidth/mapCellUnitSize);
     var mazeHeight = Math.floor(mapHeight/mapCellUnitSize);

            //mazeの生成
     var maze = new Maze(mazeWidth, mazeHeight,
                                        mazeComplexity,
                   startPoint.x, startPoint.y);

            //背景、前景、衝突判定データの初期化
     var bgMapData = new Array(mapHeight);
     var fgMapData = new Array(mapHeight);
     var collisionData = new Array(mapHeight);
     for(var i=0; i < mapHeight; i++){
         bgMapData[i] = new Array(mapWidth);
         fgMapData[i] = new Array(mapWidth);
         collisionData[i] = new Array(mapWidth);
     }

            // 背景・前景・衝突判定データの内容設定
     for(var i = 0; i < mapHeight; i++){
         for(var j = 0; j < mapWidth; j++){
                        // 迷路上の該当位置が「侵入不可能」セルであるかどうか
          var isCollid = maze.hitTest(Math.floor(j/mapCellUnitSize), Math.floor(i/mapCellUnitSize));

          if(isCollid){
              //該当位置が「侵入不可能」の場合は、壁ブロックを表示
              fgMapData[i][j] = block_id;
              bgMapData[i][j] = block_id;
              collisionData[i][j] = 1;
          }else{
              //該当位置が「侵入不可能」ではない場合は、「道」(床)を表示
              fgMapData[i][j] = empty_id;
              bgMapData[i][j] = empty_id;
              collisionData[i][j] = 0;
          }

          if(i % mapCellUnitSize == 0 && mapCellUnitSize <= i){
              var northIsCollid = maze.hitTest(Math.floor(j/mapCellUnitSize), Math.floor(i/mapCellUnitSize) - 1);
              if(! isCollid && northIsCollid){
               //上側が「侵入不可能」、該当位置が「侵入可能」の場合は、背景を「壁」に変更
               bgMapData[i][j] = wall_id;
              }
          }

          if(i % mapCellUnitSize == mapCellUnitSize - 1 && i < mapHeight - mapCellUnitSize){
              var southIsCollid = maze.hitTest(Math.floor(j/mapCellUnitSize), Math.floor(i/mapCellUnitSize) + 1);
              if(! isCollid && southIsCollid){
               //下側が「侵入不可能」、該当位置が「侵入可能」の場合は、前景を「岩」に変更
               fgMapData[i][j] = block_id;
              }
          }
         }
     }

     //プレイヤー開始位置
     this.startPoint = new Point(maze.sx * mapCellUnitSize,
                                maze.sy * mapCellUnitSize);

     //迷路データ
     this.maze = maze;
     //地形衝突判定データ
     this.collisionData = collisionData;
     //マップ前景データ
     this.fgMapData = fgMapData;
     //マップ背景データ
     this.bgMapData = bgMapData;
    },
    ....略....
});

なおここでは、「斜め上からの見下ろし」を実現するために、 キャラクタの上半身が奥の「壁」に重なって表示されたり、 下半身が手前の「岩」に隠れたりするために、

  • 上側が「侵入不可能」、該当位置が「侵入可能」の場合は、マップ背景の該当位置セルの値を「壁」に変更する
  • 下側が「侵入不可能」、該当位置が「侵入可能」の場合は、マップ前景の該当位置セルの値を「岩」に変更する

という後処理を加えています。こうした内容の「マップ背景」「マップ前景」を、 次のような順番でステージに追加してやれば、「斜め上からの見下ろし」のような表現を実現できることになります。

  • マップ背景
  • キャラクタ、アイテム
  • 爆発などの効果の表示
  • マップ前景
  • スコアなどの表示

0 件のコメント:

コメントを投稿