通过之前的介绍,我们对Compose Multiplatform项目有了一个基本的了解,但是光了解有啥意思呐,今天我们就做个有意思的项目—别踩白块,想必很多人也玩过这个游戏。
想更多了解Compose Multiplatform,也可以看看其他文章
- Compose Multiplatform 之旅 — 启程
- Compose Multiplatform 之旅 — 项目初探
- Compose Multiplatform 之旅 —做一个自己的项目(别踩白块)
- Compose Multiplatform 之旅—看看大佬在做啥
- Compose Multiplatform 之旅—为什么可以跨平台
- Compose Multiplatform 之旅—声明式UI
- Compose Multiplatform 之旅—跳转、导航(Voyager)
- Compose Multiplatform 之旅 — 数据存储(multiplatform-settings、sqldelight)
- Compose Multiplatform 之旅 — 网络请求(Ktor)
- Compose Multiplatform 之旅 — 图标、图片展示(coil)
项目设计
拆解别踩白块游戏,主要分为3点:
- 方块绘制
- 方块滚动
- 方块点击
这3点也会涉及日常开发很常见场景。
方块绘制
思路:我们首先是绘制一行,这里我们固定一行四列,然后随机一个0-3随机一个数,绘制成黑块,其他绘制成白块。 然后重复一行的思路,绘制成无数行。
@Composable
fun GameScreen() {
MaterialTheme {
// 使用LazyColumn 绘制多列,类似于Android 的RecyclerView
LazyColumn(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
item {
//重复绘制多行
repeat(200) {
//随机一个黑色块的下标
val randomIndex by remember { mutableStateOf((0..3).random()) }
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
repeat(4) { index ->
//weight 均分一行四列
Column(modifier = Modifier.weight(1f)) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(160.dp)
.background(
//设置对应的黑色块和白色块
if (index == randomIndex)
Color.Black
else
Color.White
)
)
}}}}}}}
}
通过上面的思路,一个简单的别踩白块布局就做好了,下图为桌面端效果。
不过这个效果看着怪怪的,我们再加个分割线和背景色
MaterialTheme {
val gradientColors = listOf(Color.Red, Color.Yellow, Color.Green)
val brush = Brush.linearGradient(
colors = gradientColors
)
LazyColumn(
//设置一个渐变的brush背景
Modifier.fillMaxWidth().background(brush),
horizontalAlignment = Alignment.CenterHorizontally,
) {
//...
Box(
modifier = Modifier
.background(
if (index == randomIndex)
Color(0x66222222)
else
Color(0x66ffffff)
)
//设置一个边框
.border(
width = 0.5.dp,
color = Color(0x66222222),
shape = RoundedCornerShape(1)
)
)
//...
}
}
调整后的效果
方块滚动
使用LaunchedEffect 在Composable 中,使用协程执行定时任务,使用listState进行滚动。一般滚动是从上到下,我们先给一个足够大的距离,让他滚动到底部。
val listState = rememberLazyListState()
LaunchedEffect(key1 = true) {
launch {
//给一个足够大的距离,滚到到最底部
listState.scrollBy(2000000f)
delay(1000)
while (true) {
//每次慢慢往上移一点点,形成滚动的效果
listState.scrollBy(-4f)
delay(20)
}
}
}
LazyColumn(
state = listState
){
//...
}
当当当~ 滚动的效果就完成了
方块点击
在点击,方块时,我们希望方块消失,并且会记录对应的分数。在最开始时,不进行滚动,点击开始方块时,才进行滚动计算分数。
var bgColor by remember {
mutableStateOf(
if (index == randomIndex)
Color(0x66222222)
else
Color(0x66ffffff)
)
}
Box(
modifier = Modifier
.background(
bgColor
)
.clickable {
//点击第一个方块,进行游戏
if (row == 199 && index == randomIndex) {
start = true
bgColor = Color(0x66ffffff)
} else if (index == randomIndex) {
//背景色改变,分数增加
bgColor = Color(0x66ffffff)
score++
}
}
)
最终效果展示
桌面端
网页端(存在中文显示BUG)
移动端(Android/ios)
完整代码
```
package com.example.composeApp
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
fun GameScreen() {
val listState = rememberLazyListState()
var start by remember { mutableStateOf(false) }
var score by remember { mutableStateOf(0) }
MaterialTheme {
val gradientColors = listOf(Color.Red, Color.Yellow, Color.Green)
val brush = Brush.linearGradient(
colors = gradientColors
)
LaunchedEffect(key1 = true) {
launch {
//移动到最底部
listState.scrollBy(2000000f)
}
}
LaunchedEffect(start) {
//点击开始时,才进行滚动
if (start) {
launch {
while (true) {
listState.scrollBy(-10f)
delay(16)
}
}
}
}
LazyColumn(
Modifier.fillMaxWidth().background(brush),
horizontalAlignment = Alignment.CenterHorizontally,
state = listState
) {
item {
repeat(200) { row ->
val randomIndex by remember {
mutableStateOf((0..3).random())
}
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
repeat(4) { index ->
Column(modifier = Modifier.weight(1f)) {
var bgColor by remember {
mutableStateOf(
if (index == randomIndex)
Color(0x66222222)
else
Color(0x66ffffff)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(160.dp)
.background(
bgColor
)
.border(
width = 0.5.dp,
color = Color(0x66222222),
shape = RoundedCornerShape(1)
)
.clickable {
if (row == 199 && index == randomIndex) {
start = true
bgColor = Color(0x66ffffff)
} else if (index == randomIndex) {
bgColor = Color(0x66ffffff)
score++
}
}
) {
if (row == 199 && index == randomIndex) {
Text(
"开始",
style = TextStyle(
color = Color.White,
fontSize = 20.sp
),
modifier = Modifier.align(Alignment.Center),
)
}
}
}
}
}
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.padding(10.dp)
) {
Text(
text = "分数:$score",
modifier = Modifier
.align(Alignment.TopEnd)
.padding(10.dp),
color = Color.Black
)
}
}
}
```
结语
至此,我们就完成了别踩白块的开发,希望大家可以体会到Compose Multiplatform项目的开发乐趣。不过这次开发过程中还是有很多值得研究的地方,比如remember、LaunchedEffect、LazyColumn等等,后续希望可以和大家一起继续深入。