クォータビューの作り方

概要

この記事は斜め上からの視点 Isometric (クォータービュー) の作り方について解説します。

なお Unity で実装する場合は、平行投影にしてカメラを斜め上にするだけで実装できるので、ここで紹介しているような対応は不要となります。

2D描画のみ可能な環境で「クォータービュー」を実現する方法となります。

マップチップ座標から、クォータービュー座標への変換

問題を簡単にするため、このような座標変換をすると考えます。

右回りに45度回転させる数式を以下に書いていきます。

  • w = チップの幅
  • h = チップの高さ
  • x = マップチップ座標(X)
  • y = マップチップ座標(Y)
  • X = クォータービュー座標(X)
  • Y = クォータービュー座標(Y)

とします。

■マップチップ座標からクォータービュー座標に変換する

マップチップ座標からクォータービュー座標に変換する場合は、

という変換式を使います。

となります。

■クォータービュー座標からマップチップ座標に変換する

クォータービュー座標からマップチップ座標に変換する場合は、

ちなみにマップチップ座標とは、マップエディタ内の座標のことです。

これは「8×8」のマップですが、鉛筆で赤く指しているところが左から6コ目、上から0コ目なので、マップ座標は(x,y)=(6,0)となります。

あと、そもそもマウスの判定をしないのであれば、「クォータービュー座標からマップチップ座標の変換」は不要ですね。

描画だけ考えれば、わりと簡単です。マウス座標・タッチ座標でユニットを選択する、とか考えると少し面倒な話になってきます。

Y方向を反転する必要がある

先ほどの計算式はY方向が上向きなので、Y方向を反転する必要があります。(スクリーン座標のY方向は下向きとなります)

座標オフセットの問題

座標のオフセット方法が少し特殊なので、少し注意する必要があります。

チップの開始座標

通常のトップビューであれば、左上のチップが開始座標となります。

ですが、クォータービューの場合、左中央のチップが開始座標となります。

なのでオフセットするY座標は、画面の中心あたりになります。

■チップの左上座標のオフセット

スクリーン上ではチップの中心座標は(x,y)=(w/2,h/2)だけずらす必要があります。

なのですが、クォータービュー座標は、先ほど「左中央が開始座標」と説明したとおり、チップ座標も(x,y)=(w/2,0)だけ戻してやればOKです。

ソースコードの簡単な説明

昔Flashで作ったものなので、もう動作はしないですが参考として添付しておきます。

SceneTitle.as

このクラスで変換処理やチップの描画をしています。

「drawChip関数」がマップ座標からクォータービュー座標への変換です

/**
  * チップの描画 (クォータービュー座標に変換))
  * @param	id チップ番号
  * @param	i マップ座標(X)
  * @param	j マップ座標(Y)
  */
 private function drawChip(id:int, i:int, j:int):void
 {
 	// マップ座標 -> クォータービュー座標 に変換
 	// X = w(x + y)
 	// Y = h(x - y)
 	var x:Number = CHIP_OFSX * (i + j);
 	var y:Number = CHIP_OFSY * (i - j);
 	
 	// 表示オフセットを反映
 	x = x + MAP_OFSX;
 	y = -y + MAP_OFSY; // Y方向は逆なので反転する
 	
 	// スクロールを反映
 	if (m_CenterIdx >= 0)
 	{
 		x -= m_View.getX();
 		y += m_View.getY(); // Y方向は逆なので反転する
 	}
 	
 	// チップの描画
 	var ox:Number = CHIP_WIDTH * (id%CHIP_COL);
 	var oy:Number = CHIP_HEIGHT * (Math.floor(id / CHIP_COL));
 	_g.drawFastEx("chip", x, y, ox, oy, CHIP_WIDTH, CHIP_HEIGHT);
 }

「quarterToMap関数」でクォータービュー座標からマップ座標への変換をしています。

 /**
  * クォータービュー座標をマップ座標に変換
  * @param	x クォータービュー座標(X)
  * @param	y クォータービュー座標(Y)
  * @return マップ座標インデックス
  */
 public function quarterToMap(x:int, y:int):int
 {
 	var px:Number = x;
 	var py:Number = y;
 	
 	// カメラ座標を反映
 	px += m_View.getX();
 	py -= m_View.getY(); // Y方向は逆なので反転する
 	
 	// マップ全体のオフセットを反映
 	px = (px - MAP_OFSX);
 	py = -(py - MAP_OFSY); // Y方向は逆なので反転する
 	
 	// チップのオフセットをする(クォータービュー座標は「左中心」がデカルト座標の「左上」) 
 	px += CHIP_WIDTH / 2;
 	
 	// クォータービュー座標 -> マップ座標 へ変換
 	// x = X/2w + Y/2h
 	// y = X/2w - Y/2h
 	var i:int = Math.floor((px / CHIP_OFSX / 2) + (py / CHIP_OFSY / 2));
 	var j:int = Math.floor((px / CHIP_OFSX / 2) - (py / CHIP_OFSY / 2));
 	
 	// 範囲外チェック
 	if (i < 0 || m_Layer.width() <= i) { return -1; }
 	if (j < 0 || m_Layer.height() <= j) { return -1; }
 	
 	// マップ用インデックスに変換
 	return j * m_Layer.width() + i;;
 }

View.as

このクラスでスクロールを管理していますが、スクロール座標はSceneTitle.asで設定しています。

/**
  * スクロールを開始する
  */
 public function startScroll():void
 {
 	// マップ座標 -> クォータービュー座標 に変換
 	// X = w(x + y)
 	// Y = h(x - y)
 	var cx:int = m_CenterIdx % m_Layer.width();
 	var cy:int = m_CenterIdx / m_Layer.width();
 	var nextX:Number = CHIP_OFSX * (cx + cy);
 	var nextY:Number = CHIP_OFSY * (cx - cy);
 	
 	// スクロール開始
 	m_View.start(nextX, nextY);
 }

関連記事

Godot Engine というゲームエンジンの場合、標準機能でクォータービューが実装されているので、1から作るよりも、ゲームエンジンを使うのが良いのかもしれません

【Godot】クォータービューの基本的な作り方