基于ArkUI框架的舒尔特方格游戏

系统
舒尔特方格游戏有主界面和游戏界面两个页面组成,主界面拆开为title和body两个自定义组件组成,游戏界面拆开为title,body和footer三个自定义组件组成,utils为随机生成数字公共类。

[[440822]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

1. 效果图直接先上:

B站蜡笔小新介绍游戏规则: https://www.bilibili.com/video/BV1E3411t7cK?spm_id_from=333.999.0.0

动图主界面游戏界面

基于ArkUI框架的舒尔特方格游戏-鸿蒙HarmonyOS技术社区基于ArkUI框架的舒尔特方格游戏-鸿蒙HarmonyOS技术社区基于ArkUI框架的舒尔特方格游戏-鸿蒙HarmonyOS技术社区
基于ArkUI框架的舒尔特方格游戏-鸿蒙HarmonyOS技术社区

2. 项目结构图

基于ArkUI框架的舒尔特方格游戏-鸿蒙HarmonyOS技术社区
基于ArkUI框架的舒尔特方格游戏-鸿蒙HarmonyOS技术社区

3. 项目开发介绍

舒尔特方格游戏有主界面和游戏界面两个页面组成,主界面拆开为title和body两个自定义组件组成,游戏界面拆开为title,body和footer三个自定义组件组成,utils为随机生成数字公共类。下面我们来一个一个界面和组件介绍:

3.1 主界面代码,只是一个程序入口,具体页面布局在自定义组件实现:

3.1.1 Index代码

  1. import { Title } from '../common/home/title' 
  2. import { Body } from '../common/home/body' 
  3.  
  4. @Entry 
  5. @Component 
  6. struct Index { 
  7.   build() { 
  8.     Column() { 
  9.       // 标题 
  10.       Title(); 
  11.       // 游戏主界面 
  12.       Body(); 
  13.     } 
  14.     .alignItems(HorizontalAlign.Center) 
  15.   } 

 3.1.2 Title自定义组件代码:

  1. @Component 
  2. export struct Title { 
  3.   build() { 
  4.     // 主界面标题 
  5.     Column() { 
  6.       Text("舒尔特方格"
  7.         .fontSize(34).margin({top: 30}) 
  8.         .fontWeight(FontWeight.Bold) 
  9.       Text("SchulteGrid"
  10.         .fontSize(20).margin({top: 3, bottom: 60}) 
  11.         .fontWeight(FontWeight.Bold) 
  12.     } 
  13.     .width('100%'
  14.   } 

 3.1.3 Body自定义组件代码

  1. import router from '@system.router' 
  2.  
  3. @Component 
  4. export struct Body { 
  5.   build() { 
  6.     Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { 
  7.       // 3x3, 4x4, 5x5 按钮布局 
  8.       Row() { 
  9.         Button("3X3", { type: ButtonType.Circle, stateEffect: true }) 
  10.           .width(70).height(70).backgroundColor(0x317aff).fontSize(20) 
  11.           .onClick(() => { this.startGame(3) }) 
  12.         Button("4X4", { type: ButtonType.Circle, stateEffect: true }) 
  13.           .width(70).height(70).backgroundColor(0x317aff).fontSize(20) 
  14.           .margin({left: 30, right: 30}) 
  15.           .onClick(() => { this.startGame(4) }) 
  16.         Button("5X5", { type: ButtonType.Circle, stateEffect: true }) 
  17.           .width(70).height(70).backgroundColor(0x317aff).fontSize(20) 
  18.           .onClick(() => { this.startGame(5) }) 
  19.       }.alignItems(VerticalAlign.Center).margin({bottom: 30}) 
  20.       // 6x6, 7x7 按钮布局 
  21.       Row() { 
  22.         Button("6X6", { type: ButtonType.Circle, stateEffect: true }) 
  23.           .width(70).height(70).backgroundColor(0x317aff).fontSize(20) 
  24.           .onClick(() => { this.startGame(6) }) 
  25.         Button("7X7", { type: ButtonType.Circle, stateEffect: true }) 
  26.           .width(70).height(70).backgroundColor(0x317aff).fontSize(20) 
  27.           .margin({left: 30}).onClick(() => { this.startGame(7) }) 
  28.       }.alignItems(VerticalAlign.Center).margin({bottom: 30}) 
  29.       // 8x8, 9x9 按钮布局 
  30.       Row() { 
  31.         Button("8X8", { type: ButtonType.Circle, stateEffect: true }) 
  32.           .width(70).height(70).backgroundColor(0x317aff).fontSize(20) 
  33.           .onClick(() => { this.startGame(8) }) 
  34.         Button("9X9", { type: ButtonType.Circle, stateEffect: true }) 
  35.           .width(70).height(70).backgroundColor(0x317aff).fontSize(20) 
  36.           .margin({left: 30}) 
  37.           .onClick(() => { this.startGame(9) }) 
  38.       }.alignItems(VerticalAlign.Center) 
  39.     } 
  40.     .width('100%'
  41.     .height('100%'
  42.   } 
  43.  
  44.   // 开始游戏 
  45.   startGame(idx:number) { 
  46.     router.push({ 
  47.       uri: 'pages/game'
  48.       params: {index: idx} 
  49.     }) 
  50.   } 

3.2. 游戏界面代码,具体页面布局在自定义组件实现:

3.2.1 Game代码:

  1. import router from '@system.router' 
  2. import { Title } from '../common/game/title' 
  3. import { Body } from '../common/game/body' 
  4. import { Footer } from '../common/game/footer' 
  5. import { getRandomData } from '../utils/utils' 
  6.  
  7. @Entry 
  8. @Component 
  9. struct Game { 
  10.   // 接收主界面传递过来的阵列数字 
  11.   private idx: number = router.getParams().index 
  12.   @State index: number = this.idx 
  13.   // 调用函数随机生成相应的字符数字数组 
  14.   @State numArray: String[] = getRandomData(this.idx) 
  15.   // 与body和footer子组件绑定, 变化时, body和footer子组件也会跟着变化 
  16.   @State time: number = 0 
  17.  
  18.   build() { 
  19.     Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) { 
  20.       // 标题和返回按钮 
  21.       Title() 
  22.       // 游戏界面 
  23.       Body({idx: $index, numArray: $numArray, time: $time}) 
  24.       // 状态框 
  25.       Footer({idx: $indextime: $time}) 
  26.     } 
  27.     .width('100%'
  28.     .height('100%'
  29.   } 

 3.3.2 游戏Title自定义组件代码:

  1. import router from '@system.router' 
  2.  
  3. @Component 
  4. export struct Title { 
  5.   build() { 
  6.     Row() { 
  7.       // 返回游戏主界面 
  8.       Image($r("app.media.back")) 
  9.         .objectFit(ImageFit.Contain) 
  10.         .width(50) 
  11.         .height(50) 
  12.         .margin({ right: 10 }) 
  13.         .onClick(()=>{ this.onBack() }) 
  14.       Text("游戏开始"
  15.         .fontSize(24) 
  16.         .fontColor(Color.White) 
  17.         .fontWeight(FontWeight.Bold) 
  18.     } 
  19.     .width('100%'
  20.     .padding({ top: 10, bottom: 10}) 
  21.     .backgroundColor(0x317aff) 
  22.   } 
  23.   // 回退 
  24.   onBack() { 
  25.     router.back(); 
  26.   } 

3.2.3 游戏Body自定义组件代码:

  1. @Component 
  2. export struct Body { 
  3.   // 与游戏父组件绑定, 记录当前的阵列数字 
  4.   @Link idx: number; 
  5.   // 与游戏父组件绑定, 显示相应的数字按钮 
  6.   @Link numArray: String[]; 
  7.   // 与游戏父组件绑定, 变化时, 父组件time变量也跟着变化, 同时footer子组件也会跟着变化 
  8.   @Link time: number; 
  9.  
  10.   // 根据不同的阵列, 按钮宽高显示不同的大小 
  11.   private btnSize: number[] = [32, 18, 12, 8, 6, 4, 4] 
  12.   // 根据不同的阵列, 按钮字段显示不同大小 
  13.   private btnFont: number[] = [32, 24, 22, 12, 7, 8, 6] 
  14.   // 根据不同的阵列, 显示不同界面高度 
  15.   private gridHeight: number[] = [48, 48, 48, 44, 46, 50, 66] 
  16.   // 根据不同的阵列, 显示不同的行列 
  17.   private template: string[] = ['1fr 1fr 1fr''1fr 1fr 1fr 1fr''1fr 1fr 1fr 1fr 1fr''1fr 1fr 1fr 1fr 1fr 1fr''1fr 1fr 1fr 1fr 1fr 1fr 1fr''1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr''1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr'
  18.   // 记录当前点击的数字 
  19.   private flagNum: number = 1 
  20.   // 开始计时 
  21.   private startTime: number = new Date().getTime() 
  22.  
  23.   build() { 
  24.     Grid() { 
  25.       // 循环显示阵列数字按钮 
  26.       ForEach(this.numArray, (day: string) => { 
  27.         GridItem() { 
  28.           Button(day, { type: ButtonType.Circle, stateEffect: true }) 
  29.             .width(this.btnSize[this.idx-3] * this.idx) 
  30.             .height(this.btnSize[this.idx-3] * this.idx) 
  31.             .backgroundColor(0x317aff).fontSize(this.btnFont[this.idx-3]) 
  32.             .onClick(() => { this.startGame(Number(day)) }) 
  33.         } 
  34.       }, day => day
  35.     } 
  36.     // 根据相应的阵列数字,显示相应的列数字 
  37.     .columnsTemplate(this.template[this.idx-3]) 
  38.     // 根据相应的阵列数字,显示相应的行数字 
  39.     .rowsTemplate(this.template[this.idx-3]) 
  40.     .columnsGap(10) 
  41.     .rowsGap(10) 
  42.     .width(96+'%'
  43.     .height(this.gridHeight[this.idx-3]+'%'
  44.   } 
  45.  
  46.   // 开始游戏 
  47.   startGame(num:number) { 
  48.     // 如果当前点击的数字等于阵列数组长度, 说明点击到最后一个数字, 弹出挑战成功, 计算出总共耗时 
  49.     if (num == this.numArray.length && this.flagNum == this.numArray.length ) { 
  50.       AlertDialog.show({ message: '恭喜您挑战成功'}) 
  51.       this.time = (new Date().getTime() - this.startTime) * 1.0 / 1000 
  52.     } 
  53.  
  54.     // 如果点击的数字大于累计的数字,弹出提醒信息 
  55.     if (num > this.flagNum) { 
  56.       AlertDialog.show({ message: '请点击小于此数字'}) 
  57.     // 如果点击的数字小于累计的数字,弹出提醒信息 
  58.     } else if (num < this.flagNum) { 
  59.       AlertDialog.show({ message: '当前点击的数字,已点击过'}) 
  60.     // 否则累计数字加1 
  61.     } else { 
  62.       this.flagNum++ 
  63.     } 
  64.   } 

 3.2.4 游戏Footer自定义组件代码:

  1. @Component 
  2. export struct Footer { 
  3.   // 与game父组件绑定, 记录当前的阵列数字 
  4.   @Link idx: number; 
  5.   // 与game父组件绑定, 变化时, 父组件time变量也跟着变化, 同时footer子组件也会跟着变化 
  6.   @Link time: number; 
  7.  
  8.   build() { 
  9.     Stack({ alignContent: Alignment.Bottom }) { 
  10.       Row() { 
  11.         // 耗时 
  12.         Button({ type: ButtonType.Capsule, stateEffect: false }) { 
  13.           Row() { 
  14.             Image($r('app.media.trophy')).width(20).height(20).margin({ left: 12 }) 
  15.             Text(this.time + '"').fontSize(16).fontColor(0xffffff).margin({ left: 5, right: 12 }) 
  16.           }.alignItems(VerticalAlign.Center).width(100) 
  17.         }.backgroundColor(0x317aff).opacity(0.7).width(100) 
  18.  
  19.         // 显示计时中 
  20.         Button({ type: ButtonType.Capsule, stateEffect: false }) { 
  21.           Row() { 
  22.             Image($r('app.media.time')).width(20).height(20).margin({ left: 12 }) 
  23.             Text('计时中').fontSize(16).fontColor(0xffffff).margin({ left: 5, right: 12 }) 
  24.           }.alignItems(VerticalAlign.Center).width(100) 
  25.         }.backgroundColor(0x317aff).opacity(0.7).width(100) 
  26.         .margin({left: 20, right: 20}) 
  27.  
  28.         // 帮助功能 
  29.         Button({ type: ButtonType.Capsule, stateEffect: true }) { 
  30.           Row() { 
  31.             Image($r('app.media.help')).width(20).height(20).margin({ left: 12 }) 
  32.             Text('帮助').fontSize(16).fontColor(0xffffff).margin({ left: 5, right: 12 }) 
  33.           }.alignItems(VerticalAlign.Center).width(100) 
  34.         }.backgroundColor(0x317aff).width(100) 
  35.         .onClick(() => { this.showHelp() }) 
  36.  
  37.       } 
  38.     }.width('100%').height(100).margin({ top: 5, bottom: 10 }) 
  39.   } 
  40.  
  41.   // 提示游戏帮助 
  42.   showHelp() { 
  43.     AlertDialog.show({ message: '以最快速度从 1 选到 ' + (this.idx*this.idx) }) 
  44.   } 

3.3. Utils公共函数实现:

  1. /** 
  2.  * 随机生成1-count参数的整数 
  3.  * @param idx 
  4.  */ 
  5. export function getRandomData(idx:number): Array<String> { 
  6.   // 生成count个数字 
  7.   let count:number = idx * idx; 
  8.   // 存储生成的字符数字 
  9.   let result:Array<String> = []; 
  10.  
  11.   do { 
  12.     // 随机生成一个指定范围的数字 
  13.     let num = Math.floor(Math.random() * count + 1); 
  14.     // 如果数字不在数组里, 存储到数组 
  15.     if (-1 == result.indexOf(num+'')) { 
  16.       result.push(num+''); 
  17.     } 
  18.  
  19.     // 如果随机生成的数字存储到数组的长度等于阵列数, 跳出死循环 
  20.     if (count == result.length) { 
  21.       break; 
  22.     } 
  23.  
  24.   }while(true
  25.   // 返回数组 
  26.   return result; 
  27. }; 

 **总结:**看到主界面和游戏界面代码,是不是很简洁,声明式开发范式之美,那你还等什么?跟上步伐开始声明式开发吧!!!

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2023-05-31 15:42:06

游戏开发关系型数据库

2022-07-06 20:40:27

舒尔特方格鸿蒙

2024-01-11 15:54:55

eTS语言TypeScript应用开发

2022-07-13 16:24:12

ArkUI(JS)打地鼠游戏

2022-03-17 15:28:18

五子棋HarmonyOSJSAPI

2021-12-01 15:38:33

鸿蒙HarmonyOS应用

2021-12-01 15:40:23

鸿蒙HarmonyOS应用

2022-08-04 13:55:08

拼数字小游戏鸿蒙

2022-08-22 17:28:34

ArkUI鸿蒙

2022-10-19 15:12:05

ArkUI鸿蒙

2021-11-02 14:52:17

鸿蒙HarmonyOS应用

2021-11-01 11:08:28

鸿蒙HarmonyOS应用

2024-05-31 08:43:31

2022-05-27 14:55:34

canvas画布鸿蒙

2022-08-25 21:41:43

ArkUI鸿蒙

2022-10-24 14:49:54

ArkUI心电图组件

2022-11-02 16:06:54

ArkUIETS

2010-08-10 09:11:12

Windows PhoNXA

2021-08-17 15:36:44

人工智能AI

2015-07-16 13:46:55

网络虚拟化网络虚拟
点赞
收藏

51CTO技术栈公众号