51cto 开源基础软件社区 #跟着小白一起学鸿蒙# [番外]《一起学做flappybird》
1. 项目的背景 flappy bird是一款2013年发布并在2014年特别流行的一款横向卷抽游戏。玩家控制一只鸟,试图在绿色管道之间飞行而不撞到它们。
因为openharmony具备多设备的窗口框架能力,可以支持不同设备类型的图形界面的灵活性。今天我们就一起看看如何能用openharmony学习做个flappybird。本文中引用的图片资源均来自与github。游戏的效果如下:
项目源码如下:
https://gitee.com/wshikh/ohosflappybird
2. hap应用建立 2.1 hap简介
hap文件是在openharmony系统下编译生成的可执行文件。hap 包是由代码、资源、第三方库以及应用配置文件打包生成的模块包,主要分为两种类型:entry 和 feature。
openharmony 用户应用程序包可以只包含一个基础的 entry 包,也可以包含一个基础的 entry 包和一个或多个功能型的 feature 包。
entry:应用的主模块,作为 openharmony 应用的入口,提供了应用的基础功能。feature:应用的动态特性模块,作为应用能力的扩展,可以根据用户的需求和设备的类型进行选择性安装。 2.2 hap开发工具
本开发采用:deveco studio 3.0 beta4
huawei deveco studio for openharmony是基于intellij idea community开源版本打造,面向openharmony全场景多设备的一站式集成开发环境(ide),deveco studio 3.0支持在harmonyos 3.0 beta版上开发应用及服务,并已适配arkui声明式编程范式、arkcompiler方舟编译,同时提供低代码开发、双向预览、全新构建工具、模拟器、调试调优、信息中心等功能,为开发者提供工程模板创建、开发、编译、调试、发布等e2e的openharmony应用/服务开发。
下载链接:
https://developer.harmonyos.com/cn/develop/deveco-studio#download_beta
2.3 ets
ets是基于ts扩展的声明式开发范式的方舟开发框架是一套开发极简、高性能、跨设备应用的ui开发框架,支持开发者高效的构建跨设备应用ui界面。
基于ts扩展的声明式开发范式提供了一系列基础组件,这些组件以声明方式进行组合和扩展来描述应用程序的ui界面,并且还提供了基本的数据绑定和事件处理机制,帮助开发者实现应用交互逻辑。
@entry //用@entry装饰的自定义组件用作页面的默认入口组件,也可以理解为页面的根节点。 一个页面有且仅能有一个@entry,只有被@entry修饰的组件或者其子组件,才会在页面上显示。@component //@component装饰的struct表示该结构体具有组件化能力,能够成为一个独立的组件,这种类型的组件也称为自定义组件,在build方法里描述ui结构。struct hello { //在声明式ui中,所有的页面都是由组件构成。组件的数据结构为struct @state mytext: string = 'world' build() { //build函数用于定义组件的声明式ui描述,在build方法中以声明式方式进行组合自定义组件或系统内置组件。 column() { //column:沿垂直方向布局的容器。 text('hello') //text:显示一段文本的组件。 .fontsize(30) text(this.mytext) .fontsize(32) divider() //divider:提供分隔器组件,分隔不同内容块/内容元素。 button() { //button:按钮组件,可快速创建不同样式的按钮,通常用于响应用户的点击操作。 text('click me') .fontcolor(color.red) }.onclick(() => { this.mytext = 'ui' }) .width(500) .height(200) } }} 2.4 hap基本概念
装饰器:方舟开发框架定义了一些具有特殊含义的装饰器,用于装饰类、结构、方法和变量。装饰器就是某一种修饰,给被装饰的对象赋予某一种能力,比如@entry就是页面入口的能力,@component就是组件化能力。
自定义组件:可重用的ui单元,可以与其他组件组合,如@component装饰的struct hello。
ui描述:声明性描述ui结构,例如build()方法中的代码块。
内置组件:框架中默认内置的基本组件和布局组件,开发者可以直接调用,仅用于解释ui描述规范。如column、text、divider、button等。
属性方法:用于配置组件属性,如fontsize()、width()、height()、color()等。
事件方法:在事件方法的回调中添加组件响应逻辑。例如,为button组件添加onclick方法,在onclick方法的回调中添加点击响应逻辑。
2.5 page文件
基于ts扩展的声明式开发范式提供了一系列基础组件,如:基础组件,容器组件,媒体组件,绘制组件和画布组件等,本节我们主要使用画布组件。
这里我们就不赘述hap项目的建立过程,以下就是基础的hap的page文件:index.ets
build() { row() { column() { canvas(this.context) .width('100%') .height('100%') .onclick((ev: clickevent) => { console.info(click!!) //响应鼠标左击 this.doclick() }) .onready(() =>{ //绘制基础 this.context.imagesmoothingenabled = false this.drawblock() }) } .width('100%') } .height('100%') .backgroundimage($r(app.media.backgroundday)) .backgroundimagesize(imagesize.cover) } build是基础页面的构造函数,用于界面的元素构造,其他的页面的生命周期函数如下:
declare class customcomponent { /** * customize the pop-up content constructor. * @since 7 */ build(): void; /** * abouttoappear method * @since 7 */ abouttoappear?(): void; /** * abouttodisappear method * @since 7 */ abouttodisappear?(): void; /** * onpageshow method * @since 7 */ onpageshow?(): void; /** * onpagehide method * @since 7 */ onpagehide?(): void; /** * onbackpress method * @since 7 */ onbackpress?(): void;} 3. canvas画布介绍 canvas是画布组件用于自定义绘制图形,具体的api页面如下:
https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-components-canvas-canvas-0000001333641081
页面显示前会调用abouttoappear()函数,此函数为页面生命周期函数。
canvas组件初始化完毕后会调用onready()函数,函数内部实现小游戏的初始页面的绘制。
3.1 初始化页面数据
drawblock() { this.context.clearrect(0,0,this.context.width,this.context.height) this.context.drawimage( this.baseimg,this.basex,this.basey,500,300) switch(this.flappystate) { case 0: this.context.drawimage( this.messageimg,this.startx,this.starty,300,500) this.drawbird() break; case 1: this.drawbird() this.context.drawimage( this.pipegreenimg,this.pipex,this.pipey,50,150) break; case 2: this.context.drawimage( this.gameoverimg,this.startx,this.starty*3,300,90) break } } 页面状态有三:
0:等待开始界面
1:游戏进行
2:游戏结束
3.2 绘制bird
drawbird() { switch(this.birdtype) { case 0: this.context.drawimage( this.midbirdimg,this.slotx,this.sloty,this.birdh,this.birdw) break case 1: this.context.drawimage( this.upbirdimg,this.slotx,this.sloty,this.birdh,this.birdw) break; case 2: this.context.drawimage( this.downbirdimg,this.slotx,this.sloty,this.birdh,this.birdw) break; default: break; } } 小鸟飞行状态有三种:
翅膀在中间:0
翅膀在上:1
翅膀在下:2
4. 游戏逻辑 4.1 主体游戏逻辑:
简单的小游戏主体游戏逻辑为:等待开始,开始,结束流程图如下:
graph lr等待开始 --> click[点击]click[点击] --> 游戏开始游戏开始 --> 点击 --> |游戏开始|小鸟飞,水管动 --> |小鸟碰到水管| 游戏结束 --> 点击 --> |游戏结束| 等待开始小鸟飞,水管动 --> |小鸟没碰到水管| 游戏继续 --> 点击 doclick() { switch (this.flappystate) { case 0: { // 开始 this.flappystate = 1 break } case 1: { //上下飞 // this.flappystate = 2 this.sloty -= this.flyheight console.log(this.sloty.tostring()) break } case 2: { //由结束到待开始 this.flappystate = 0 this.sloty = this.slotstarty this.pipex = this.pipestartx break } default: break } this.drawblock() } 4.2 完整游戏逻辑:@entry@componentstruct index { @state message: string = 'hello world' private baseimg:imagebitmap = new imagebitmap(common/images/base.png) private messageimg:imagebitmap = new imagebitmap(common/images/message.png) private zeroimg:imagebitmap = new imagebitmap(common/images/0.png) private gameoverimg:imagebitmap = new imagebitmap(common/images/gameover.png) private upbirdimg:imagebitmap = new imagebitmap(common/images/bluebirdupflap.png) private midbirdimg:imagebitmap = new imagebitmap(common/images/bluebirdmidflap.png) private downbirdimg:imagebitmap = new imagebitmap(common/images/bluebirddownflap.png) private pipegreenimg:imagebitmap = new imagebitmap(common/images/pipegreen.png) private settings: renderingcontextsettings = new renderingcontextsettings(true); private context: canvasrenderingcontext2d = new canvasrenderingcontext2d(this.settings); private flappystate: number = 0 private startx = 30; private starty = 100; private slotstarty = 410; private slotx = 50; private sloty = this.slotstarty; private basex = 0; private basey = 650; private pipestartx = 330; private pipex = this.pipestartx; private pipey = 500; private birdh = 60; private birdw = 50; private birdtimer: number; private birdtype: number = 0; private count = 1; private flyheight = 20; private pipemove = 10; drawbird() { switch(this.birdtype) { case 0: this.context.drawimage( this.midbirdimg,this.slotx,this.sloty,this.birdh,this.birdw) break case 1: this.context.drawimage( this.upbirdimg,this.slotx,this.sloty,this.birdh,this.birdw) break; case 2: this.context.drawimage( this.downbirdimg,this.slotx,this.sloty,this.birdh,this.birdw) break; default: break; } } drawblock() { this.context.clearrect(0,0,this.context.width,this.context.height) this.context.drawimage( this.baseimg,this.basex,this.basey,500,300) switch(this.flappystate) { case 0: this.context.drawimage( this.messageimg,this.startx,this.starty,300,500) this.drawbird() break; case 1: this.drawbird() this.context.drawimage( this.pipegreenimg,this.pipex,this.pipey,50,150) break; case 2: this.context.drawimage( this.gameoverimg,this.startx,this.starty*3,300,90) break } } doclick() { switch (this.flappystate) { case 0: { // 开始 this.flappystate = 1 break } case 1: { //上下飞 // this.flappystate = 2 this.sloty -= this.flyheight console.log(this.sloty.tostring()) break } case 2: { //由结束到待开始 this.flappystate = 0 this.sloty = this.slotstarty this.pipex = this.pipestartx break } default: break } this.drawblock() } dofly(): void { console.log(dofly ------ !!) this.birdtype += 1 if (this.birdtype/5 == 0) { this.message = dofly ---555--- !! } } async sleep(ms: number) { return new promise((r) => { setinterval(() => { this.birdtype += 1 this.message = this.birdtype.tostring() if (this.birdtype == 3) { this.birdtype = 0 } console.log(this.message) if (this.flappystate == 1) { this.pipex -= this.pipemove if (this.pipex < 0) { this.pipex = 330 } this.sloty += this.flyheight/5 } if ((((this.pipex-this.slotx) <= this.birdw) && ((this.pipey-this.sloty) = this.basey) { this.flappystate = 2 } this.drawblock() }, ms) }) } abouttodisappear() { } abouttoappear() { this.sleep(200) } build() { row() { column() { canvas(this.context) .width('100%') .height('100%') .onclick((ev: clickevent) => { console.info(click!!) this.doclick() }) .onready(() =>{ this.context.imagesmoothingenabled = false this.drawblock() }) } .width('100%') } .height('100%') .backgroundimage($r(app.media.backgroundday)) .backgroundimagesize(imagesize.cover) }} 5. 游戏的瑕疵 水管只在下层显示:可以在上层显示;
地面没有让动
游戏声音问题:目前ohos不支持音频播放资源音频,看之后版本是否支持
devecoy用setinterval重绘canvas会导致ide崩溃
本文完
写在最后我们最近正带着大家玩嗨openharmony。如果你有好玩的东东,欢迎投稿,让我们一起嗨起来!有点子,有想法,有demo,立刻联系我们:合作邮箱:zzliang@atomsource.org
原文标题:玩嗨openharmony:基于openharmony的小游戏:一起学做flappybird
文章出处:【微信公众号:开源技术服务中心】欢迎添加关注!文章转载请注明出处。
促进中国LED制造业转型升级
汉高在中国光博会展示全新高速率光模块材料
通用汽车和Stellantis入局无稀土电机
开关电源变压器的绕线屏蔽原理及具体操作详解
土壤成分检测仪的使用说明以及效果分析
玩嗨OpenHarmony:基于OpenHarmony的小游戏:一起学做FlappyBird
双三极管ECC822电子管组成的OTL耳机放大器电路
同轴电缆传输距离有多远?可以支持多少兆
Ansys三度荣获2023-2024卓越职场认证
高通博通双方台面上斗志、台面下斗智,高通没看起来那么自信?
模拟集群系统和iDEN系统共用频段时的干扰分析
全自动抽油烟机控制电路
共模电感如何抑制共模信号
储能变流器上市公司
TEC模块 TEC制冷片 ATE1-17
2023年全球低功耗广域网的连接总数预计将超过17亿
长寿型LED楼道智能夜灯设计
家庭安全用电注意事项
配电室环境智能监测系统
逐次比较式模数转换器如何获取最佳采样频率