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 |
コメント