天天看點

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

::: hljs-center

體驗者:半個月亮團  學校:中原工學院

:::

  本篇文章主要說明的是在大禹200的開發闆上,開發的一個彈球小遊戲。以此體驗大禹200對OpenHarmony作業系統的支援,以及OpenHarmony作業系統上應用開發。

1. 選擇裝置

  目前,能夠承載OpenHarmony作業系統的開發闆是有一些的,但是作為一窮二白的我想擷取一個能用的還是有不少困難的。是以,與其說是選擇裝置,還不如說是裝置沒有選擇。

  榮幸的是大禹體驗官活動給了我們機會。是以,無論如何必須插入一條硬廣告,廣告詞“大禹200将引領未來”。 拿到的大禹200的開發闆子是下面這個樣子的。如圖1。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖1 大禹200開發闆

:::

2. 更新系統

  OpenHarmony作業系統的版本更新是非常快的,為了獲得更好的體驗,拿到大禹200後的第一項工作是更新作業系統。更新系統其實就是把系統重新在開發闆上燒錄一下。

2.1 燒錄前準備

  将裝置連接配接電源線,以及USB裝置燒寫線,如圖2所示。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖2 連接配接大禹200

:::

  到ci.openharmony.cn網站上選擇對應的版本下載下傳固件,http://ci.openharmony.cn/dailys/dailybuilds 如圖3。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖3 固件下載下傳頁面

:::

2.2 燒錄步驟

  安裝 USB 驅動,這個過程比較簡單,可以說是傻瓜式安裝,這裡跳過。

  打開瑞芯微開發燒寫工具(可以自己下載下傳安裝),預設打開的是 Maskrom 模式,如圖4所示。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖4 燒寫工具

:::

  如果出現問題,說明需要進入loader模式,先按住recovery不要放,接着按一下reset釋放,等待一兩秒釋放recovery按鍵,界面會加載出,發現一個LOADER裝置,如圖5。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖5 進入LOADER模式

:::

  接下來選擇固件進行燒錄,如圖6。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖6 選擇固件

:::

  點選執行按鈕,開始燒寫,直到完成,如圖7。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖7 燒寫完成

:::

  成功後,重新開機裝置,即可進入新版本的OpenHarmony系統,如圖8。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖8 OpenHarmony系統

:::

  至此,裝置和鴻蒙系統都準好了,接下來可以開發我們的遊戲了。

3. 建立項目

  開發鴻蒙應用需要下載下傳華為提供的DevEco Studio,目前針對OpenHarmony的是DevEco Studio 3.0 Beta3 for OpenHarmony。下載下傳位址為:https://developer.harmonyos.com/cn/develop/deveco-studio/。下載下傳後,運作安裝即可。

  安裝完成DevEco Studio後,啟動該內建開發環境,并通過向導建立項目。本彈球小遊戲的項目結構如下:

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖9 項目結構

:::

  其中,entry:OpenHarmony工程子產品,編譯建構生成一個Hap包。

  src > main > js:用于存放js源碼。

  src > main > js > MainAbility:應用/服務的入口。

  src > main > js > MainAbility > i18n:用于配置不同語言場景資源内容,比如應用文本詞條、圖檔路徑等資源。

  src > main > js > MainAbility > pages:MainAbility包含的頁面。

  src > main > js > MainAbility > app.js:承載Ability生命周期。

  src > main > resources:用于存放應用/服務所用到的資源檔案,如圖形、多媒體、字元串、布局檔案等。

  base>element:包括字元串、整型數、顔色、樣式等資源的json檔案。每個資源均由json格式進行定義。

  base>media:多媒體檔案,如圖形、視訊、音頻等檔案,支援的檔案格式包括:.png、.gif、.mp3、.mp4等。

  src > main > config.json:子產品配置檔案,主要包含HAP包的配置資訊、應用在具體裝置上的配置資訊以及應用的全局配置資訊。

  entry > build-profile.json5:目前的子產品資訊、編譯資訊配置項,包括buildOption、targets配置等。

  entry > hvigorfile.js:子產品級編譯建構任務腳本,開發者可以自定義相關任務和代碼實作。

  build-profile.json5:應用級配置資訊,包括簽名、産品配置等。

  hvigorfile.js:應用級編譯建構任務腳本。

  需要說明的是,針對本文實作的打磚塊小遊戲,這裡多數檔案不用修改。

4. 具體實作彈球遊戲

4.1 遊戲效果

  打磚塊小遊戲主要有兩個界面,第一個是進入遊戲界面,第一個是玩遊戲界面。如圖10、11所示。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖10 進入界面

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖11 玩遊戲

:::

  進入界面的功能實作在項目的index目錄中,包括index.css、index.hml和index.js三個檔案。

  玩遊戲的功能及控制功能實作在項目的second目錄中,包括second.css、second.hml和second.js三個檔案。如圖12。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖12 項目功能實作檔案

:::

  在基于JS的鴻蒙應用中,可以有多個頁面(Pages),每一個頁面有三個檔案構成,分别是樣式檔案(css)、頁面檔案(hml)、腳本代碼(js)。

  這裡的打磚塊遊戲,第一個界面(圖10)功能比較簡單,主要是一個響應進入遊戲按鈕,第二界面(圖11)為玩遊戲界面,控制遊戲過程,稍微複雜。這裡重點說一下遊戲控制基本思路。

4.2 遊戲思路

  圖11上半部分有一個放置磚塊盒子,磚塊在second.hml檔案中建立,在second.js檔案中根據其id名取出進行操作;下方放置小球和滑塊并設定相應樣式;底部防止兩個按鍵“開始遊戲”和“再來一局”。

  磚塊是通過div表示的,由樣式定義磚塊的大小和顔色等。當判斷出小球碰撞到磚塊時,将磚塊的visibility樣式變為隐藏,達到磚塊消失的效果,即磚塊被打掉。

  當按下開始遊戲按鍵時會觸發小球運動的函數,采取每20毫秒傳回一次的資料來定位小球的實時位置,進而達到小球運動軌迹的顯現。小球在碰到磚塊時會把磚塊打掉并反彈,在碰到左、上、右邊牆壁時會反彈,在碰到滑塊時也會反彈,掉到底部則遊戲結束。

  觸摸滑塊時會有三種事件被觸發。開始觸摸時,會提示“開始拖動”;拖拽過程中會使滑塊跟随手指一起移動;結束拖拽時,會提示“拖動結束”。

4.3 實作基本過程

  1) 建立項目

  2) 建立page

  3) 實作代碼

  4) 調試運作

  對于1)建立項目基本過程是打開DevEco Studio -> 選擇建立項目(Create Project) -> 選擇模闆 -> 點選Next。如圖13所示。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖13 建立項目

:::

  接着進入配置項目頁面(Configure Your Project),輸入項目名稱(Project Name)等基本資訊,由于這裡采用的JS開發的,是以語言選擇JS,最後點選完成(Finish)按鈕即可完成項目的建立。如圖14所示。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖14 填寫項目基本資訊

:::

  建立好的項目如圖15所示,預設已經建立好了項目的基本結構,且已經建立了一個預設的起始頁(index),不過代碼還需要修改。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖15 建立的預設項目

:::

  接下來建立第二個界面(second),把滑鼠發到pages上,點選右鍵,選擇New -> Page,如圖16所示。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖16 建立Page

:::

  在彈出的如圖17所示的對話框中輸入名稱,然後點選完成(Finish)即可建立第二個界面(second)的基本代碼檔案結構。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

圖17 輸入Page名稱

:::

  至此,基本的項目檔案基本結構已經建立成功,接下來需要實作玩遊戲的邏輯了。

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲
#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲
圖1 進入界面 圖2 玩遊戲

:::

  進入界面的功能實作在項目的index目錄中,包括index.hml、index.css和index.js三個檔案。玩遊戲的功能及控制功能實作在項目的second目錄中,包括second.hml、second.css和second.js三個檔案。

1 進入頁面實作

1.1 index.hml

<div class="container">
    <text class="title">
        彈磚塊
    </text>
    <input class="btn" type="button" value="進入遊戲" onclick="onclick">
    </input>
</div>
           

1.1 index.css

.container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    left: 0px;
    top: 0px;
    width: 100%;
    height: 100%;
}
.title {
    font-size: 60px;
    text-align: center;
    width: 100%;
    height: 40%;
    margin: 10px;
}
.btn {
    font-size: 60px;
    font-weight: bold;
    text-align: center;
    width: 40%;
    height: 8%;
    margin-top: 20px;
}
@media screen and (device-type: phone) and (orientation: landscape) {
    .title {
        font-size: 60px;
    }
}
@media screen and (device-type: tablet) and (orientation: landscape) {
    .title {
        font-size: 100px;
    }
}
           

1.1 index.js

import router from '@system.router';

export default {
    data: {
        title: ""
    },
    onInit() {
        this.title = this.$t('strings.world');
    },
    onclick: function(){
        router.push({
            uri: "pages/second/second"
        })
    }
}
           

2 玩遊戲實作

2.1 second.hml

<div class="container">
    <div class="brickBox" id="brickBox">
        <div class="brick"   id="brick1"  style="visibility: visible;">
        </div>
        <div class="brick"   id="brick2"  style="visibility: visible;">
        </div>
        <div class="brick"   id="brick3"  style="visibility: visible;">
        </div>
        <div class="brick"   id="brick4"  style="visibility: visible;">
        </div>
        <div class="brick"   id="brick5"  style="visibility: visible;">
        </div>

        <dialog id="hintDialog" style="margin-bottom: 50%;">
            <div class="dialog-div">
                <div class="inner-txt">
                    <text class="txt">彈磚塊</text>
                </div>
                <text class="text">遊戲結束</text>
                <div class="inner-btn">
                    <button type="text" value="确定" onclick="sethintDialog" class="btn-txt"></button>
                </div>
            </div>
        </dialog>
    </div>

    <div class="ball" id="ball"></div>

    <div class="slider" id="slider" ondragstart="dragstart" ondrag="drag"  ondragend="dragend" style="position: absolute;left:{{left}};" ></div>

    <div class="Top">
        <button class="buttons"  onclick="Rebound">
            開始遊戲
        </button>
        <button class="buttons"  onclick="restart" >
            再來一局
        </button>
    </div>
</div>
           

2.2 second.css

.container {
    background-color: bisque;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    left: 0px;
    top: 0px;
    width: 100%;
    height: 100%;
}

.buttons {
    margin-top: 15px;
    width: 45%;
    height: 80px;
    text-align: center;
    font-size: 60px;
    border-radius: 10px;
    background-color: chocolate;
}

.title {
    font-size: 13px;
    margin-top: 60px;
    margin-left: 20px;
    color: grey;
}

.Top {
    width: 100%;
    height: 100px;
    position: relative;
    background-color: chocolate;

}

.brickBox {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: space-around;
    align-items: center;
    background-color: burlywood;/**自動換行**/
    flex-wrap: wrap;
}

.brick {
    width: 20%;
    height: 60px;
    background-color: brown;
    border: 3px solid black;
}

.ball {
    width: 50px;
    height: 50px;
    background-color: darkgreen;
    border-radius: 250px;
    position: absolute;
    bottom: 200px;
    left: 50%;
}

.slider {
    width: 30%;
    height: 50px;
    background-color: cadetblue;
    position: absolute;
    left: 50%;
    bottom: 150px;
}

.dialog-div {
    flex-direction: column;
    align-items: center;
}

.inner-txt {
    width: 80%;
    height: 100px;
    align-items: center;
    flex-direction: column;
    justify-content: space-around;
}

.inner-btn {
    width: 80%;
    height: 100px;
    align-items: center;
    justify-content: space-around;
}

.txt {
    font-size: 18px;
    color: #000000;
    font-weight: bold;
}

.text {
    font-size: 16px;
}
           

2.3 second.js

import prompt from '@system.prompt';

export default {
    data: {
        left: 250,
    },
    onInit() {

    },
    drag(e) {
        this.left = e.globalX;
    },
    // 實作小球上下左右不斷移動
    // 小球反彈(速度*-1)
    Rebound() {
        var brickBox = this.$element('brickBox');
        var ball = this.$element('ball');
        var slider = this.$element('slider');
        var brick1 = this.$element('brick1');
        var brick2 = this.$element('brick2');
        var brick3 = this.$element('brick3');
        var brick4 = this.$element('brick4');
        var brick5 = this.$element('brick5');
        var hintDialog = this.$element('hintDialog');
        // 小球運動
        var interId = null;
        var speedX = this.getRandom(5, 10);
        var speedY = -this.getRandom(5, 10);
        interId = setInterval(function () {
            // 設定x軸方向的移動速度
            var lf = ball.getBoundingClientRect().left + speedX;
            // 設定y軸方向的移動速度
            var tp = ball.getBoundingClientRect().top + speedY;
            // 碰撞銷毀磚塊
            // if進行判斷,判斷小球與磚塊接觸
            if ((lf + ball.getBoundingClientRect().width / 2) >= brick1.getBoundingClientRect().left && (lf + ball.getBoundingClientRect().width / 2) <= (brick1.getBoundingClientRect().left + brick1.getBoundingClientRect().width) && (brick1.getBoundingClientRect().top + brick1.getBoundingClientRect().height) >= ball.getBoundingClientRect().top) {
                brick1.setStyle(
                    "visibility", "hidden"
                )
                // Y軸的移動速度
                speedY = 5;
            }
            if ((lf + ball.getBoundingClientRect().width / 2) >= brick2.getBoundingClientRect().left && (lf + ball.getBoundingClientRect().width / 2) <= (brick2.getBoundingClientRect().left + brick2.getBoundingClientRect().width) && (brick2.getBoundingClientRect().top + brick2.getBoundingClientRect().height) >= ball.getBoundingClientRect().top) {
                brick2.setStyle(
                    "visibility", "hidden"
                )
                // Y軸的移動速度
                speedY = 5;
            }
            if ((lf + ball.getBoundingClientRect().width / 2) >= brick3.getBoundingClientRect().left && (lf + ball.getBoundingClientRect().width / 2) <= (brick3.getBoundingClientRect().left + brick3.getBoundingClientRect().width) && (brick3.getBoundingClientRect().top + brick3.getBoundingClientRect().height) >= ball.getBoundingClientRect().top) {
                brick3.setStyle(
                    "visibility", "hidden"
                )
                // Y軸的移動速度
                speedY = 5;
            }
            if ((lf + ball.getBoundingClientRect().width / 2) >= brick4.getBoundingClientRect().left && (lf + ball.getBoundingClientRect().width / 2) <= (brick4.getBoundingClientRect().left + brick4.getBoundingClientRect().width) && (brick4.getBoundingClientRect().top + brick4.getBoundingClientRect().height) >= ball.getBoundingClientRect().top) {
                brick4.setStyle(
                    "visibility", "hidden"
                )
                // Y軸的移動速度
                speedY = 5;
            }
            if ((lf + ball.getBoundingClientRect().width / 2) >= brick5.getBoundingClientRect().left && (lf + ball.getBoundingClientRect().width / 2) <= (brick5.getBoundingClientRect().left + brick5.getBoundingClientRect().width) && (brick5.getBoundingClientRect().top + brick5.getBoundingClientRect().height) >= ball.getBoundingClientRect().top) {
                brick5.setStyle(
                    "visibility", "hidden"
                )
                // Y軸的移動速度
                speedY = 5;
            }
            //lf:x  tp:y
            if (lf < 0) {
                speedX = -speedX;
            }
            if (lf >= (brickBox.getBoundingClientRect().width - ball.getBoundingClientRect().width)) {
                speedX = -speedX;
            }
            if (tp <= 0) {
                speedY = 5;
            } else if ((ball.getBoundingClientRect().top + ball.getBoundingClientRect().height) >= slider.getBoundingClientRect().top && (ball.getBoundingClientRect().left + ball.getBoundingClientRect().width / 2) >= slider.getBoundingClientRect().left && (ball.getBoundingClientRect().left + ball.getBoundingClientRect().width / 2) <= (slider.getBoundingClientRect().left + slider.getBoundingClientRect().width)) {
                speedY = -5;
            } else if (ball.getBoundingClientRect().top >= slider.getBoundingClientRect().top) {
                // 遊戲結束
                // 彈框提示遊戲該結束
                hintDialog.show();
                // 清除間隔
                clearInterval(interId);
            }
            //實時改變小球位置
            ball.setStyle("left", lf + "px")
            ball.setStyle("top", tp + "px")

        }, 20)
    },

    // 封裝擷取随機數的函數
    getRandom(a, b) {
        var max = Math.max(a, b);
        var min = Math.min(a, b)
        return Math.floor(Math.random() * (max - min + 1)) + min;
    },
    //關閉彈窗
    sethintDialog(e) {
        this.$element('hintDialog').close()
    },
    //拖拽結束
    dragend(e) {
        prompt.showToast({
            message: '拖動結束'
        })
    },
    //拖拽開始
    dragstart(e) {
        prompt.showToast({
            message: '開始拖動'
        })
    },
    //再來一局
    restart() {
        var slider = this.$element('slider');
        var ball = this.$element('ball');
        var brick1 = this.$element('brick1');
        var brick2 = this.$element('brick2');
        var brick3 = this.$element('brick3');
        var brick4 = this.$element('brick4');
        var brick5 = this.$element('brick5');
        slider.setStyle("left", 50 + "%");
        slider.setStyle("bottom", 150 + "px")
        ball.setStyle("left", 50 + "%")
        ball.setStyle("bottom", 200 + "px")
        brick1.setStyle(
            "visibility", "visible"
        )
        brick2.setStyle(
            "visibility", "visible"
        )
        brick3.setStyle(
            "visibility", "visible"
        )
        brick4.setStyle(
            "visibility", "visible"
        )
        brick5.setStyle(
            "visibility", "visible"
        )
        this.Rebound();
    }
}
           

3 玩遊戲

  下面通過動畫看一下玩的效果,請看:

::: hljs-center

#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲
#Dayu200體驗官#OpenHarmony應用開發之彈球小遊戲

:::

4 問題

  盡管本文給大家展示了一個可以玩的彈球遊戲,但是還有好多問題直到思考和進一步體驗。

  1)如何實作多關? 當完成一關後進入下一關,這樣遊戲就更好玩了。。。

  2)如何實作實作磚塊的動态産生?針對磚塊動态産生,本次體驗也嘗試了一些方法但是沒有成功。。。

  3)如何使得遊戲更加流暢?這個是一個值得進一步實驗的問題,涉及到硬體和算法。。。 <br>

  最後,需要說明的是目前該遊戲還有很多不足,還可以繼續改進完善。。。

繼續閱讀