compose Android 贪吃蛇思路实践

Compose 实现贪吃蛇思路梳理

  1. 定义背景格子
  2. 定义起始结束块,追加跟随起始块运动的第二第三块
  3. 点击空白块变更结束块位置
  4. 让起始块动起来用到协程,定义while加delay控制速度
  5. 起始结束块重叠结束,结束时放出重置按钮

具体操作

定义Box和canvas填充全屏

  • 从canvas中获取画布大小,size.height、size.width
  • 以1080X1920作为标准除以60定义块大小和行列数
ini 复制代码
val row = 32
val column = 18
val spaceY = size.height.div(row).dp
val spaceX = size.width.div(column).dp

绘制方格横竖线,间距spaceX和spaceY

  • 绘制横线,特点起始0结束size.width,位置间隔spaceX乘以行数
  • 绘制竖线,特点起始0结束size.height,位置间隔spaceY乘以列数
ini 复制代码
for (stepY in 1 until row) {//横线
   drawLine(
       start = Offset(0f, spaceY.times(stepY).value),
       end = Offset(size.width, spaceY.times(stepY).value),
       strokeWidth = 1f,
       color = Color.Magenta
   )
}
for (stepX in 1 until column) {//竖线
   drawLine(
       start = Offset(spaceX.times(stepX).value, 0f),
       end = Offset(spaceX.times(stepX).value, size.height),
       strokeWidth = 1f,
       color = Color.Magenta
   )
}
  • 效果

绘制开始和结束块

  • 按照间距大小绘制块
  • 定义初始开始点位置[0,0]结束点位置[column - 1,row - 1]
ini 复制代码
var piece = Size(spaceX.value, spaceY.value)

drawRect(//1run
    color = Color.Yellow,
    topLeft = Offset(spaceX.times(rawX).value, spaceY.times(rawY).value),
    size = piece, style = Fill
)
drawRect(//end
    color = Color(0xFFee4866),
    topLeft = Offset(spaceX.times(endX).value, spaceY.times(endY).value),
    size = piece, style = Fill
)

添加点击点位事件动态调整结束块位置

  • 定义初始结束值Offset(-1f,-1f),-1是为了初始没点击时不更改endX、endY
  • 单击画布改变初始Offset ,实现pointerInput获取单击事件拾取点击动作X,y坐标
  • 计算单机位置在屏幕第几个格子位置公式:行数=x/行间距,列数=y/列间距
  • 最后赋值给endX,endY
scss 复制代码
//定义
var endOffset by remember {
    mutableStateOf(Offset(-1f, -1f))
}
//改变
Modifier
    .fillMaxSize()
    .background(Color.Black)
    .pointerInput(Unit) {
        detectTapGestures(
            onPress = {
                if (!stop) {
                    endOffset = it
                }
            },
        )
    }
//赋值
if (endOffset.x != -1f) {
    endX = endOffset.x
        .div(spaceX.value)
        .toInt()
    endY = endOffset.y
        .div(spaceY.value)
        .toInt()
}

在协程中定义起始块运动频次以及前后左右方向

  • 增加运动开关控制动画开始结束
  • 定义rememberCoroutineScope配合LaunchedEffect控制频率方向刷新
scss 复制代码
var stop by remember {
    mutableStateOf(false)
}
val scope = rememberCoroutineScope()
if (!stop) {
    LaunchedEffect(key1 = "piece", block = {
        scope.launch {
            while (!stop) {     
                //频次
                delay(1000)
                //改变位置方向步数
            }
        }
    })
}

结束动画重置动作

  • 定义一个按钮结束时显示,点击时将所有状态恢复到初始值
ini 复制代码
if (stop) {
    Toast.makeText(LocalContext.current, "结束", Toast.LENGTH_SHORT).show()
    Button(
        onClick = {
            //reset
        }, modifier = Modifier
            .align(Alignment.BottomCenter)
            .padding(bottom = 24.dp)
    ) {
        Text(text = "重置")
    }
}

效果展示

代码示例

ini 复制代码
@Composable
private fun runBox() {
    Box(
        Modifier
            .fillMaxSize()
            .background(Color.Transparent)
    ) {
        //1080X1920
        val row = 32
        val column = 18
        val scope = rememberCoroutineScope()
        var rawThirdX by remember {//跟随第二个格子
            mutableIntStateOf(0)
        }
        var rawThirdY by remember {
            mutableIntStateOf(0)
        }

        var rawSecondX by remember {//跟随第一个格子
            mutableIntStateOf(0)
        }
        var rawSecondY by remember {
            mutableIntStateOf(0)
        }
        var rawX by remember {//初始格子
            mutableIntStateOf(0)
        }
        var rawY by remember {
            mutableIntStateOf(0)
        }
        var endX by remember {//终点格子
            mutableIntStateOf(column - 1)
        }
        var endY by remember {
            mutableIntStateOf(row - 1)
        }
        var stop by remember {
            mutableStateOf(false)
        }
        var endOffset by remember {
            mutableStateOf(Offset(-1f, -1f))
        }

        if (!stop) {
            LaunchedEffect(key1 = "run", block = {
                scope.launch {
                    while (!stop) {
                        delay(1000)
                        if (rawY == endY && rawX == endX) {
                            stop = true
                        } else {
                            if (rawY == endY) {
                                if (rawThirdY != rawSecondY) {
                                    if (rawThirdY > rawSecondY) {
                                        rawThirdY--
                                    } else {
                                        rawThirdY++
                                    }
                                } else {
                                    if (rawThirdX > rawSecondX) {
                                        rawThirdX--
                                    } else {
                                        rawThirdX++
                                    }
                                }
                                if (rawSecondY != rawY) {
                                    if (rawSecondY > rawY) {
                                        rawSecondY--
                                    } else {
                                        rawSecondY++
                                    }
                                } else {
                                    rawSecondX = rawX
                                }
                                if (rawX > endX) {
                                    rawX--
                                } else {
                                    rawX++
                                }
                            } else {
                                if (rawThirdX != rawSecondX) {
                                    if (rawThirdX > rawSecondX) {
                                        rawThirdX--
                                    } else {
                                        rawThirdX++
                                    }
                                } else {
                                    if (rawThirdY > rawSecondY) {
                                        rawThirdY--
                                    } else {
                                        rawThirdY++
                                    }
                                }
                                if (rawSecondX != rawX) {
                                    if (rawSecondX > rawX) {
                                        rawSecondX--
                                    } else {
                                        rawSecondX++
                                    }
                                } else {
                                    rawSecondY = rawY
                                }

                                if (rawY > endY) {
                                    rawY--
                                } else {
                                    rawY++
                                }
                            }

                        }

                    }
                }
            })
        }

        Canvas(modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
            .pointerInput(Unit) {
                detectTapGestures(
                    onPress = {
                        if (!stop) {
                            endOffset = it
                        }
                    },
                )
            }, onDraw = {
            val spaceY = size.height.div(row).dp
            val spaceX = size.width.div(column).dp
            for (stepY in 1 until row) {//横线
                drawLine(
                    start = Offset(0f, spaceY.times(stepY).value),
                    end = Offset(size.width, spaceY.times(stepY).value),
                    strokeWidth = 1f,
                    color = Color.Magenta
                )
            }
            for (stepX in 1 until column) {//竖线
                drawLine(
                    start = Offset(spaceX.times(stepX).value, 0f),
                    end = Offset(spaceX.times(stepX).value, size.height),
                    strokeWidth = 1f,
                    color = Color.Magenta
                )
            }
            if (endOffset.x != -1f) {
                endX = endOffset.x
                    .div(spaceX.value)
                    .toInt()
                endY = endOffset.y
                    .div(spaceY.value)
                    .toInt()
            }
            var piece = Size(spaceX.value, spaceY.value)
            drawRect(//end
                color = Color(0xFFee4866),
                topLeft = Offset(spaceX.times(endX).value, spaceY.times(endY).value),
                size = piece, style = Fill
            )
            Log.d("CANVAS", "drawing line")
            drawRect(//3run
                color = Color.Yellow,
                topLeft = Offset(
                    spaceX.times(rawThirdX).value,
                    spaceY.times(rawThirdY).value
                ),
                size = piece, style = Fill
            )
            drawRect(//2run
                color = Color.Yellow,
                topLeft = Offset(
                    spaceX.times(rawSecondX).value,
                    spaceY.times(rawSecondY).value
                ),
                size = piece, style = Fill
            )
            drawRect(//1run
                color = Color.Yellow,
                topLeft = Offset(spaceX.times(rawX).value, spaceY.times(rawY).value),
                size = piece, style = Fill
            )

        })
        if (stop) {
            Toast.makeText(LocalContext.current, "结束", Toast.LENGTH_SHORT).show()
            Button(
                onClick = {
                    //reset
                    rawThirdX = 0
                    rawThirdY = 0
                    rawSecondX = 0
                    rawSecondY = 0
                    rawX = 0
                    rawY = 0
                    endX = column - 1
                    endY = row - 1
                    stop = false
                    endOffset = Offset(-1f, -1f)
                }, modifier = Modifier
                    .align(Alignment.BottomCenter)
                    .padding(bottom = 24.dp)
            ) {
                Text(text = "重置")
            }
        }
    }
}

多次尝试难点问题攻克

  • 画布、协程、点击点位知识点
  • 控制动画开关刷新问题
  • 计算步数方向问题
  • 追加块计算问题
相关推荐
黄林晴13 分钟前
Android17 为什么重写 MessageQueue
android
阿巴斯甜21 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android