暴打七夕青蛙—HarmonyOS服務卡片小游戲
前言
七夕節,令我“幸福”的是,被熱心市民送來了七夕青蛙,聽取一天了蛙聲一片。吃飽了一整天的狗糧后,有點撐著,于是決定加班加點,用服務卡片實現了一個簡單的小游戲:暴打七夕青蛙!游戲雖簡單,但玩起來是真的爽!
效果展示視頻
效果圖


編寫過程
更改程序標簽和程序的圖標
在resources文件下的zh.element中的string.json中修改如下,就把應用的名字修改為“七夕青蛙”,而主頁面也會從“Hello World!” 變成 “GoodBye World!”

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

創建JS卡片
編寫卡片的樣式
游戲卡片的結構非常非常非常非常簡單,就是一個div容器,通過設置div容器的背景圖來實現游戲效果。playGame卡片的hml代碼如下:
- <div style="width:100100%;" >
- <div style="flex_direction:column;width:100%;height:100%;background-image:{{background}} ;"onclick="messageEvent" >
- </div>
- </div>
GamePanel的樣式稍微復雜一點,但是其實也很簡單。需要設置兩個變量,得分score和倒計時countdown,其中得分設置成一個上下結構。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>
- <!-- 倒計時 -->
- <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%;">開始</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”如下圖,默認一開始是沒有青蛙出現的。

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

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

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

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