(C)WorldWideSoftware
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>へクス(ヘキサ)のマップ2</title>
</head>
<body>
<canvas id="bg" width="480" height="480"></canvas><br>
<a href="http://www.wwsft.com">(C)WorldWideSoftware</a><br>
【説明】六角形を組み合わせて構成されるへクスのマップのサンプルです。
カーソルキーでカーソルを移動、スペースキーを押すとその位置から全マスに対し距離の検索を行います。
<strong>紺色のヘクスはキャラクターが入れない場所という想定です。</strong>
検索した結果、例えば10の位置からは10→9→8→‥→2→1→0と辿ることで最短距離で目的地に移動できます。
<br>
<a href="hexa01src.html">ソースコードの確認</a>
<script>
//描画面(キャンバス)の準備
var cvs = document.getElementById("bg");
var ctx = cvs.getContext("2d");
ctx.lineWidth = 4;//線の太さ
ctx.font = "24px monospace";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
//キー入力
var key = 0;
onkeydown = function(ev) { key = ev.keyCode; }
onkeyup = function(ev) { key = 0; }
//定数
var HEXA_W = 10;//横のマスの数 但し偶数行は横9マスとする
var HEXA_H = 10;//縦のマスの数
var HEXA_D = 48;//へクス間の画面上の距離(ドット数)
//二次元配列でマップを管理する
var hexa = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 9],//←9は入れないマス
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],//←1はキャラ(兵士、戦車etc)が入れない場所を想定、カーソルは入れる
[0, 0, 0, 1, 0, 1, 0, 0, 0, 9],
[0, 0, 0, 1, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 0, 1, 0, 1, 0, 9],
[0, 0, 0, 0, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 9],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
var dist = [//こちらは距離の検索で使う
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
function resetDis() {//距離データをリセット
var x, y;
for(y=0; y<HEXA_H; y++) {
for(x=0; x<HEXA_W; x++) {
dist[y][x] = -1;
}
}
}
//6方向への移動量(方向は左を0とし時計回り)
var XP0 = [-1, 0, 1, 1, 1, 0];//偶数列(y%2==0)
var YP0 = [ 0, -1, -1, 0, 1, 1];//
var XP1 = [-1, -1, 0, 1, 0, -1];//奇数列(y%2==1)
var YP1 = [ 0, -1, -1, 0, 1, 1];//
/*
(x,y)マスの6方向にある配列はこれ↓であり、その値を定義している
y%2==0 y%2==1
┌─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┐
│ │ │ │ │ │ │ │ │ │ │ │
├─┼─┼─┼─┼─┤ ├─┼─┼─┼─┼─┤
│ │ │1│2│ │ │ │1│2│ │ │
├─┼─┼─┼─┼─┤ ├─┼─┼─┼─┼─┤
│ │0│■│3│ │ │ │0│■│3│ │
├─┼─┼─┼─┼─┤ ├─┼─┼─┼─┼─┤
│ │ │5│4│ │ │ │5│4│ │ │
├─┼─┼─┼─┼─┤ ├─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │ │ │ │
└─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┘
*/
//あるマス(今回はカーソルの位置)をスタート地点とし、そこからの距離を検索する
function calcDis(dis) {
var d, x, y, nx, ny;
for(y=0; y<HEXA_H; y++) {
for(x=0; x<HEXA_W; x++) {
if(dist[y][x] == dis) {//現在の距離
for(d=0; d<6; d++) {//6方向を調べる
if(y%2 == 0) {
nx = x + XP0[d];//【重要】この記述が上で定義した6方向
ny = y + YP0[d];
}
else {
nx = x + XP1[d];//【重要】この記述が上で定義した6方向
ny = y + YP1[d];
}
if(0 <= nx && nx <= HEXA_W-1 && 0 <= ny && ny <= HEXA_H-1) {
if(hexa[ny][nx] == 0 && dist[ny][nx] == -1) dist[ny][nx] = dis+1;//隣マスに距離の値をセット
}
}
}
}
}
}
//へクスのキャンバス上の座標
//配列[y][x]の(x,y)の値から画面上の座標を求める
function hexaX(x, y) {
var X = HEXA_D/2 + x*HEXA_D;
if(y%2 == 0) X += HEXA_D/2;
return X;
}
function hexaY(x, y) {
var Y = HEXA_D/2 + y*HEXA_D;
return Y;
}
//カーソル
var cur_x = 0;
var cur_y = 0;
function moveCur() {//方向キーでカーソルを移動
var x = cur_x;
var y = cur_y;
if(key == 37) x--;
if(key == 39) x++;
if(key == 38) {
if(y%2==1 && x==HEXA_W-1) x--;//右端で引っかからないように
y--;
}
if(key == 40) {
if(y%2==1 && x==HEXA_W-1) x--;//右端で引っかからないように
y++;
}
if(0 <= x && x <= HEXA_W-1 && 0 <= y && y <=HEXA_H-1) {//移動可能な範囲
if(hexa[y][x] < 9) {//入れるマス
cur_x = x;
cur_y = y;
}
}
}
function drawHex(x, y, r) {//六角形の表示
var i, vx, vy;
ctx.beginPath();
for(i=0; i<6; i++) {//三角関数で六角形の頂点位置を計算
vx = hexaX(x,y) + r*Math.cos(Math.PI*(30+i*60)/180);
vy = hexaY(x,y) + r*Math.sin(Math.PI*(30+i*60)/180);
if(i == 0) ctx.moveTo(vx, vy);
else ctx.lineTo(vx, vy);
}
ctx.closePath();
ctx.fill();
}
function drawBG() {//背景全体の描画
var x, y;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 480, 480);
for(y=0; y<HEXA_H; y++) {
for(x=0; x<HEXA_W; x++) {
if(hexa[y][x] < 9) {
if(hexa[y][x] == 0) ctx.fillStyle = "blue";
if(hexa[y][x] == 1) ctx.fillStyle = "navy";
drawHex(x, y, 26);
if(dist[y][x] >= 0) {//距離の表示
ctx.fillStyle = "rgb("+(dist[y][x]*16)+",255,0)";//距離の値が大きいほど黄色に近い色にする
ctx.fillText(dist[y][x], hexaX(x,y), hexaY(x,y));
}
}
}
}
}
//メイン処理
var idx = 0;
var tmr = 0;
function mainProc() {
tmr++;
if(idx == 0) {//キー入力
moveCur();//カーソルの移動
drawBG();//BG描画
//カーソルの表示
var s = 24+tmr%4;//カーソルのアニメーション
ctx.strokeStyle = "red";
ctx.strokeRect(hexaX(cur_x,cur_y)-s, hexaY(cur_x,cur_y)-s, s*2, s*2);
if(key == 32 && hexa[cur_y][cur_x] == 0) {//スペースキーで距離検索の処理へ
resetDis();
dist[cur_y][cur_x] = 0;//検索開始位置
idx = 1;
tmr = -9;
}
}
if(idx == 1) {//距離の検索
drawBG();//BG描画
if(tmr < 150 && tmr%10 == 0) calcDis(parseInt(tmr/10));//10フレームごとに距離を検索する
if(tmr > 150) idx = 0;
}
}
resetDis();
setInterval(mainProc, 100);
</script>
</body>
</html>
←動作確認に戻る