Android & Kotlinの環境でマインスイーパーをアプリ開発する方法を説明します。
今回は、前回の記事で配置した9x9のタイルに対して、タッチしたタイルの場所が縦、横何番目なのかを判定します。

そそたた
タイルの描画には、Matrxを使用した拡大縮小、縦横斜めのスクロールができるカスタムイメージビューを使用しているので計算が少し複雑です。
動作イメージ
黄色●をタッチすると0オリジンで横3番目、縦1番目のタイルをタッチしたと判定する。

ソースコード
開発環境は次の通りです。
| PC | MacBook Pro(2016年モデル) | 
| IDE | Android Studio 4.0.1 | 
| Android SDK | minSdkVersion 16 targetSdkVersion 30  | 
| 言語 | Kotlin 1.3.72 | 
package com.sosotata.minesweeper.ui.widgets
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import com.sosotata.minesweeper.LOG
import com.sosotata.minesweeper.model.TileController
/**
 * 四角タイルゲーム画面
 */
class SquareTileGameView : SosotataImageView {
    /** タイル描画用ビットマップ */
    private lateinit var mSourceBitmap: Bitmap
    /** タイル描画用キャンバス */
    private lateinit var mRenderCanvas: Canvas
    /** タイル制御 */
    private lateinit var mTile: TileController
    /**
     * コンストラクタ
     */
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
    /**
     * タイルを描画する
     */
    fun setTileController(tile: TileController) {
        mTile = tile
        mSourceBitmap = Bitmap.createBitmap(
            mTile.tileWidth * mTile.numX, mTile.tileHeight * mTile.numY, Bitmap.Config.ARGB_8888
        )
        mRenderCanvas = Canvas(this.mSourceBitmap)
        for (y in 0 until mTile.numY) {
            for (x in 0 until mTile.numX) {
                mRenderCanvas.drawBitmap(
                    mTile.getTileStateImage(x, y),
                    mTile.tileWidth.toFloat() * x,
                    mTile.tileHeight.toFloat() * y,
                    null
                )
            }
        }
        setImage(mSourceBitmap)
    }
    /**
     * [android.view.View.onTouchEvent]
     */
    override fun onTouchEvent(e: MotionEvent?): Boolean {
        if (e != null) {
            if (e.action == MotionEvent.ACTION_UP) {
                val values = FloatArray(9)
                this.mRenderMatrix.getValues(values)
                val x = ((e.x - values[Matrix.MTRANS_X]) / values[Matrix.MSCALE_X] / mTile.tileWidth).toInt()
                val y = ((e.y - values[Matrix.MTRANS_Y]) / values[Matrix.MSCALE_Y] / mTile.tileHeight).toInt()
                LOG.d("横:${x}番目、縦:${y}番目")
            }
        }
        return super.onTouchEvent(e)
    }
}
解説
タイルを少し拡大したケースを例にタッチしたタイルの場所の判定方法を解説します。
こちらの例も黄色●をタッチすると0オリジンで横3番目、縦1番目だと判定します。

タッチ場所の判定は、タッチイベントハンドラのonTouchEventでタッチアップ(ACTION_UP)時に実施しています。
現在のマトリクス値を取得し、次の計算方法でタッチした場所を算出します。
- 横何番目 = (タッチX座標 ー マトリクスX座標) / X倍率 / タイル幅
 - 縦何番目 = (タッチY座標 ー マトリクスY座標) / Y倍率 / タイル高さ
 
val values = FloatArray(9)
this.mRenderMatrix.getValues(values)
val x = ((e.x - values[Matrix.MTRANS_X]) / values[Matrix.MSCALE_X] / mTile.tileWidth).toInt()
val y = ((e.y - values[Matrix.MTRANS_Y]) / values[Matrix.MSCALE_Y] / mTile.tileHeight).toInt()
本ケースでの具体的な値は次の通りです。
| タッチ座標X | e.x | 247.7 | 
| タッチ座標Y | e.y | 387.7 | 
| マトリクスX座標 | value[Matrix.MTRANS_X] | -131.1 | 
| マトリクスY座標 | value[Matrix.MTRANS_Y] | 212.6 | 
| X倍率 | values[Matrix.MSCALE_X] | 1.14 | 
| Y倍率 | values[Matrix.MSCALE_Y] | 1.14 | 
| タイル幅 | mTile.tileWidth | 96 | 
| タイル高さ | mTile.tileHeight | 96 | 
  
  
  
  
コメント