作者 : 尹寶榮
本文正在參加星光計劃3.0–夏日挑戰賽
前言
初學 HarmonyOS ArkUI(JS) ,對于FA的開發還是不太熟悉,單純看文檔,不使用起來的話,始終掌握不了,代碼還是要多敲多思考才能進步。是以周末突發奇想利用HarmonyOS 寫了一個簡單的打地鼠遊戲。
實作效果
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiYWan5CZ1AzYzQmZ4YTYlZDZhFmYzcDM0EmZmVWZ5I2YldzNkFzNiF2M08CX3AjMyAjMvw1cldWYtl2Lc12bj5yb0NWM14ycvlnbv1mchhWLsR2Lc9CX6MHc0RHaiojIsJye.gif)
遊戲說明
點選開始按鈕之後,頂部剩餘時間會從60秒開始倒計時,命中數和得分數清0,同時格子中會随機出現地鼠,點選格子出現錘子敲擊地鼠,打中地鼠之後命中數+1,分數根據難度相應增加。難度分為四個星級,玩家可以根據自己的手速選擇相應難度,難度越高擊中得分越高,地鼠消失的間隔時間越短。60秒倒計時結束之後,地鼠不再出現,彈框提示遊戲結束,并顯示最終得分。
項目說明
主要用到知識:animation,倒計時,HarmonyOS 基礎元件
圖檔來源: iconfont-阿裡巴巴矢量圖示庫
實作步驟
- 繪制4x4的格子,每個格子中有一張地鼠的圖檔和一張錘子的圖檔,都預設隐藏。錘子的圖檔定位在地鼠頭上
- 點選開始按鈕的時候
- 開始60秒倒計時
- 每隔一段時間随機顯示一張地鼠圖檔,根據難度顯示幾秒後隐藏
- 點選“地鼠”時
- 錘子出現并有一個向下打擊的animation動畫效果
- 根據目前格子中地鼠的圖檔和錘子的圖檔是否顯示,判斷是否打中地鼠
- 如果打中地鼠,命中次數+1,得分相應增加
- 難度切換後,命中單個地鼠的得分改變,地鼠消失的時間間隔改變
- 60秒倒計時結束後,遊戲結束,地鼠不再出現,倒計時停止,彈框提示得分
代碼實作
1. hml部分
1.1 最上面是一些計數資訊資料,用到了 input 元件
<div class="container">
<!-- 頭部計數資訊子產品-->
<div class="game-title">
<text class="title-text">打地鼠遊戲</text>
</div>
<div class="header-info">
<!-- 剩餘 -->
<div class="input-box">
<text class="input-title">剩餘:</text>
<input class="input" type="text" value="{{ timeInt }}"></input>
<text class="input-unit">秒</text>
</div>
<!-- 命中 -->
<div class="input-box">
<text class="input-title">命中:</text>
<input class="input" type="text" value="{{ countInt }}"></input>
<text class="input-unit">個</text>
</div>
<!-- 共計 -->
<div class="input-box">
<text class="input-title">共計:</text>
<input class="input" type="text" value="{{ scoreInt }}"></input>
<text class="input-unit">分</text>
</div>
1.2 難度星級設定,用到了 rating 元件
<!-- 難度選擇-->
<div class="hard-container">
<text class="input-title">難度設定:</text>
<rating numstars="4" rating="{{ hardRating }}" stepsize="1" @change="changeRating" id="rating">
</rating>
</div>
<!-- 過關分數說明 -->
<div class="pass-score">
<text class="pass-text">目前難度: {{hardRating}} 星 命中單個得分:{{ passScore }} 分</text>
</div>
</div>
附表:rating元件的屬性和事件
1.3 打地鼠的格子區域 ,是用資料for循環出來的,寫的時候沒有考慮周全,代碼有一些備援,感興趣的夥伴可以把這塊代碼重構一下,看起來會更簡潔
<!-- 打地鼠區域 -->
<div class="game-area">
<!-- 第1行-->
<div class="game-tr1">
<div class="game-td" for="item in trOne[0]" on:click="showHammer({{ item }})">
<image class="hammer-pic cover" id="{{ item.idValue }}" src="common/images/hammer.png"
show="{{ item.showHammer }}">
</image>
<image class="game-pic cover" src="common/images/dishu.png" show="{{ item.showMose }}">
</image>
</div>
</div>
<!-- 第2行 -->
<div class="game-tr1">
<div class="game-td" for="item in trOne[1]" on:click="showHammer({{ item }})">
<image class="hammer-pic cover" id="{{ item.idValue }}" src="common/images/hammer.png"
show="{{ item.showHammer }}">
</image>
<image class="game-pic cover" src="common/images/dishu.png" show="{{ item.showMose }}">
</image>
</div>
</div>
<!-- 第3行 -->
<div class="game-tr1">
<div class="game-td" for="item in trOne[2]" on:click="showHammer({{ item }})">
<image class="hammer-pic cover" id="{{ item.idValue }}" src="common/images/hammer.png"
show="{{ item.showHammer }}">
</image>
<image class="game-pic cover" src="common/images/dishu.png" show="{{ item.showMose }}">
</image>
</div>
</div>
<!-- 第4行 -->
<div class="game-tr1 last-tr">
<div class="game-td" for="item in trOne[3]" on:click="showHammer({{ item }})">
<image class="hammer-pic cover" id="{{ item.idValue }}" src="common/images/hammer.png"
show="{{ item.showHammer }}">
</image>
<image class="game-pic cover" src="common/images/dishu.png" show="{{ item.showMose }}">
</image>
</div>
</div>
</div>
1.4 開始按鈕和彈框,用到了 button 元件和 dialog 元件
<!-- 開始按鈕 -->
<div class="btn-area">
<button class="btn-start" value="開始" on:click="startGame">
</button>
</div>
<!-- 提示框 -->
<dialog id="hintDialog" style="margin-bottom : 50%;">
<div class="dialog-div">
<div class="inner-txt">
<text class="txt">GAME OVER</text>
</div>
<text class="text">遊戲得分:{{ scoreInt }}</text>
<div class="inner-btn">
<button type="text" value="确定" onclick="closeDialog" class="btn-txt"> </button>
</div>
</div>
</dialog>
</div>
2. css部分
.container {
padding-top: 20px;
flex-direction: column;
}
/*頭部資訊樣式*/
.game-title {
display: flex;
justify-content: center;
color: chocolate;
margin-bottom: 20px;
}
.header-info {
flex-direction: column;
}
.input-title {
font-size: 18px;
}
.input {
width: 100px;
height: 30px;
}
.input-unit {
font-size: 18px;
margin-left: 8px;
}
.input-box {
margin-bottom: 14px;
margin-left: 20px;
}
/*難度系數選擇樣式*/
.hard-container{
margin-left: 20px;
margin-bottom: 15px;
}
rating {
width: 160px;
height: 40px;
}
.pass-score{
margin-left: 20px;
}
.pass-text{
font-size: 16px;
color: mediumblue;
}
/*遊戲區域樣式*/
.game-area {
border: 1px solid black;
flex-direction: column;
margin: 20px;
background-color: lightgreen;
}
.game-tr1 {
height: 80px;
border-bottom: 1px solid black;
}
.last-tr {
border-bottom: none;
flex: 1;
}
.game-td {
position: relative;
width: 80px;
height: 80px;
border-right: 1px solid black;
display: flex;
justify-content: center;
align-items: center;
}
.game-pic {
width: 85%;
height: 85%;
}
.hammer-pic {
position: absolute;
top: 0px;
left: 20px;
width: 60px;
height: 60px;
z-index: 99;
}
/*按鈕樣式*/
.btn-area {
display: flex;
justify-content: center;
margin-top: 10px;
}
.btn-start {
width: 100px;
height: 30px;
}
/*彈框樣式*/
.dialog-div {
flex-direction: column;
align-items: center;
}
.area {
width: 88%;
border-radius: 5px;
}
.inner-txt {
width: 80%;
height: 100px;
align-items: center;
flex-direction: column;
justify-content: space-around;
}
.txt {
font-size: 18px;
color: #000000;
font-weight: bold;
}
.text {
font-size: 16px;
}
.inner-btn {
width: 80%;
height: 100px;
align-items: center;
justify-content: space-around;
}
3. js部分
export default {
data: {
timeInt: 60, //剩餘時間
countInt: 0, //命中個數
scoreInt: 0, //得分
animation: '', //敲擊動畫
//地鼠資料
trOne: [
[{
tr: 1,
id: 1,
idValue: 'A1',
showHammer: false,
showMose: false
},
{
tr: 1,
id: 2,
idValue: 'A2',
showHammer: false,
showMose: false
},
{
tr: 1,
id: 3,
idValue: 'A3',
showHammer: false,
showMose: false
},
{
tr: 1,
id: 4,
idValue: 'A4',
showHammer: false,
showMose: false
},],
[{
tr: 2,
id: 1,
idValue: 'A5',
showHammer: false,
showMose: false
},
{
tr: 2,
id: 2,
idValue: 'A6',
showHammer: false,
showMose: false
},
{
tr: 2,
id: 3,
idValue: 'A7',
showHammer: false,
showMose: false
},
{
tr: 2,
id: 4,
idValue: 'A8',
showHammer: false,
showMose: false
},],
[{
tr: 3,
id: 1,
idValue: 'A9',
showHammer: false,
showMose: false
},
{
tr: 3,
id: 2,
idValue: 'A10',
showHammer: false,
showMose: false
},
{
tr: 3,
id: 3,
idValue: 'A11',
showHammer: false,
showMose: false
},
{
tr: 3,
id: 4,
idValue: 'A12',
showHammer: false,
showMose: false
},],
[{
tr: 4,
id: 1,
idValue: 'A13',
showHammer: false,
showMose: false
},
{
tr: 4,
id: 2,
idValue: 'A14',
showHammer: false,
showMose: false
},
{
tr: 4,
id: 3,
idValue: 'A15',
showHammer: false,
showMose: false
},
{
tr: 4,
id: 4,
idValue: 'A16',
showHammer: false,
showMose: false
},]
],
hardRating: 1, //難度等級(預設為1)
passScore: 1, //命中單個得分
disAppearTime: 2500, //消失時間
hardList: [
{
hardId: 1,
add: 1,
disappear: 2500,
},
{
hardId: 2,
add: 2,
disappear: 2000,
},
{
hardId: 3,
add: 3,
disappear: 1500,
},
{
hardId: 4,
add: 5,
disappear: 500,
}
],
},
onInit() {
},
onShow() {
this.hardRating = 1
},
//打開彈框
showDialog(e) {
this.$element('hintDialog').show()
},
//關閉彈框
closeDialog(e) {
this.$element('hintDialog').close()
},
//開始按鈕 資料清零
startGame() {
this.timeInt = 60;
this.countInt = 0;
this.scoreInt = 0;
//生成地鼠
let moseTimer = setInterval(this.showMose, 100)
//遊戲時間倒計時
let countDown = setInterval(() => {
this.timeInt--;
//倒計時結束清除定時器
if (this.timeInt === 0) {
clearInterval(countDown);
clearInterval(moseTimer);
//彈出遊戲結束彈框
this.showDialog();
}
}, 1000)
},
//随機生成地鼠
showMose() {
let i = Math.round(Math.random() * 3);
let j = Math.round(Math.random() * 3);
this.trOne[j][i].showMose = true;
//根據難度決定隔多少毫秒地鼠消失
setTimeout(() => {
this.trOne[j][i].showMose = false;
}, this.disAppearTime)
},
//敲擊地鼠動畫效果
showHammer(item) {
this.trOne[item.tr-1][item.id-1].showHammer = true;
var options = {
duration: 50,
easing: 'friction',
delay: 0,
fill: 'forwards',
iterations: 2,
direction: 'normal',
};
var frames = [
{
transform: {
rotate: '0deg'
}, opacity: 0.1, offset: 0.0
},
{
transform: {
rotate: '-30deg'
}, opacity: 1.0, offset: 1.0
}
];
this.animation = this.$element(item.idValue).animate(frames, options);
this.animation.play();
//命中數及分數變化
this.countHander()
//錘子消失
setTimeout(() => {
this.trOne[item.tr-1][item.id-1].showHammer = false;
}, 300)
},
//命中數及分數變化
countHander() {
this.trOne.forEach((item, index) => {
item.forEach((_item, _i) => {
if (_item.showHammer == true && _item.showMose == true) {
this.countInt++;
//根據難度計算得分
this.scoreInt += this.passScore
}
})
})
},
//難度改變
changeRating(e) {
this.hardRating = e.rating;
//命中得分改變
this.passScore = this.hardList[this.hardRating-1].add;
//地鼠消失時間改變
this.disAppearTime = this.hardList[this.hardRating-1].disappear
}
}
總結
對于FA目前還在摸索的路上,這個遊戲寫的比較簡單,思考的不夠全面,還有很多不足之處。希望大家有什麼想法和意見可以提出來,共同進步。
更多原創内容請關注:中軟國際 HarmonyOS 技術團隊
入門到精通、技巧到案例,系統化分享HarmonyOS開發技術,歡迎投稿和訂閱,讓我們一起攜手前行共建鴻蒙生态。