安卓开发: Jetpack compose + kotlin 实现 俄罗斯方块游戏
文章目录
- 前言
- 俄罗斯方块开发文档
- 1.摘要
- 2.开发工具选取
- 2.1.Compose 的自身优点
- 2.2.数据驱动界面
- 3.设计需求
- 3.1.功能需求
- 3.1.1.基本游戏功能
- 3.1.2.拓展功能
- 3.2.界面需求
- 3.2.1.整体界面设计
- 3.2.2.特色界面设计
- 4.项目文件及其功能
- 4.1.游戏架构基于 MVI 设计
- 4.2.源文件功能简述表
- 4.3 功能索引
- 5.遇到的困难和解决方案
- 5.1.零基础学习新的语言和框架
- 5.2.使用 Gitee 托管仓库
- 5.3.编辑 build.gradle 环境配置文件
- 5.4.用 Kotlin 编写游戏逻辑实现 Model 层次
- 5.6.基于 Compose 实现 View 层次
- 俄罗斯方块游戏屏幕
- 俄罗斯方块游戏按钮布局
- 俄罗斯方块游戏机背景
- 游戏设置页面
- 5.7.实现界面切换和导航功能
- 5.8.设置显示界面主题
- 5.9.动画设计
- 附录:源代码 Git 仓库链接
前言
项目地址:github 开源地址,码云开源地址
项目背景:刚刚结束的暑假小学期中,和我的两个神仙队友WYX,XPF(我的队友超级棒!)共同完成的第一个安卓开发项目——俄罗斯方块游戏。
框架和语言:kotlin+ jetpack compose (compose 是谷歌最新推出的开发框架,AndroidStudio刚出了支持compose的稳定版本)
参考项目:参考了https://github.com/leavesC/compose_tetris
在此基础上增加了许多丰富的功能(如切换游戏难度,切换背景音乐,记录最高分…)
俄罗斯方块开发文档
应用名称:鹅螺蛳方块
应用类型:休闲游戏
小组成员:WYX、ZYW、XPF
开发时间:2021年7月26日 - 2021年8月14日
1.摘要
本组使用 Android studio 作为集成式开发环境,完全自主学习 Kotlin 编程语言和 Jetpack Compose 框架,编写了一个功能齐全、具有动态界面的俄罗斯方块安卓游戏。
主要工作包括:搭建 Compose Activity 项目环境,全栈开发实现俄罗斯方块的后端游戏逻辑、界面交互、前端界面设计、音效播放、动画效果等功能(详见“设计需求”一节)。
学习使用全新的 Jetpack Compose 框架是本项目最大的挑战。Jetpack Compose 是谷歌推出的新式声明性界面工具包 ,包括一整套全新的渲染、布局、事件、刷新机制,需要一段时间才能入门,但有效提高了本小组的开发效率。
本文档用于描述《计算机科学与技术专业实践与训练》课程所编写程序的设计方案,文档阅读对象为本课程授课教师及本课堂同学。
2.开发工具选取
Jetpack Compose 是用于构建原生界面的最新的 Android 工具包,结合了反应式编程模型和 Kotlin 编程语言的简洁性和易用性。
2.1.Compose 的自身优点
- 采用声明式 UI 设计
- 提供丰富的 Material 组件,加速开发
- 具有直观的 Kotlin API
- 拥有更简单的、自定义的实时交互预览功能
2.2.数据驱动界面
游戏的执行程序可以概括为不断等待用户的输入信息,进行状态查询,渲染界面的过程。而这种游戏模型的逻辑非常符合前端开发的思想:数据驱动界面。
当下的小游戏多以前端为主,客户端开发成本较大,而使用 Compose 可以降低开发成本。Compose 实现数据驱动是依赖类似 Flutter 的 Provide 一样的更新机制,但并没有采用采用了传统 UI 的多层继承结构,而是多个 View 组合成一个 View ,更新数据只要在使用的实体上面加注解 @Model ,在更新实体的同时,会通知 UI 进行修改。
3.设计需求
3.1.功能需求
3.1.1.基本游戏功能
- 实现俄罗斯方块的游戏的基本逻辑:控制方块的运动和旋转
- 实现游戏整体功能控制:开始游戏、暂停游戏、重新开始、音乐开启关闭;
3.1.2.拓展功能
- 实现游戏高级设置:调整游戏难度,游戏界面主题切换,更改游戏背景音乐;
- 增加计分系统,在游戏界面实时显示目前得分,统计历史最高得分;
- 增加玩家档案信息,使玩家可以输入保存自己的姓名;
- 增加游戏说明的信息,使得玩家了解游戏规则和技巧;
- 实现关于我们页面:展示应用版本信息、开发团队的留言、产品设计理念;
3.2.界面需求
3.2.1.整体界面设计
- 设计导航栏使得界面的跳转更加简明快捷。
- 设计垂直滚动式页面增加单个页面展示的空间。
- 设计白天与黑夜两种模式的界面配色,启动应用时会自动根据手机夜间模式切换,界面内包括太阳和月亮的动画。
- 设计多种模式的渐变色按钮,增加按钮的质感以提升玩家的体验。
- 设计多种风格的字体样式以区分不同的内容和突出重点。
- 设计多种矢量图标,使得信息的展现更加形象。
3.2.2.特色界面设计
- 游戏屏幕可由用户自己调节大小,并当调节范围超出最大和最小的界限时,设计弹窗提醒用户。
- 游戏屏幕边框实现色彩的渐变,增添游戏的炫酷感。
- 设计手势控制的旋转圆形头像,可变带宽和模糊度的圆环边框,可变模糊度和曲线形状的顶部背景图片。
- 设计 3D 翻折动画,隐藏和展开游戏说明,使得界面更加简洁灵动。
- 设计水墨渲染动画,通过手势将灰度图片点亮,增添界面的高级感。
- 设计上下两个图层,通过下图层的阴影使得可360°翻转的图片有肉眼3D的效果
- 设计星空背景和火箭发射的动画,更形象的展示我们的理念和深层寓意。
- 设计色彩渐变的字体,是界面更加有趣美观。
- 仿照胶片样式,设计横向滚动的图像卡片,展示开发者留言。
- 设计关键信息可复制的文本,展示项目地址,开发者联系方式。
4.项目文件及其功能
4.1.游戏架构基于 MVI 设计
MVI 即 Model-View-Intent ,提倡一种单向数据流的设计思想,非常适合在 Compose 项目中实现逻辑部分,可以彻底贯彻“数据驱动 UI”的核心思想。
- View 层:基于 Compose 打造,所有 UI 元素都由代码实现
- Model 层:ViewModel 维护 State 的变化,游戏逻辑交由 reduce 处理
- V-M 通信:通过 StateFlow 驱动 Compose 刷新,用户事件由 Action 分发至 ViewModel
4.2.源文件功能简述表
如下表所示,所有源代码部分都在 com.android_tetris 包内:
4.3 功能索引
5.遇到的困难和解决方案
5.1.零基础学习新的语言和框架
本程序完全使用 Kotlin 语言开发,并选用 Jetpack Compose 框架作为前端框架。
我们完全自主学习了 Jetpack Compose (以下简称 Compose )这个还未推出正式版的声明式 UI 框架。由于谷歌暂未推出 Compose 的正式版本,现在网上相关资料和教程都还非常稀少,API文档还是全英文的,我们常常在意想不到的问题上被卡住还没有什么办法。
虽然入门过程极其艰难,但是在学习使用 Compose 的全新框架开发的过程中,我们的英语水平、信息检索能力、解决问题的能力,都得到了极大的锻炼,相信这段艰苦的自主学习经历对我们之后的编程之路一定有所帮助。
5.2.使用 Gitee 托管仓库
由于这是一个工程量比较大的项目,我们采用 Gitee 进行版本管理。但在使用过程中,我们上传经常遇到“文件超过 100M ”的错误提示。
为解决这一问题,我们学习了 .gitignore 文件的使用方法:在该文件按优先级从高到低,写明让 Git 仓库上传时忽略掉的文件目录/后缀名, Git 就会主动忽略这些文件。
我们可以用这一办法处理项目编译产生的文件和庞大的开发环境文件。
5.3.编辑 build.gradle 环境配置文件
开发初期,由于 Kotlin 和 Compose 还在频繁更新 API ,版本迭代很快,因此我们遇到了很多次 build 不成功的情况,报错位置都出现在 build.gradle 文件。
查阅资料发现, Project 层级和 Module 层级各有相对应的两个 build.gradle 文件,在 Android studio 中分别显示为 build.gradle (Project: 项目名) 和 build.gradle (Module: 项目名.app) 。
在 build.gradle (Project: 项目名) 中, buildscript { } 代码块是该项目的 gradle 配置,只存放用到的代码托管库和项目构建级别的依赖:
buildscript {ext {compose_version = '1.0.0-beta08'}repositories {google()mavenCentral()}dependencies {classpath 'com.android.tools.build:gradle:7.1.0-alpha02'classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files// (此处的注释提示:不要把应用程序的依赖库在此引用)}
}
在 build.gradle (Module: 项目名.app) 中, build.gradle 主要用于配置模块级别的配置信息和依赖:
plugins {id 'com.android.application'id 'kotlin-android'
}android {compileSdk 30buildToolsVersion "30.0.3"defaultConfig {applicationId "com.android_tetris"minSdk 21targetSdk 30versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"vectorDrawables {useSupportLibrary true}}// 输出 APK 用到的应用签名信息signingConfigs {releaseConfig {storeFile file("..\\key.jks")storePassword "123456"keyAlias "key0"keyPassword "123456"v1SigningEnabled truev2SigningEnabled true}}buildTypes {debug {signingConfig signingConfigs.releaseConfigminifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}release {signingConfig signingConfigs.releaseConfigminifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'useIR = true}buildFeatures {compose true}composeOptions {kotlinCompilerExtensionVersion compose_versionkotlinCompilerVersion '1.5.10'}
}dependencies {testImplementation 'junit:junit:4.13.2'androidTestImplementation 'androidx.test.ext:junit:1.1.2'androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'implementation 'androidx.appcompat:appcompat:1.2.0'implementation 'com.google.android.material:material:1.3.0'...
}
5.4.用 Kotlin 编写游戏逻辑实现 Model 层次
为遵守 MVI 设计逻辑,对于游戏界面,所有会由游戏内行为发出再导致前端显示刷新的变化,都集中存放在 TetrisState.kt 文件中。界面的所有变化都依赖后端 State 的变化而刷新。
俄罗斯方块共有 7 种形状,每种形状有随着旋转还会有几种不同形态,本项目在 MockData.kt 文件中选用 44 的二维 IntArray 数组储存方块形状,在 TetrisState.kt 中选用 1024 的二维 IntArray 数组表示存放方块的屏幕。
以下是 TetrisState.kt 文件中一部分重要函数和类的代码:
// class Tetris:对于单个俄罗斯方块的 State 状态类
data class Tetris constructor(val shapes: List<List<Location>>, // 此方块所有可能的旋转结果val type: Int, // 用于标记当前处于哪种旋转状态val offset: Location, // 方块相对屏幕左上角的偏移量
) { ... }// class TetrisState:游戏的 State 状态类
data class TetrisState(val brickArray: Array<IntArray>, // 屏幕坐标系val tetris: Tetris, // 下落的方块val gameStatus: GameStatus = GameStatus.Welcome, // 游戏状态val soundEnable: Boolean = true, // 是否开启音效val nextTetris: Tetris = Tetris(), // 下一个方块var currentScore: Int = infoStorage.currentScore // 当前分数
) { ... }// class Action:接受用户 View 层次传入信号的 Action 类
sealed class Action { object Welcome : Action()object Start : Action()object Pause : Action()object Reset : Action()object Sound : Action()object Settings : Action()object Background : Action()object Resume : Action()data class Transformation(val transformationType: TransformationType) : Action()
}// fun combinedPlayListener() 函数:Action 行为的监听器,监听器接收到信号后,分别再发送给 private fun TetrisState.onStart() 等 TetrisState 的私有函数,进行处理
fun combinedPlayListener( onStart: () -> Unit = {},onPause: () -> Unit = {},onReset: () -> Unit = {},onSound: () -> Unit = {},onSettings: () -> Unit = {},onTransformation: (TransformationType) -> Unit = {}
) = PlayListener(onStart = onStart,onPause = onPause,onReset = onReset,onSound = onSound,onSettings = onSettings,onTransformation = onTransformation
)
5.5.View 层和 Model 层的交互机制
以下是 TetrisViewModel 类声明部分代码 :
class TetrisViewModel : ViewModel() {//功能:接收 Action 信号,由用户的动作改变 ViewModelfun dispatch(action: Action) {...} //功能:改变下落速度fun changeDownSpeed(newSpeed :Long){...} //功能:初始进入软件时的欢迎效果private fun onWelcome() {...}//功能:开始游戏private fun onStartGame(){...}//功能:暂停游戏private fun onPauseGame(){...}//功能:结束游戏private fun onGameOver(){...}//功能:开始清空界面private fun startClearScreenJob(invokeOnCompletion: () -> Unit){...}//功能:取消清空界面private fun cancelClearScreenJob(){...}//功能:方块下降private fun startDownJob() {...}//功能:暂停方块下降private fun cancelDownJob() {...}//功能:将当前界面状态传递给用户private fun dispatchState(tetrisState: TetrisState) {..}//功能:游戏背景音乐播放private fun playSound(action: Action) {...}//功能:播放不同类型的背景音乐private fun playSound(soundType: SoundType){...}
}
5.6.基于 Compose 实现 View 层次
由于文档篇幅有限,详细的媒体内容展示可参考源代码、 PPT、和应用展示视频。
俄罗斯方块游戏屏幕
主要用到了 Compose 的自定义图像核心可组合项 Canvas ,预览效果如下图:
主要逻辑功能包括:绘制游戏屏幕背景、绘制以难度指定速度不断下落的方块、为方块提供按键移动功能、判断是否进行消行、如果方块超出当前屏幕则结束游戏。
代码如下:
// 源文件:TetrisScreen.kt
package com.android_tetris.ui
import...
@Composable
fun TetrisScreen(tetrisState: TetrisState) {... Canvas(modifier=Modifier.(...)){...kotlin.run { //绘制方块矩阵screenMatrix.forEachIndexed { y, ints ->ints.forEachIndexed { x, isFill ->translate(...) {drawBrick(...)}}}} kotlin.run { ...drawPath(...)}//绘制下落方块 kotlin.run { drawRightPanel(...)}//绘制右侧得分栏 kotlin.run { drawHint(...)} //绘制提示文字}
}
//绘制单个方块
fun DrawScope.drawBrick(brickSize: Float,//每一个方块的sizecolor: Color,//砖块颜色background: Color //背景颜色
) {...translate(...) {drawRect(...)}//绘制外部矩形边框translate(...) {drawRect(...)}//绘制内部矩形边框
}
private fun DrawScope.drawRightPanel(...) {...}//绘制右侧得分栏
private fun DrawScope.drawHint(...) {...}//绘制提示文字
俄罗斯方块游戏按钮布局
预览效果如下图:
ConstraintLayout 是一种根据可组合项的相对位置关系显示的布局类型,代码如下:
//TetrisButton.kt
fun TetrisButton(playListener: PlayListener = combinedPlayListener()
) {Column(...){Row(...){ControlButton(...){ playListener.onStart()}//开始游戏ControlButton(...){ playListener.onPause()}//暂停游戏ControlButton(...){ playListener.onReset()}//重新开始ControlButton(...){ playListener.onSound()}//音乐开关}ConstraintLayout(...){PlayButton(...){playListener.onTransformation(Rotate)}//旋转方块PlayButton(...){playListener.onTransformation(Fall)}//方块加速下落PlayButton(...){ playListener.onTransformation(Left)}//方块左移PlayButton(...){playListener.onTransformation(Right)}//方块右移PlayButton(...){playListener.onTransformation(FastDown)}//方块直接降落到最底部} }
}
俄罗斯方块游戏机背景
预览效果如下图:
代码如下:
// TetrisBody.kt
package com.android_tetris.ui
fun TetrisBody(tetrisScreen: @Composable (() -> Unit), tetrisButton: @Composable (() -> Unit),
){...val size by animateDpAsState(...)//改变大小状态val color by infiniteTransition.animateColor(..,)//改变颜色状态Column(...){TopBar(...){Row(...){Icon(...); Text(...)//转到setting界面Icon(...); Text(...)//转到More界面Icon(...); Text(...)//增大屏幕Icon(...); Text(...)//减小屏幕}}Box(...){Column(...){ tetrisScreen()}//游戏屏幕区}}tetrisButton()//游戏按钮区
}
最终主界面的整体组合效果:
游戏设置页面
预览效果如下图:
代码如下:
//TetrisSettingScreen.kt
fun SettingsScreen(){Box(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())//垂直滚动){Box(...){Box{Column(...){Box(...){LoginPageTopBlurImage(...)//顶部模糊背景LoginPageTopRotaAndScaleImage(...)//顶部旋转头像}Column(...){Row{Text(...)//game setting 图标if(...){... PlanetMoon()}//黑夜模式展示月亮动画else AnimateSun(Modifier.size(50.dp))//白天模式展示太阳}ConstraintLayout(...){...Row(...){...//难度选择Column(...){RadioSelectionButton(label = "Easy",...)//简单RadioSelectionButton(label = "Normal",...)//普通RadioSelectionButton(label = "Hard",...)//困难}}Row(...){...//主题选择Column(...){RectangularButton(...){viewModel.Theme = ComposeTetrisTheme.Theme.Light} //白天模式RectangularButton(...){viewModel.Theme = ComposeTetrisTheme.Theme.Dark} //黑夜模式}}Row(...){...//音乐切换RectangularButton(...){when(infoStorage.bgm){...}}} }Text( text = "Player Profile",...)//个人主页标题ConstraintLayout(...){Row(...){...//用户名TextField(...)}Row(...){...//最高分Text(text = "$highest",...) }Row(...){...//帮助文档DropDownAnimate(...){Text(...)}}}}}}}}
}
关于我们页面
预览效果如图所示:
代码如下:
//AboutUsScreen.kt
package com.android_tetris
...
fun AboutUsScreen(){Box(modifier = Modifier.verticalScroll(rememberScrollState())//垂直滚动界面.background(color = Color.Black)){Column(...){topBarView_More() //顶部导航栏Text( buildAnnotatedString {...})//彩虹式标题BlinkTag {...}//提示文字,点击图片Row(...){InkColorCanvas()} // 水墨渲染图片Row(...){Parallax()}//3D安卓小人,展示开发信息Row(...){Text(text = "Our Faith",...)//标题Spacer(...)//间距Image(...)//月亮图片}Row(...){...BoxWithConstraints(...){Rocket(...)//发射动画LaunchButton(...)//发射按钮}}Text(text = "Message Board",...)//标题Image(...)//胶片上边框Box( modifier = Modifier.horizontalScroll(rememberScrollState())... ){//横向滚动Row{Box(...){ ImageCard(...)}...//留言卡片一Box(...){ ImageCard(...)}...//留言卡片二Box(...){ ImageCard(...)}...//留言卡片三} }Text(text = "Contact us",...)//标题Text(...)//提示信息Box( modifier = Modifier.horizontalScroll(rememberScrollState())... ){//横向滚动Row{Box(...){Card(...){...}}//项目地址信息Box(...){Card(...){...}}//开发者邮箱Box(...){Card(...){...}}//开发者码云账户} }}}
}
5.7.实现界面切换和导航功能
最开始,我们想借助 Jetpack Navigation 框架的接口实现界面跳转,但进行了很多天的尝试也未能成功。
随着我们更加深入理解 Compose 的特性,我们认识到 Compose 作为“声明式界面”本身就能够自动更新其中的数据。因此,可以定义一个顶级声明变量,使该变量指代当前应该显示的屏幕,从而采用选择性显示或隐藏某界面的方式,间接实现界面切换功能。
// MainActivity.kt 中选择性显示界面的代码
ComposeTetrisTheme(viewModel.Theme) {when (infoStorage.currentScreen) {0 -> {TetrisGameScreen()}1 -> {SettingsScreen()}2 -> {AboutUsScreen()}}
}//Topbar.kt
package com.android_tetris.ui.theme//设置界面导航栏
@Composable
fun topBarView_Set(){...}//关于我们界面导航栏
@Preview
@Composable
fun topBarView_More(){...}
5.8.设置显示界面主题
定义主题颜色:分白天和黑夜两个主题,对游戏界面背景颜色、设置界面背景颜色、字体和按钮等颜色进行设置。根据用户所选择的主题,确定颜色组合,赋值给 MaterialTheme 的 colors 成员。
// 黑色主题
private val DarkColorPalette = darkColors( ... )
// 白天主题
private val LightColorPalette = lightColors( ... )构建 ComposeTetrisTheme 对象,用枚举类存放白天黑夜两个主题供用户选择。object ComposeTetrisTheme {enum class Theme {Light, Dark}
}
编写主题设置函数:定义 fun ComposeTetrisTheme () 函数,该函数第一个参数接收一个主题对象参数,用于参数判断当前的主题类型;第二个参数接收一个 content 参数确定主题函数作用范围。将变量 colors 传给 MaterialTheme。
fun ComposeTetrisTheme(theme: ComposeTetrisTheme.Theme = ComposeTetrisTheme.Theme.Light,content: @Composable () -> Unit
) {val colors = if (theme == ComposeTetrisTheme.Theme.Dark)DarkColorPaletteelseLightColorPaletteMaterialTheme(colors = colors,typography = Typography,shapes = Shapes,content = content)
}
设置按钮事件实现主题切换:绘制两个按钮分别代表白天模式和黑夜模式,效果是点击按钮 day mode,将 viewModel.Theme 置为 Light,点击按钮 Night mode,将 viewModel.Theme 置为 Dark。主题 Theme 的控制变量放在 ThemeViewModel 类中。 ThemeViewModel 继承自 ViewModel , ViewModel 旨在以生命周期意识的方式,存储和管理用户界面相关的数据,目的是存放应用程序页面所需的数据。这样页面只需要处理用户交互,以及负责展示数据的工作。
ThemeViewModel 只包含一个成员 var Theme ,用 by mutableStateOf(ComposeTetrisTheme.Theme.Light) 初始化。其中 mutableStateOf 函数的作用是显式标明这个 Theme 是有状态的,如果 Theme 的状态发生了改变,所有引用这个状态的控件都需要重新绘制。
var viewModel : ThemeViewModel = viewModel()Column(modifier = Modifier.fillMaxWidth()) {RectangularButton(label = "Daytime mode",) {viewModel.Theme = ComposeTetrisTheme.Theme.Light}RectangularButton(label = "Night mode",) {viewModel.Theme = ComposeTetrisTheme.Theme.Dark}}class ThemeViewModel : ViewModel() {// 选择夜间模式还是普通配色var Theme by mutableStateOf(ComposeTetrisTheme.Theme.Light)
}
5.9.动画设计
Jetpack Compose 为各种动画效果提供实验性 API,可能会在之后的版本中逐步完善:
// Animation.kt
package com.android_tetris.ui.theme// 绘制动态月亮
@Composable
fun PlanetMoon() {...}// 插入月亮图片
@Composable
private fun buildEarthFloatAnimation(): State<Float>{...}//实现月亮垂直方向浮动效果
@Composable
private fun buildEarthRotationAnimation(): State<Float> {...}//实现月亮沿着竖直轴旋转效果// 水墨渲染动画
@Composable
fun InkColorCanvas() {...}// 旋转太阳的动画
@Composable
fun AnimateSun(modifier: Modifier = Modifier){...}
@Composable
fun Sun(modifier: Modifier = Modifier) {...}// 3D页面下翻动画
@Composable
fun DropDownAnimate(text: String,modifier: Modifier = Modifier,initiallyOpened: Boolean = false,content: @Composable () -> Unit
) {...}//3D 安卓小人动画
fun Parallax() {...}
fun getRotationAngles(start: Pair<Float, Float>,end: Pair<Float, Float>,size: Size
): Pair<Float, Float> {...}
fun getDistances(p1: Pair<Float, Float>, p2: Pair<Float, Float>): Pair<Float, Float> {...}
fun getTranslation(angle: Float, maxDistance: Float): Float {...}// 火箭发射动画
@Composable
fun Rocket(isRocketEnabled: Boolean,maxWidth: Dp,maxHeight: Dp
) {...}
//火箭发射按钮
@ExperimentalAnimationApi
@Composable
fun LaunchButton( animationState: Boolean,onToggleAnimationState: () -> Unit,
){...}//ImageCard
@Composable
fun ImageCard(painter: Painter,contentDescription: String,title: String,modifier:Modifier=Modifier
){...}// Login.kt
package com.android_tetris.ui.theme
import...//登陆背景模糊头缩放部图片
@Composable
fun LoginPageTopBlurImage(animatedBitmap: Animatable<Float, AnimationVector1D>,animatedOffset: Animatable<Float, AnimationVector1D>,animatedScales: Animatable<Float, AnimationVector1D>
) {...}//登陆页面头部旋转缩放的图片
@Composable
fun LoginPageTopRotaAndScaleImage(animatedColor: Animatable<androidx.compose.ui.graphics.Color, AnimationVector4D>,animatedScales: Animatable<Float, AnimationVector1D>,animatedOffset: Animatable<Float, AnimationVector1D>
) {...}//圆形图片
@Stable
class CicleImageShape(val circle: Float = 0f) : Shape {override fun createOutline(...): Outline {...}
}//形状裁剪
@Stable
class QureytoImageShapes(var hudu: Float = 100f, var controller:Float=0f) : Shape {override fun createOutline(...): Outline {...}
}// 模糊效果
object BitmapBlur {fun doBlur(sentBitmap: Bitmap, radiu: Int = 1, canReuseInBitmap: Boolean): Bitmap {}
}//RainbowSpark.kt
package com.android_tetris.ui.theme
import...// 彩虹渐变字体
@ExperimentalAnimationApi
@Composable
fun MultiColorSmoothText(modifier: Modifier = Modifier,text: String,style: TextStyle = LocalTextStyle.current,rainbow: List<Color> = PastelRainbow,startIndex: Int = 0,duration: Int
) {...}//闪烁字体
@ExperimentalAnimationApi
@Composable
fun BlinkTag(modifier: Modifier = Modifier,duration: Int = 500000,content: @Composable (modifier: Modifier) -> Unit
) {...}
附录:源代码 Git 仓库链接
Gitee:https://gitee.com/yxwang2023/android_tetris
GitHub:https://github.com/zyw-stu/Andriod_Tetris
安卓开发: Jetpack compose + kotlin 实现 俄罗斯方块游戏相关推荐
- 用Jetpack Compose做一个俄罗斯方块游戏机
本文介绍如何使用Jetpack Compose打造一个经典版的俄罗斯方块游戏. 玩过上面这种游戏机的朋友应该会对本文内容感到亲切,废话不多说,先看东西: 1. 为什么Compose适合做游戏? 通常一 ...
- 移动开发 Jetpack Compose 组件布局
Jetpack Compose 是用于构建原生 Android 界面的新工具包.它可简化并加快 Android 上的界面开发,使用更少的代码.强大的工具和直观的 Kotlin API,快速让应用生动而 ...
- app安卓开发教程!Kotlin可能带来的一个深坑,知乎上已获万赞
前言 从2010年开始Android市场开始需要大量的Android开发人员,招聘市场上也开始大量招Android开发人员,大量java开发者开始学习Android开发,招聘市场面试要求上只要有一定j ...
- 学不动了,尝试用Android Jetpack Compose重写微信经典飞机大战游戏
前段时间看了TechMerger大佬写的<一气呵成:用Compose完美复刻Flappy Bird!>,甚是有趣,按耐不住那躁动的心,笔者决定跟随大佬的脚步通过写游戏的方式学习Jetpac ...
- arm linux 俄罗斯方块,基于ARM的俄罗斯方块游戏的开发教材.doc
学号: 常 州 大 学 毕业设计(论文) (2012届) 题 目 学 生 学 院 专业班级 校内指导教师 专业技术职务 校外指导老师 专业技术职务 二○ 基于ARM的俄罗斯方块游戏的开发 摘 要:随着 ...
- java俄罗斯方块ppt_基于Java俄罗斯方块游戏设计与开发PPT.ppt
基于Java俄罗斯方块游戏设计与开发PPT 基于Java的俄罗斯方块游戏的设计与实现 答 辩 人:xxx 学 号:201012120201 指导老师:xxx副教授 专 业:网络工程 学 院:信息科学与 ...
- Jetpack Compose入门详解(实时更新)
Jetpack Compose入门详解 前排提醒 前言(Compose是什么) 1.实战准备 一.优势与缺点 二.前四课 三.标准布局组件 1.Column 2.Row 3.Box 四.xml和com ...
- 【java毕业设计】基于java+swing+Eclipse的俄罗斯方块游戏GUI设计与实现(毕业论文+程序源码)——俄罗斯方块游戏
基于java+swing+Eclipse的俄罗斯方块游戏GUI设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+swing+Eclipse的俄罗斯方块游戏GUI设计与实现,文章末尾 ...
- 安卓开发实现俄罗斯方块游戏
1.实现方式 *使用安卓开发实现 *使用开发工具:AndroidStudio 2.样例展示 1.游戏开始样例 2.游戏进行中 3.游戏暂停 4.游戏结束 3.代码展示 * 1.结构说明:控制显示的xm ...
最新文章
- 深入JVM锁机制2-Lock
- 什么是JSON?我为什么要使用它?
- linux启动phpstudy,phpstudy启动不了解决方法
- 基于matlab_simulink汽车三自由度模型仿真
- 【Tools】VMware Workstation 15.5 Pro安装详解
- Common Lisp中调用R
- 开发无法复现是什么意思_我与你无法执迷不悟什么歌-我与你无法执迷不悟歌曲意思、出处、含义介绍...
- 百度英伟达联手推混合精度训练,同样性能只需一半内存 | 附论文
- 用java写 java1,1,2,4,7,13,24,44算法
- 出中的意思是什么_从里出来是什么意思
- 计算机表格函数公式在表格中人数,计算机办公自动化常用公式和函数在电子表格中的应用...
- Astronauts UVA - 1391(2-sat)
- [苹果开发者账号]04 申请苹果开发者账号 美国报税表
- React项目案例-影视资源网站
- 工程施工阶段成本变化
- 防火墙NAT综合实验——nat控制,豁免,远程,DMZ区域(带命令)
- 哪些场景N1 mode是disable状态
- 因ubuntu内核升级导致的显卡驱动问题的解决方案
- DW1000开发笔记(一)DW1000芯片概览
- SQLServer 查询分析器里大小写转换快捷键