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

多次尝试难点问题攻克

  • 画布、协程、点击点位知识点
  • 控制动画开关刷新问题
  • 计算步数方向问题
  • 追加块计算问题
相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui