基于ArkUI框架的舒爾特方格游戲
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
1. 效果圖直接先上:
B站蠟筆小新介紹游戲規(guī)則: https://www.bilibili.com/video/BV1E3411t7cK?spm_id_from=333.999.0.0
動(dòng)圖主界面游戲界面




2. 項(xiàng)目結(jié)構(gòu)圖


3. 項(xiàng)目開(kāi)發(fā)介紹
舒爾特方格游戲有主界面和游戲界面兩個(gè)頁(yè)面組成,主界面拆開(kāi)為title和body兩個(gè)自定義組件組成,游戲界面拆開(kāi)為title,body和footer三個(gè)自定義組件組成,utils為隨機(jī)生成數(shù)字公共類。下面我們來(lái)一個(gè)一個(gè)界面和組件介紹:
3.1 主界面代碼,只是一個(gè)程序入口,具體頁(yè)面布局在自定義組件實(shí)現(xiàn):
3.1.1 Index代碼
- import { Title } from '../common/home/title'
- import { Body } from '../common/home/body'
- @Entry
- @Component
- struct Index {
- build() {
- Column() {
- // 標(biāo)題
- Title();
- // 游戲主界面
- Body();
- }
- .alignItems(HorizontalAlign.Center)
- }
- }
3.1.2 Title自定義組件代碼:
- @Component
- export struct Title {
- build() {
- // 主界面標(biāo)題
- Column() {
- Text("舒爾特方格")
- .fontSize(34).margin({top: 30})
- .fontWeight(FontWeight.Bold)
- Text("SchulteGrid")
- .fontSize(20).margin({top: 3, bottom: 60})
- .fontWeight(FontWeight.Bold)
- }
- .width('100%')
- }
- }
3.1.3 Body自定義組件代碼
- import router from '@system.router'
- @Component
- export struct Body {
- build() {
- Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
- // 3x3, 4x4, 5x5 按鈕布局
- Row() {
- Button("3X3", { type: ButtonType.Circle, stateEffect: true })
- .width(70).height(70).backgroundColor(0x317aff).fontSize(20)
- .onClick(() => { this.startGame(3) })
- Button("4X4", { type: ButtonType.Circle, stateEffect: true })
- .width(70).height(70).backgroundColor(0x317aff).fontSize(20)
- .margin({left: 30, right: 30})
- .onClick(() => { this.startGame(4) })
- Button("5X5", { type: ButtonType.Circle, stateEffect: true })
- .width(70).height(70).backgroundColor(0x317aff).fontSize(20)
- .onClick(() => { this.startGame(5) })
- }.alignItems(VerticalAlign.Center).margin({bottom: 30})
- // 6x6, 7x7 按鈕布局
- Row() {
- Button("6X6", { type: ButtonType.Circle, stateEffect: true })
- .width(70).height(70).backgroundColor(0x317aff).fontSize(20)
- .onClick(() => { this.startGame(6) })
- Button("7X7", { type: ButtonType.Circle, stateEffect: true })
- .width(70).height(70).backgroundColor(0x317aff).fontSize(20)
- .margin({left: 30}).onClick(() => { this.startGame(7) })
- }.alignItems(VerticalAlign.Center).margin({bottom: 30})
- // 8x8, 9x9 按鈕布局
- Row() {
- Button("8X8", { type: ButtonType.Circle, stateEffect: true })
- .width(70).height(70).backgroundColor(0x317aff).fontSize(20)
- .onClick(() => { this.startGame(8) })
- Button("9X9", { type: ButtonType.Circle, stateEffect: true })
- .width(70).height(70).backgroundColor(0x317aff).fontSize(20)
- .margin({left: 30})
- .onClick(() => { this.startGame(9) })
- }.alignItems(VerticalAlign.Center)
- }
- .width('100%')
- .height('100%')
- }
- // 開(kāi)始游戲
- startGame(idx:number) {
- router.push({
- uri: 'pages/game',
- params: {index: idx}
- })
- }
- }
3.2. 游戲界面代碼,具體頁(yè)面布局在自定義組件實(shí)現(xiàn):
3.2.1 Game代碼:
- import router from '@system.router'
- import { Title } from '../common/game/title'
- import { Body } from '../common/game/body'
- import { Footer } from '../common/game/footer'
- import { getRandomData } from '../utils/utils'
- @Entry
- @Component
- struct Game {
- // 接收主界面?zhèn)鬟f過(guò)來(lái)的陣列數(shù)字
- private idx: number = router.getParams().index
- @State index: number = this.idx
- // 調(diào)用函數(shù)隨機(jī)生成相應(yīng)的字符數(shù)字?jǐn)?shù)組
- @State numArray: String[] = getRandomData(this.idx)
- // 與body和footer子組件綁定, 變化時(shí), body和footer子組件也會(huì)跟著變化
- @State time: number = 0
- build() {
- Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
- // 標(biāo)題和返回按鈕
- Title()
- // 游戲界面
- Body({idx: $index, numArray: $numArray, time: $time})
- // 狀態(tài)框
- Footer({idx: $index, time: $time})
- }
- .width('100%')
- .height('100%')
- }
- }
3.3.2 游戲Title自定義組件代碼:
- import router from '@system.router'
- @Component
- export struct Title {
- build() {
- Row() {
- // 返回游戲主界面
- Image($r("app.media.back"))
- .objectFit(ImageFit.Contain)
- .width(50)
- .height(50)
- .margin({ right: 10 })
- .onClick(()=>{ this.onBack() })
- Text("游戲開(kāi)始")
- .fontSize(24)
- .fontColor(Color.White)
- .fontWeight(FontWeight.Bold)
- }
- .width('100%')
- .padding({ top: 10, bottom: 10})
- .backgroundColor(0x317aff)
- }
- // 回退
- onBack() {
- router.back();
- }
- }
3.2.3 游戲Body自定義組件代碼:
- @Component
- export struct Body {
- // 與游戲父組件綁定, 記錄當(dāng)前的陣列數(shù)字
- @Link idx: number;
- // 與游戲父組件綁定, 顯示相應(yīng)的數(shù)字按鈕
- @Link numArray: String[];
- // 與游戲父組件綁定, 變化時(shí), 父組件time變量也跟著變化, 同時(shí)footer子組件也會(huì)跟著變化
- @Link time: number;
- // 根據(jù)不同的陣列, 按鈕寬高顯示不同的大小
- private btnSize: number[] = [32, 18, 12, 8, 6, 4, 4]
- // 根據(jù)不同的陣列, 按鈕字段顯示不同大小
- private btnFont: number[] = [32, 24, 22, 12, 7, 8, 6]
- // 根據(jù)不同的陣列, 顯示不同界面高度
- private gridHeight: number[] = [48, 48, 48, 44, 46, 50, 66]
- // 根據(jù)不同的陣列, 顯示不同的行列
- 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']
- // 記錄當(dāng)前點(diǎn)擊的數(shù)字
- private flagNum: number = 1
- // 開(kāi)始計(jì)時(shí)
- private startTime: number = new Date().getTime()
- build() {
- Grid() {
- // 循環(huán)顯示陣列數(shù)字按鈕
- ForEach(this.numArray, (day: string) => {
- GridItem() {
- Button(day, { type: ButtonType.Circle, stateEffect: true })
- .width(this.btnSize[this.idx-3] * this.idx)
- .height(this.btnSize[this.idx-3] * this.idx)
- .backgroundColor(0x317aff).fontSize(this.btnFont[this.idx-3])
- .onClick(() => { this.startGame(Number(day)) })
- }
- }, day => day)
- }
- // 根據(jù)相應(yīng)的陣列數(shù)字,顯示相應(yīng)的列數(shù)字
- .columnsTemplate(this.template[this.idx-3])
- // 根據(jù)相應(yīng)的陣列數(shù)字,顯示相應(yīng)的行數(shù)字
- .rowsTemplate(this.template[this.idx-3])
- .columnsGap(10)
- .rowsGap(10)
- .width(96+'%')
- .height(this.gridHeight[this.idx-3]+'%')
- }
- // 開(kāi)始游戲
- startGame(num:number) {
- // 如果當(dāng)前點(diǎn)擊的數(shù)字等于陣列數(shù)組長(zhǎng)度, 說(shuō)明點(diǎn)擊到最后一個(gè)數(shù)字, 彈出挑戰(zhàn)成功, 計(jì)算出總共耗時(shí)
- if (num == this.numArray.length && this.flagNum == this.numArray.length ) {
- AlertDialog.show({ message: '恭喜您挑戰(zhàn)成功'})
- this.time = (new Date().getTime() - this.startTime) * 1.0 / 1000
- }
- // 如果點(diǎn)擊的數(shù)字大于累計(jì)的數(shù)字,彈出提醒信息
- if (num > this.flagNum) {
- AlertDialog.show({ message: '請(qǐng)點(diǎn)擊小于此數(shù)字'})
- // 如果點(diǎn)擊的數(shù)字小于累計(jì)的數(shù)字,彈出提醒信息
- } else if (num < this.flagNum) {
- AlertDialog.show({ message: '當(dāng)前點(diǎn)擊的數(shù)字,已點(diǎn)擊過(guò)'})
- // 否則累計(jì)數(shù)字加1
- } else {
- this.flagNum++
- }
- }
- }
3.2.4 游戲Footer自定義組件代碼:
- @Component
- export struct Footer {
- // 與game父組件綁定, 記錄當(dāng)前的陣列數(shù)字
- @Link idx: number;
- // 與game父組件綁定, 變化時(shí), 父組件time變量也跟著變化, 同時(shí)footer子組件也會(huì)跟著變化
- @Link time: number;
- build() {
- Stack({ alignContent: Alignment.Bottom }) {
- Row() {
- // 耗時(shí)
- Button({ type: ButtonType.Capsule, stateEffect: false }) {
- Row() {
- Image($r('app.media.trophy')).width(20).height(20).margin({ left: 12 })
- Text(this.time + '"').fontSize(16).fontColor(0xffffff).margin({ left: 5, right: 12 })
- }.alignItems(VerticalAlign.Center).width(100)
- }.backgroundColor(0x317aff).opacity(0.7).width(100)
- // 顯示計(jì)時(shí)中
- Button({ type: ButtonType.Capsule, stateEffect: false }) {
- Row() {
- Image($r('app.media.time')).width(20).height(20).margin({ left: 12 })
- Text('計(jì)時(shí)中').fontSize(16).fontColor(0xffffff).margin({ left: 5, right: 12 })
- }.alignItems(VerticalAlign.Center).width(100)
- }.backgroundColor(0x317aff).opacity(0.7).width(100)
- .margin({left: 20, right: 20})
- // 幫助功能
- Button({ type: ButtonType.Capsule, stateEffect: true }) {
- Row() {
- Image($r('app.media.help')).width(20).height(20).margin({ left: 12 })
- Text('幫助').fontSize(16).fontColor(0xffffff).margin({ left: 5, right: 12 })
- }.alignItems(VerticalAlign.Center).width(100)
- }.backgroundColor(0x317aff).width(100)
- .onClick(() => { this.showHelp() })
- }
- }.width('100%').height(100).margin({ top: 5, bottom: 10 })
- }
- // 提示游戲幫助
- showHelp() {
- AlertDialog.show({ message: '以最快速度從 1 選到 ' + (this.idx*this.idx) })
- }
- }
3.3. Utils公共函數(shù)實(shí)現(xiàn):
- /**
- * 隨機(jī)生成1-count參數(shù)的整數(shù)
- * @param idx
- */
- export function getRandomData(idx:number): Array<String> {
- // 生成count個(gè)數(shù)字
- let count:number = idx * idx;
- // 存儲(chǔ)生成的字符數(shù)字
- let result:Array<String> = [];
- do {
- // 隨機(jī)生成一個(gè)指定范圍的數(shù)字
- let num = Math.floor(Math.random() * count + 1);
- // 如果數(shù)字不在數(shù)組里, 存儲(chǔ)到數(shù)組
- if (-1 == result.indexOf(num+'')) {
- result.push(num+'');
- }
- // 如果隨機(jī)生成的數(shù)字存儲(chǔ)到數(shù)組的長(zhǎng)度等于陣列數(shù), 跳出死循環(huán)
- if (count == result.length) {
- break;
- }
- }while(true)
- // 返回?cái)?shù)組
- return result;
- };
**總結(jié):**看到主界面和游戲界面代碼,是不是很簡(jiǎn)潔,聲明式開(kāi)發(fā)范式之美,那你還等什么?跟上步伐開(kāi)始聲明式開(kāi)發(fā)吧!!!
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)