一、简介
继《OpenHarmony有氧拳击设备端的开发》后,本次为大家带来酷炫的应用端开发。如下,开发者伴随着音乐,律动出拳后,那开发板屡屡播放“挨打”效果,这究竟是怎么一回事?让我们一探背后原理。
这款拳击游戏开始时会播放音乐,然后以随机速度下落“击拳方块”。当小哥哥在击拳区域内挥拳时,游戏会判断方块的位置,根据不同位置确定播放普通击中或完美击中的动画效果。
二、动画
游戏中一共使用两种动画:属性动画和Lottie动画,分别实现下落和击中的效果。“击拳方块”下落效果是利用属性动画进行修改偏移量来实现。游戏同时设置定时器,定时获取挥拳状态和“击拳方块”的所处位置,用于判断当前挥拳是否得分。若得分则根据击中区间来播放不同效果的Lottie动画。
三、“下落”动画
1、属性动画介绍
从上图可以看到,游戏中“击拳方块”是自上而下匀速移动。这种简单控制通用属性进行动画变化的动画,便很适合使用属性动画来实现。属性动画是指组件的通用属性发生变化时,会根据开始状态和通用属性改变后的状态作为动画关键帧,在指定时间内实现渐变效果。换言之我们只需要确定设置好组件在动画结束时的组件属性,然后调用animateTo(value: AnimationOptions, event: ()=> void),即可创建属性动画。
AnimationOptions对象说明
● 属性
● 接口
2、动画实现
编写“击拳方块”UI组件,并将组件的相对布局偏移量offset属性,绑定到@state leftY1变量中,那么通过修改leftY1的值即可实现修改组件所在位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @State leftY1: string = '50%' @Builder LeftBoxing(offsetY: string ) { Image($r( 'app.media.icon_boxing_left' )) .width(144) .height(110) .offset({ x: "-30%" , y: offsetY }) .touchable( true ) } build() { Stack() { // ..... // 左侧 this .LeftBoxing( this .leftY1) // ..... } |
3、创建动画
调用animateTo显式动画来播放动画实现单个“击拳方块”自上而下地移动,再通过设置delay参数实现4个“击拳方块”按顺序分别移动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | async leftAnimate(){ // 设置图标下滑动画 // 动画持续时间 let leftDuration = this .getRandomDuration() this .leftDuration = leftDuration // 延迟时长 let leftDelay = leftDuration / ( this .boxingCount - 1) // 设置开始时间 let now = new Date this .animateLeftTimestamp = now.getTime() // 左侧animateTo动画 animateTo({ duration: leftDuration, curve: Curve.Linear,delay:0 * leftDelay ,iterations: 1 }, () => { this .leftY1 = "50%" }) animateTo({ duration: leftDuration, curve: Curve.Linear,delay:1 * leftDelay, iterations: 1 }, () => { this .leftY2 = "50%" }) animateTo({ duration: leftDuration, curve: Curve.Linear,delay:2 * leftDelay, iterations: 1 }, () => { this .leftY3 = "50%" }) animateTo({ duration: leftDuration, curve: Curve.Linear,delay:3 * leftDelay, iterations: 1 }, () => { this .leftY4 = "50%" }) let totalTime = leftDuration + 3 * leftDelay await this .sleep(totalTime) this .resetAnimate( true ) this .leftAnimate() } |
4、设置击中区域监听
设置定时器定时查询当前是否挥拳,若检测到挥拳再通过计算当前动画运行时间来判断“击拳方块”位置,从而执行击中或完美击中的逻辑,以下为监听逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | setScoreListen(){ this .intervalNumber = setInterval(async()=>{ let res = await BoxingGameNAPI.recvMsg(); if (res?.message.length > 0){ if (res.message.includes( 'left' ) && ! this .leftAnimateLock){ // 检测到左手挥拳 this .judgeLeft() } } },200) } judgeLeft(){ let nowTime = new Date().getTime() // 首次抵达目标顶部时间 let firstTime = this .animateLeftTimestamp + ( this .percentToPoint( this .targetOffsetY)+ this .percentToPoint( '50%' ) - this .percentToPoint( '10%' )) * this .leftDuration // 结束时间 let endTime = this .animateLeftTimestamp + this .leftDuration * 2 if (nowTime > firstTime - 200 && nowTime < endTime){ // 得分时间界限 let leftDelay = this .leftDuration /( this .boxingCount -1 ) let handleTime = (nowTime - firstTime) % leftDelay let judgeTime = this .leftDuration /6 CommonLog.info(TAG,`leftDelay:${leftDelay},handleTime:${handleTime},judgeTime:${judgeTime}`) // 完美击中 if (judgeTime/4 < handleTime && handleTime < (judgeTime *(3/4))) { } else if (handleTime < judgeTime){ // 普通击中 } else { // 不得分 } } else { // 未抵达区域 } } |
四、击中动画
像前文提到的“下落”动画适合使用属性动画,那么当我们需要实现更复杂,如上图的动画效果时,该如何来实现呢?
Lottie介绍
Lottie是一款能够为应用程序添加动画的开源组件,它可以解析AE(After Effects)导出的json文件,让复杂的动画资源轻松运行在应用程序中。如图所示,动画文件通过AE的bodymovin插件将动画转换成通用的json格式描述文件后,应用开发者只需使用Lottie解析json文件,就能将动画绘制出来。
Lottie优点:
1. 只需使用Lottie解析json文件就能实现动画的加载,基本上实现了0代码开发;
2. 应用开发者可以通过修改json文件的参数,将动画运行到不同的应用程序中,实现动画的一次设计多端使用;
3. 应用开发者可从网络如https://lottiefiles.com/直接下载json文件,实时更新动画资源;
4. Lottie基于canvas画布进行基础的2D渲染,让动画流畅度更高;
5. Lottie可以将UX设计师给出的复杂动画效果100%还原到应用程序中 ;
6. Lottie提供了丰富的API,让开发者能轻松控制动画,大大提高了开发效率。
如何使用Lottie?
1. 导入Lottie
在Terminal窗口使用npm install @ohos/lottieETS命令下载Lottie,并在页面中导入@ohos/lottieETS,如下:
1 2 | import lottie from '@ohos/lottieETS' |
放置动画资源
将After Effects导出的json动画资源文件保存到项目common/lottie路径中,具体路径如下:entry/src/main/ets/MainAbility/common/lottie/animation.json
2. 创建Lottie动画
Lottie基于canvas画布进行基础的2D渲染,创建canvas画布后设置相关播放参数即可创建并播放Lottie动画,Lottie更多信息可参考Lottie接口。
创建canvas画布:
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Builder TargetArea(controller:CanvasRenderingContext2D,lottieName: string ) { Stack() { Canvas(controller) .aspectRatio(1) .width(300) .offset({ y: this .targetOffsetY }) .onAppear(() => { }) Animator( '__lottie_ets' ) // declare Animator('__lottie_ets') when use lottie }.height( '100%' ).width(220) } |
设置Lottie动画参数:
1 2 3 4 5 6 7 8 9 10 11 12 | setLottie(controller:CanvasRenderingContext2D,lottieName: string ,animatePath: string ){ lottie.loadAnimation({ container: controller, renderer: 'canvas' , loop: false , autoplay: false , name: lottieName, path: animatePath, }) lottie.setSpeed(1,lottieName) } |
在“下落”动画击拳监听中加入播放不同效果的Lottie动画逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | judgeLeft(){ ...... if (nowTime > firstTime - 200 && nowTime < endTime){ ...... // 完美击中 if (judgeTime/4 < handleTime && handleTime < (judgeTime *(3/4))) { lottie.destroy( 'animate_left' ) this .setLottie( this .controllerLeft, 'animate_left' , this .animatePerfectPath) lottie.play( 'animate_left' ) // 播放完美击中动画 // 等动画执行完成后才能进入下一次挥拳判定 this .leftAnimateLock = true setTimeout(()=>{ lottie.stop() lottie.destroy( 'animate_left' ) this .leftAnimateLock = false }, this .lottieDuration) } else if (handleTime < judgeTime){ // 击中 lottie.destroy( 'animate_left' ) this .setLottie( this .controllerLeft, 'animate_left' , this .animateJustPath) lottie.play( 'animate_left' ) // 播放击中动画 this .leftAnimateLock = true setTimeout(()=>{ lottie.stop() lottie.destroy( 'animate_left' ) this .leftAnimateLock = false }, this .lottieDuration) } } } |
五、总结
本文主要讲述了拳击互动游戏中,如何使用属性动画实现简单属性变化的动画效果,如游戏中“击拳方块”自上往下移动;使用Lottie组件实现复杂绚丽的动画效果,如游戏中的击拳效果。
本样例是OpenHarmony知识体系工作组(相关链接在文章末尾)为广大开发者分享的样例。知识体系工作组结合日常生活,给开发者规划了各种场景的Demo样例,如智能家居场景、影音娱乐场景、运动健康场景等。欢迎广大开发者一同参与OpenHarmony的开发,更加完善样例,相互学习,相互进步。
六、参考链接
本样例代码下载链接
https://growing.openharmony.cn/mainPlay/detail?sampleId=4070
属性动画
Lottie
https://mp.weixin.qq.com/s/2adu8dNI9nedcNn4D4fjJw
OpenHarmony知识体系共建开发仓
https://gitee.com/openharmony-sig/knowledge/blob/master/docs/co-construct_demos/README_zh.md
OpenHarmony学习路径
https://growing.openharmony.cn/mainPlay/learnPath
小熊派BearPi-HM Nano开发板学习路径
https://growing.openharmony.cn/mainPlay/learnPathMaps?id=19
https://gitee.com/bearpi/bearpi-hm_nano/tree/master
润和DAYU200(RK3568)开发板介绍
https://growing.openharmony.cn/mainPlay/learnPathMaps?id=27
OpenHarmony有氧拳击之设备端开发
https://mp.weixin.qq.com/s/qVEFKid2gHsU34VearI7Gw
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析