kotlin快速实现一款小游戏,糖果雨来啦
前言
回想小时候,一到冬天就开始期盼着学校快点放寒假,期盼着快点过年。因为过年有放不完的鞭炮与吃不完的糖果,犹记得那时候我的口袋里总是充满着各式各样的糖果。今天就以糖果为主题,实现糖果雨来啦这个互动小游戏。
效果展示
开始引导页面 | 糖果收集页面 | 收集结束页面 |
---|---|---|
实现细节
具体实现其实也很简单,主要分为3块内容:
开始引导页面:提供开始按钮来告诉用户如何开始,3秒倒计时动画,让用户做好准备。
糖果收集页面:自动生成糖果并从上往下掉落,用户点击糖果完成收集(糖果消失 & 糖果收集总数加一)。
收集结束页面:告诉用户一共收集了多少糖果,提供再玩一次按钮入口。
引导动画
如果单单是一个静态页面,提供文字来提醒用户如何开始游戏,会略显单调,所以我加了一些自定义View动画,模拟点击动作,来达到提醒用户作用。
利用三个动画组合在一起同时执行,从达到该效果,分别是:
手指移动去点击动画。
点击后的水波纹动画。
点击后糖果
+1
动画。
这里我们以 点击后糖果+1动画 举例。
我们先建一个res/anim/candy_add_anim.xml
文件,如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="3000"
android:fromAlpha="0.0"
android:repeatCount="-1"
android:repeatMode="restart"
android:toAlpha="1.0" />
<translate
android:duration="3000"
android:fromYDelta="0%"
android:interpolator="@android:anim/accelerate_interpolator"
android:repeatCount="-1"
android:repeatMode="restart"
android:toYDelta="-10%p" />
<scale
android:duration="3000"
android:fromXScale="0"
android:fromYScale="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:repeatMode="restart"
android:toXScale="1"
android:toYScale="1" />
</set>
然后在指定的View
中执行该动画,如下:
binding.candyAddOneTv.apply {
val animation = AnimationUtils.loadAnimation(context, R.anim.candy_add_anim)
startAnimation(animation)
}
糖果的生成
从效果展示图中也可以看出,糖果的样式是各式各样的且其位置坐标是随机的。
我通过代码动态生成一个大小固定的TextView
,然后通过设置layoutParams.setMargins
来确定其坐标,通过setBackground(drawable)
来设置糖果背景(为了使生成的糖果是各式各样的,所以我找了一些糖果的SVG
图来作为背景),然后加入到View.root
。
具体代码如下:
//随机生成X坐标
val leftMargin = (0..(getScreenWidth() - 140)).random()
TextView(this).apply {
layoutParams = FrameLayout.LayoutParams(140, 140).apply {
setMargins(leftMargin, -140, 0, 0)
}
background = ContextCompat.getDrawable(this@MainActivity, generateRandomCandy())
binding.root.addView(this)
}
并且通过协程delay(250)
,来达到一秒钟生成4颗糖果。
fun generatePointViewOnTime() {
viewModelScope.launch {
for (i in 1..60) {
Log.e(TAG, "generatePointViewOnTime: i = $i")
pointViewLiveData.value = i
if (i % 4 == 0) {
countDownTimeLiveData.postValue(i / 4)
}
delay(250)
}
}
}
糖果的掉落
介绍完了糖果的生成,接着就是糖果的掉落效果实现。
这里我们同样使用View动画
即可完成,通过translationY(getScreenHeight().toFloat() + 200)
来让糖果从最上方平移出屏幕最下方,同时为其设置加速插值器,达到掉落速度越来越快的效果。
整个平移时间设置为3s,具体代码如下:
private fun startMoving(view: View) {
view.apply {
animate().apply {
interpolator = AccelerateInterpolator()
duration = 3000
translationY(getScreenHeight().toFloat() + 200)
start()
}
}
}
糖果的收集
点击糖果,糖果消失,糖果收集总数+1。所以我们只需为其设置点击监听器,在用户点击时,为TextView
设置visibility
以及catchNumber++
即可。
TextView(this).apply {
···略···
setOnClickListener {
this.visibility = View.GONE
Log.e(TAG, "onCreate: tag = ${it.tag}, id = ${it.id}")
catchNumber++
binding.catchNumberTv.text = getString(R.string.catch_number, catchNumber)
doVibratorEffect()
}
}
点击反馈
为了更好的用户体验,为点击设置震动反馈效果。
private fun doVibratorEffect() {
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager =
getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(30, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(30)
}
}
结束弹窗
当糖果收集结束后,弹出一个结束弹窗来告诉用户糖果收集情况,这里采用属性动画,让弹窗弹出的效果更加的生动。
private fun showAnimation(view: View) {
view.scaleX = 0F
view.scaleY = 0F
//zoom in 放大;zoom out 缩小;normal 恢复正常
val zoomInHolderX = PropertyValuesHolder.ofFloat("scaleX", 1.05F)
val zoomInHolderY = PropertyValuesHolder.ofFloat("scaleY", 1.05F)
val zoomOutHolderX = PropertyValuesHolder.ofFloat("scaleX", 0.8F)
val zoomOutHolderY = PropertyValuesHolder.ofFloat("scaleY", 0.8F)
val normalHolderX = PropertyValuesHolder.ofFloat("scaleX", 1F)
val normalHolderY = PropertyValuesHolder.ofFloat("scaleY", 1F)
val zoomIn = ObjectAnimator.ofPropertyValuesHolder(
view,
zoomInHolderX,
zoomInHolderY
)
val zoomOut = ObjectAnimator.ofPropertyValuesHolder(
view,
zoomOutHolderX,
zoomOutHolderY
)
zoomOut.duration = 400
val normal = ObjectAnimator.ofPropertyValuesHolder(
view,
normalHolderX,
normalHolderY
)
normal.duration = 500
val animatorSet = AnimatorSet()
animatorSet.playSequentially(zoomIn, zoomOut, normal)
animatorSet.start()
}
总结
如果你对该小游戏有兴趣,想进一步了解一下代码,可以参考Github Candy-Catch,欢迎你给我点个小星星。
相信很多人都有这样的感受,随着年龄的增加,越来越觉得这年味越来越淡了,随之而来对过年的期盼度也是逐年下降。在这里,我愿大家童心未泯,归来仍是少年!
最后,给大家拜个早年,祝大家新春快乐!
其实分享文章的最大目的正是等待着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。
另外,如果你觉得文章不错,对你有所帮助,请给我点个赞,就当鼓励,谢谢~Peace~!
作者:Jere_Chen
来源:juejin.cn/post/7054194708410531876