#冲刺创作新星#一起学做鸿蒙“羊了个羊” 原创 精华

左翼风发
发布于 2022-9-26 17:39
浏览
8收藏

简介

最近大火了一个小游戏火遍朋友圈,我们就一起看看如何能用OpenHarmony学习做个”羊了个羊“。本文中引用的图片资源均来自:https://github.com/Jetereting/ylgy。

#冲刺创作新星#一起学做鸿蒙“羊了个羊”-鸿蒙开发者社区


开发

1. HAP应用建立

《#跟着小白一起学鸿蒙#[六]如何编写一个hap应用》里我们介绍了简单的Hap应用的开发以及基础控件的介绍,这里我们就不赘述Hap项目的建立过程,以下就是基础的Hap的page文件:index.ets

build() {
    Row() {
      Column() {
        Canvas(this.context)
          .width('100%')
          .height('100%')
          .onClick((ev: ClickEvent) => {
            console.log("screen.xy:"+ev.screenX+":"+ev.screenY)
            console.log("xy:"+ev.x+":"+ev.y)
          })
          .onReady(() =>{
            this.context.imageSmoothingEnabled = false
            this.drawBlock()
          })
      }
      .height("80%")
      .width("100%")
    }
    .height('100%')
    .width('100%')
    .backgroundImage($r("app.media.grass"))
    .backgroundImageSize(ImageSize.Cover)
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

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;
}
  • 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.

2. Canvas介绍

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

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

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

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

2.1 初始化页面数据
initBlocks() {
    for (let i=0;i<this.avaliableCnt;i++) {
      let lineCn = Math.floor(i/3)
      let rowCn = Math.floor(i%3)
      if (lineCn == 0) {
        this.blockList[i] = {
          img: "censer",
          isShow: true,
          x: this.startX+rowCn*30,
          y: this.startY,
          w: 55,
          h: 53,
        }
      } else if (lineCn == 1) {
        this.blockList[i] = {
          img: "cloud",
          isShow: true,
          x: this.startX+rowCn*30,
          y: this.startY+lineCn*90,
          w: 55,
          h: 53,
        }
      } else if (lineCn == 2) {
        this.blockList[i] = {
          img: "knif",
          isShow: true,
          x: this.startX+rowCn*30,
          y: this.startY+lineCn*90,
          w: 55,
          h: 53,
        }
      }
    }
  }
  • 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.

小游戏的每个卡片都是用canvas绘制的图片资源,用于进行排列以及点击判断所以在此设计了个数据结构

{
	img: 卡片资源类型,用于图片渲染和相似图片消除
	isShow: 卡片是否显示标志,用于渲染的时候进行判断
	x:卡片渲染左上角横坐标
	y:卡片渲染左上角纵坐标
	w:卡片渲染宽度
	h: 卡片渲染高度
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

现在制作的是用固定方法初始化卡片的方法即渲染3行,每行3个图片,之后改进可以改成明确一个区域,然后采用随机算法进行位置和卡片类型生成。

2.2 初始化页面绘制
drawBlock() {
    //初始化消除区域的卡片
    this.blockList.forEach((block)=>{
      if (block.isShow) {
        let imgItem:ImageBitmap = null
        switch(block.img) {
          case "censer":
            imgItem = this.censerImg
            break
          case "cloud":
            imgItem = this.cloudImg
            break
          case "knif":
            imgItem = this.knifImg
            break
          default:
            imgItem = this.censerImg
            break
        }
        this.context.drawImage( this.cardImg,block.x,block.y,this.blockw,this.blockh)
        this.context.drawImage( imgItem,block.x+5,block.y+5,block.w,block.h)
      }
    })
	//初始化选择卡片区域
    this.context.drawImage( this.slotImg,this.slotX,this.slotY,300,39)
    let pos = 0
    for (let i=0;i<5;i++) {
      this.context.drawImage( this.cardImg,this.slotX + pos,this.slotY+40,61,69)
      if (i < this.emptyList.length) {
        let emptyText = this.emptyList[i]
        let pItem = null;
        switch (emptyText) {
          case "censer":
            pItem = this.censerImg;
            break;
          case "cloud":
            pItem = this.cloudImg;
            break;
          case "knif":
            pItem = this.knifImg;
            break;
          default:
            break;
        }
        if (pItem) {
          this.context.drawImage(pItem,this.slotX + pos + 3,this.slotY+40,55,59)
        }
      }
      pos += 60
    }
  }
  • 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.

整个绘制区域分两个区域:

  • 消除区域:绘制卡片背景和卡片类型,利用初始化的卡片数据进行卡片绘制;
  • 选择区域:绘制栏杆,卡片背景,以及选择的卡片

3. 游戏逻辑

简单的小游戏主体游戏逻辑为:初始化(之前的章节已经介绍),点击(选中,选不中,消除,选择区域满,消除区域空)流程图如下:

graph LR
init[初始化] --> click[点击]
click[点击] --> isSelect{是否点中}
isSelect -->|点中| yes[点中]
isSelect -->|没点中| no[没点中]
yes --> isEmpty{是否选择区域满}
isEmpty -->|满| full[无法消除]
isEmpty -->|不满| notfull[加入选择区域]
notfull --> canClear{有3个相同}
canClear -->|能消除| clear[消除]
canClear -->|不能消除| append[进入选择区域]
append --> 重绘
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
.onClick((ev: ClickEvent) => {
            if (this.needRestart) {
              this.needRestart = false
              this.emptyList.splice(0, this.emptyList.length)
              this.blockList.splice(0, this.blockList.length)
              this.emptyCnt = 5
              this.avaliableCnt = 9
              this.initBlocks()
              this.context.clearRect(0,0,this.context.width,this.context.height)
              this.drawBlock()
              return
            }
            console.log("screen.xy:"+ev.screenX+":"+ev.screenY)
            console.log("xy:"+ev.x+":"+ev.y)
            //判断是否点中方块
            let flag = this.isSelect(ev.x, ev.y)
            console.info("flag:"+flag)
            if (flag == 1) {
              //如果可以移动或消除则清空重填
              this.context.clearRect(0,0,this.context.width,this.context.height)
              this.drawBlock();
            } else if (flag == 2) {
              //如果清空显示胜利画面
              this.context.drawImage( this.blackImg,0,0,this.context.width,this.context.height)
              this.context.drawImage( this.winImg,this.slotX+50,this.slotY-300,200,200)
              this.context.font="100px bold"
              this.context.fillText("欢迎你加入羊群", this.slotX+50,this.slotY-350,500)
              this.needRestart = true
            } else if (flag == 3) {
              this.context.clearRect(0,0,this.context.width,this.context.height)
              this.drawBlock();
              this.context.drawImage( this.blackImg,0,0,this.context.width,this.context.height)
              this.context.drawImage( this.ylgyImg,this.slotX+50,this.slotY-300,200,100)
              this.context.font="100px bold"
              this.context.fillText("加入羊群失败", this.slotX+50,this.slotY-350,500)
              this.needRestart = true
            }
          })
  • 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.

4. 完整逻辑

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  @State _translate: TranslateOptions = {
    x: 0,
    y: 0,
    z: 0
  }
  @State _scale: ScaleOptions = {
    x: 1,
    y: 1,
    z: 1
  }
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  private cardImg:ImageBitmap = new ImageBitmap("common/images/iback.png")
  private slotImg:ImageBitmap = new ImageBitmap("common/images/lan.png")
  private ylgyImg:ImageBitmap = new ImageBitmap("common/images/ylgy.png")
  private blackImg:ImageBitmap = new ImageBitmap("common/images/black.png")
  private censerImg:ImageBitmap = new ImageBitmap("common/images/censer.png")
  private cloudImg:ImageBitmap = new ImageBitmap("common/images/cloud.png")
  private knifImg:ImageBitmap = new ImageBitmap("common/images/knif.png")
  private winImg:ImageBitmap = new ImageBitmap("common/images/win.png")
  private startX = 50;
  private startY = 10;
  private slotX = 20;
  private slotY = 450;
  private blockw = 62;
  private blockh = 69;
  private blockList = []
  private emptyList = []
  private emptyCnt =  5;
  private avaliableCnt = 9;
  private clearLen = 3;
  private needRestart = false;
  animationStep(value: AnimateParam, event: () => void) {
    return () => {
      return new Promise((resolve) => {
        let onFinish = value.onFinish
        value.onFinish = () => {
          if(onFinish) onFinish()
          resolve(true)
        }
        animateTo(value, event)
      })
    }
  }
  async pulse(time) {
    // 0% - 50%
    let step1 = this.animationStep({
      duration: time * 0.5, // 动画时长
      tempo: 0.5, // 播放速率
      curve: Curve.EaseInOut, // 动画曲线
      delay: 0, // 动画延迟
      iterations: 1, // 播放次数
      playMode: PlayMode.Normal, // 动画模式
    }, () => {
      this._scale = {
        x: 1.05,
        y: 1.05,
        z: 1.05
      }
    })

    // 50% - 100%
    let step2 = this.animationStep({
      duration: time * 0.5, // 动画时长
      tempo: 0.5, // 播放速率
      curve: Curve.EaseInOut, // 动画曲线
      delay: 0, // 动画延迟
      iterations: 1, // 播放次数
      playMode: PlayMode.Normal, // 动画模式
    }, () => {
      this._scale = {
        x: 1,
        y: 1,
        z: 1
      }
    })

    await step1()
    await step2()
  }
  initBlocks() {
    for (let i=0;i<this.avaliableCnt;i++) {
      let lineCn = Math.floor(i/3)
      let rowCn = Math.floor(i%3)
      if (lineCn == 0) {
        this.blockList[i] = {
          img: "censer",
          isShow: true,
          x: this.startX+rowCn*30,
          y: this.startY,
          w: 55,
          h: 53,
        }
      } else if (lineCn == 1) {
        this.blockList[i] = {
          img: "cloud",
          isShow: true,
          x: this.startX+rowCn*30,
          y: this.startY+lineCn*90,
          w: 55,
          h: 53,
        }
      } else if (lineCn == 2) {
        this.blockList[i] = {
          img: "knif",
          isShow: true,
          x: this.startX+rowCn*30,
          y: this.startY+lineCn*90,
          w: 55,
          h: 53,
        }
      }
    }
  }
  aboutToAppear() {
    this.initBlocks()
    let audioPlayer = media.createAudioPlayer();
    audioPlayer.on('dataLoad', () => {            //设置'dataLoad'事件回调,src属性设置成功后,触发此回调
      console.info('audio set source success');
      audioPlayer.play();                       //开始播放,并触发'play'事件回调
    });
//    audioPlayer.src = $r("app.media.background")
  }
  clearEmpty() {
    let emptyMap:Map<string, number> = new Map()
    console.info("emptylen:"+this.emptyList.length)
    for (let i=0;i<this.emptyList.length;i++) {
      let txt = this.emptyList[i]
      if (emptyMap[txt]) {
        let num = emptyMap[txt]
        emptyMap[txt] = num + 1
        if (emptyMap[txt] == 3) {
          for (let j=0;j<3;j++) {
            this.emptyList.splice(this.emptyList.indexOf(txt), 1)
          }
          this.emptyCnt += 3
          console.info("key:"+txt+"   n:"+this.emptyList.length)
        }
      } else {
        emptyMap[txt] = 1
      }
    }
  }
  isSelect(x, y) : number {
    let noshowCnt = 0
    let nofind = 0
    for (let i=0;i<this.blockList.length;i++) {
//    this.blockList.forEach((block)=>{
      let block = this.blockList[i]
      noshowCnt += 1
      x = Math.ceil(x)
      y = Math.ceil(y)
//      console.info("x:"+x+"y:"+y)
//      console.info("blockx:"+block.x+"block.y:"+block.y)
      let endx = block.x+this.blockw
      let endy = block.y+this.blockh

      if ((block.x <= x && endx >= x) &&
        (block.y <= y && endy >= y)) {
        console.info("isFind")
        if (block.isShow == true && this.emptyCnt > 0) {
          block.isShow = false;
          this.emptyCnt -= 1;
          this.avaliableCnt -= 1;
          this.emptyList.push(block.img)
          this.clearEmpty()
          //找到block
          if (this.avaliableCnt == 0) {
            return 2
          } else {
            if (this.emptyList.length == 5) {
              return 3
            } else {
              return 1
            }
          }
        } else if (this.emptyCnt == 0) {
          //没有空闲空间
          return 3
        } else if (block.isShow == false) {
          nofind += 1
        }
      } else {
        console.info("noFind")
        nofind += 1
      }
    }
    if (nofind == this.blockList.length) {
      //没有点中
      return 0
    }
    if (noshowCnt == this.blockList.length) {
      //没有block
      return 2
    }

  }
  drawBlock() {
    this.blockList.forEach((block)=>{
      if (block.isShow) {
        let imgItem:ImageBitmap = null
        switch(block.img) {
          case "censer":
            imgItem = this.censerImg
            break
          case "cloud":
            imgItem = this.cloudImg
            break
          case "knif":
            imgItem = this.knifImg
            break
          default:
            imgItem = this.censerImg
            break
        }
        this.context.drawImage( this.cardImg,block.x,block.y,this.blockw,this.blockh)
        this.context.drawImage( imgItem,block.x+5,block.y+5,block.w,block.h)
      }
    })

    this.context.drawImage( this.slotImg,this.slotX,this.slotY,300,39)
    let pos = 0
    for (let i=0;i<5;i++) {
      this.context.drawImage( this.cardImg,this.slotX + pos,this.slotY+40,61,69)
      if (i < this.emptyList.length) {
        let emptyText = this.emptyList[i]
        let pItem = null;
        switch (emptyText) {
          case "censer":
            pItem = this.censerImg;
            break;
          case "cloud":
            pItem = this.cloudImg;
            break;
          case "knif":
            pItem = this.knifImg;
            break;
          default:
            break;
        }
        if (pItem) {
          this.context.drawImage(pItem,this.slotX + pos + 3,this.slotY+40,55,59)
        }
      }
      pos += 60
    }
  }
  build() {
    Row() {
      Column() {
        Canvas(this.context)
          .width('100%')
          .height('100%')
          .onClick((ev: ClickEvent) => {
            if (this.needRestart) {
              this.needRestart = false
              this.emptyList.splice(0, this.emptyList.length)
              this.blockList.splice(0, this.blockList.length)
              this.emptyCnt = 5
              this.avaliableCnt = 9
              this.initBlocks()
              this.context.clearRect(0,0,this.context.width,this.context.height)
              this.drawBlock()
              return
            }
            console.log("screen.xy:"+ev.screenX+":"+ev.screenY)
            console.log("xy:"+ev.x+":"+ev.y)
            //判断是否点中方块
            let flag = this.isSelect(ev.x, ev.y)
            console.info("flag:"+flag)
            if (flag == 1) {
              //如果可以移动或消除则清空充填
              this.context.clearRect(0,0,this.context.width,this.context.height)
              this.drawBlock();
            } else if (flag == 2) {
              //如果清空显示胜利画面
              this.context.drawImage( this.blackImg,0,0,this.context.width,this.context.height)
              this.context.drawImage( this.winImg,this.slotX+50,this.slotY-300,200,200)
              this.context.font="100px bold"
              this.context.fillText("欢迎你加入羊群", this.slotX+50,this.slotY-350,500)
              this.needRestart = true
            } else if (flag == 3) {
              this.context.clearRect(0,0,this.context.width,this.context.height)
              this.drawBlock();
              this.context.drawImage( this.blackImg,0,0,this.context.width,this.context.height)
              this.context.drawImage( this.ylgyImg,this.slotX+50,this.slotY-300,200,100)
              this.context.font="100px bold"
              this.context.fillText("加入羊群失败", this.slotX+50,this.slotY-350,500)
              this.needRestart = true
            }
          })
          .onReady(() =>{
            this.context.imageSmoothingEnabled = false
            this.drawBlock()
          })
      }
      .height("80%")
      .width("100%")
    }
    .height('100%')
    .width('100%')
    .backgroundImage($r("app.media.grass"))
    .backgroundImageSize(ImageSize.Cover)
  }
}
  • 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.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.
  • 292.
  • 293.
  • 294.
  • 295.
  • 296.
  • 297.
  • 298.
  • 299.
  • 300.
  • 301.
  • 302.
  • 303.
  • 304.
  • 305.
  • 306.
  • 307.
  • 308.
  • 309.

#冲刺创作新星#一起学做鸿蒙“羊了个羊”-鸿蒙开发者社区

遗留问题:

  1. 点击选择没有判断图层:可以在卡片数据结构里增加图层标识,最下面的卡片为图层标识为1,上面的多一层加1,点中选择的时候可以判断,增加是否可以选中的逻辑;

  2. 消除区域布局可灵活配置:增加布局配置逻辑,使用数据结构设定布局逻辑,可规定卡片种类,数量,布局行数,列数以及层级

  3. 游戏声音问题:目前ohos不支持音频播放资源音频,看之后版本是否支持

5. 获取源码

仓库地址:https://gitee.com/wshikh/ohosylgy.git


总结

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

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-9-26 17:39:01修改
11
收藏 8
回复
举报
11
9
8
9条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

不错,学习后做个更高难度的发给室友。

回复
2022-9-26 18:29:03
唐克
唐克

这篇厉害!正在学习

回复
2022-9-27 10:04:41
张三疯_
张三疯_

大神

回复
2022-9-27 16:25:23
longlong899
longlong899

牛....

回复
2022-9-28 10:08:56
人工智能姬
人工智能姬

期待第二关

回复
2022-9-28 14:18:41
笨笨的婧婧
笨笨的婧婧

执行力MAX

回复
2022-9-28 15:33:37
真庐山升龙霸
真庐山升龙霸

学习下如何实现的

回复
2022-9-30 10:26:26
物联风景
物联风景

不错不错,学习了

回复
2022-9-30 11:15:25
只看看不说话
只看看不说话

我还没通关,游戏都已经做出来了

回复
2022-9-30 12:57:15


回复
    相关推荐