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 = "重置")
            }
        }
    }
}

多次尝试难点问题攻克

  • 画布、协程、点击点位知识点
  • 控制动画开关刷新问题
  • 计算步数方向问题
  • 追加块计算问题
相关推荐
编程乐学(Arfan开发工程师)5 小时前
06、基础入门-SpringBoot-依赖管理特性
android·spring boot·后端
androidwork5 小时前
使用 Kotlin 和 Jetpack Compose 开发 Wear OS 应用的完整指南
android·kotlin
繁依Fanyi6 小时前
Animaster:一次由 CodeBuddy 主导的 CSS 动画编辑器诞生记
android·前端·css·编辑器·codebuddy首席试玩官
奔跑吧 android8 小时前
【android bluetooth 框架分析 02】【Module详解 6】【StorageModule 模块介绍】
android·bluetooth·bt·aosp13·storagemodule
田一一一12 小时前
Android framework 中间件开发(三)
android·中间件·framework·jni
androidwork17 小时前
掌握 Kotlin Android 单元测试:MockK 框架深度实践指南
android·kotlin
田一一一17 小时前
Android framework 中间件开发(二)
android·中间件·framework
追随远方17 小时前
FFmpeg在Android开发中的核心价值是什么?
android·ffmpeg
神探阿航18 小时前
HNUST湖南科技大学-安卓Android期中复习
android·安卓·hnust
千里马-horse20 小时前
android vlc播放rtsp
android·media·rtsp·mediaplayer·vlc