虽然我不是做游戏的,闲的没事,emm,写了个扫雷小游戏(Android)

Posted 第三女神程忆难

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虽然我不是做游戏的,闲的没事,emm,写了个扫雷小游戏(Android)相关的知识,希望对你有一定的参考价值。

文章目录


🔥老规矩,先上效果图


🔥需求分析

实现扫雷高级版,高级版有30*16的网格,480个格子,99个地雷,381个安全区,通过以下操作逻辑完全避开99个地雷视为通关,可使用小红旗最大数量为99个!

操作逻辑:

  1. 单次长按插小红旗,第二次长按填问号,再次长按恢复正常状态
  2. 单次点击进行开疆扩土,踩到地雷本局游戏结束,并显示所有地雷
  3. 当所有雷排干净时,游戏胜利!

🔥实现分析

分配2个地图二维数组,一个是雷区,一个是用户操作图

当用户操作是,判断点击位置,上下左右,以及周围的角,进行扩展

当用户第一次点击时,才是创建雷区的时候,防止用户点击上来就炸!!!

通过递归的方式查询,判断周围雷区,开疆扩土!

话不多说,源码开始!!!


🔥代码实现

代码实现开始了!

MinefieldUtil

公共地图类,这个类承担的作用,如下注释,包含创建,分配地图等,注释写的很详细了,可以直接看:

package com.cyn.traps

import android.util.Log
import java.util.*

/**
 * 雷区工具类
 */
object MinefieldUtil 

    private const val TAG = "log-trap"

    //是否已经建立了游戏
    var isEstablish = false

    //剩余小红旗数量
    var flagNum = 0

    //已排除的格子数量
    var turnedOnNum = 0

    //是否公开雷区,当公开雷区也就意味着游戏结束,不能再点击
    var isOpen = false

    //创建一张二维数组代表地雷布置
    //-1:地雷区域
    //0-8:周围地雷数量
    val gameMap = Array(16)  Array(30)  0  

    //用户操作图记录,与地图大小相等
    //0:未开采
    //1:已开踩
    //2:标记小红旗
    //3:问号
    val operationMap = Array(16)  Array(30)  0  

    //特殊坐标,该坐标不允许创建雷区
    private lateinit var specialCoordinate: MutableList<Int>

    /**
     * 创建雷区,当开采第一个方块后,才会开始布置雷区,防止用户上来就炸,并且用户点击处和周围1格不再布置雷区
     */
    fun establish(dTemp: Int, kTemp: Int) 

        if (!isEstablish) 
            isEstablish = true
         else 
            return
        

        //创建特殊坐标
        createSpecialCoordinates(dTemp, kTemp)

        //重置用户操作图
        for (d in operationMap.indices) 
            for (k in operationMap[d].indices) 
                operationMap[d][k] = 0
            
        

        //剩余小红旗数量重置
        flagNum = 99

        val random = Random()
        val temp = mutableSetOf<Int>()

        //生成要埋地雷的下标
        while (true) 
            val nextInt = random.nextInt(479)

            dTemp * 30 + kTemp


            //如果不是用户点击处以及周围1格,才会采取该坐标
            if (!specialCoordinate.contains(nextInt)) 
                temp.add(nextInt)
            

            if (99 == temp.size) 
                break
            
        

        //埋下地雷
        for (i in temp) 
            val d = i / 30
            val k = i - 30 * d
            gameMap[d][k] = -1
        

        //计算周围地雷数量
        createTrapsNumber()

        //====log
        Log.d(
            TAG,
            "\\t\\t\\t0\\t1\\t2\\t3\\t4\\t5\\t6\\t7\\t8\\t9\\t10\\t11\\t12\\t13\\t14\\t15\\t16\\t17\\t18\\t19\\t20\\t21\\t22\\t23\\t24\\t25\\t26\\t27\\t28\\t29"
        )
        Log.d(
            TAG,
            "\\t\\t--------------------------------------------------------------------------------------------------------------------------"
        )
        for ((c, i) in gameMap.withIndex()) 
            var str = "$c->\\t\\t"
            for (k in i) 
                str += k.toString() + "\\t"
            
            Log.d(TAG, str)
        

    

    /**
     * 获取陷阱数量
     */
    private fun createTrapsNumber() 
        for (i in gameMap.indices) 
            for (j in gameMap[i].indices) 
                //当此时坐标不是炸弹时候开始计算
                if (-1 != gameMap[i][j]) 
                    var trapNum = 0

                    //查询目标点左侧
                    if (j - 1 >= 0 && -1 == gameMap[i][j - 1]) 
                        trapNum++
                    

                    //查询目标上侧
                    if (i - 1 >= 0 && -1 == gameMap[i - 1][j]) 
                        trapNum++
                    

                    //查询目标右侧
                    if (j + 1 <= 29 && -1 == gameMap[i][j + 1]) 
                        trapNum++
                    

                    //查询目标下侧
                    if (i + 1 <= 15 && -1 == gameMap[i + 1][j]) 
                        trapNum++
                    

                    //查询左上角
                    if (j - 1 >= 0 && i - 1 >= 0 && -1 == gameMap[i - 1][j - 1]) 
                        trapNum++
                    

                    //查询右上角
                    if (j + 1 <= 29 && i - 1 >= 0 && -1 == gameMap[i - 1][j + 1]) 
                        trapNum++
                    

                    //查询右下角
                    if (j + 1 <= 29 && i + 1 <= 15 && -1 == gameMap[i + 1][j + 1]) 
                        trapNum++
                    

                    //查询左下角
                    if (j - 1 >= 0 && i + 1 <= 15 && -1 == gameMap[i + 1][j - 1]) 
                        trapNum++
                    

                    //赋值地雷个数
                    gameMap[i][j] = trapNum

                
            
        
    

    /**
     * 创建特殊坐标
     */
    private fun createSpecialCoordinates(dTemp: Int, kTemp: Int) 
        specialCoordinate = mutableListOf()

        //点击位置
        specialCoordinate.add(dTemp * 30 + kTemp)

        //点击坐标左侧
        if (kTemp >= 1) 
            specialCoordinate.add(dTemp * 30 + kTemp - 1)
        

        //点击坐标上侧
        if (dTemp >= 1) 
            specialCoordinate.add((dTemp - 1) * 30 + kTemp)
        

        //点击坐标右侧
        if (kTemp <= 28) 
            specialCoordinate.add(dTemp * 30 + kTemp + 1)
        

        //点击坐标下侧
        if (dTemp <= 14) 
            specialCoordinate.add((dTemp + 1) * 30 + kTemp)
        

        //点击坐标的左上
        if (dTemp >= 1 && kTemp >= 1) 
            specialCoordinate.add((dTemp - 1) * 30 + kTemp - 1)
        

        //点击坐标的右上
        if (dTemp >= 1 && kTemp <= 28) 
            specialCoordinate.add((dTemp - 1) * 30 + kTemp + 1)
        

        //点击坐标的右下
        if (dTemp <= 14 && kTemp <= 28) 
            specialCoordinate.add((dTemp + 1) * 30 + kTemp + 1)
        

        //点击坐标的左下
        if (dTemp <= 14 && kTemp >= 1) 
            specialCoordinate.add((dTemp + 1) * 30 + kTemp - 1)
        

        for (i in specialCoordinate) 
            Log.d(TAG, i.toString())
        

    

    /**
     * 重置
     */
    fun reset() 
        isEstablish = false

        isOpen = false

        turnedOnNum = 0

        for (d in gameMap.indices) 
            for (k in gameMap[d].indices) 
                gameMap[d][k] = 0
                operationMap[d][k] = 0
            
        

        flagNum = 0
    




RcAdapter

地图适配器,这是一个RecyclerView的适配器,直接用RecyclerView写的,作用是显示地图以及赋予点击事件:

package com.cyn.traps

import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView

class RcAdapter(var context: Context) : RecyclerView.Adapter<RcAdapter.Holder>() 

    //当游戏失败后,失败处的坐标,此处要着重显示
    var overPosition = 0

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder 

        return Holder(LayoutInflater.from(context).inflate(R.layout.item_lattice, parent, false))
    

    override fun getItemCount(): Int 
        return 480
    

    override fun onBindViewHolder(holder: Holder, position: Int) 
        val d = position / 30
        val k = position - 30 * d

        //-1:地雷区域
        //0-8:周围地雷数量
        val indexGame = MinefieldUtil.gameMap[d][k]

        //0:未开采
        //1:已开踩
        //2:标记小红旗
        //3:问号
        val indexOperation = MinefieldUtil.operationMap[d][k]

        //判断是否公开雷区
        if (MinefieldUtil.isOpen) 
            //公开雷区,游戏结束

            when (indexOperation) 
                0, 3 -> 
                    if (indexGame == -1) 
                        holder.itemText.setBackgroundResource(R.mipmap.icon_trap_open)
                        holder.itemText.text = ""
                     else 
                        holder.itemText.setBackgroundResource(R.mipmap.icon_lattice)
                        holder.itemText.text = ""
                    
                
                1 -> 
                    holder.itemText.setBackgroundResource(R.mipmap.icon_empty)
                    holder.itemText.text = indexGame.toString()

                    if (0 != indexGame) 
                        holder.itemText.text = indexGame.toString()
                     else 
                        holder.itemText.text = ""
                    

                    holder.itemText.setTextColor(
                        when (indexGame) 
                            1 -> ContextCompat.getColor(context, R.color.index1)
                            2 -> ContextCompat.getColor(context, R.color.index2)
                            3 -> ContextCompat.getColor(context, R.color.index3)
                            4 -> ContextCompat.getColor(context, R.color.index4)
                            5 -> ContextCompat.getColor(context, R.color.index5)
                            6 -> ContextCompat.getColor(context, R.color.index6)
                            7 -> ContextCompat.getColor(context, R.color.index7)
                            else -> ContextCompat.getColor(context, R.color.index8)
                        
                    )
                
                2 -> 
                    if (indexGame == -1) 
                        holder.itemText.setBackgroundResource(R.mipmap.icon_flag)
                        holder.itemText.text = ""
                     else 
                        holder.itemText.setBackgroundResource(R.mipmap.icon_flag_error)
                        holder.itemText.text = ""
                    
                
            

            if (indexOperation == 0 && -1 == indexGame) 
                holder.itemText.setBackgroundResource(R.mipmap.icon_trap_open)
                holder.itemText.text = ""
            

            if (overPosition == position) 
                holder.itemText.setBackgroundResource(R.mipmap.icon_trap)
                holder.itemText.text = ""
            

         else 
            //隐藏雷区
            when (indexOperation) 
                0 -> 
                    holder.itemText.setBackgroundResource(R.mipmap.icon_lattice)
                    holder.itemText.text = ""
                    holder.itemText.setOnClickListener 
                        //开采区域
                        if (-1 == indexGame) 
                            //踩到地雷,游戏结束
                            MinefieldUtil.isOpen = true
                            overPosition = position
                            notifyDataSetChanged()
                            dataCallBack?.gameOver()
                         else 

//                            dataCallBack?.gameWins()

                            //回调游戏开始
                            if (!MinefieldUtil.isEstablish) 
                                dataCallBack?.gameStart()
                            

                            //本次点击排除一个格子
                            MinefieldUtil.turnedOnNum++

                            //创建地雷,本局游戏只会执行一次,内部已封装好方法
                            MinefieldUtil.establish(d, k)

                            //递归开采其他模块
                            exploitation(d, k)


                            //判断是否已经排除完地雷
                            if (381 == MinefieldUtil.turnedOnNum) 
                                dataCallBack?.gameWins()
                            

                            //刷新
                            notifyDataSetChanged()

                        
                    
                    holder.itemText.setOnLongClickListener 
                        //在该区域插上小红旗

                        //判断小红旗是否用完了
                        if (MinefieldUtil.flagNum <= 0) 
                            return@setOnLongClickListener true
                        

                        MinefieldUtil.operationMap[d][k] = 2

                        //回调使用了小红旗
                        dataCallBack?.useFlag()

                        notifyDataSetChanged()
                        return@setOnLongClickListener true
                    
                

                1 -> 
                    if (0 == indexGame) 
                        //已开采周围没有地雷的方块
                        holder.itemText.setBackgroundResource(R.mipmap.icon_empty)
                        holder.itemText.text = ""
                     else 
                        //已开采周围有地雷的方块
                        holder.itemText.setBackgroundResource(R.mipmap.icon_empty)
                        holder.itemText.text = indexGame.toString()
                        holder.

以上是关于虽然我不是做游戏的,闲的没事,emm,写了个扫雷小游戏(Android)的主要内容,如果未能解决你的问题,请参考以下文章

虽然我不是做游戏的,闲的没事,emm,写了个扫雷小游戏(Android)

闲的无聊写了个很(wu)有(liao)意(dao)思(bao)的程序

JavaScript 做的网页版扫雷小游戏

java小项目之:扫雷,这游戏其实没有那么简单!

第一次写小小小小小游戏, 扫雷

源码项目+解析C语言/C++开发,打造一个小项目扫雷小游戏!