Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码

Posted 第三女神程忆难

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码相关的知识,希望对你有一定的参考价值。


效果图

废话不多说,先干效果图,源码在文章末尾



游戏概念

《俄罗斯方块》是由七种方块,开始时,一个 落下期间,玩家可以以90度为单位旋转方块,以格子为单位左右移动方块,或让方块加速落下。当方块下落到区域最下方或着落到其他方块上无法再向下移动时,就会固定在该处,然后一个新的随机的方块会出现在区域上方开始落下。当区域中某一横行(同时消除的行数越多,得分指数级上升。当固定的方块堆到区域最顶端而无法消除层数时,游戏就会结束。


操作设计

起初我想的是在屏幕底部做按键操作的,可后来一想,这也太扯淡了

现在是触屏手机,做按钮的话不好按,容易误触,所定以下的手势:

  1. 向上滑动:顺时针旋转正在下落的方块90°
  2. 向左滑动:正在下落的方块向左移动一个方格单位
  3. 向右滑动,正在下落的方块向右移动一个方格单位
  4. 向左持续滑动:正在下落的方块向左持续移动
  5. 向右持续滑动:正在下落的方块向右持续移动
  6. 向下滑动:方块直落底部

说到扯淡但,来个更扯淡的操作吧,你可以设置成
向下滑动和向上滑动操作相反
向左滑动和向右滑动操作相反
向左持续滑动和向右持续滑动操作相反
(反正这个操作没多大工作量,写个if调换以下方法位置就行…)


算法规则

当行满足无空格时,消除当前行,有几行会消除几行,一次消除的越多,得分也就越多,咱就不定1分2分的了,这么玩着没啥意思

干脆消除1行100分
一次性消除2行是200分
一次性消除3行是400分
一次性消除4行是800分

方块下落速度会越来越快,刚开始下落速度为750ms/格,逐渐累加,最快是150ms/格
按方块下落的个数计算,每5个方块为1个递增,递增20ms/格(这边是我自己想的)


整体算法概述

UI使用自定义View来实现,命名为GameView,背后的运行逻辑通过二维数组来控制,10X20布局;
当方块每下落一格和消除时,刷新整个GameView;
0表示可操作空间占位;
1表示已固定方块类型一方块占位,11表示正在下落的类型一方块
2表示已固定方块类型二方块占位,12表示正在下落的类型二方块
3表示已固定方块类型三方块占位,13表示正在下落的类型三方块
以此类推共7种


代码实操

“只说不练的把式,光练不说傻把式”,这是程序员的最大忌讳,昨天媳妇给力(做的饭好吃),今天快快地撸代码!

操作设计

创建二维数组,并允许可以静态调用,其默认值为0。封装多个公共调用方法!详细看以下注释

package com.blog.tetris.operation

import java.lang.Thread
import kotlin.math.pow

/**
 * 操作类,包含地图、游戏的主运行逻辑、操作入口
 */
object Operation 

    //地图
    //0表示可操作空间占位(默认值)
    //1表示已固定方块占位
    //2表示正在下落方块占位
    val gameMap = Array(20)  Array(10)  0  

    //方块下落速度
    //初始默认值750毫秒(0.75s)/格
    var speed = 750L

    var isInGame = false

    //当前旋转度,默认是1
    private var rotate = 1

    //游戏分数
    var score = 0L

    //要生成的方块
    var forecastType = 0

    //已经生成的方块个数,根据此变量提升方块下落速度
    var boxNum = 0

    //游戏线程
    private lateinit var thread: Thread

    /**
     * 开始游戏
     */
    fun startGame() 

        if (isInGame) return

        isInGame = true

        thread = Thread 

            while (isInGame) 

                Thread.sleep(speed)

                val downBox = downBox()
                //是否无法继续下移
                if (!downBox) 

                    //固定模块
                    fixedMoveBox()

                    //消除方块
                    eliminate()

                    //生成一个新的模块
                    createTopBox()

                
                changeListener?.onChange()
            
        

        thread.start()

    

    //消除行数量
    var fullLine = 0

    /**
     * 消除方块
     * 从下到上递归扫描方块,一行一行消除
     */
    private fun eliminate(): Int 

        for (i in gameMap.size - 1 downTo 0) 

            //当前行是否满足
            var isFull = true

            for (j in gameMap[i].indices) if (gameMap[i][j] == 0) isFull = false

            if (isFull) 

                fullLine++

                for (j in gameMap[i].indices) 
                    gameMap[i][j] = 0
                

                for (k in i downTo 1) for (n in gameMap[k].indices)
                    gameMap[k][n] = gameMap[k - 1][n]

                eliminate()
            
        

        if (fullLine > 0) 

            //根据消除行算出分数
            score += (2.toDouble().pow((fullLine - 1).toDouble())).toLong() * 100

            changeListener?.onChange()
            Thread.sleep(200)
            fullLine = 0
        

        return fullLine
    

    /**
     * 变形-正在下落的模块顺时针旋转90度
     */
    fun deformation() 

        //扫描正在下落的方块
        val boxArr = mutableListOf<Coordinate>()

        /**
         * 重置置为空
         */
        fun reset() 
            for (coordinate in boxArr) gameMap[coordinate.x][coordinate.y] = 0
        

        //当前下落模块左上角坐标
        var boxMinX = 20
        var boxMinY = 10

        for (i in gameMap.indices) for (j in gameMap[i].indices) if (gameMap[i][j] in 12..17) 
            boxArr.add(Coordinate(i, j, gameMap[i][j]))
            if (i < boxMinX) boxMinX = i
            if (j < boxMinY) boxMinY = j
        

        if (0 == boxArr.size) return

        val boxType = boxArr[0].value

        if (0 != boxArr.size) 

            //判断是否符合旋转标准
            //有足够的空间变形(不触碰左、右、下边界,不触碰其他固定方块)

            //取出模块类型
            when (boxType) 

                12 -> 
                    when (rotate) 
                        1 -> 
                            //判断是否符合旋转条件
                            if (boxMinX + 2 < 20
                                && boxMinY + 1 < 10
                                && gameMap[boxMinX][boxMinY] !in 1..7
                                && gameMap[boxMinX][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 2][boxMinY + 1] !in 1..7
                            ) 
                                reset()
                                gameMap[boxMinX][boxMinY] = 12
                                gameMap[boxMinX][boxMinY + 1] = 12
                                gameMap[boxMinX + 1][boxMinY + 1] = 12
                                gameMap[boxMinX + 2][boxMinY + 1] = 12
                                rotate = 2
                            
                        
                        2 -> 
                            if (boxMinX + 1 < 20
                                && boxMinY + 2 < 10
                                && gameMap[boxMinX][boxMinY] !in 1..7
                                && gameMap[boxMinX][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX][boxMinY + 2] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY] !in 1..7
                            ) 
                                reset()
                                gameMap[boxMinX][boxMinY] = 12
                                gameMap[boxMinX][boxMinY + 1] = 12
                                gameMap[boxMinX][boxMinY + 2] = 12
                                gameMap[boxMinX + 1][boxMinY] = 12
                                rotate = 3
                            
                        
                        3 -> 
                            if (boxMinX + 2 < 20
                                && boxMinY + 1 < 10
                                && gameMap[boxMinX][boxMinY] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY] !in 1..7
                                && gameMap[boxMinX + 2][boxMinY] !in 1..7
                                && gameMap[boxMinX + 2][boxMinY + 1] !in 1..7
                            ) 
                                reset()
                                gameMap[boxMinX][boxMinY] = 12
                                gameMap[boxMinX + 1][boxMinY] = 12
                                gameMap[boxMinX + 2][boxMinY] = 12
                                gameMap[boxMinX + 2][boxMinY + 1] = 12
                                rotate = 4
                            
                        
                        4 -> 
                            if (boxMinX + 1 < 20
                                && boxMinY + 2 < 10
                                && gameMap[boxMinX][boxMinY + 2] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 2] !in 1..7
                            ) 
                                reset()
                                gameMap[boxMinX][boxMinY + 2] = 12
                                gameMap[boxMinX + 1][boxMinY] = 12
                                gameMap[boxMinX + 1][boxMinY + 1] = 12
                                gameMap[boxMinX + 1][boxMinY + 2] = 12
                                rotate = 1
                            
                        
                    
                

                13 -> 
                    when (rotate) 
                        1 -> 
                            if (boxMinX + 2 < 20
                                && boxMinY + 1 < 10
                                && gameMap[boxMinX][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 2][boxMinY] !in 1..7
                            ) 
                                reset()
                                gameMap[boxMinX][boxMinY + 1] = 13
                                gameMap[boxMinX + 1][boxMinY] = 13
                                gameMap[boxMinX + 1][boxMinY + 1] = 13
                                gameMap[boxMinX + 2][boxMinY] = 13
                                rotate = 2
                            
                        
                        2 -> 
                            if (
                                boxMinX + 1 < 20
                                && boxMinY + 2 < 10
                                && gameMap[boxMinX][boxMinY] !in 1..7
                                && gameMap[boxMinX][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 2] !in 1..7
                            ) 
                                reset()
                                gameMap[boxMinX][boxMinY] = 13
                                gameMap[boxMinX][boxMinY + 1] = 13
                                gameMap[boxMinX + 1][boxMinY + 1] = 13
                                gameMap[boxMinX + 1][boxMinY + 2] = 13
                                rotate = 1
                            
                        
                    
                

                14 -> 
                    when (rotate) 
                        1 -> 

                            if (boxMinX + 2 < 20
                                && boxMinY + 1 < 10
                                && gameMap[boxMinX][boxMinY] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 2][boxMinY + 1] !in 1..7
                            ) 
                                reset()
                                gameMap[boxMinX][boxMinY] = 14
                                gameMap[boxMinX + 1][boxMinY] = 14
                                gameMap[boxMinX + 1][boxMinY + 1] = 14
                                gameMap[boxMinX + 2][boxMinY + 1] = 14
                                rotate = 2
                            
                        
                        2 -> 

                            if (boxMinX + 2 < 20
                                && boxMinY + 1 < 10
                                && gameMap[boxMinX][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX][boxMinY + 2] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 1] !in 1..7
                            ) 
                                reset()
                                gameMap[boxMinX][boxMinY + 1] = 14
                                gameMap[boxMinX][boxMinY + 2] = 14
                                gameMap[boxMinX + 1][boxMinY] = 14
                                gameMap[boxMinX + 1][boxMinY + 1] = 14
                                rotate = 1
                            
                        
                    
                

                15 -> 
                    when (rotate) 
                        1 -> 

                            if (boxMinX + 2 < 20
                                && boxMinY + 1 < 10
                                && gameMap[boxMinX][boxMinY + 1] !in 1..7
                                && gameMap[boxMinX + 1][boxMinY + 1] !in 1..7
                                && gameMap

以上是关于Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码的主要内容,如果未能解决你的问题,请参考以下文章

Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码

Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码

Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码

Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码

1024个人爆款文章Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码

1024个人爆款文章Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码