6

跟着小白一起学鸿蒙—[番外]一起学做FlappyBird

 1 year ago
source link: https://www.51cto.com/article/722673.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

跟着小白一起学鸿蒙—[番外]一起学做FlappyBird

作者:王石 2022-11-14 17:01:34
本文主要介绍小游戏的开发,画布功能的使用。
b43fa9c733a3e10bb691344fcebb7e664043da.png

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

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

记得很久以前有个大火的像素游戏叫FlappyBird,我们就一起看看如何能用OpenHarmony学习做个FlappyBird。本文中引用的图片资源均来自与Github。

#跟着小白一起学鸿蒙# [番外]一起学做FlappyBird-开源基础软件社区

1、HAP应用建立

这里我们就不赘述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;
}

2、Canvas介绍

canvas是画布组件用于自定义绘制图形,具体的API页面如下:

https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-components-canvas-canvas-0000001333641081。

页面显示前会调用aboutToAppear()函数,此函数为页面生命周期函数。

canvas组件初始化完毕后会调用onReady()函数,函数内部实现小游戏的初始页面的绘制。

(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:游戏结束
(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

3、游戏逻辑

简单的小游戏主体游戏逻辑为:等待开始,开始,结束流程图如下:

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、完整逻辑

@Entry
@Component
struct 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.birdH)) ||
          this.pipeY >= 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)
  }
}

遗留问题:

  1. 水管只在下层显示:可以在上层显示;
  2. 地面没有让动
  3. 游戏声音问题:目前ohos不支持音频播放资源音频,看之后版本是否支持
  4. DevEcoy用setInterval重绘canvas会导致ide崩溃

5、获取源码

见附件
https://gitee.com/wshikh/ohosflappybird。

本文主要介绍了小游戏的开发,画布功能的使用。

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

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

责任编辑:jianghua 来源: 51CTO开源基础软件社区

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK