一、簡(jiǎn)介
繼《OpenHarmony有氧拳擊設(shè)備端的開發(fā)》后,本次為大家?guī)砜犰诺膽?yīng)用端開發(fā)。如下視頻,開發(fā)者伴隨著音樂,律動(dòng)出拳后,那開發(fā)板屢屢播放“挨打”效果,這究竟是怎么一回事?讓我們一探背后原理。 這款拳擊游戲開始時(shí)會(huì)播放音樂,然后以隨機(jī)速度下落“擊拳方塊”。當(dāng)小哥哥在擊拳區(qū)域內(nèi)揮拳時(shí),游戲會(huì)判斷方塊的位置,根據(jù)不同位置確定播放普通擊中或完美擊中的動(dòng)畫效果。

二、動(dòng)畫
游戲中一共使用兩種動(dòng)畫:屬性動(dòng)畫和Lottie動(dòng)畫,分別實(shí)現(xiàn)下落和擊中的效果?!皳羧綁K”下落效果是利用屬性動(dòng)畫進(jìn)行修改偏移量來實(shí)現(xiàn)。游戲同時(shí)設(shè)置定時(shí)器,定時(shí)獲取揮拳狀態(tài)和“擊拳方塊”的所處位置,用于判斷當(dāng)前揮拳是否得分。若得分則根據(jù)擊中區(qū)間來播放不同效果的Lottie動(dòng)畫。
三、“下落”動(dòng)畫

1、屬性動(dòng)畫介紹
從上圖可以看到,游戲中“擊拳方塊”是自上而下勻速移動(dòng)。這種簡(jiǎn)單控制通用屬性進(jìn)行動(dòng)畫變化的動(dòng)畫,便很適合使用屬性動(dòng)畫來實(shí)現(xiàn)。屬性動(dòng)畫是指組件的通用屬性發(fā)生變化時(shí),會(huì)根據(jù)開始狀態(tài)和通用屬性改變后的狀態(tài)作為動(dòng)畫關(guān)鍵幀,在指定時(shí)間內(nèi)實(shí)現(xiàn)漸變效果。換言之我們只需要確定設(shè)置好組件在動(dòng)畫結(jié)束時(shí)的組件屬性,然后調(diào)用animateTo(value: AnimationOptions, event: ()=> void),即可創(chuàng)建屬性動(dòng)畫。
AnimationOptions對(duì)象說明
● 屬性

●接口

2、動(dòng)畫實(shí)現(xiàn)
編寫“擊拳方塊”UI組件,并將組件的相對(duì)布局偏移量offset屬性,綁定到@state leftY1變量中,那么通過修改leftY1的值即可實(shí)現(xiàn)修改組件所在位置。
@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() { // ..... // 左側(cè) this.LeftBoxing(this.leftY1) // .....}

3、創(chuàng)建動(dòng)畫
調(diào)用animateTo顯式動(dòng)畫來播放動(dòng)畫實(shí)現(xiàn)單個(gè)“擊拳方塊”自上而下地移動(dòng),再通過設(shè)置delay參數(shù)實(shí)現(xiàn)4個(gè)“擊拳方塊”按順序分別移動(dòng)。
async leftAnimate(){ // 設(shè)置圖標(biāo)下滑動(dòng)畫 // 動(dòng)畫持續(xù)時(shí)間 let leftDuration = this.getRandomDuration() this.leftDuration = leftDuration // 延遲時(shí)長 let leftDelay = leftDuration / (this.boxingCount - 1) // 設(shè)置開始時(shí)間 let now = new Date this.animateLeftTimestamp = now.getTime() // 左側(cè)animateTo動(dòng)畫 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、設(shè)置擊中區(qū)域監(jiān)聽
設(shè)置定時(shí)器定時(shí)查詢當(dāng)前是否揮拳,若檢測(cè)到揮拳再通過計(jì)算當(dāng)前動(dòng)畫運(yùn)行時(shí)間來判斷“擊拳方塊”位置,從而執(zhí)行擊中或完美擊中的邏輯,以下為監(jiān)聽邏輯。
setScoreListen(){ this.intervalNumber = setInterval(async()=>{ let res = await BoxingGameNAPI.recvMsg(); if(res?.message.length > 0){ if(res.message.includes('left') && !this.leftAnimateLock){ // 檢測(cè)到左手揮拳 this.judgeLeft() } } },200) }
judgeLeft(){ let nowTime = new Date().getTime() // 首次抵達(dá)目標(biāo)頂部時(shí)間 let firstTime = this.animateLeftTimestamp + (this.percentToPoint(this.targetOffsetY)+this.percentToPoint('50%') - this.percentToPoint('10%')) * this.leftDuration // 結(jié)束時(shí)間 let endTime = this.animateLeftTimestamp + this.leftDuration * 2 if(nowTime > firstTime - 200 && nowTime < endTime){ // 得分時(shí)間界限 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{ // 未抵達(dá)區(qū)域 }????}
四、擊中動(dòng)畫

像前文提到的“下落”動(dòng)畫適合使用屬性動(dòng)畫,那么當(dāng)我們需要實(shí)現(xiàn)更復(fù)雜,如上圖的動(dòng)畫效果時(shí),該如何來實(shí)現(xiàn)呢?
Lottie介紹
Lottie是一款能夠?yàn)閼?yīng)用程序添加動(dòng)畫的開源組件,它可以解析AE(After Effects)導(dǎo)出的json文件,讓復(fù)雜的動(dòng)畫資源輕松運(yùn)行在應(yīng)用程序中。如圖所示,動(dòng)畫文件通過AE的bodymovin插件將動(dòng)畫轉(zhuǎn)換成通用的json格式描述文件后,應(yīng)用開發(fā)者只需使用Lottie解析json文件,就能將動(dòng)畫繪制出來。
Lottie優(yōu)點(diǎn):
1. 只需使用Lottie解析json文件就能實(shí)現(xiàn)動(dòng)畫的加載,基本上實(shí)現(xiàn)了0代碼開發(fā);
2. 應(yīng)用開發(fā)者可以通過修改json文件的參數(shù),將動(dòng)畫運(yùn)行到不同的應(yīng)用程序中,實(shí)現(xiàn)動(dòng)畫的一次設(shè)計(jì)多端使用;
3. 應(yīng)用開發(fā)者可從網(wǎng)絡(luò)如https://lottiefiles.com/直接下載json文件,實(shí)時(shí)更新動(dòng)畫資源;
4. Lottie基于canvas畫布進(jìn)行基礎(chǔ)的2D渲染,讓動(dòng)畫流暢度更高;
5. Lottie可以將UX設(shè)計(jì)師給出的復(fù)雜動(dòng)畫效果100%還原到應(yīng)用程序中 ;
6. Lottie提供了豐富的API,讓開發(fā)者能輕松控制動(dòng)畫,大大提高了開發(fā)效率。
如何使用Lottie?
1.導(dǎo)入Lottie
在Terminal窗口使用npm install @ohos/lottieETS命令下載Lottie,并在頁面中導(dǎo)入@ohos/lottieETS,如下:
importlottiefrom'@ohos/lottieETS'
放置動(dòng)畫資源
將After Effects導(dǎo)出的json動(dòng)畫資源文件保存到項(xiàng)目common/lottie路徑中,具體路徑如下:entry/src/main/ets/MainAbility/common/lottie/animation.json

2. 創(chuàng)建Lottie動(dòng)畫
Lottie基于canvas畫布進(jìn)行基礎(chǔ)的2D渲染,創(chuàng)建canvas畫布后設(shè)置相關(guān)播放參數(shù)即可創(chuàng)建并播放Lottie動(dòng)畫,Lottie更多信息可參考Lottie接口。
創(chuàng)建canvas畫布:
@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)}
設(shè)置Lottie動(dòng)畫參數(shù):
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)}
在“下落”動(dòng)畫擊拳監(jiān)聽中加入播放不同效果的Lottie動(dòng)畫邏輯:
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') // 播放完美擊中動(dòng)畫 // 等動(dòng)畫執(zhí)行完成后才能進(jìn)入下一次揮拳判定 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')// 播放擊中動(dòng)畫 this.leftAnimateLock = true setTimeout(()=>{ lottie.stop() lottie.destroy('animate_left') this.leftAnimateLock = false },this.lottieDuration) } }}
五、總結(jié)
本文主要講述了拳擊互動(dòng)游戲中,如何使用屬性動(dòng)畫實(shí)現(xiàn)簡(jiǎn)單屬性變化的動(dòng)畫效果,如游戲中“擊拳方塊”自上往下移動(dòng);使用Lottie組件實(shí)現(xiàn)復(fù)雜絢麗的動(dòng)畫效果,如游戲中的擊拳效果。
本樣例是OpenHarmony知識(shí)體系工作組(相關(guān)鏈接在文章末尾)為廣大開發(fā)者分享的樣例。知識(shí)體系工作組結(jié)合日常生活,給開發(fā)者規(guī)劃了各種場(chǎng)景的Demo樣例,如智能家居場(chǎng)景、影音娛樂場(chǎng)景、運(yùn)動(dòng)健康場(chǎng)景等。歡迎廣大開發(fā)者一同參與OpenHarmony的開發(fā),更加完善樣例,相互學(xué)習(xí),相互進(jìn)步。
-
定時(shí)器
+關(guān)注
關(guān)注
23文章
3375瀏覽量
124683 -
動(dòng)畫
+關(guān)注
關(guān)注
0文章
20瀏覽量
8801 -
OpenHarmony
+關(guān)注
關(guān)注
33文章
3977瀏覽量
21384
原文標(biāo)題:OpenHarmony有氧拳擊之應(yīng)用端開發(fā)
文章出處:【微信號(hào):gh_e4f28cfa3159,微信公眾號(hào):OpenAtom OpenHarmony】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
HarmonyOS開發(fā)案例:【動(dòng)效】
分享---儲(chǔ)能UI界面能量流動(dòng)動(dòng)畫實(shí)現(xiàn)方法
LabVIEW中動(dòng)畫實(shí)現(xiàn)的一種新方法
Scratch實(shí)現(xiàn)一個(gè)按鈕的動(dòng)畫效果
HarmonyOS Lottie組件,讓動(dòng)畫繪制更簡(jiǎn)單
基于HarmonyOS實(shí)現(xiàn)的電池充電動(dòng)畫效果
OpenHarmony有氧拳擊之應(yīng)用端開發(fā)
HarmonyOS/OpenHarmony應(yīng)用開發(fā)-屬性動(dòng)畫
HarmonyOS屬性動(dòng)畫開發(fā)示例(ArkTS)
QML中的動(dòng)畫設(shè)計(jì),太“難”了
在QML動(dòng)畫設(shè)計(jì)中通過指定關(guān)鍵幀創(chuàng)建時(shí)間線動(dòng)畫
鴻蒙開發(fā)之發(fā)動(dòng)畫篇
OpenHarmony實(shí)戰(zhàn)開發(fā)-如何實(shí)現(xiàn)組件動(dòng)畫。
HarmonyOS開發(fā)案例:【自定義下拉刷新動(dòng)畫】
如何使用屬性動(dòng)畫實(shí)現(xiàn)簡(jiǎn)單屬性變化的動(dòng)畫效果
評(píng)論