クォータビューの作り方

投稿者: | 2012年5月22日

★概要

クォータビューの作り方をまとめてみました。

This movie requires Flash Player 9

■サンプルコード

ちなみにサンプルはこちらです。

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

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

右回りに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なのです。

★ソースコード

最後に、「サンプル」の簡単な説明をしておきます。

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);
 }

まとめてみたら、 「Y方向は逆なので反転する」というコメントが多いので、あらかじめY方向を下向きにして計算したほうが良かったような気がしなくもないですね…。