見出し画像

自動生成マップ(チャンク+ダイヤモンドスクエア)

<html>
<body>
</body>
<script>

// ダイヤモンドスクエア
function dmsq(s, sz, mz, h, mp){
  var m=(eval(h.join("+"))>>2);
  if (sz==1) { mp[s]=m; return }
  else {
    sz>>=1;
    dmsq(s, sz, mz, [ h[0], (h[0]+h[1])>>1, (h[0]+h[2])>>1, m ], mp);
    dmsq(s+sz, sz, mz, [ (h[0]+h[1])>>1, h[1], m, (h[1]+h[3])>>1 ], mp);
    dmsq(s+sz*mz, sz, mz, [ (h[0]+h[2])>>1, m, h[2], (h[2]+h[3])>>1 ], mp);
    dmsq(s+sz*mz+sz, sz, mz, [ m, (h[1]+h[3])>>1,  (h[2]+h[3])>>1, h[3] ], mp);
  }
}

// チャンク
function chunk(cz,sz){
  var mp=[], h=[], _mp=[];
  for(var i=sz**2+sz+1; i--; h[i]=Math.random()*255|0);
  for(var i=sz**2; i--;){
    dmsq(0 ,cz, cz, [ h[i], h[i+1], h[i+sz] , h[i+sz+1] ], _mp);
    for(var j=_mp.length; j--;){
      var xy=[(i/sz|0),i%sz], c=[(j/cz|0),j%cz];
      mp[ xy[0]*cz**2*sz+xy[1]*cz+c[0]*cz*sz+c[1] ]=_mp[j];
    }
  }
  return mp;
}

// HTML出力
function viewmap(sz,mp){
  var t=[];
  for( var i=mp.length; i--;
    t.push("<td bgcolor={c} width=10 height=10></td>".replace(/{(.*?)}/g,function(m,$1){
      return {
        var h=mp[i];
        "c":( function(i){
          return h<50  ? '#00f' : h<80  ? '#ff0' : h<140 ? '#0f0' : h<200 ? '#090' : h<250 ? '#0ff' : '#fff';
          })(i)
      }[$1];
    })),
    i%sz==0 && t.push("</tr><tr>")
  );
  document.body.innerHTML="<table border=0 style='border-collapse:collapse;'><tr>"+t.reverse().join("")+"</td></table>";
}

viewmap(16*16, chunk(16,16));

</script>
</html>


ダイヤモンドスクエアアルゴリズムは正方形の四隅に高さを与え、中央に4点の平均値を求め、中央の上下左右、四隅の中間点を区切って4分割し、再帰的に処理する

マップ生成は複数のチャンクを張り合わせることで作る
チャンク同士のつながりがスムーズになるように与える高さが隣同士で共有されるようにする

高度配列の数はチャンク数+1列+1にしている
マップが1次元配列で左右両端(東西)がつながっている状態で移動可能として考える
高さは1つのチャンクの四隅 [ 左上、右上、左下、右下 ] と4点必要で共有される必要がある
左上を原点と考え、チャンク番号と同じ番号とする
h=[ i, i+1, i+sz, i+sz+1 ]
と表すことができる

右端のチャンクの右上、右下は
i+1, i+sz+1
で1段下の左端の左上、左下の高さを流用できる
これは1次元配列のマップで左右両端が移動できるのと同じで東西がつながった状態としてマップを作ることができる

しかし、下側の番号がチャンク番号を超えてしまう場合がある
下側のチャンクでは左下、右下( i+sz, i+sz+1 )
一番右下の隅のチャンクでは右上、左下、右下( i+1, i+sz, i+sz+1 )
がそれぞれチャンク番号がないのでundefinedとなってしまう

上下を繋げようとして最上段の高度を流用するとマップの最下段だけゆがみがでてしまう
そこで「便宜的」に余分な1列分の高さを作ってしまう
すると
i+1 , i+sz, i+sz+1
が存在するのでスムーズなチャンクのつながりを作ることができる

チャンクではダイヤモンドスクエアに与える高さの管理と生成されたチャンクをマップに格納する部分を担う

生成されたチャンクに格納されている高度をマップに格納する為、
チャンク内の位置 → マップの位置
に変換が必要

for(var j=_mp.length; j--;){
  var xy=[(i/sz|0),i%sz], c=[(j/cz|0),j%cz];
  mp[ xy[0]*cz**2*sz+xy[1]*cz+c[0]*cz*sz+c[1] ]=_mp[j];
}

マップの位置(xy)チャンクサイズの位置
チャンクの位置(c)ブロックサイズの位置
をそれぞれ求め、マップ(mp)にチャンク(_mp)から格納する

描画

t.push("<td bgcolor={c} width={s} height={s}>#{h}</td>".replace(/{(.*?)}/g,function(m,$1){
      return {
        var h=mp[i];
        "s":10,
        "h":mp[i],
        "c":( function(i){
          return h<50  ? '#00f'
               : h<80  ? '#ff0'
               : h<140 ? '#0f0'
               : h<200 ? '#090'
               : h<250 ? '#0ff'
               : '#fff';
          })(i)
      }[$1];
    })),

マップをテーブルとして出力する為に、ブロック単位をTDとして書き換える
テンプレートを作り、ブロックの個数分繰り返して書き換える
書き換えは主に高度を色=地形として表示させる

書き換えについては過去の記事を参照のこと
ロジック搭載式テンプレート



中点変位法

var m=(eval(h.join("+"))>>2);

↓

var m=Math.min((eval(h.join("+"))>>2+Math.random()*8,255);

四隅の高さの平均を求めた時、ランダム値を加算することで中点変位をさせることも可能


この記事が気に入ったらサポートをしてみませんか?