谷动谷力

 找回密码
 立即注册
查看: 459|回复: 0
打印 上一主题 下一主题
收起左侧

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

[复制链接]
跳转到指定楼层
楼主
发表于 2022-12-6 12:45:52 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
鸿湖万联“竞”开发板体验:基于eTSUI框架的2048小游戏


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

过程

打开DevEco Studio 3.0.0.993


打开工程:





更新:




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





代码分析

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

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

  2. const TAG: string = `[App]`

  3. export default {
  4. onCreate() {
  5. Logger.info(TAG, `Application onCreate`)
  6. },
  7. onDestroy() {
  8. Logger.info(TAG, `Application onDestroy`)
  9. },
  10. }
复制代码


逻辑代码位于:src/main/ets/MainAbility/model/GameRule.ts//gameStatus
  1. //gameStatus
  2. enum GameStatus {
  3.     BEFORE = -1,
  4.     RUNNING = 1,
  5.     OVER = 0
  6. }

  7. export class GameRule {
  8.     private row: number = 4
  9.     private column: number = 4

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

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

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

  19.     //random
  20.     randomNum() {
  21.         do {
  22.             let a = Math.floor(Math.random() * this.dataNumbers.length)
  23.             if (this.dataNumbers[a] === 0) {
  24.                 let num = Math.random() > 0.3 ? 2 : 4
  25.                 this.dataNumbers[a] = num
  26.                 break
  27.             }
  28.         } while (this.dataNumbers.some((val) => {
  29.             return val === 0
  30.         }))
  31.     }

  32.     //init
  33.     init() {
  34.         this.dataNumbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  35.         this.status = GameStatus.RUNNING
  36.         this.score = 0
  37.         this.randomNum()
  38.         this.randomNum()
  39.     }

  40.     //move
  41.     move(direction: string) {
  42.         let before = String(this.dataNumbers)
  43.         let length = (direction === 'left' || direction === 'right') ? this.row : this.column
  44.         for (let a = 0;a < length; a++) {
  45.             this.moveInRow(direction, a)
  46.         }
  47.         let after = String(this.dataNumbers)
  48.         if (before !== after) {
  49.             this.randomNum()
  50.             if (this.isGameOver()) {
  51.                 this.status = GameStatus.OVER
  52.             }
  53.         }
  54.     }

  55.     //prepare to move
  56.     moveInRow(direction: string, a: number) {
  57.         if (direction === 'left') {
  58.             for (let b = 0;b < this.column - 1; b++) {
  59.                 let next = this.moveNext(a, b, direction)
  60.                 b = this.dataChange(a, b, next, direction).b
  61.                 if (next === -1) break
  62.             }
  63.         } else if (direction === 'right') {
  64.             for (let b = this.column - 1;b > 0; b--) {
  65.                 let next = this.moveNext(a, b, direction)
  66.                 b = this.dataChange(a, b, next, direction).b
  67.                 if (next === -1) break
  68.             }
  69.         } else if (direction === 'up') {
  70.             for (let b = 0;b < this.row - 1; b++) {
  71.                 let next = this.moveNext(b, a, direction)
  72.                 b = this.dataChange(b, a, next, direction).a
  73.                 if (next === -1) break
  74.             }
  75.         } else if (direction === 'down') {
  76.             for (let b = this.row - 1;b > 0; b--) {
  77.                 let next = this.moveNext(b, a, direction)
  78.                 b = this.dataChange(b, a, next, direction).a
  79.                 if (next === -1) break
  80.             }
  81.         }
  82.     }

  83.     //new number moveStatus
  84.     moveNext(a: number, b: number, direction: string) {
  85.         if (direction === 'left') {
  86.             for (let i = b + 1;i < this.column; i++) {
  87.                 if (this.dataNumbers[this.index(a, i)] !== 0) {
  88.                     return i
  89.                 }
  90.             }
  91.         } else if (direction === 'right') {
  92.             for (let i = b - 1;i >= 0; i--) {
  93.                 if (this.dataNumbers[this.index(a, i)] !== 0) {
  94.                     return i
  95.                 }
  96.             }
  97.         } else if (direction === 'up') {
  98.             for (let i = a + 1;i < 4; i++) {
  99.                 if (this.dataNumbers[this.index(i, b)] !== 0) {
  100.                     return i
  101.                 }
  102.             }
  103.         } else if (direction === 'down') {
  104.             for (let i = a - 1;i >= 0; i--) {
  105.                 if (this.dataNumbers[this.index(i, b)] !== 0) {
  106.                     return i
  107.                 }
  108.             }
  109.         }
  110.         return -1
  111.     }

  112.     //get gameStatus
  113.     isGameOver() {
  114.         for (let a = 0;a < this.row; a++) {
  115.             for (let b = 0;b < this.column; b++) {
  116.                 let tempA = this.index(a, b)
  117.                 if (this.dataNumbers[tempA] === 0) {
  118.                     return false
  119.                 }
  120.                 if (a < this.row - 1) {
  121.                     if (this.dataNumbers[tempA] === this.dataNumbers[this.index(a + 1, b)]) {
  122.                         return false
  123.                     }
  124.                 }
  125.                 if (b < this.column - 1) {
  126.                     if (this.dataNumbers[tempA] === this.dataNumbers[tempA+1]) {
  127.                         return false
  128.                     }
  129.                 }
  130.             }
  131.         }
  132.         return true
  133.     }

  134.     //move and merge
  135.     dataChange(a: number, b: number, next: number, direction: string) {
  136.         let tempA = this.index(a, b)
  137.         let tempB = 0
  138.         if (direction === 'left' || direction === 'right') {
  139.             tempB = this.index(a, next)
  140.         } else {
  141.             tempB = this.index(next, b)
  142.         }
  143.         if (next !== -1) {
  144.             if (this.dataNumbers[tempA] === 0) {
  145.                 this.dataNumbers[tempA] = this.dataNumbers[tempB]
  146.                 this.dataNumbers[tempB] = 0
  147.                 direction === 'left' && b--
  148.                 direction === 'right' && b++
  149.                 direction === 'up' && a--
  150.                 direction === 'down' && a++
  151.             } else if (this.dataNumbers[tempA] === this.dataNumbers[tempB]) {
  152.                 this.dataNumbers[tempA] *= 2
  153.                 this.score += this.dataNumbers[tempA]
  154.                 this.dataNumbers[tempB] = 0
  155.             }
  156.         }
  157.         return {
  158.             a, b
  159.         }
  160.     }
复制代码

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

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

  3.   public totalCount(): number {
  4.     return 0
  5.   }

  6.   public getData(index: number): any {
  7.     return undefined
  8.   }

  9.   registerDataChangeListener(listener: DataChangeListener): void {
  10.     if (this.listeners.indexOf(listener) < 0) {
  11.       this.listeners.push(listener)
  12.     }
  13.   }

  14.   unregisterDataChangeListener(listener: DataChangeListener): void {
  15.     const pos = this.listeners.indexOf(listener);
  16.     if (pos >= 0) {
  17.       this.listeners.splice(pos, 1)
  18.     }
  19.   }

  20.   notifyDataReload(): void {
  21.     this.listeners.forEach(listener => {
  22.       listener.onDataReloaded()
  23.     })
  24.   }

  25.   notifyDataAdd(index: number): void {
  26.     this.listeners.forEach(listener => {
  27.       listener.onDataAdded(index)
  28.     })
  29.   }

  30.   notifyDataChange(index: number): void {
  31.     this.listeners.forEach(listener => {
  32.       listener.onDataChanged(index)
  33.     })
  34.   }
  35. }

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

  38.   constructor(ele) {
  39.     super()
  40.     for (var index = 0;index < ele.length; index++) {
  41.       this.dataArray.push(ele[index])
  42.     }
  43.   }

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

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

  50.   public addData(index: number, data: string): void {
  51.     this.dataArray.splice(index, 0)
  52.     this.notifyDataAdd(index)
  53.   }
  54. }

  55. enum GameStatus {
  56.   BEFORE = -1,
  57.   RUNNING = 1,
  58.   OVER = 0
  59. }

  60. import display from '@ohos.display'
  61. import Logger from '../model/Logger'
  62. import dataStorage from '@ohos.data.storage'
  63. import { GameRule } from '../model/GameRule'
  64. import featureAbility from '@ohos.ability.featureAbility'

  65. const TAG = '[Game2048]'

  66. @Entry
  67. @Component
  68. struct Game2048 {
  69.   @State dataNumbers: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  70.   private gameRule: GameRule = new GameRule(this.dataNumbers)
  71.   @State maxScore: number = 123456
  72.   @State curScore: number = 0
  73.   @State @Watch("onGameOver") gameStatus: number = GameStatus.BEFORE
  74.   @State textColor: string[] = ['#f0fff0', '#eee3da', '#ede0c8', '#f2b179', '#f59563', '#f67c5f', '#f65e3b', '#edcf72', '#edcc61', '#9c0', '#33b5e5', '#09c', '#a6c', '#93c']
  75.   dialogController: CustomDialogController = new CustomDialogController({
  76.     builder: ScorePannel({
  77.       curScore: this.curScore,
  78.       maxScore: this.maxScore,
  79.       gameStart: this.gameStart.bind(this)
  80.     }),
  81.     autoCancel: false
  82.   })
  83.   @State screenSize: {
  84.     x: number,
  85.     y: number
  86.   } = { x: px2vp(1080), y: px2vp(0) }

  87.   //gameStatus listener
  88.   onGameOver() {
  89.     if (this.gameStatus === GameStatus.OVER) {
  90.       this.curScore = this.gameRule.score
  91.       this.dialogController.open()
  92.     }
  93.   }

  94.   //restart game
  95.   gameStart() {
  96.     this.gameRule.init()
  97.     this.dataNumbers = this.gameRule.dataNumbers
  98.     this.gameStatus = GameStatus.RUNNING
  99.     this.handleLocalData('put')
  100.   }

  101.   //dataView
  102.   dataView() {
  103.     this.dataNumbers = this.gameRule.dataNumbers
  104.     this.gameStatus = this.gameRule.status
  105.     this.curScore = this.gameRule.score
  106.   }

  107.   aboutToAppear() {
  108.     display.getDefaultDisplay((err, data) => {
  109.       if (data.height > data.width) {
  110.         this.screenSize = { x: px2vp(data.width), y: px2vp(data.height) }
  111.       } else {
  112.         this.screenSize = { x: px2vp(750), y: px2vp(data.width) }
  113.       }
  114.       Logger.info(TAG, `宽 ${this.screenSize.x}`)
  115.       Logger.info(TAG, `高 ${this.screenSize.y}`)
  116.     })
  117.     this.handleLocalData('has')
  118.   }

  119.   //handle local data
  120.   handleLocalData(method: string) {
  121.     let context = featureAbility.getContext()
  122.     context.getFilesDir((err, path) => {
  123.       let storage = dataStorage.getStorageSync(path + '/mystore')
  124.       if (method === 'put') {
  125.         storage.putSync('gameData', JSON.stringify(this.dataNumbers))
  126.         let score: string = this.gameRule.score.toString()
  127.         storage.putSync('score', score)
  128.         storage.putSync('gameStatus', this.gameRule.status.toString())
  129.         storage.flushSync()
  130.       } else if (method === 'has') {
  131.         if (storage.hasSync('gameData')) {
  132.           this.gameRule.score = this.curScore = Number(storage.getSync('score', 'string'))
  133.           this.gameStatus = this.gameRule.status = Number(storage.getSync('gameStatus', 'string'))
  134.           this.dataNumbers = this.gameRule.dataNumbers = JSON.parse(storage.getSync('gameData', 'string').toString())
  135.         }
  136.       }
  137.     })
  138.   }

  139.   build() {
  140.     Column() {
  141.       Column() {
  142.         Row() {
  143.           Image($r('app.media.logo2048'))
  144.             .width(this.screenSize.x * 0.25)
  145.             .height(this.screenSize.x * 0.25)

  146.           Column() {
  147.             Text('Score')
  148.               .fontSize('30px')
  149.               .fontColor('#efe1d3')
  150.               .fontWeight(FontWeight.Bolder)

  151.             Text(`${this.gameRule.score}`)
  152.               .fontSize('30px')
  153.               .fontColor('#fcf8f5')
  154.               .fontWeight(FontWeight.Bolder)
  155.           }
  156.           .alignItems(HorizontalAlign.Center)
  157.           .justifyContent(FlexAlign.Center)
  158.           .backgroundColor('#bbada0')
  159.           .width(this.screenSize.x * 0.25)
  160.           .height(this.screenSize.x * 0.25)
  161.           .borderRadius(15)

  162.           Column() {
  163.             Text('Max')
  164.               .fontSize('50px')
  165.               .fontColor('#efe1d3')
  166.               .fontWeight(FontWeight.Bolder)

  167.             Text(`${this.maxScore}`)
  168.               .fontSize('30px')
  169.               .fontColor('#fcf8f5')
  170.               .fontWeight(FontWeight.Bolder)
  171.           }
  172.           .alignItems(HorizontalAlign.Center)
  173.           .justifyContent(FlexAlign.Center)
  174.           .backgroundColor('#bbada0')
  175.           .width(this.screenSize.x * 0.25)
  176.           .height(this.screenSize.x * 0.25)
  177.           .borderRadius(15)
  178.         }
  179.         .alignItems(VerticalAlign.Center)
  180.         .justifyContent(FlexAlign.SpaceAround)
  181.         .margin({ bottom: 20 })
  182.         .width(this.screenSize.x)

  183.         Grid() {
  184.           LazyForEach(new MyDataSource(this.dataNumbers), (item) => {
  185.             GridItem() {
  186.               Text(`${item === 0 ? '' : item}`)
  187.                 .fontSize('85px')
  188.                 .fontColor(item <= 4 ? '#000' : '#fcf8f5')
  189.                 .fontWeight(FontWeight.Bolder)
  190.                 .backgroundColor('#f0fff0')
  191.                 .width('100%')
  192.                 .height('100%')
  193.                 .textAlign(TextAlign.Center)
  194.                 .borderRadius(10)
  195.                 .backgroundColor(this.textColor[(Math.log(item) / Math.log(2))])
  196.             }
  197.           })
  198.         }
  199.         .columnsTemplate('1fr 1fr 1fr 1fr')
  200.         .rowsTemplate('1fr 1fr 1fr 1fr')
  201.         .columnsGap(10)
  202.         .rowsGap(10)
  203.         .width(this.screenSize.x)
  204.         .padding(10)
  205.         .backgroundColor('rgba(80,69,46,0.26)')
  206.         .height(this.screenSize.x)
  207.         .borderRadius(10)
  208.         .gesture(GestureGroup(GestureMode.Exclusive,
  209.         PanGesture({ direction: PanDirection.Left }).onActionEnd(() => {
  210.           this.gameRule.status === 1 && this.gameRule.move('left')
  211.           this.dataView()
  212.           this.handleLocalData('put')
  213.         }),
  214.         PanGesture({ direction: PanDirection.Right }).onActionEnd(() => {
  215.           this.gameRule.status === 1 && this.gameRule.move('right')
  216.           this.dataView()
  217.           this.handleLocalData('put')
  218.         }),
  219.         PanGesture({ direction: PanDirection.Up }).onActionEnd(() => {
  220.           this.gameRule.status === 1 && this.gameRule.move('up')
  221.           this.dataView()
  222.           this.handleLocalData('put')
  223.         }),
  224.         PanGesture({ direction: PanDirection.Down }).onActionEnd(() => {
  225.           this.gameRule.status === 1 && this.gameRule.move('down')
  226.           this.dataView()
  227.           this.handleLocalData('put')
  228.         })
  229.         ))

  230.         if (this.gameStatus === -1) {
  231.           Button('Start', { type: ButtonType.Normal })
  232.             .borderRadius(5)
  233.             .margin({ top: 50 })
  234.             .width(200)
  235.             .key('startGame')
  236.             .onClick(() => {
  237.               this.gameStart()
  238.             })
  239.         }
  240.       }
  241.       .alignItems(HorizontalAlign.Center)
  242.       .justifyContent(FlexAlign.Center)
  243.       .height('100%')
  244.       .width('100%')
  245.     }
  246.     .alignItems(HorizontalAlign.Center)
  247.     .justifyContent(FlexAlign.Start)
  248.     .width('100%')
  249.     .height('100%')
  250.     .backgroundImage($r('app.media.gridBackground'))
  251.     .backgroundImageSize(ImageSize.Cover)
  252.   }
  253. }

  254. @CustomDialog
  255. struct ScorePannel {
  256.   controller: CustomDialogController
  257.   gameStart: () => void
  258.   curScore: number
  259.   maxScore: number

  260.   build() {
  261.     Column() {
  262.       Text('Game Over')
  263.         .fontSize(30)
  264.         .fontWeight(FontWeight.Medium)
  265.         .margin({ top: 10 })

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

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

  274.       Text(`maxScore:${this.maxScore}`)
  275.         .fontSize(20)
  276.         .width('90%')
  277.         .borderRadius(20)
  278.         .margin({ top: 10 })
  279.         .height(40)
  280.         .textAlign(TextAlign.Center)

  281.       Row() {
  282.         Button('Reset', { type: ButtonType.Normal })
  283.           .borderRadius(5)
  284.           .margin({ top: 10 })
  285.           .width(200)
  286.           .onClick(() => {
  287.             this.gameStart()
  288.             this.controller.close()
  289.           })
  290.       }.justifyContent(FlexAlign.SpaceAround)
  291.       .margin({ top: 10, bottom: 10 })
  292.     }
  293.     .backgroundColor('#f0f0f0')
  294.     .borderRadius(25)
  295.   }
  296. }
复制代码




总结

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

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|深圳市光明谷科技有限公司|光明谷商城|Sunshine Silicon Corpporation ( 粤ICP备14060730号|Sitemap

GMT+8, 2024-5-5 11:17 , Processed in 0.138948 second(s), 43 queries .

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表