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

多次尝试难点问题攻克

  • 画布、协程、点击点位知识点
  • 控制动画开关刷新问题
  • 计算步数方向问题
  • 追加块计算问题
相关推荐
还鮟2 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡4 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi004 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil5 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你6 小时前
Android View的绘制原理详解
android
移动开发者1号9 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号9 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best13 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk14 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭18 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin