鸿湖万联“竞”开发板体验:基于eTSUI框架的2048小游戏

描述

前言

2048是一款比较流行的数字游戏,本demo基于ets ui框架,在grid组件基础上实现。

 

过程

从以下地址下载代码:https://gitee.com/qinyunti/knowledge_demo_smart_home.git

 

打开DevEco Studio 3.0.0.993

 

开源社区

 

打开工程:

 

开源社区

 

开源社区

 

更新:

 

开源社区

 

点击Run,有如下提示按图配置即可:

 

开源社区

 

开源社区

 

代码分析

 

程序入口:src/main/ets/MainAbility/app.ets

import Logger from '../MainAbility/model/Logger'


const TAG: string = `[App]`


export default {
onCreate() {
Logger.info(TAG, `Application onCreate`)
},
onDestroy() {
Logger.info(TAG, `Application onDestroy`)
},
}

(左右移动查看全部内容)

 

逻辑代码位于:src/main/ets/MainAbility/model/GameRule.ts

//gameStatus
enum GameStatus {
    BEFORE = -1,
    RUNNING = 1,
    OVER = 0
}


export class GameRule {
    private row: number = 4
    private column: number = 4


    private index(i: number, j: number) {
        return i * this.row + j
    }


    dataNumbers: number[]
    status: number = GameStatus.BEFORE
    score: number = 0


    constructor(dataNumbers: number[]) {
        this.dataNumbers = dataNumbers
    }


    //random
    randomNum() {
        do {
            let a = Math.floor(Math.random() * this.dataNumbers.length)
            if (this.dataNumbers[a] === 0) {
                let num = Math.random() > 0.3 ? 2 : 4
                this.dataNumbers[a] = num
                break
            }
        } while (this.dataNumbers.some((val) => {
            return val === 0
        }))
    }


    //init
    init() {
        this.dataNumbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        this.status = GameStatus.RUNNING
        this.score = 0
        this.randomNum()
        this.randomNum()
    }


    //move
    move(direction: string) {
        let before = String(this.dataNumbers)
        let length = (direction === 'left' || direction === 'right') ? this.row : this.column
        for (let a = 0;a < length; a++) {
            this.moveInRow(direction, a)
        }
        let after = String(this.dataNumbers)
        if (before !== after) {
            this.randomNum()
            if (this.isGameOver()) {
                this.status = GameStatus.OVER
            }
        }
    }


    //prepare to move
    moveInRow(direction: string, a: number) {
        if (direction === 'left') {
            for (let b = 0;b < this.column - 1; b++) {
                let next = this.moveNext(a, b, direction)
                b = this.dataChange(a, b, next, direction).b
                if (next === -1) break
            }
        } else if (direction === 'right') {
            for (let b = this.column - 1;b > 0; b--) {
                let next = this.moveNext(a, b, direction)
                b = this.dataChange(a, b, next, direction).b
                if (next === -1) break
            }
        } else if (direction === 'up') {
            for (let b = 0;b < this.row - 1; b++) {
                let next = this.moveNext(b, a, direction)
                b = this.dataChange(b, a, next, direction).a
                if (next === -1) break
            }
        } else if (direction === 'down') {
            for (let b = this.row - 1;b > 0; b--) {
                let next = this.moveNext(b, a, direction)
                b = this.dataChange(b, a, next, direction).a
                if (next === -1) break
            }
        }
    }


    //new number moveStatus
    moveNext(a: number, b: number, direction: string) {
        if (direction === 'left') {
            for (let i = b + 1;i < this.column; i++) {
                if (this.dataNumbers[this.index(a, i)] !== 0) {
                    return i
                }
            }
        } else if (direction === 'right') {
            for (let i = b - 1;i >= 0; i--) {
                if (this.dataNumbers[this.index(a, i)] !== 0) {
                    return i
                }
            }
        } else if (direction === 'up') {
            for (let i = a + 1;i < 4; i++) {
                if (this.dataNumbers[this.index(i, b)] !== 0) {
                    return i
                }
            }
        } else if (direction === 'down') {
            for (let i = a - 1;i >= 0; i--) {
                if (this.dataNumbers[this.index(i, b)] !== 0) {
                    return i
                }
            }
        }
        return -1
    }


    //get gameStatus
    isGameOver() {
        for (let a = 0;a < this.row; a++) {
            for (let b = 0;b < this.column; b++) {
                let tempA = this.index(a, b)
                if (this.dataNumbers[tempA] === 0) {
                    return false
                }
                if (a < this.row - 1) {
                    if (this.dataNumbers[tempA] === this.dataNumbers[this.index(a + 1, b)]) {
                        return false
                    }
                }
                if (b < this.column - 1) {
                    if (this.dataNumbers[tempA] === this.dataNumbers[tempA+1]) {
                        return false
                    }
                }
            }
        }
        return true
    }


    //move and merge
    dataChange(a: number, b: number, next: number, direction: string) {
        let tempA = this.index(a, b)
        let tempB = 0
        if (direction === 'left' || direction === 'right') {
            tempB = this.index(a, next)
        } else {
            tempB = this.index(next, b)
        }
        if (next !== -1) {
            if (this.dataNumbers[tempA] === 0) {
                this.dataNumbers[tempA] = this.dataNumbers[tempB]
                this.dataNumbers[tempB] = 0
                direction === 'left' && b--
                direction === 'right' && b++
                direction === 'up' && a--
                direction === 'down' && a++
            } else if (this.dataNumbers[tempA] === this.dataNumbers[tempB]) {
                this.dataNumbers[tempA] *= 2
                this.score += this.dataNumbers[tempA]
                this.dataNumbers[tempB] = 0
            }
        }
        return {
            a, b
        }
    }

(左右移动查看全部内容)

 

绘图位于:src/main/ets/MainAbility/pages/Game2048.ets

class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = []


  public totalCount(): number {
    return 0
  }


  public getData(index: number): any {
    return undefined
  }


  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener)
    }
  }


  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      this.listeners.splice(pos, 1)
    }
  }


  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded()
    })
  }


  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdded(index)
    })
  }


  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChanged(index)
    })
  }
}


class MyDataSource extends BasicDataSource {
  public dataArray: string[] = []


  constructor(ele) {
    super()
    for (var index = 0;index < ele.length; index++) {
      this.dataArray.push(ele[index])
    }
  }


  public totalCount(): number {
    return this.dataArray.length
  }


  public getData(index: number): any {
    return this.dataArray[index]
  }


  public addData(index: number, data: string): void {
    this.dataArray.splice(index, 0)
    this.notifyDataAdd(index)
  }
}


enum GameStatus {
  BEFORE = -1,
  RUNNING = 1,
  OVER = 0
}


import display from '@ohos.display'
import Logger from '../model/Logger'
import dataStorage from '@ohos.data.storage'
import { GameRule } from '../model/GameRule'
import featureAbility from '@ohos.ability.featureAbility'


const TAG = '[Game2048]'


@Entry
@Component
struct Game2048 {
  @State dataNumbers: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  private gameRule: GameRule = new GameRule(this.dataNumbers)
  @State maxScore: number = 123456
  @State curScore: number = 0
  @State @Watch("onGameOver") gameStatus: number = GameStatus.BEFORE
  @State textColor: string[] = ['#f0fff0', '#eee3da', '#ede0c8', '#f2b179', '#f59563', '#f67c5f', '#f65e3b', '#edcf72', '#edcc61', '#9c0', '#33b5e5', '#09c', '#a6c', '#93c']
  dialogController: CustomDialogController = new CustomDialogController({
    builder: ScorePannel({
      curScore: this.curScore,
      maxScore: this.maxScore,
      gameStart: this.gameStart.bind(this)
    }),
    autoCancel: false
  })
  @State screenSize: {
    x: number,
    y: number
  } = { x: px2vp(1080), y: px2vp(0) }


  //gameStatus listener
  onGameOver() {
    if (this.gameStatus === GameStatus.OVER) {
      this.curScore = this.gameRule.score
      this.dialogController.open()
    }
  }


  //restart game
  gameStart() {
    this.gameRule.init()
    this.dataNumbers = this.gameRule.dataNumbers
    this.gameStatus = GameStatus.RUNNING
    this.handleLocalData('put')
  }


  //dataView
  dataView() {
    this.dataNumbers = this.gameRule.dataNumbers
    this.gameStatus = this.gameRule.status
    this.curScore = this.gameRule.score
  }


  aboutToAppear() {
    display.getDefaultDisplay((err, data) => {
      if (data.height > data.width) {
        this.screenSize = { x: px2vp(data.width), y: px2vp(data.height) }
      } else {
        this.screenSize = { x: px2vp(750), y: px2vp(data.width) }
      }
      Logger.info(TAG, `宽 ${this.screenSize.x}`)
      Logger.info(TAG, `高 ${this.screenSize.y}`)
    })
    this.handleLocalData('has')
  }


  //handle local data
  handleLocalData(method: string) {
    let context = featureAbility.getContext()
    context.getFilesDir((err, path) => {
      let storage = dataStorage.getStorageSync(path + '/mystore')
      if (method === 'put') {
        storage.putSync('gameData', JSON.stringify(this.dataNumbers))
        let score: string = this.gameRule.score.toString()
        storage.putSync('score', score)
        storage.putSync('gameStatus', this.gameRule.status.toString())
        storage.flushSync()
      } else if (method === 'has') {
        if (storage.hasSync('gameData')) {
          this.gameRule.score = this.curScore = Number(storage.getSync('score', 'string'))
          this.gameStatus = this.gameRule.status = Number(storage.getSync('gameStatus', 'string'))
          this.dataNumbers = this.gameRule.dataNumbers = JSON.parse(storage.getSync('gameData', 'string').toString())
        }
      }
    })
  }


  build() {
    Column() {
      Column() {
        Row() {
          Image($r('app.media.logo2048'))
            .width(this.screenSize.x * 0.25)
            .height(this.screenSize.x * 0.25)


          Column() {
            Text('Score')
              .fontSize('30px')
              .fontColor('#efe1d3')
              .fontWeight(FontWeight.Bolder)


            Text(`${this.gameRule.score}`)
              .fontSize('30px')
              .fontColor('#fcf8f5')
              .fontWeight(FontWeight.Bolder)
          }
          .alignItems(HorizontalAlign.Center)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#bbada0')
          .width(this.screenSize.x * 0.25)
          .height(this.screenSize.x * 0.25)
          .borderRadius(15)


          Column() {
            Text('Max')
              .fontSize('50px')
              .fontColor('#efe1d3')
              .fontWeight(FontWeight.Bolder)


            Text(`${this.maxScore}`)
              .fontSize('30px')
              .fontColor('#fcf8f5')
              .fontWeight(FontWeight.Bolder)
          }
          .alignItems(HorizontalAlign.Center)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#bbada0')
          .width(this.screenSize.x * 0.25)
          .height(this.screenSize.x * 0.25)
          .borderRadius(15)
        }
        .alignItems(VerticalAlign.Center)
        .justifyContent(FlexAlign.SpaceAround)
        .margin({ bottom: 20 })
        .width(this.screenSize.x)


        Grid() {
          LazyForEach(new MyDataSource(this.dataNumbers), (item) => {
            GridItem() {
              Text(`${item === 0 ? '' : item}`)
                .fontSize('85px')
                .fontColor(item <= 4 ? '#000' : '#fcf8f5')
                .fontWeight(FontWeight.Bolder)
                .backgroundColor('#f0fff0')
                .width('100%')
                .height('100%')
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(this.textColor[(Math.log(item) / Math.log(2))])
            }
          })
        }
        .columnsTemplate('1fr 1fr 1fr 1fr')
        .rowsTemplate('1fr 1fr 1fr 1fr')
        .columnsGap(10)
        .rowsGap(10)
        .width(this.screenSize.x)
        .padding(10)
        .backgroundColor('rgba(80,69,46,0.26)')
        .height(this.screenSize.x)
        .borderRadius(10)
        .gesture(GestureGroup(GestureMode.Exclusive,
        PanGesture({ direction: PanDirection.Left }).onActionEnd(() => {
          this.gameRule.status === 1 && this.gameRule.move('left')
          this.dataView()
          this.handleLocalData('put')
        }),
        PanGesture({ direction: PanDirection.Right }).onActionEnd(() => {
          this.gameRule.status === 1 && this.gameRule.move('right')
          this.dataView()
          this.handleLocalData('put')
        }),
        PanGesture({ direction: PanDirection.Up }).onActionEnd(() => {
          this.gameRule.status === 1 && this.gameRule.move('up')
          this.dataView()
          this.handleLocalData('put')
        }),
        PanGesture({ direction: PanDirection.Down }).onActionEnd(() => {
          this.gameRule.status === 1 && this.gameRule.move('down')
          this.dataView()
          this.handleLocalData('put')
        })
        ))


        if (this.gameStatus === -1) {
          Button('Start', { type: ButtonType.Normal })
            .borderRadius(5)
            .margin({ top: 50 })
            .width(200)
            .key('startGame')
            .onClick(() => {
              this.gameStart()
            })
        }
      }
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.Center)
      .height('100%')
      .width('100%')
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Start)
    .width('100%')
    .height('100%')
    .backgroundImage($r('app.media.gridBackground'))
    .backgroundImageSize(ImageSize.Cover)
  }
}


@CustomDialog
struct ScorePannel {
  controller: CustomDialogController
  gameStart: () => void
  curScore: number
  maxScore: number


  build() {
    Column() {
      Text('Game Over')
        .fontSize(30)
        .fontWeight(FontWeight.Medium)
        .margin({ top: 10 })


      Text('Score')
        .fontColor('#C8A584')
        .fontSize(20)
        .margin({ top: 10 })


      Text(`${this.curScore}`)
        .fontColor('#5D5D5D')
        .fontSize(40)
        .margin({ top: 10 })


      Text(`maxScore:${this.maxScore}`)
        .fontSize(20)
        .width('90%')
        .borderRadius(20)
        .margin({ top: 10 })
        .height(40)
        .textAlign(TextAlign.Center)


      Row() {
        Button('Reset', { type: ButtonType.Normal })
          .borderRadius(5)
          .margin({ top: 10 })
          .width(200)
          .onClick(() => {
            this.gameStart()
            this.controller.close()
          })
      }.justifyContent(FlexAlign.SpaceAround)
      .margin({ top: 10, bottom: 10 })
    }
    .backgroundColor('#f0f0f0')
    .borderRadius(25)
  }
}

(左右移动查看全部内容)

 

总结

基于eTS UI框架能够,基于各种组件进行快速的UI应用开发。

 

 

更多热点文章阅读

  • LiteOS-A内核中的procfs文件系统分析
  • 移植speexdsp到OpenHarmony标准系统②
  • 移植speexdsp到OpenHarmony标准系统③
  • 移植speexdsp到OpenHarmony标准系统④
  • 基于OpenHarmony的智能门禁系统,让出行更便捷
     

提示:本文由电子发烧友社区发布,转载请注明以上来源。如需社区合作及入群交流,请添加微信EEFans0806,或者发邮箱liuyong@huaqiu.com。


原文标题:鸿湖万联“竞”开发板体验:基于eTSUI框架的2048小游戏

文章出处:【微信公众号:电子发烧友开源社区】欢迎添加关注!文章转载请注明出处。


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分