#夏日挑战赛# HarmonyOS - 基于ArkUI(JS)实现打地鼠游戏 原创 精华

中软国际鸿蒙生态
发布于 2022-7-13 09:58
浏览
2收藏

作者 : 尹宝荣

本文正在参加星光计划3.0–夏日挑战赛

前言

初学 HarmonyOS ArkUI(JS) ,对于FA的开发还是不太熟悉,单纯看文档,不使用起来的话,始终掌握不了,代码还是要多敲多思考才能进步。所以周末突发奇想利用HarmonyOS 写了一个简单的打地鼠游戏。

实现效果

#夏日挑战赛# HarmonyOS - 基于ArkUI(JS)实现打地鼠游戏-鸿蒙开发者社区

游戏说明

点击开始按钮之后,顶部剩余时间会从60秒开始倒计时,命中数和得分数清0,同时格子中会随机出现地鼠,点击格子出现锤子敲击地鼠,打中地鼠之后命中数+1,分数根据难度相应增加。难度分为四个星级,玩家可以根据自己的手速选择相应难度,难度越高击中得分越高,地鼠消失的间隔时间越短。60秒倒计时结束之后,地鼠不再出现,弹框提示游戏结束,并显示最终得分。

项目说明

主要用到知识:animation,倒计时,HarmonyOS 基础组件

图片来源: iconfont-阿里巴巴矢量图标库

实现步骤

  1. 绘制4x4的格子,每个格子中有一张地鼠的图片和一张锤子的图片,都默认隐藏。锤子的图片定位在地鼠头上

  2. 点击开始按钮的时候

  • 开始60秒倒计时

  • 每隔一段时间随机显示一张地鼠图片,根据难度显示几秒后隐藏

  1. 点击“地鼠”时
  • 锤子出现并有一个向下打击的animation动画效果

  • 根据当前格子中地鼠的图片和锤子的图片是否显示,判断是否打中地鼠

  1. 如果打中地鼠,命中次数+1,得分相应增加

  2. 难度切换后,命中单个地鼠的得分改变,地鼠消失的时间间隔改变

  3. 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.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

附表:rating组件的属性和事件

#夏日挑战赛# HarmonyOS - 基于ArkUI(JS)实现打地鼠游戏-鸿蒙开发者社区

#夏日挑战赛# HarmonyOS - 基于ArkUI(JS)实现打地鼠游戏-鸿蒙开发者社区

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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.

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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

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;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.

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
    }
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.

总结

对于FA目前还在摸索的路上,这个游戏写的比较简单,思考的不够全面,还有很多不足之处。希望大家有什么想法和意见可以提出来,共同进步。

更多原创内容请关注:中软国际 HarmonyOS 技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
6
收藏 2
回复
举报
6
6
2
6条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

游戏的过程中可以随时修改难度的这个设定很新颖,虽然注意力都在关注打的到底是兔子还是地鼠了。

3
回复
2022-7-13 10:21:32
鸿蒙坚果派
鸿蒙坚果派

等我搞eTS版本的出来

2
回复
2022-7-13 10:53:39
中软国际鸿蒙生态
中软国际鸿蒙生态 回复了 鸿蒙坚果派
等我搞eTS版本的出来

那你实现过程中,有什么好的想法也可以共享一下哦

 

回复
2022-7-13 11:20:16
中软国际鸿蒙生态
中软国际鸿蒙生态 回复了 红叶亦知秋
游戏的过程中可以随时修改难度的这个设定很新颖,虽然注意力都在关注打的到底是兔子还是地鼠了。

哈哈哈,高等级感觉手不够用了

回复
2022-7-13 11:25:08
82王先生
82王先生

荣姐很有想法,跪了跪了

1
回复
2022-7-13 11:31:22
Whyalone
Whyalone 回复了 82王先生
荣姐很有想法,跪了跪了

你凭什么说人是个妹子,有照片么?U•ェ•*U

回复
2022-7-13 16:50:25
回复
    相关推荐