HarmonyOS学习第七周(实践版)

因为有事,断更了一个月,一个月没怎么碰,已经有点忘记了,这周做点简单案例进行一下“康复训练”。

本周的案例是一个色格翻转的小游戏

如上图,点击格子后,所点击的格子及其相邻的格子会翻转(变成橙色)当全部翻转时,则通关。

这个案例的布局没有什么难度,就跳过。

整体实现逻辑

本案例的整体实现逻辑如下:

1、先进行UI设计,实现基础布局

2、定义box类,实现翻转逻辑

3、定义boxes类,用于存放有box类型组成的对象数组,并对其进行初始化。

4、定义boxView视图,通过@Observed监听box类,然后@ObjectLink的方式传入,才能实现响应式更改box的状态。(因为harmonyos中无法直接进行深度监听,所以就算我们修改了boxes中的box中的turned,也无法实现视图上的更改。)

5、实现相邻格子的翻转逻辑。

6、判断是否所有格子都已翻转,都翻转则进入下一关。

7、进入下一关后的页面渲染、更改逻辑。

8、实现三个按钮的功能。

box类

//单独的方块
@Observed
export class box{
  colIndex:number
  rowIndex:number
  turned:boolean
  index:number

  constructor(colIndex:number,rowIndex:number,turned:boolean,index:number) {
    this.colIndex=colIndex
    this.rowIndex=rowIndex
    this.turned=turned
    this.index=index
  }
  changeTurned(){
    this.turned=!this.turned
  }
  setBox(colIndex:number,rowIndex:number,turned:boolean,index:number) {
    this.colIndex=colIndex
    this.rowIndex=rowIndex
    this.turned=turned
    this.index=index
  }
}

首先我们要给每个定义一个格子类 ,因为我们的格子需要包含一些属性和方法。

colIndex是该格子是第几列,rowIndex则是第几行,这是为了服务后面判断他周围格子一起翻转用的。turned是判断盒子是否翻转,index则是格子的编号。

constructor这个函数是类中用于初始化的。changeTurned函数用于翻转格子。setBox函数用于外部可以修改格子的值。

boxes类

import { DEFAULT } from '@ohos/hypium'
import {box} from "../viewmodel/box"

//方块集合
export class boxes{
  boxes:Array<box> = []
  index:number =0

  different:number

  constructor() {

  }

  init(startLine:number){
    for(let i=1;i<=startLine;i++){
      for(let j=1;j<=startLine;j++){
        this.boxes.push(new box(i,j,false,this.index))
        this.index++
      }
    }
  }

  reload(startLine:number){
    //扩容
    this.different = startLine*startLine - (startLine-1)*(startLine-1)
    for(let i =0 ;i<this.different;i++){
      this.boxes.push(new box(0,0,false,0))
    }
    this.index=0
    for(let i=1;i<=startLine;i++){
      for(let j=1;j<=startLine;j++){
        this.boxes[this.index].setBox(i,j,false,this.index)
        this.index++
      }
    }
  }

  reLevel(){
    this.index=0
    for (let i = 0; i < this.boxes.length; i++) {
      this.boxes[i].turned=false
    }
  }

  restart(){
    this.index=0;
    this.boxes.splice(1,this.boxes.length-1)
    this.boxes[0].setBox(1,1,false,0)
  }
}

const boxList = new boxes()
export default boxList as boxes

有了存放单个box的类,同时也要有存放所有格子的类,也就是我们的boxes类,他是由box类型组成的数组。

 init函数用于新建boxes数组,因为我们要给每个box传入一个标记index,所以我们这里也定义一个index,从0开始,每标记一个格子index+1,这样就可以实现每个盒子都有自己的标记,便于后续的使用。

reload函数用于跳转到下一关时的初始化,第一个for循环中用于扩容,因为上一关时我们的boxes中就有一定数量的box了,所以我们只需要在push一定数量的box到boxes中即可。第二个嵌套for循环用于给扩容后的boxes进行初始化。

reLevel函数用于实现重玩本关的功能,主要就是将boxes中的所有box的turned改为false,也就是未翻转。

restart则是重新开始游戏,也就是回到第一关,那么我们则应该把boxes删除直至只剩一个box,然后重置这个box即可。

boxView视图

import {box} from "../viewmodel/box"

@Component
export struct boxView{

  @ObjectLink b:box
  build(){
    Row(){

    }.height("80%")
    .width("90%")
    .margin(10)
    .backgroundColor(this.b.turned?Color.Orange:Color.Blue)
  }
}

由这段代码我们可以看出,其实每个格子呢都是一个Row容器,然后设置了宽高背景色,翻转其实就是背景色的变换。@ObjectLink用于绑定b这个box对象。

index主页

初始化及变量

import {box} from  "../viewmodel/box"
import {boxes} from  "../viewmodel/boxes"
import boxList from "../viewmodel/boxes"
import { boxView } from "../view/boxView"

@Entry
@Component
struct Index {
  @State startLine:number = 1
  @State boxes:boxes =boxList
  aboutToAppear(){
    boxList.init(this.startLine)
  }

首先引入所有我们需要用到的类和视图,这里还引入了一个boxList,boxList其实是boxes的一个实例对象,在boxes的最后我们导出了一个boxList,在这里引入了。

startLine是本案例中最重要的一个变量之一,它用于记录第几关,所有的格子数量的逻辑都与他有关。第一关会有一的平方个格子,第二关有二的平方个,第三关有三的平方个,以此类推。

同时在aboutToAppear生命周期钩子中实现了boxList的初始化,并将其赋值给boxes,实现初始化一个boxes实例的目的。 

相邻翻转逻辑

  turnOver(box:box){
    //右侧
    if(box.rowIndex<this.startLine){
      this.boxes.boxes[box.index+1].changeTurned()
    }
    //左侧
    if(box.rowIndex>1){
      this.boxes.boxes[box.index-1].changeTurned()
    }
    //下方
    if(box.colIndex<this.startLine){
      this.boxes.boxes[box.index+this.startLine].changeTurned()
    }
    // 上方
    if(box.colIndex>1){
      this.boxes.boxes[box.index-this.startLine].changeTurned()
    }
    this.isAllOver()
  }

这个逻辑很简单,左右就是当前index减一或加一就可找到左一个或者右一个格子。而每个格子和他的上一个或下一个则刚好差一行,就是startLine的大小。因此加减一个startLine就可得到上面一个或者下面一个。

判断是否都翻转

 isAllOver(){
    for(let i=0;i<this.boxes.boxes.length;i++){
      if(!this.boxes.boxes[i].turned){
        return
      }
    }
    AlertDialog.show({
      title:"成功过关",
      message:`恭喜你成功通过第${this.startLine}关`,
      confirm:{
        value:"ok",
        action:()=>{

        }
      }
    })
    this.startLine++
    this.boxes.reload(this.startLine)
  }

这里遍历整个boxes数组,如果其中有任何一个box的turned属性为false,也就是还未翻转,则直接return,若都已经翻转了,则出现弹框,表示已经通过。然后调用reload函数,也就是跳转到下一关的逻辑函数。 

完整代码如下:

import {box} from  "../viewmodel/box"
import {boxes} from  "../viewmodel/boxes"
import boxList from "../viewmodel/boxes"
import { boxView } from "../view/boxView"

@Entry
@Component
struct Index {
  @State startLine:number = 1
  @State boxes:boxes =boxList
  aboutToAppear(){
    boxList.init(this.startLine)
  }
  turnOver(box:box){
    //右侧
    if(box.rowIndex<this.startLine){
      this.boxes.boxes[box.index+1].changeTurned()
    }
    //左侧
    if(box.rowIndex>1){
      this.boxes.boxes[box.index-1].changeTurned()
    }
    //下方
    if(box.colIndex<this.startLine){
      this.boxes.boxes[box.index+this.startLine].changeTurned()
    }
    // 上方
    if(box.colIndex>1){
      this.boxes.boxes[box.index-this.startLine].changeTurned()
    }
    this.isAllOver()
  }

  isAllOver(){
    for(let i=0;i<this.boxes.boxes.length;i++){
      if(!this.boxes.boxes[i].turned){
        return
      }
    }
    AlertDialog.show({
      title:"成功过关",
      message:`恭喜你成功通过第${this.startLine}关`,
      confirm:{
        value:"ok",
        action:()=>{

        }
      }
    })
    this.startLine++
    this.boxes.reload(this.startLine)
  }

  build() {
    Row() {
      Column() {
        Row(){
          Text(`第${this.startLine}关`)
            .fontSize(40)
            .fontWeight(FontWeight.Bold)
            .width("100%")
            .textAlign(TextAlign.Center)
        }
        .width("100%")
        .height("10%")

        Row(){
          Button("重新开始")
            .onClick(()=> {
              AlertDialog.show({
                title: "重新开始",
                message: "会把你传送到第一关",
                confirm: {
                  value: "ok",
                  action: () => {
                    this.startLine = 1
                    this.boxes.restart()
                  }
                }
              })
            })
          Button("重玩本关")
            .onClick(()=>{
              AlertDialog.show({
                title:"重玩本关",
                message:"会把你重新传送到刚进入本关的状态",
                confirm:{
                  value:"ok",
                  action:()=>{
                    this.boxes.reLevel()
                  }
                }
              })
            })

          Button("游戏介绍")
            .onClick(()=>{
              AlertDialog.show({
                title:"游戏说明",
                message:"让所有色块变成橙色",
                confirm:{
                  value:"ok",
                  action:()=>{

                  }
                }
              })
            })
        }.width("90%")
        .height("25%")
        .justifyContent(FlexAlign.SpaceEvenly)

        Column(){
          GridRow({columns:this.startLine}){
            ForEach(this.boxes.boxes,(box:box)=>{
              GridCol(){
                Row(){
                  boxView({b:box})
                }
                .onClick(()=>{
                  box.turned=!box.turned
                  this.turnOver(box)
                })
              }
              .width("70%")
              .height(400/this.startLine)
            })
          }
        }.width("100%")
        .height("60%")

      }
      .width('100%')
    }
  }
}

这里还有一个需要注意的地方:

在每一个方块的点击事件中,我们需要调用相邻翻转逻辑,此时我们一定要记得,先修改自己点击的这个格子的turned值。因为相邻翻转逻辑的最后会调用判断是否全部翻转的逻辑,你如果没有先修改点击格子的值,则会判定你还未修改前的值,则会出现错误的情况。

  • 41
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值