::: hljs-center
體驗者:半個月亮團 學校:中原工學院
:::
本篇文章主要說明的是在大禹200的開發闆上,開發的一個彈球小遊戲。以此體驗大禹200對OpenHarmony作業系統的支援,以及OpenHarmony作業系統上應用開發。
1. 選擇裝置
目前,能夠承載OpenHarmony作業系統的開發闆是有一些的,但是作為一窮二白的我想擷取一個能用的還是有不少困難的。是以,與其說是選擇裝置,還不如說是裝置沒有選擇。
榮幸的是大禹體驗官活動給了我們機會。是以,無論如何必須插入一條硬廣告,廣告詞“大禹200将引領未來”。 拿到的大禹200的開發闆子是下面這個樣子的。如圖1。
::: hljs-center

圖1 大禹200開發闆
:::
2. 更新系統
OpenHarmony作業系統的版本更新是非常快的,為了獲得更好的體驗,拿到大禹200後的第一項工作是更新作業系統。更新系統其實就是把系統重新在開發闆上燒錄一下。
2.1 燒錄前準備
将裝置連接配接電源線,以及USB裝置燒寫線,如圖2所示。
::: hljs-center
圖2 連接配接大禹200
:::
到ci.openharmony.cn網站上選擇對應的版本下載下傳固件,http://ci.openharmony.cn/dailys/dailybuilds 如圖3。
::: hljs-center
圖3 固件下載下傳頁面
:::
2.2 燒錄步驟
安裝 USB 驅動,這個過程比較簡單,可以說是傻瓜式安裝,這裡跳過。
打開瑞芯微開發燒寫工具(可以自己下載下傳安裝),預設打開的是 Maskrom 模式,如圖4所示。
::: hljs-center
圖4 燒寫工具
:::
如果出現問題,說明需要進入loader模式,先按住recovery不要放,接着按一下reset釋放,等待一兩秒釋放recovery按鍵,界面會加載出,發現一個LOADER裝置,如圖5。
::: hljs-center
圖5 進入LOADER模式
:::
接下來選擇固件進行燒錄,如圖6。
::: hljs-center
圖6 選擇固件
:::
點選執行按鈕,開始燒寫,直到完成,如圖7。
::: hljs-center
圖7 燒寫完成
:::
成功後,重新開機裝置,即可進入新版本的OpenHarmony系統,如圖8。
::: hljs-center
圖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
圖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
圖10 進入界面
圖11 玩遊戲
:::
進入界面的功能實作在項目的index目錄中,包括index.css、index.hml和index.js三個檔案。
玩遊戲的功能及控制功能實作在項目的second目錄中,包括second.css、second.hml和second.js三個檔案。如圖12。
::: hljs-center
圖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
圖13 建立項目
:::
接着進入配置項目頁面(Configure Your Project),輸入項目名稱(Project Name)等基本資訊,由于這裡采用的JS開發的,是以語言選擇JS,最後點選完成(Finish)按鈕即可完成項目的建立。如圖14所示。
::: hljs-center
圖14 填寫項目基本資訊
:::
建立好的項目如圖15所示,預設已經建立好了項目的基本結構,且已經建立了一個預設的起始頁(index),不過代碼還需要修改。
::: hljs-center
圖15 建立的預設項目
:::
接下來建立第二個界面(second),把滑鼠發到pages上,點選右鍵,選擇New -> Page,如圖16所示。
::: hljs-center
圖16 建立Page
:::
在彈出的如圖17所示的對話框中輸入名稱,然後點選完成(Finish)即可建立第二個界面(second)的基本代碼檔案結構。
::: hljs-center
圖17 輸入Page名稱
:::
至此,基本的項目檔案基本結構已經建立成功,接下來需要實作玩遊戲的邏輯了。
::: hljs-center
| |
---|---|
圖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
| |
---|
:::
4 問題
盡管本文給大家展示了一個可以玩的彈球遊戲,但是還有好多問題直到思考和進一步體驗。
1)如何實作多關? 當完成一關後進入下一關,這樣遊戲就更好玩了。。。
2)如何實作實作磚塊的動态産生?針對磚塊動态産生,本次體驗也嘗試了一些方法但是沒有成功。。。
3)如何使得遊戲更加流暢?這個是一個值得進一步實驗的問題,涉及到硬體和算法。。。 <br>
最後,需要說明的是目前該遊戲還有很多不足,還可以繼續改進完善。。。