0
点赞
收藏
分享

微信扫一扫

#Dayu200体验官#OpenHarmony应用开发之弹球小游戏

::: hljs-center
体验者:半个月亮团  学校:中原工学院
:::
  本篇文章主要说明的是在大禹200的开发板上,开发的一个弹球小游戏。以此体验大禹200对OpenHarmony操作系统的支持,以及OpenHarmony操作系统上应用开发。

1. 选择设备

  目前,能够承载OpenHarmony操作系统的开发板是有一些的,但是作为一穷二白的我想获取一个能用的还是有不少困难的。因此,与其说是选择设备,还不如说是设备没有选择。
  荣幸的是大禹体验官活动给了我们机会。所以,无论如何必须插入一条硬广告,广告词“大禹200将引领未来”。 拿到的大禹200的开发板子是下面这个样子的。如图1。
::: hljs-center

文章01350.png
图1 大禹200开发板

:::

2. 升级系统

  OpenHarmony操作系统的版本更新是非常快的,为了获得更好的体验,拿到大禹200后的第一项工作是升级操作系统。升级系统其实就是把系统重新在开发板上烧录一下。

2.1 烧录前准备

  将设备连接电源线,以及USB设备烧写线,如图2所示。
::: hljs-center

文章01492.png
图2 连接大禹200

:::

  到ci.openharmony.cn网站上选择对应的版本下载固件,http://ci.openharmony.cn/dailys/dailybuilds 如图3。
::: hljs-center

文章01591.png
图3 固件下载页面

:::

2.2 烧录步骤

  安装 USB 驱动,这个过程比较简单,可以说是傻瓜式安装,这里跳过。
  打开瑞芯微开发烧写工具(可以自己下载安装),默认打开的是 Maskrom 模式,如图4所示。
::: hljs-center

文章01695.png
图4 烧写工具

:::

  如果出现问题,说明需要进入loader模式,先按住recovery不要放,接着按一下reset释放,等待一两秒释放recovery按键,界面会加载出,发现一个LOADER设备,如图5。
::: hljs-center

文章01798.png
图5 进入LOADER模式

:::

  接下来选择固件进行烧录,如图6。
::: hljs-center

文章01832.png
图6 选择固件

:::

  点击执行按钮,开始烧写,直到完成,如图7。
::: hljs-center

文章01864.png
图7 烧写完成

:::

  成功后,重启设备,即可进入新版本的OpenHarmony系统,如图8。
::: hljs-center

文章01911.png
图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

文章011186.png
图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

文章011990.png
图10 进入界面
文章011996.png
图11 玩游戏

:::

  进入界面的功能实现在项目的index目录中,包括index.css、index.hml和index.js三个文件。
  玩游戏的功能及控制功能实现在项目的second目录中,包括second.css、second.hml和second.js三个文件。如图12。
::: hljs-center

文章012159.png
图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

文章012832.png
图13 创建项目

:::

  接着进入配置项目页面(Configure Your Project),输入项目名称(Project Name)等基本信息,由于这里采用的JS开发的,因此语言选择JS,最后点击完成(Finish)按钮即可完成项目的创建。如图14所示。
::: hljs-center

文章012960.png
图14 填写项目基本信息

:::

  创建好的项目如图15所示,默认已经建立好了项目的基本结构,且已经创建了一个默认的起始页(index),不过代码还需要修改。
::: hljs-center

文章013037.png
图15 创建的默认项目

:::

  接下来创建第二个界面(second),把鼠标发到pages上,点击右键,选择New -> Page,如图16所示。
::: hljs-center

文章013109.png
图16 创建Page

:::

  在弹出的如图17所示的对话框中输入名称,然后点击完成(Finish)即可创建第二个界面(second)的基本代码文件结构。
::: hljs-center

文章013184.png
图17 输入Page名称

:::

  至此,基本的项目文件基本结构已经创建成功,接下来需要实现玩游戏的逻辑了。

::: hljs-center

文章011996.png 文章011990.png
图1 进入界面 图2 玩游戏

:::
  进入界面的功能实现在项目的index目录中,包括index.hml、index.css和index.js三个文件。玩游戏的功能及控制功能实现在项目的second目录中,包括second.hml、second.css和second.js三个文件。

1 进入页面实现

1.1 index.hml

```html/xml
<div class="container">
<text class="title">
弹砖块
</text>
<input class="btn" type="button" value="进入游戏" onclick="onclick">
</input>
</div>

### 1.1 index.css
```html/xml
.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

```html/xml
<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
```html/xml
.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

20220629183203.gif 20220629183252.gif

:::

4 问题

  尽管本文给大家展示了一个可以玩的弹球游戏,但是还有好多问题直到思考和进一步体验。
  1)如何实现多关? 当完成一关后进入下一关,这样游戏就更好玩了。。。
  2)如何实现实现砖块的动态产生?针对砖块动态产生,本次体验也尝试了一些方法但是没有成功。。。
  3)如何使得游戏更加流畅?这个是一个值得进一步实验的问题,涉及到硬件和算法。。。 <br>
  最后,需要说明的是目前该游戏还有很多不足,还可以继续改进完善。。。

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz

举报

相关推荐

0 条评论