1024个人爆款文章Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码
Posted 第三女神程忆难
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1024个人爆款文章Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码相关的知识,希望对你有一定的参考价值。
效果图
废话不多说,先干效果图,源码在文章末尾
游戏概念
《俄罗斯方块》是由七种方块,开始时,一个 落下期间,玩家可以以90度为单位旋转方块,以格子为单位左右移动方块,或让方块加速落下。当方块下落到区域最下方或着落到其他方块上无法再向下移动时,就会固定在该处,然后一个新的随机的方块会出现在区域上方开始落下。当区域中某一横行(同时消除的行数越多,得分指数级上升。当固定的方块堆到区域最顶端而无法消除层数时,游戏就会结束。
操作设计
起初我想的是在屏幕底部做按键操作的,可后来一想,这也太扯淡了
现在是触屏手机,做按钮的话不好按,容易误触,所定以下的手势:
- 向上滑动:顺时针旋转正在下落的方块90°
- 向左滑动:正在下落的方块向左移动一个方格单位
- 向右滑动,正在下落的方块向右移动一个方格单位
- 向左持续滑动:正在下落的方块向左持续移动
- 向右持续滑动:正在下落的方块向右持续移动
- 向下滑动:方块直落底部
说到扯淡但,来个更扯淡的操作吧,你可以设置成
向下滑动和向上滑动操作相反
向左滑动和向右滑动操作相反
向左持续滑动和向右持续滑动操作相反
(反正这个操作没多大工作量,写个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以上是关于1024个人爆款文章Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码的主要内容,如果未能解决你的问题,请参考以下文章
Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码
Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码
Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码
Android 安卓原生UI实现游戏《俄罗斯方块》,算法太多,把我写崩溃了,附源码