3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ゲームをつくる:Hex描画を実装

Last updated at Posted at 2019-10-29

#この記事は、
ヘックス・ターン制SLG好きが、最近スマホでその手の面白いゲームが無いので自分で作る、と頑張る記録です。

この記事のまとめ

  • Hexを、座標計算してCocosで線で描画してみた
  • 正六角形は、正三角形が6個集まったものと考えて座標計算した

実装してわかった、設計上のポイントは、こちらの記事

Cocos Creatorを使ってJavaScriptでヘックスマップを描画してみた

まずはプリミティブなLine描画を使って、ヘックスマップを実装してみます。
まず動くものを作って実践して、雰囲気を掴みます。作ってみないとわからないことが沢山あると思うからです。ぶっちゃけ、マップは最終的にはスプライト表示になるので、このライン描画は使わないと思うのですが(その割には結構苦労した)、まずは動くものを作ります。

こんな感じのものを作りました。

CocosCreatorで作成して、Web viewで動作しています。
スクリーンショット 2019-10-29 20.47.16.png
今後は動くものを貼っていきたいので、どこか無料で、良いHTML5ホスティングサービスがあったら教えて下さい。

どのように実装したか

正六角形は、正三角形が6個集まってできています。
正三角形は、角度30°・60°・90°からなる直角三角形が2つ合わさったものだと考えることができます。
スクリーンショット 2019-10-29 22.49.09.png
この直角三角形:図では赤の三角形に注目します。
この赤の三角形の斜辺の長さ:UNITは、正六角形に外接する円の半径、と考えることができます。
ここから、底辺の長さと高さ、UNIT_BとUNIT_Hをsin,cosを使って計算することができます。

DrawHex.js
import CONST from '../../Defines/Constant'; // 定数

// JSはクラスメンバ参照が遅いので、高速化のためグローバル変数を宣言
// 高速化のため、CONSTの値をローカル変数に再代入
let UNIT = CONST.UNIT;
let UNIT_B = CONST.UNIT_B;
let UNIT_H = CONST.UNIT_H;
let HeightMod = CONST.HeightMod; // 視点の傾きの高さ補正

// 描画用変数群
let BaseNode; // 描画用の親ノード
let Grp; // cc.Graphics
let hexPoints = []; // 描画するHexの座標群

let Nodes = []; // マップ座標の文字表示用のノード

// *********************************
// Hexを描画する DrawHex
// ********************************
class DH {

  // *********************************
  // DrawHesクラスの初期化
  // node: 描画の親ノード
  // ********************************
  static init(node) {
    BaseNode = node;

    // cc.Graphicsの初期化
    Grp = node.addComponent(cc.Graphics);
    Grp.lineWidth = 3;
    Grp.strokeColor = cc.Color.RED;

    // ヘックスの描画用の座標6つをvec2で確保
    for (let loop = 0; loop < 6; loop++) {
      hexPoints[loop] = new cc.Vec2();
    };

    // 高さの補正を計算
    UNIT *= HeightMod;
    UNIT_H *= HeightMod;
  }

  // **********************************************************
  // 与えられたマップ座標からヘックスを1つ描画
  // mapX, mapY: マップ座標
  // **********************************************************
  static DrawHex(mapX, mapY) {
    // マップ座標からヘックス描画の中心座標を計算
    let drawCenterPosX = (mapX * 2 + mapY % 2) * UNIT_B;
    let drawCenterPosY = mapY * (UNIT + UNIT_H);

    // ヘックスを構成する座標群を計算
    // ヘックスの上の座標
    hexPoints[0].x = drawCenterPosX;
    hexPoints[0].y = drawCenterPosY + UNIT;
    // ヘックスの右上の座標
    hexPoints[1].x = drawCenterPosX + UNIT_B;
    hexPoints[1].y = drawCenterPosY + UNIT_H;
    // ヘックスの右上の座標
    hexPoints[2].x = drawCenterPosX + UNIT_B;
    hexPoints[2].y = drawCenterPosY - UNIT_H;
    // ヘックスの下の座標
    hexPoints[3].x = drawCenterPosX;
    hexPoints[3].y = drawCenterPosY - UNIT;
    // ヘックスの左下の座標
    hexPoints[4].x = drawCenterPosX - UNIT_B;
    hexPoints[4].y = drawCenterPosY - UNIT_H;
    // ヘックスの左上の座標
    hexPoints[5].x = drawCenterPosX - UNIT_B;
    hexPoints[5].y = drawCenterPosY + UNIT_H;

    // 座標群を渡してヘックスを描画
    DH.DrawLine(hexPoints);

    if (CC_DEBUG) { // 開発用座標表示
      DH.DrawMapCord(mapX, mapY, drawCenterPosX, drawCenterPosY);
    }
  }

  // **********************************************************
  // 線を描画
  // points: 線を描画するvex2の配列
  // **********************************************************
  static DrawLine(points) {
    Grp.moveTo(points[0].x, points[0].y); // 開始座標をセット
    let length = points.length;
    for (let loop = 1; loop < length; loop++) {
      Grp.lineTo(
        points[loop].x, points[loop].y);
    };
    Grp.close(); // 最後に線を閉じる
    Grp.stroke(); // 描画
  }

  // **********************************************************
  // マップ座標を描画(開発用)
  // 念の為、何度呼び出されても良いような作り
  // x, y: マップ座標
  // drawX, drawY: 描画座標
  // **********************************************************
  static DrawMapCord(x, y, drawX, drawY) {
    // Nodeがない場合、新規に作成する
    if (!Nodes[x]) {
      Nodes[x] = [];
    }
    if (!Nodes[x][y]) {
      let node = new cc.Node(); // ノードを作成
      Nodes[x][y] = node;
      node.label = node.addComponent(cc.Label); // ラベルコンポーネントをセット
      node.label.fontSize = 32;
      node.label.lineHeight = 32;
      node.color = cc.Color.BLUE;
      node.parent = BaseNode;
      // cc.log('Node Created: ' + x + ', ' + y);
    }
    Nodes[x][y].setPosition(drawX, drawY); // ラベル表示位置をセット
    Nodes[x][y].label.string = x + ', ' + y; // ラベル文字をセット
  }

  // **********************************************************
  // Graphoc描画のクリア用関数
  // **********************************************************
  static Clear() {
    Grp.clear();
  }

}
export default DH;

外部で宣言している定数はこんな感じ。

Constant.js
const CONSTANT = {
  UNIT: 80, // 六角形を構成する12の直角三角形の斜辺=六角形に概説する円の半径
  UNIT_B: 80 * Math.cos(30 * Math.PI / 180), // 六角形を構成する12の直角三角形の底辺(長い方):Bottom
  UNIT_H: 80 * Math.sin(30 * Math.PI / 180), // 六角形を構成する12の直角三角形の高さ(短い方):Height

  HeightMod: 0.7, // マップ視点の傾きの高さ補正
};
export default CONSTANT;

Nodeツリー内のNodeに、以下のコンポーネントをつけて呼び出します。

MapManager.js
const { ccclass, property } = cc._decorator;
import CONST from '../Defines/Constant'; // 定数宣言
import DH from './DrawHex'; // ヘックス描画クラス

// *********************************
// Map Manager
// ********************************
@ccclass
class MAPM extends cc.Component {

  // **********************************************************
  // オンロード
  // **********************************************************
  onLoad() {
    DH.init(this.node); // 初期化
    DH.Clear(); // 描画クリア
    for (let loop1 = 0; loop1 < 11; loop1++) {
      for (let loop2 = 0; loop2 < 12; loop2++) {
        DH.DrawHex(loop2, loop1); // ヘックス描画
      };
    };
  }
}
export default MAPM;

次は、タッチした座標を取得するインターフェイスを実装しようと思います。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?