From 6ca0d6a14c22c5abc65cf894d93a3c6fbbb6a579 Mon Sep 17 00:00:00 2001 From: YXWang <19281171@bjtu.edu.cn> Date: Wed, 4 Aug 2021 23:07:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=B8=B8=E6=88=8F=E9=9A=BE?= =?UTF-8?q?=E5=BA=A6=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD(wyx)=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E5=8F=AF=E6=94=B6=E8=B5=B7=E7=9A=84?= =?UTF-8?q?3D=E6=96=87=E6=9C=AC=E6=A1=86how=20to=20play=E5=92=8C=E5=B0=8F?= =?UTF-8?q?=E5=A4=AA=E9=98=B3=E5=8A=A8=E7=94=BB(zyw)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leavesc/compose_tetris/MainActivity.kt | 3 +- .../compose_tetris/TetrisGameScreen.kt | 3 + .../compose_tetris/TetrisSettingScreen.kt | 129 +++++++----- .../compose_tetris/logic/TetrisViewModel.kt | 6 +- .../compose_tetris/ui/theme/Animation.kt | 199 ++++++++++++++++++ .../leavesc/compose_tetris/ui/theme/Button.kt | 61 +++++- .../leavesc/compose_tetris/ui/theme/Color.kt | 16 +- daemon/7.0/registry.bin | Bin 835 -> 51 bytes daemon/7.0/registry.bin.lock | Bin 17 -> 17 bytes 9 files changed, 347 insertions(+), 70 deletions(-) create mode 100644 app/src/main/java/github/leavesc/compose_tetris/ui/theme/Animation.kt diff --git a/app/src/main/java/github/leavesc/compose_tetris/MainActivity.kt b/app/src/main/java/github/leavesc/compose_tetris/MainActivity.kt index 059d360..ae7a229 100644 --- a/app/src/main/java/github/leavesc/compose_tetris/MainActivity.kt +++ b/app/src/main/java/github/leavesc/compose_tetris/MainActivity.kt @@ -21,7 +21,7 @@ class ScreenToScreenInfo { var currentScreen by mutableStateOf(0) // 难度选择 - var difficulty by mutableStateOf("Normal") + var difficulty by mutableStateOf(500L) // 选择夜间模式还是普通配色 var tetrisTheme by mutableStateOf(0) // 音乐选择 @@ -38,7 +38,6 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - when(infoStorage.currentScreen) { 0 -> { TetrisGameScreen() diff --git a/app/src/main/java/github/leavesc/compose_tetris/TetrisGameScreen.kt b/app/src/main/java/github/leavesc/compose_tetris/TetrisGameScreen.kt index 67110ce..9b77df0 100644 --- a/app/src/main/java/github/leavesc/compose_tetris/TetrisGameScreen.kt +++ b/app/src/main/java/github/leavesc/compose_tetris/TetrisGameScreen.kt @@ -24,6 +24,9 @@ import github.leavesc.compose_tetris.ui.TetrisScreen @Composable fun TetrisGameScreen() { val tetrisViewModel = viewModel() + + tetrisViewModel.changeDownSpeed(infoStorage.difficulty) // 改变下落速度 + val lifecycle = LocalLifecycleOwner.current.lifecycle val tetrisState by tetrisViewModel.tetrisStateLD.collectAsState() diff --git a/app/src/main/java/github/leavesc/compose_tetris/TetrisSettingScreen.kt b/app/src/main/java/github/leavesc/compose_tetris/TetrisSettingScreen.kt index 4f5c3a7..d9e82ed 100644 --- a/app/src/main/java/github/leavesc/compose_tetris/TetrisSettingScreen.kt +++ b/app/src/main/java/github/leavesc/compose_tetris/TetrisSettingScreen.kt @@ -1,25 +1,17 @@ package github.leavesc.compose_tetris import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.* -import androidx.compose.foundation.selection.selectable -import androidx.compose.foundation.selection.selectableGroup -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.constraintlayout.compose.ConstraintLayout -import github.leavesc.compose_tetris.ui.theme.BodyBackground import androidx.compose.ui.unit.* -import github.leavesc.compose_tetris.ui.theme.RectangularButton -import github.leavesc.compose_tetris.ui.theme.brushYellow +import github.leavesc.compose_tetris.ui.theme.* @Composable fun SettingsScreen() { @@ -32,12 +24,18 @@ fun SettingsScreen() { val biggerMargin = 16.dp val smallerMargin = 5.dp - Text( - text = "Game Settings", - fontSize = 36.sp, - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(top = smallerMargin, bottom = smallerMargin) - ) + Row(){ + Text( + text = "Game Settings", + fontSize = 36.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(top = smallerMargin, bottom = smallerMargin) + ) + Spacer(modifier = Modifier.width(30.dp)) + if(isSystemInDarkTheme()) {/* TODO: 我还想要个天上的月亮 QAQ */} + else AnimateSun(Modifier.size(50.dp)) + } + ConstraintLayout( modifier = Modifier .wrapContentWidth(align = Alignment.CenterHorizontally) @@ -56,35 +54,51 @@ fun SettingsScreen() { modifier = Modifier.padding(smallerMargin) ) - // 难度选择框 TODO: 还没完成难度选择功能 + // Switch difficulty 难度选择框 val difficultyOptions = listOf("Easy", "Normal", "Hard") - val (selectedOption, onOptionSelected) = - mutableStateOf(difficultyOptions[1]) - Column(Modifier.selectableGroup()) { - difficultyOptions.forEach { text -> - Row( - Modifier - .padding(vertical = smallerMargin) - .fillMaxWidth() - .selectable( - selected = (text == selectedOption), - onClick = { onOptionSelected(text) }, - role = Role.RadioButton - ) - .padding(horizontal = biggerMargin), - verticalAlignment = Alignment.CenterVertically - ) { - RadioButton( - selected = (text == selectedOption), - onClick = { /* TODO: 按一下就改变方块的下落速度 DOWN_SPEED */ }) - Text( - text = text, - style = MaterialTheme.typography.body1.merge(), - modifier = Modifier.padding(start = 12.dp) - ) + val difficultyDownSpeed = listOf(700L, 500L, 300L) + var select = 1 + + when(infoStorage.difficulty) { + difficultyDownSpeed[0] -> select = 0 + difficultyDownSpeed[1] -> select = 1 + difficultyDownSpeed[2] -> select = 2 + } + + Column( + modifier = Modifier.fillMaxWidth() + ) { + RadioSelectionButton( + icon = "Easy", + modifier = Modifier, + isSelected = ("Easy" == difficultyOptions[select]), + onPress = { + select = 0 + infoStorage.difficulty = difficultyDownSpeed[select] } - } + ) + RadioSelectionButton( + icon = "Normal", + modifier = Modifier, + isSelected = ("Normal" == difficultyOptions[select]), + onPress = { + select = 1 + infoStorage.difficulty = difficultyDownSpeed[select] + } + ) + RadioSelectionButton( + icon = "Hard", + modifier = Modifier, + isSelected = ("Hard" == difficultyOptions[select]), + onPress = { + select = 2 + infoStorage.difficulty = difficultyDownSpeed[select] + } + ) } + + + } // end of Row(modifier = Modifier.constrainAs(difficultySwitch) Row(modifier = Modifier.constrainAs(themeSwitch) { @@ -210,18 +224,22 @@ fun SettingsScreen() { style = MaterialTheme.typography.body1.merge(), modifier = Modifier.padding(smallerMargin) ) - } - Text( - text = "Press \"Rotate\" to rotate the block\n" + - "Press \"◀\" and \"▶\" to move the block horizontally\n" + - "Press \"▼\" to speed up the block fall\n" + - "Press \"✔\" directly to drop the current block\n", - fontSize = 14.sp, - modifier = Modifier - .padding(smallerMargin) - .fillMaxSize() - ) + DropDownAnimate( + text = "* Click to expand *", + modifier = Modifier.padding(smallerMargin) + ) { + Text( + text = "Press \"Rotate\" to rotate the block\n" + + "Press \"◀\" and \"▶\" to move the block horizontally\n" + + "Press \"▼\" to speed up the block fall\n" + + "Press \"✔\" directly to drop the current block\n", + fontSize = 14.sp, + modifier = Modifier + .padding(smallerMargin) + .fillMaxSize() + ) + } } Row(modifier = Modifier @@ -239,7 +257,6 @@ fun SettingsScreen() { start = 0.dp ) ) { - /* TODO: 这里需要设置返回游戏页面的动作 */ infoStorage.currentScreen = 0 } } @@ -247,6 +264,10 @@ fun SettingsScreen() { } } + + + + @Preview @Composable fun PreviewTetrisSetting() { diff --git a/app/src/main/java/github/leavesc/compose_tetris/logic/TetrisViewModel.kt b/app/src/main/java/github/leavesc/compose_tetris/logic/TetrisViewModel.kt index 1469aaf..f6ed937 100644 --- a/app/src/main/java/github/leavesc/compose_tetris/logic/TetrisViewModel.kt +++ b/app/src/main/java/github/leavesc/compose_tetris/logic/TetrisViewModel.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.launch class TetrisViewModel : ViewModel() { companion object { - private const val DOWN_SPEED = 500L + private var DOWN_SPEED = 500L private const val CLEAR_SCREEN_SPEED = 30L } @@ -111,6 +111,10 @@ class TetrisViewModel : ViewModel() { } } + fun changeDownSpeed(newSpeed :Long) { + DOWN_SPEED = newSpeed + } + private fun onWelcome() { startClearScreenJob { dispatchState( diff --git a/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Animation.kt b/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Animation.kt new file mode 100644 index 0000000..560d9ad --- /dev/null +++ b/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Animation.kt @@ -0,0 +1,199 @@ +package github.leavesc.compose_tetris.ui.theme + +import androidx.compose.animation.core.* +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.draw.scale +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.graphics.drawscope.Fill +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlin.math.cos +import kotlin.math.sin + +// 旋转太阳的动画 +@Composable +fun AnimateSun(modifier: Modifier = Modifier) { + val transition = rememberInfiniteTransition() + + val animateTween by transition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable(tween(5000), RepeatMode.Restart) + ) + + Canvas(modifier.rotate(animateTween)) { + + val radius = size.width / 6 + val stroke = size.width / 20 + val centerOffset = Offset(size.width / 30, size.width / 30) + + // draw circle + drawCircle( + color = Color.Black, + radius = radius + stroke / 2, + style = Stroke(width = stroke), + center = center + centerOffset + ) + drawCircle( + color = Color.White, + radius = radius, + style = Fill, + center = center + centerOffset + ) + + // draw line + + val lineLength = radius * 0.6f + val lineOffset = radius * 1.8f + (0..7).forEach { i -> + + val radians = Math.toRadians(i * 45.0) + + val offsetX = lineOffset * cos(radians).toFloat() + val offsetY = lineOffset * sin(radians).toFloat() + + val x1 = size.width / 2 + offsetX + centerOffset.x + val x2 = x1 + lineLength * cos(radians).toFloat() + + val y1 = size.height / 2 + offsetY + centerOffset.y + val y2 = y1 + lineLength * sin(radians).toFloat() + + drawLine( + color = Color.Black, + start = Offset(x1, y1), + end = Offset(x2, y2), + strokeWidth = stroke, + cap = StrokeCap.Round + ) + } + } +} + +// 画动态太阳 +@Composable +fun Sun(modifier: Modifier = Modifier) { + Canvas(modifier) { + + val radius = size.width / 6 + val stroke = size.width / 20 + + // draw circle + drawCircle( + color = Color.Black, + radius = radius + stroke / 2, + style = Stroke(width = stroke), + ) + drawCircle( + color = Color.White, + radius = radius, + style = Fill, + ) + + // draw line + + val lineLength = radius * 0.2f + val lineOffset = radius * 1.8f + (0..7).forEach { i -> + + val radians = Math.toRadians(i * 45.0) + + val offsetX = lineOffset * cos(radians).toFloat() + val offsetY = lineOffset * sin(radians).toFloat() + + val x1 = size.width / 2 + offsetX + val x2 = x1 + lineLength * cos(radians).toFloat() + + val y1 = size.height / 2 + offsetY + val y2 = y1 + lineLength * sin(radians).toFloat() + + drawLine( + color = Color.Black, + start = Offset(x1, y1), + end = Offset(x2, y2), + strokeWidth = stroke, + cap = StrokeCap.Round + ) + } + } +} + +// 3D页面下翻动画 +@Composable +fun DropDownAnimate( + text: String, + modifier: Modifier = Modifier, + initiallyOpened: Boolean = false, + content: @Composable () -> Unit +) { + var isOpen by remember { + mutableStateOf(initiallyOpened) + } + val alpha = animateFloatAsState( + targetValue = if(isOpen) 1f else 0f, + animationSpec = tween( + durationMillis = 300 + ) + ) + val rotateX = animateFloatAsState( + targetValue = if(isOpen) 0f else -90f, + animationSpec = tween( + durationMillis = 300 + ) + ) + Column( + modifier = modifier + .fillMaxWidth() + ) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clickable { + isOpen = !isOpen + } + .fillMaxWidth() + ) { + Text( + text = text, + color = Color.Black.copy(0.6f), + fontSize = 16.sp + ) + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = "Open or close the drop down", + tint = Color.Black, + modifier = Modifier + .scale(1f, if (isOpen) -1f else 1f) + ) + } + Spacer(modifier = Modifier.height(10.dp)) + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .graphicsLayer { + transformOrigin = TransformOrigin(0.5f, 0f) + rotationX = rotateX.value + } + .alpha(alpha.value) + ) { + content() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Button.kt b/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Button.kt index acee2da..bf81c18 100644 --- a/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Button.kt +++ b/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Button.kt @@ -1,11 +1,9 @@ package github.leavesc.compose_tetris.ui.theme +import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -13,7 +11,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp @@ -41,7 +42,7 @@ fun ControlButton( Box( modifier = Modifier .size(width = btnSize, height = btnSize) - .shadow(elevation = 60.dp, shape = PlayButtonShape) + .shadow(elevation = 36.dp, shape = PlayButtonShape) .clip(shape = PlayButtonShape) .background( brush = brushYellow @@ -91,6 +92,7 @@ fun RectangularButton( Box( modifier = modifier .clip(shape = RoundedCornerShape(7.dp)) + .shadow(elevation = 30.dp, shape = RectangleShape) .background( brush = brushYellow ) @@ -106,4 +108,53 @@ fun RectangularButton( color = Color.Black.copy(0.9f) ) } +} + +@Composable +fun RadioSelectionButton( + /* TODO: 造这玩意儿是因为 Compose 自己的 RadioButton 居然用不了!!! */ + icon: String, + modifier: Modifier, + circleSize: Dp = 22.dp, + fontSize: TextUnit = 16.sp, + isSelected: Boolean = false, + onPress: () -> Unit +) { + Row( + modifier = Modifier + .clickable { + onPress() + }, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start + ) { + Canvas( + modifier = Modifier + .padding(5.dp) + .size(circleSize) + ) { + drawCircle( + /* TODO: 先看看这里尺寸 */ + color = if (isSelected) Color.Black else PlayButtonColor3.copy(0.9f), + center = Offset(x = size.width / 2, y = size.height / 2), + radius = size.minDimension / 2 + ) + } + Text( + modifier = Modifier.padding(5.dp), + text = icon, + fontSize = fontSize, + color = Color.Black.copy(0.9f) + ) + } +} + +@Preview +@Composable +fun PreviewRadioButton() { + Column() { + RadioSelectionButton("Easy", Modifier, onPress = {}) + RadioSelectionButton("Normal", Modifier, onPress = {}, isSelected = true) + RadioSelectionButton("Hard", Modifier, onPress = {}) + } } \ No newline at end of file diff --git a/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Color.kt b/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Color.kt index 95af717..06aaabd 100644 --- a/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Color.kt +++ b/app/src/main/java/github/leavesc/compose_tetris/ui/theme/Color.kt @@ -15,14 +15,6 @@ val PlayButtonColor = Color(0xF2FDDF3A) val PlayButtonColor2 = Color(0xF2C5A708) val PlayButtonColor3 = Color(0xF2947C01) -val BodyBackgroundNight = Color(0xF200EAFF) -val ScreenBackgroundNight = Color(0xFF86ADA4) -val PlayButtonColorNight = Color(0xF24784FF) -val PlayButtonColorNight2 = Color(0xF2245AC7) -val PlayButtonColorNight3 = Color(0xF2123E97) -val BrickAlpha = Color.Black.copy(alpha = 0.2f) -val BrickFill = Color.Black.copy(alpha = 0.9f) - val brushYellow = Brush.linearGradient( colors = listOf( PlayButtonColor, @@ -31,6 +23,14 @@ val brushYellow = Brush.linearGradient( ) ) +val BodyBackgroundNight = Color(0xF200EAFF) +val ScreenBackgroundNight = Color(0xFF86ADA4) +val PlayButtonColorNight = Color(0xF24784FF) +val PlayButtonColorNight2 = Color(0xF2245AC7) +val PlayButtonColorNight3 = Color(0xF2123E97) +val BrickAlpha = Color.Black.copy(alpha = 0.2f) +val BrickFill = Color.Black.copy(alpha = 0.9f) + val brushBlue = Brush.linearGradient( colors = listOf( PlayButtonColorNight, diff --git a/daemon/7.0/registry.bin b/daemon/7.0/registry.bin index f7efac7ffd4c082fc823d1537789ba393de05e20..647983bfe99cd083fd3cdd36bb7054a685e7afca 100644 GIT binary patch delta 33 ecmX@iW-Q6b00BUnv08BB_B04rsA2MdCMf_;fCaMv literal 835 zcmbu7PiqrF6u{rc9u%RrLg-C%s1^^it{S!*A%~FYDdyCohlMhkd7Dhk&V>Nc>v&fUte7Kymq5;_-1Rc zawHGF037~s4_24=JpBKD!%KqZG=dm53qwD4F8Ij%9o6A&Fp>wDJtK z&67)`C-r6ruAaHvIC9#<`feYV&wOL51(n8ajnmR>Utezgw6>Mxd`oEbH$FqNiu WGb