Compose 实现贪吃蛇思路梳理
- 定义背景格子
- 定义起始结束块,追加跟随起始块运动的第二第三块
- 点击空白块变更结束块位置
- 让起始块动起来用到协程,定义while加delay控制速度
- 起始结束块重叠结束,结束时放出重置按钮
具体操作
定义Box和canvas填充全屏
- 从canvas中获取画布大小,size.height、size.width
- 以1080X1920作为标准除以60定义块大小和行列数
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乘以列数
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]
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
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
控制频率方向刷新
var stop by remember {
mutableStateOf(false)
}
val scope = rememberCoroutineScope()
if (!stop) {
LaunchedEffect(key1 = "piece", block = {
scope.launch {
while (!stop) {
delay(1000)
}
}
})
}
结束动画重置动作
- 定义一个按钮结束时显示,点击时将所有状态恢复到初始值
if (stop) {
Toast.makeText(LocalContext.current, "结束", Toast.LENGTH_SHORT).show()
Button(
onClick = {
//reset
}, modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 24.dp)
) {
Text(text = "重置")
}
}
效果展示
代码示例
@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 = "重置")
}
}
}
}
多次尝试难点问题攻克
- 画布、协程、点击点位知识点
- 控制动画开关刷新问题
- 计算步数方向问题
- 追加块计算问题