在 9 月 30號更新的 OpenHarmony3.0 LTS 上,標(biāo)準(zhǔn)系統(tǒng)新增支持了方舟開發(fā)框架(ArkUI)、分布式組網(wǎng)和 FA 跨設(shè)備遷移能力等新特性,因此我們結(jié)合了這三種特性使用 ets 開發(fā)了一款如下圖所示傳炸彈應(yīng)用。
打開應(yīng)用在通過邀請用戶進(jìn)行設(shè)備認(rèn)證后,用戶須根據(jù)提示完成相應(yīng)操作,然后通過分布式流轉(zhuǎn)實(shí)現(xiàn)隨機(jī)傳遞炸彈給下一位用戶的效果。
那么這樣一款傳炸彈應(yīng)用如何進(jìn)行開發(fā)呢?完整的項(xiàng)目結(jié)構(gòu)目錄如下:
├─entry │└─src │└─main ││config.json//應(yīng)用配置 ││ │├─ets ││└─MainAbility │││app.ets//ets應(yīng)用程序主入口 │││ ││└─pages ││CommonLog.ets//日志類 ││game.ets//游戲首頁 ││RemoteDeviceManager.ets//設(shè)備管理類 ││ │└─resources//靜態(tài)資源目錄 │├─base ││├─element │││ ││├─graphic ││├─layout ││├─media//存放媒體資源 │││ ││└─profile │└─rawfile
我們可以分為如下 3 步:
編寫聲明式 UI 界面
添加分布式能力
編寫游戲邏輯
編寫聲明式UI 界面
①新增工程
在 DevEco Studio 中點(diǎn)擊 File→New Project→Standard Empty Ability→Next,Language 選擇 ETS 語言,最后點(diǎn)擊 Finish 即創(chuàng)建成功。
②編寫游戲頁面
效果圖如下可以分為兩部分:
頂部狀態(tài)提示欄:
首先在 @entry 組件入口 build() 中使用 Stack 作為容器,達(dá)到圖片和文字堆疊的效果。
接著依次寫入 Image 包裹的兩個 Text 組件。
Stack(){ Image($r("app.media.title")).objectFit(ImageFit.Contain).height(120) Column(){ Text(this.duration.toString()+'ms').fontColor(Color.White) Text(this.touchText).fontColor(Color.White) } }
中間游戲炸彈九宮格區(qū)域:
使用 Grid 網(wǎng)格容器來編寫九宮格區(qū)域。
在 GridItem 中 Stack (容器依次添加方塊背景圖片和炸彈圖片。
在 visibility 屬性中用 bombIndex 變量值來決定炸彈顯示的位置。
通過 onClick 點(diǎn)擊事件和 GestureGroup 組合手勢加入單擊、雙擊和長按的監(jiān)聽事件。
Stack(){ Image($r("app.media.background")).objectFit(ImageFit.Contain) Grid(){ ForEach(this.grid,(item)=>{ GridItem(){ Stack(){ Image($r("app.media.squares")).objectFit(ImageFit.Contain) Image($r("app.media.bomb")) .width('50%') .objectFit(ImageFit.Contain) .visibility(this.bombIndex==item?Visibility.Visible:Visibility.Hidden) //炸彈點(diǎn)擊事件 .onClick((event)=>{ //單擊 this.judgeGame(RuleType.click) }) .gesture( GestureGroup(GestureMode.Exclusive, LongPressGesture({repeat:false}) .onAction((event:GestureEvent)=>{ //長按 this.judgeGame(RuleType.longPress) }), TapGesture({count:2}) .onAction(()=>{ //雙擊 this.judgeGame(RuleType.doubleClick) }) ) } }.forceRebuild(false) },item=>item) } .columnsTemplate('1fr1fr1fr') .rowsTemplate('1fr1fr1fr') .columnsGap(10) .rowsGap(10) .width('90%') .height('75%') }.width('80%').height('70%')③添加彈窗
創(chuàng)建規(guī)則游戲彈窗:通過@CustomDialog 裝飾器來創(chuàng)建自定義彈窗,使用方式可參考《自定義彈窗文檔》:
https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-methods-custom-dialog-box.md
規(guī)則彈窗效果如下,彈窗組成由兩個 Text 和兩個 Image 豎向排列組成,所以我們可以在 build() 下使用 Column 容器來包裹。
組件代碼如下:
@CustomDialog structRuleDialog{ controller:CustomDialogController confirm:()=>void invite:()=>void @ConsumedeviceList:RemoteDevice[] build(){ Column(){ Text('游戲規(guī)則').fontSize(30).margin(20) Text('炸彈會隨機(jī)出現(xiàn)在9個方塊內(nèi),需要在規(guī)定時間內(nèi)完成指定操作(點(diǎn)擊、雙擊或長按),即可將炸彈傳遞給下一個人,小心炸彈可是會越來越快的喔!') .fontSize(24).margin({bottom:10}) Image($r("app.media.btn_start")).objectFit(ImageFit.Contain).height(80).margin(10) .onClick(()=>{ console.info(TAG+'Clickstartgame') if(checkTrustedDevice(this.remoteDeviceModel)){ this.controller.close() this.confirm() } }) Image($r("app.media.btn_Invite")).objectFit(ImageFit.Contain).height(80).margin(10) .onClick(()=>{ this.invite() }) }.width('90%') .margin(20) .backgroundColor(Color.White) } }
在 @entry 創(chuàng)建 CustomDialogController 對象并傳入彈窗所需參數(shù),后面可通過該對象 open() 和 close() 方法進(jìn)行打開和關(guān)閉彈窗。
@ProvidedeviceList:RemoteDevice[]=[] privateruleDialog:CustomDialogController=newCustomDialogController({ builder:RuleDialog({ invite:()=>this.InvitePlayer(), confirm:()=>this.startGame(), deviceList:this.deviceList }), autoCancel:false })
創(chuàng)建游戲失敗彈窗,并添加動畫效果,如下圖:
編寫彈窗布局:將游戲失敗文本、炸彈圖片和再來一局按鈕圖片放置于 Column 容器中。
用變量來控制動畫起始和結(jié)束的位置:用 Flex 容器包裹炸彈圖片,并用 @State 裝飾變量 toggle,通過變量來動態(tài)修改 [Flex] 的 direction 屬性。
@Statetoggle:boolean=true privatecontroller:CustomDialogController @ConsumedeviceList:RemoteDevice[] privateconfirm:()=>void privateinterval=null build(){ Column(){ Text('游戲失敗').fontSize(30).margin(20) Flex({ direction:this.toggle?FlexDirection.Column:FlexDirection.ColumnReverse, alignItems:ItemAlign.Center }) { Image($r("app.media.bomb")).objectFit(ImageFit.Contain).height(80) }.height(200) Image($r("app.media.btn_restart")).objectFit(ImageFit.Contain).height(120).margin(10) .onClick(()=>{ this.controller.close() this.confirm() }) } .width('80%') .margin(50) .backgroundColor(Color.White) }
設(shè)置動畫效果:使用 animateTo 顯式動畫接口炸彈位置切換時添加動畫,并且設(shè)置定時器定時執(zhí)行動畫。
aboutToAppear(){ this.setBombAnimate() } setBombAnimate(){ letfun=()=>{ this.toggle=!this.toggle; } this.interval=setInterval(()=>{ animateTo({duration:1500,curve:Curve.Sharp},fun) },1600) }
添加分布式流轉(zhuǎn)
分布式流轉(zhuǎn)需要在同一網(wǎng)絡(luò)下通過 DeviceManager 組件進(jìn)行設(shè)備間發(fā)現(xiàn)和認(rèn)證。
獲取到可信設(shè)備的 deviceId 調(diào)用 FeatureAbility.startAbility(parameter),即可把應(yīng)用程序流轉(zhuǎn)到另一設(shè)備。
原本分布式流轉(zhuǎn)應(yīng)用流程如下:
創(chuàng)建 DeviceManager 實(shí)例。
調(diào)用實(shí)例的 startDeviceDiscovery(),開始設(shè)備發(fā)現(xiàn)未信任設(shè)備。
設(shè)置設(shè)備狀態(tài)監(jiān)聽 on('deviceStateChange',callback),監(jiān)聽設(shè)備上下線狀態(tài)。
設(shè)置設(shè)備狀態(tài)監(jiān)聽 on('deviceFound',callback),監(jiān)聽設(shè)備發(fā)現(xiàn)。
傳入未信任設(shè)備參數(shù),調(diào)用實(shí)例 authenticateDevice 方法,對設(shè)備進(jìn)行 PIN 碼認(rèn)證。
若是已信任設(shè)備,可通過實(shí)例的 getTrustedDeviceListSync() 方法來獲取設(shè)備信息。
將設(shè)備信息中的 deviceId 傳入featureAbility.startAbility 方法,實(shí)現(xiàn)流轉(zhuǎn)。
流轉(zhuǎn)接收方可通過 featureAbility.getWant() 獲取到發(fā)送方攜帶的數(shù)據(jù)。
注銷設(shè)備發(fā)現(xiàn)監(jiān)聽 off('deviceFound')。
注銷設(shè)備狀態(tài)監(jiān)聽 off('deviceStateChange')。
項(xiàng)目中將上面設(shè)備管理封裝至 RemoteDeviceManager,通過 RemoteDeviceManager 的四個方法來動態(tài)維護(hù) deviceList 設(shè)備信息列表。
項(xiàng)目實(shí)現(xiàn)分布式流轉(zhuǎn)只需如下流程:
①創(chuàng)建 RemoteDeviceManager 實(shí)例
導(dǎo)入 RemoteDeviceManager:
import{RemoteDeviceManager}from'./RemoteDeviceManager'
聲明 @Provide 裝飾的設(shè)備列表變量 deviceList,和創(chuàng)建 RemoteDeviceManager 實(shí)例。
@ProvidedeviceList:RemoteDevice[]=[] privateremoteDm:RemoteDeviceManager=newRemoteDeviceManager(this.deviceList)
②刷新設(shè)備列表
在生命周期 aboutToAppear 中,調(diào)用刷新設(shè)備列表和開始發(fā)現(xiàn)設(shè)備。
aboutToAppear 定義:函數(shù)在創(chuàng)建自定義組件的新實(shí)例后,在執(zhí)行其 build 函數(shù)之前執(zhí)行。
aboutToAppear(){ this.remoteDm.refreshRemoteDeviceList()//刷新設(shè)備列表 this.remoteDm.startDeviceDiscovery()//開始發(fā)現(xiàn)設(shè)備 }
③設(shè)備認(rèn)證
代碼如下:
invitePlayer(remoteDevice:RemoteDevice){ if(remoteDevice.status==RemoteDeviceStatus.ONLINE){ prompt.showToast({message:"Alreadyinvited!"}) return } this.remoteDm.authDevice(remoteDevice).then(()=>{ prompt.showToast({message:"Invitesuccess!deviceName="+remoteDevice.deviceName}) }).catch(()=>{ prompt.showToast({message:"Invitefail!"}) }) }
④跨設(shè)備流轉(zhuǎn)
從 deviceList 中獲取設(shè)備列表在線的設(shè)備 Id,通過 featureAbility.startAbility 進(jìn)行流轉(zhuǎn)。
asyncstartAbilityRandom(){ letdeviceId=this.getRandomDeviceId()//隨機(jī)獲取設(shè)備id CommonLog.info('featureAbility.startAbilitydeviceId='+deviceId); letbundleName=awaitgetBundleName() letwantValue={ bundleName:bundleName, abilityName:'com.sample.bombgame.MainAbility', deviceId:deviceId, parameters:{ ongoing:true, transferNumber:this.transferNumber+1 } }; featureAbility.startAbility({ want:wantValue }).then((data)=>{ CommonLog.info('featureAbility.startAbilityfinished,'+JSON.stringify(data)); featureAbility.terminateSelf((error)=>{ CommonLog.info('terminateSelffinished,error='+error); }); }); }⑤注銷監(jiān)聽
在聲明周期 aboutToDisappear 進(jìn)行注銷監(jiān)聽。
aboutToDisappear 定義:函數(shù)在自定義組件析構(gòu)消耗之前執(zhí)行。
aboutToDisappear(){ this.remoteDm.stopDeviceDiscovery()//注銷監(jiān)聽 }
編寫游戲邏輯
①開始游戲
代碼如下:
startGame(){ CommonLog.info('startGame'); this.randomTouchRule()//隨機(jī)游戲點(diǎn)擊規(guī)則 this.setRandomBomb()//隨機(jī)生成炸彈位置 this.stopCountDown()//停止倒計(jì)時 if(this.transferNumber10)?{ ????this.duration?=?3000?-?this.transferNumber?*?100 ??}?else?{ ????this.duration?=?2000 ??} ??const?interval:?number?=?500 ??//?開始倒計(jì)時 ??this.timer?=?setInterval(()?=>{ if(this.duration<=?interval)?{ ??????this.duration?=?0 ??????clearInterval(this.timer) ??????this.timer?=?null ??????this.gameFail() ????}?else?{ ??????this.duration?-=?interval ????} ??},?interval) }
②判斷輸贏
編寫判斷邏輯,用于不同的點(diǎn)擊事件中調(diào)用。
/** *判斷游戲輸贏 *@paramoperation點(diǎn)擊類型 */ judgeGame(operation:RuleType){ this.stopCountDown() if(operation!=this.ruleText){ this.gameFail() }else{ prompt.showToast({message:"finish"}) this.bombIndex=-1 this.startAbilityRandom() } }
③游戲失敗
游戲失敗,彈出游戲失敗彈框。
gameFail(){ prompt.showToast({ message:'GameFail' }) CommonLog.info('gameFail'); this.gameFailDialog.open() }
項(xiàng)目下載和導(dǎo)入
項(xiàng)目倉庫地址:
https://gitee.com/openharmony-sig/knowledge_demo_temp/tree/master/FA/Entertainment/BombGame
①git 下載:
gitclonehttps://gitee.com/openharmony-sig/knowledge_demo_temp.git
②項(xiàng)目導(dǎo)入:打開 DevEco Studio,點(diǎn)擊 File→Open→下載路徑/FA/Entertainment/BombGame。
約束與限制
①設(shè)備編譯約束
獲取 OpenHarmony 源碼(OpenHarmony 版本須 3.0LTS 以上):
https://www.openharmony.cn/pages/0001000202/#%E5%AE%89%E8%A3%85%E5%BF%85%E8%A6%81%E7%9A%84%E5%BA%93%E5%92%8C%E5%B7%A5%E5%85%B7
安裝開發(fā)板環(huán)境:
https://www.openharmony.cn/pages/0001000400/#hi3516%E5%B7%A5%E5%85%B7%E8%A6%81%E6%B1%82
開發(fā)板燒錄:
https://www.openharmony.cn/pages/0001000401/#%E4%BD%BF%E7%94%A8%E7%BD%91%E5%8F%A3%E7%83%A7%E5%BD%95
②應(yīng)用編譯約束
參考《應(yīng)用開發(fā)快速入門》:
https://www.openharmony.cn/pages/00090000/
集成開發(fā)環(huán)境:DevEco Studio 3.0.0.601 版本以上,下載地址:
https://developer.harmonyos.com/cn/develop/deveco-studio#download_beta
OpenHarmony SDK 3.0.0.0 以上。
作者:林嘉成 OpenAtom OpenHarmony
原文標(biāo)題:OpenHarmony上玩“傳炸彈”小游戲
文章出處:【微信公眾號:HarmonyOS技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
容器
+關(guān)注
關(guān)注
0文章
499瀏覽量
22122 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3747瀏覽量
16588
原文標(biāo)題:OpenHarmony上玩“傳炸彈”小游戲
文章出處:【微信號:gh_834c4b3d87fe,微信公眾號:OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論