【マインスイーパー開発 #8】リセットボタンを実装する|Android & Kotlinアプリ開発

Android & Kotlin

Android & Kotlinの環境でマインスイーパーを開発する方法を説明します。

今回は、ゲームのリセットボタンを実装します。

リセットボタンは、実行するとゲームをリセットする機能以外に「通常状態」、「ゲームオーバー」、「ゲームクリア」の状態を表す機能を持たせます。

動作イメージ

リセットボタンは、画面上段真ん中に配置している黄色いやつです。

通常状態

ゲームオーバー

ゲームクリア

そそたた
そそたた

レセットボタンのイメージは仮です。

ソースコード

開発環境は次の通りです。

PCMacBook Pro(2016年モデル)
IDEAndroid Studio 4.0.1
Android SDKminSdkVersion 16
targetSdkVersion 30
言語Kotlin 1.3.72

package com.sosotata.minesweeper.ui.main

import android.graphics.BitmapFactory
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.ScaleAnimation
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProviders
import com.sosotata.minesweeper.R
import com.sosotata.minesweeper.model.TileController
import com.sosotata.minesweeper.model.TileType
import com.sosotata.minesweeper.ui.widgets.PlayTimeCounter
import kotlinx.android.synthetic.main.game_fragment.*

class GameFragment : Fragment() {
    companion object {
        fun newInstance() = GameFragment()
    }

    private lateinit var viewModel: GameViewModel
    private var playTimeCounter: PlayTimeCounter? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.game_fragment, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(GameViewModel::class.java)

        viewModel.tile = TileController.create(TileType.Square)
        viewModel.tile.startedGame = {
            playTimeCounter?.start()
        }

        viewModel.tile.gameOver = {
            playTimeCounter?.stop()
            animateResetButton {
                resetButton.setImageBitmap(BitmapFactory.decodeResource(context?.resources, R.drawable.reset_button_gameover))
            }
        }

        viewModel.tile.clearedGame = {
            playTimeCounter?.stop()
            animateResetButton {
                resetButton.setImageBitmap(BitmapFactory.decodeResource(context?.resources, R.drawable.reset_button_victory))
            }
        }

        viewModel.tile.initialize(requireContext())

        playTimeCounter = PlayTimeCounter(playTimer)
        squareView.initialize(viewModel.tile)

        resetButton.setOnClickListener {
            animateResetButton {
                viewModel.tile.initialize(requireContext())
                squareView.initialize(viewModel.tile)
                resetButton.setImageBitmap(BitmapFactory.decodeResource(context?.resources, R.drawable.reset_button_normal))
                playTimeCounter?.stop()
                playTimeCounter?.reset()
            }
        }
    }

    override fun onResume() {
        playTimeCounter?.resume()
        super.onResume()
    }

    override fun onPause() {
        playTimeCounter?.pause()
        super.onPause()
    }

    private fun animateResetButton(Animated: () -> Unit)
    {
        val btnEffect = ScaleAnimation(
            1.0f, 1.3f, 1.0f,1.3f,
            Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
        btnEffect.duration = 100
        btnEffect.setAnimationListener(object : Animation.AnimationListener {
            override fun onAnimationStart(animation: Animation) {}
            override fun onAnimationRepeat(animation: Animation) {}
            override fun onAnimationEnd(animation: Animation) {
                Animated()
            }
        })
        resetButton.startAnimation(btnEffect)
    }
}

解説

リセットボタンは、#6でアニメーションを使ってボタンっぽいエフェクトをかけています。

ボタンの状態が変わった時にも同じエフェクトをかけたいのでanimateResetButton()を定義して共通処理にします。

private fun animateResetButton(Animated: () -> Unit)
{
    val btnEffect = ScaleAnimation(
        1.0f, 1.3f, 1.0f, 1.3f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
    btnEffect.duration = 100
    btnEffect.setAnimationListener(object : Animation.AnimationListener {
        override fun onAnimationStart(animation: Animation) {}
        override fun onAnimationRepeat(animation: Animation) {}
        override fun onAnimationEnd(animation: Animation) {
            Animated()
        }
    })
    resetButton.startAnimation(btnEffect)
}
そそたた
そそたた

エフェクトのアニメーション実行後に処理したいことがそれぞれ違うため、引数に関数を指定する形にして対処します。

リセットボタン実行。

resetButton.setOnClickListener {
    animateResetButton {
        viewModel.tile.initialize(requireContext())
        squareView.initialize(viewModel.tile)
        resetButton.setImageBitmap(BitmapFactory.decodeResource(context?.resources, R.drawable.reset_button_normal))
        playTimeCounter?.stop()
        playTimeCounter?.reset()
    }
}

ゲームオーバー。

viewModel.tile.gameOver = {
    playTimeCounter?.stop()
    animateResetButton {
        resetButton.setImageBitmap(BitmapFactory.decodeResource(context?.resources, R.drawable.reset_button_gameover))
    }
}

ゲームクリア。

viewModel.tile.clearedGame = {
    playTimeCounter?.stop()
    animateResetButton {
        resetButton.setImageBitmap(BitmapFactory.decodeResource(context?.resources, R.drawable.reset_button_victory))
    }
}

コメント

タイトルとURLをコピーしました