暴打七夕青蛙—HarmonyOS服務(wù)卡片小游戲
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
前言
七夕節(jié),令我“幸福”的是,被熱心市民送來(lái)了七夕青蛙,聽(tīng)取一天了蛙聲一片。吃飽了一整天的狗糧后,有點(diǎn)撐著,于是決定加班加點(diǎn),用服務(wù)卡片實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的小游戲:暴打七夕青蛙!游戲雖簡(jiǎn)單,但玩起來(lái)是真的爽!
效果展示視頻
效果圖


編寫(xiě)過(guò)程
更改程序標(biāo)簽和程序的圖標(biāo)
在resources文件下的zh.element中的string.json中修改如下,就把應(yīng)用的名字修改為“七夕青蛙”,而主頁(yè)面也會(huì)從“Hello World!” 變成 “GoodBye World!”

在config.json文件修改icon的值,引用到media里面的青蛙。

創(chuàng)建JS卡片
編寫(xiě)卡片的樣式
游戲卡片的結(jié)構(gòu)非常非常非常非常簡(jiǎn)單,就是一個(gè)div容器,通過(guò)設(shè)置div容器的背景圖來(lái)實(shí)現(xiàn)游戲效果。playGame卡片的hml代碼如下:
- <div style="width:100100%;" >
- <div style="flex_direction:column;width:100%;height:100%;background-image:{{background}} ;"onclick="messageEvent" >
- </div>
- </div>
GamePanel的樣式稍微復(fù)雜一點(diǎn),但是其實(shí)也很簡(jiǎn)單。需要設(shè)置兩個(gè)變量,得分score和倒計(jì)時(shí)countdown,其中得分設(shè)置成一個(gè)上下結(jié)構(gòu)。hml代碼如下:
- <div>
- <div class="normal_container">
- <div class="pic_title_container" onclick="settings" >
- <div style="flex-direction: row;" >
- <!-- 得分 -->
- <div style="flex-direction: column;width: 50%;margin-top: 20px;" >
- <text style="text-align: center;width: 100%;font-size: 25px;">
- SCORE
- </text>
- <text style="text-align: center;width: 100%;font-size: 35px;color: ghostwhite;">
- {{ score }}
- </text>
- </div>
- <!-- 倒計(jì)時(shí) -->
- <text style="text-align: center;width: 40%;font-size: 60px;color: brown;" >
- {{ countDown }}
- </text>
- </div>
- <div style="margin-right: 10px;" >
- <button onclick="start" type="capsule" style="opacity: 0.5;margin-right: 40px;text-align: center;width: 40%;">開(kāi)始</button>
- <button onclick="stop" type="capsule" style="opacity: 0.5;margin-right: 40px;text-align: center;width: 40%;">停止</button>
- </div>
- </div>
- </div>
- </div>
給變量賦初值
在playGame卡片的index.json文件中,修改“data”如下圖,默認(rèn)一開(kāi)始是沒(méi)有青蛙出現(xiàn)的。

在GamePanel卡片的index.json文件中,修改“data”如下圖

設(shè)置卡片的動(dòng)作事件
“actions”數(shù)組是所有事件的集合,下面跟著每個(gè)事件的名稱(chēng),名稱(chēng)里面又包含事件的類(lèi)型“action”和攜帶的參數(shù)“params”。
在playGame卡片的index.json文件中,修改“actions”如下圖

在GamePanel卡片的index.json文件中,修改“actions”如下圖

編寫(xiě)游戲部分
七夕青蛙的隨機(jī)出現(xiàn)
七夕青蛙的出現(xiàn)上文提到過(guò)是通過(guò)設(shè)置div容器的背景圖片來(lái)實(shí)現(xiàn),所以可以通過(guò)產(chǎn)生一個(gè)隨機(jī)數(shù)的方式來(lái)隨機(jī)地設(shè)置div容器的背景來(lái)實(shí)現(xiàn)游戲過(guò)程,因此需要把div的背景設(shè)置成變量,并添加一個(gè)onclick標(biāo)簽。
- //兩種背景圖的路徑
- private static final String frog="url(\"/common/frog1.png\")";
- private static final String hole="url(\"/common/hole.png\")";
- public String rand_bg()
- {
- String bg;
- double randnumber=Math.random();
- if(randnumber>0.65)//隨機(jī)數(shù)大于0.65時(shí)把返回的字符串對(duì)應(yīng)青蛙圖,這個(gè)數(shù)值可以自行設(shè)定
- bg=frog;
- else
- bg=hole;
- return bg;
- }
創(chuàng)建一個(gè)數(shù)據(jù)體來(lái)存儲(chǔ)卡片的信息,并使用MAP將其存儲(chǔ)起來(lái)
不同卡片的回調(diào)事件都是共用一個(gè)回調(diào)方法的,所以想要區(qū)分到底是哪一種,哪一個(gè)卡片發(fā)出的回調(diào),就需要把卡片的信息:卡片的名稱(chēng),卡片的ID,卡片的相關(guān)參數(shù)等記錄下來(lái)。這里采用編寫(xiě)一個(gè)卡片數(shù)據(jù)類(lèi)來(lái)存儲(chǔ)1*2格式的卡片的信息。
- public class GameWigetData
- {
- public String background;
- public long FormId;
- public GameWigetData()
- {
- super();
- }
- }
- public static Map<Long, GameWigetData> gameWidgetDataMap=new HashMap<>()//鍵是FormId,值是數(shù)據(jù)體
修改onCreateForm()方法
onCreateForm()方法在兩種情況被調(diào)用。第一種是上滑呼出卡片的時(shí)候,這時(shí)候上滑卡片是哪一種卡片,就會(huì)調(diào)用一次onCreateForm()方法生成一張?jiān)摲N卡片;
第二種情形是長(zhǎng)按應(yīng)用,然后點(diǎn)擊"服務(wù)卡片",此時(shí)會(huì)顯示應(yīng)用的所有卡片,并每一張卡片都會(huì)回調(diào)一次onCreateForm()方法并生成一個(gè)卡片,當(dāng)選擇了其中某一張卡片添加到桌面之后,其他卡片回調(diào)onDeleteForm()方法來(lái)刪除卡片。所有卡片都是調(diào)用同一個(gè)方法一起生成的,所以需要對(duì)卡片的名稱(chēng)進(jìn)行一次判斷,以確定卡片的種類(lèi)。在onCreateForm()中添加如下代碼:
- if(formName.equals("GamePanel"))//如果是游戲控制面板卡片,則有如下操作
- {
- if(gamePanelFormId==0)
- {//如果放置了兩個(gè),那么只有放置的第一個(gè)有作用,應(yīng)該游戲控制面板只需要一個(gè)
- gamePanelFormId=formId;
- }
- }
- //如果是游戲卡片,那么創(chuàng)建一個(gè)數(shù)據(jù)體實(shí)例,并把它的卡片id和數(shù)據(jù)體實(shí)例一同傳入Map中
- else if(formName.equals("playGame"))
- {
- GameWigetData gameWidgetData = new GameWigetData();
- gameWigetDataMap.put(formId, gameWidgetData);
- // System.out.println("formID->"+formId);
- }
修改onDeleteForm()方法
在onDeleteForm()方法中,要補(bǔ)充兩種卡片刪除時(shí)的設(shè)置,這里很重要,一開(kāi)始我沒(méi)有對(duì)游戲卡片進(jìn)行設(shè)置,結(jié)果運(yùn)行會(huì)拋出沒(méi)有對(duì)應(yīng)的FormId的錯(cuò)誤,查看卡片的時(shí)候所有卡片都會(huì)調(diào)用onCreateForm()方法,然后所有12卡片的ID都寫(xiě)入Map里面,但是當(dāng)其中一個(gè)卡片放置到桌面,而其他卡片回調(diào)onDeleteForm()進(jìn)行刪除的時(shí)候,12卡片的信息并沒(méi)有從Map中移除。這就會(huì)導(dǎo)致并不是每一個(gè)Map中的FormId都有對(duì)應(yīng)的卡片。
- if(gamePanelFormId==formId){
- gamePanelFormId=0;
- }
- else{
- gameWigetDataMap.remove(formId);
編寫(xiě)變量的更新方法
由于后面的操作需要頻繁地用到更新,修改卡片上的變量的操作,所以在進(jìn)行下面的操作之前,我們先編寫(xiě)一個(gè)修改變量的方法。
- //更新值是字符串時(shí)
- private void updateWidget(long formId,String key, String value) {
- try {
- ZSONObject zsonObject = new ZSONObject();
- zsonObject.put(key, value);
- FormBindingData formBindingData = new FormBindingData(zsonObject);
- updateForm(formId, formBindingData);
- } catch (Exception e) {
- System.out.println(e.getMessage());
- }
- }
- //更新值是整數(shù)時(shí)
- public void updateWidget(long formId,String key, int value) {
- updateWidget(formId, key, String.valueOf(value));
- }
修改onTriggerFormEvent()方法
在這個(gè)游戲中,總共有三個(gè)點(diǎn)擊事件需要響應(yīng):開(kāi)始鍵,停止鍵,打青蛙。同樣的,這三個(gè)事件共用一個(gè)回調(diào)方法,因此需要通過(guò)事件所攜帶的參數(shù)來(lái)判斷到底是哪一個(gè)事件回調(diào)了方法。在onTriggerFormEvent()中添加如下代碼:
- //接受事件傳遞的參數(shù)
- ZSONObject zsonObject=ZSONObject.stringToZSON(message);
- String message1=zsonObject.getString("message");
- //如果是開(kāi)始鍵觸發(fā)的事件,則把開(kāi)始標(biāo)志設(shè)置為真
- if (message1.equals("start"))
- {
- startFlag=true;
- System.out.println("start");
- }
- //如果是停止鍵觸發(fā)的事件,則把開(kāi)始標(biāo)志設(shè)置為假,并重置面板上的數(shù)據(jù)
- else if (message1.equals("stop"))
- {
- startFlag = false;
- score = 0;
- countdown1 = 60;
- updateWidget(gamePanelFormId,"score",score);
- updateWidget(gamePanelFormId,"countdown",countdown1);
- }
- //如果是“打青蛙”事件
- else
- {
- if(startFlag)//如果游戲在進(jìn)行中
- {
- //判斷現(xiàn)在面板中是不是青蛙
- GameWigetData gameFormData=gameWigetDataMap.get(formId);
- if(gameFormData.background.equals(frog))
- {
- score =score+10;//達(dá)到一個(gè)加十分
- System.out.println("現(xiàn)在的分?jǐn)?shù)是"+score);
- gameFormData.background=hole;//打完重新設(shè)置為洞
- updateWidget(gamePanelFormId,"score",score);
- updateWidget(formId,"background",gameFormData.background);
- }
- }
- else
- System.out.println("游戲已經(jīng)結(jié)束了");
- }
編寫(xiě)游戲線(xiàn)程
在onStart()方法中添加游戲線(xiàn)程如下:
- if(gameThread==null)
- {//如果還未創(chuàng)建游戲線(xiàn)程,則創(chuàng)建游戲線(xiàn)程
- gameThread=new Thread(() -> {
- while(true)
- {
- try {
- Thread.sleep(50);
- if(startFlag)
- {
- //對(duì)所有的卡片都隨機(jī)地設(shè)置背景
- for(GameWigetData gameWigetData:gameWigetDataMap.values())
- {
- gameWigetData.background=tool.rand_bg();
- }
- //對(duì)所有的1*2卡片進(jìn)行更新
- for(long gameWigetFormId:gameWigetDataMap.keySet())
- {
- GameWigetData gameWigetData=gameWigetDataMap.get(gameWigetFormId);
- updateWidget(gameWigetFormId,"background",gameWigetData.background);
- }
- }
- Thread.sleep(750);
- }catch (Exception e)
- {
- System.out.println(e.getMessage());
- startFlag=false;
- }
- }
- });
- gameThread.start();
- }
編寫(xiě)倒計(jì)時(shí)線(xiàn)程
在onStart()方法中編寫(xiě)倒計(jì)時(shí)線(xiàn)程如下:
- if(countDownThread==null)
- {
- countDownThread=new Thread(new Runnable()
- {
- public void run()
- {
- while(true)
- {
- try{
- Thread.sleep(50);
- if(startFlag)
- {
- if(countdown1>0)
- {
- updateWidget(gamePanelFormId,"countdown",countdown1);
- countdown1--;
- System.out.println("現(xiàn)在剩余的時(shí)間是"+countdown1);
- }
- else//countdown==0的時(shí)候,復(fù)位
- {
- updateWidget(gamePanelFormId,"countdown",0);
- startFlag=false;
- countdown1=60;
- System.out.println("游戲結(jié)束!");
- }
- }
- Thread.sleep(1000);
- }catch (Exception e)
- {
- System.out.println(e.getMessage());
- }
- }
- }
- });
- countDownThread.start();
- }
項(xiàng)目踩的小坑
1.score要設(shè)置成靜態(tài)變量,否則被釋放,導(dǎo)致盡管打中很多次都只能到10分,而不能夠往上累積。
2.onDeleteForm()方法要記得從Map中remove掉已經(jīng)刪除掉的卡片的ID
最后
最后祝有情人終成眷屬啦,祝單身狗早日脫單!還有就是感謝我那群為我瞎操心的朋友們!(文后附上脫單壓縮包)
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)