写点什么

鸿蒙开发案例:实现数字华容道游戏

作者:zhongcx
  • 2024-10-12
    广东
  • 本文字数:4835 字

    阅读完需:约 16 分钟

数字华容道游戏是一种经典的益智游戏,由 8 个编号为 1 至 8 的方块和一个空白格组成。玩家通过滑动方块将其按照正确的顺序排列。

【支持的功能】

1. 点击操作游戏支持通过点击数字方块来移动它们。当玩家点击一个可移动的方块时,该方块会自动滑向相邻的空白格。

2. 滑动操作除了点击操作之外,游戏还支持滑动手势来移动方块。玩家可以通过在屏幕上滑动来指示移动方向,游戏会检测滑动的方向并相应地移动方块。

3. 动画效果方块在移动时会有流畅的动画效果,这使得游戏的操作体验更加自然和直观。动画效果可以通过 shouldAnimate 状态开关来控制开启或关闭。

4. 游戏胜利提示当玩家成功将所有方块按照正确的顺序排列时,游戏会弹出一个对话框提示玩家胜利,并显示所用的时间。玩家可以选择重新开始游戏。

5. 重新开始按钮游戏提供了一个“重新开始”按钮,玩家可以在任何时候点击此按钮来重新开始游戏。重新开始会打乱游戏面板,使游戏回到初始状态。

【注意事项】

1. 确保游戏始终可解在设计游戏时,一个重要的注意事项是确保游戏面板被打乱后仍可解。如果随机打乱游戏面板,可能会导致某些情况下游戏无解。因此,我们在打乱游戏面板时,采用了模拟合法滑动的方式,即通过一系列合法的滑动操作来打乱面板,而不是简单地随机交换方块位置。这样可以确保每次游戏都有解。

2. 操作体验优化为了提供更好的操作体验,游戏设计时充分考虑了用户的交互习惯。点击和滑动两种操作方式的结合使得游戏更加灵活和方便。此外,动画效果的加入让方块的移动更加流畅自然,增强了游戏的趣味性和互动性。

【完整代码】

import { promptAction } from '@kit.ArkUI';@Entry@Componentstruct NumberPuzzle {  @State gameBoard: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 0]; // 游戏面板数据,0表示空白格  @State selectedTile: number = -1; // 当前选中的方块  @State isGameOver: boolean = false; // 游戏是否结束  @State cellSize: number = 100; // 单元格大小  @State cellMargin: number = 5; // 单元格边距  @State startTime: number = 0; // 游戏开始时间  @State screenStartX: number = 0; // 触摸开始时的屏幕X坐标  @State screenStartY: number = 0; // 触摸开始时的屏幕Y坐标  @State lastScreenX: number = 0; // 触摸结束时的屏幕X坐标  @State lastScreenY: number = 0; // 触摸结束时的屏幕Y坐标  @State shouldAnimate: boolean = true; // 控制动画是否开启
aboutToAppear(): void { this.shuffleGameBoard(); // 在组件出现之前打乱游戏面板 }
// 检查指定位置的方块是否可以移动到空白处 private canMove(tileIndex: number): boolean { const blankIndex = this.gameBoard.indexOf(0); const blankRow = Math.floor(blankIndex / 3); const blankCol = blankIndex % 3; const tileRow = Math.floor(tileIndex / 3); const tileCol = tileIndex % 3;
return ( (tileRow === blankRow && Math.abs(tileCol - blankCol) === 1) || (tileCol === blankCol && Math.abs(tileRow - blankRow) === 1) ); }
// 移动方块 private moveTile(tileIndex: number) { if (this.canMove(tileIndex)) { const blankIndex = this.gameBoard.indexOf(0); let temp = this.gameBoard[tileIndex]; this.gameBoard[tileIndex] = this.gameBoard[blankIndex]; this.gameBoard[blankIndex] = temp; this.selectedTile = -1; this.checkForWin(); // 检查是否获胜 } }
// 检查是否获胜 private checkForWin() { const winState = [1, 2, 3, 4, 5, 6, 7, 8, 0]; this.isGameOver = this.gameBoard.join(',') === winState.join(','); if (this.isGameOver) { promptAction.showDialog({ title: '游戏胜利!', message: '恭喜你,用时:' + ((Date.now() - this.startTime) / 1000).toFixed(3) + '秒', buttons: [{ text: '重新开始', color: '#ffa500' }] }).then(() => { this.shuffleGameBoard(); // 重新开始游戏 }); } }
// 打乱游戏面板 private shuffleGameBoard() { this.startTime = Date.now(); let tempBoard = [...this.gameBoard]; let moves = 0; const maxMoves = 10000; // 最大移动次数 this.shouldAnimate = false; // 关闭动画
// 寻找空白格的位置 const findBlankIndex = () => tempBoard.indexOf(0);
// 合法的移动方向 const validDirections = (index: number) => { let valid: string[] = []; if (index % 3 > 0) { valid.push('left'); } if (index % 3 < 2) { valid.push('right'); } if (index >= 3) { valid.push('up'); } if (index <= 5) { valid.push('down'); } return valid; };
// 移动空白格 const moveBlank = (direction: string, index: number) => { let newIndex = index; switch (direction) { case 'up': newIndex -= 3; break; case 'down': newIndex += 3; break; case 'left': newIndex -= 1; break; case 'right': newIndex += 1; break; } let temp = tempBoard[newIndex]; tempBoard[newIndex] = tempBoard[index]; tempBoard[index] = temp; };
// 模拟手势移动 while (moves < maxMoves) { const blankIndex = findBlankIndex(); const possibleDirections = validDirections(blankIndex); if (possibleDirections.length > 0) { const direction = possibleDirections[Math.floor(Math.random() * possibleDirections.length)]; moveBlank(direction, blankIndex); moves++; } else { break; } }
this.gameBoard = tempBoard; this.selectedTile = -1; this.isGameOver = false; this.shouldAnimate = true; // 重新开启动画 }
// 更新动画 private updateAnim(index: number) { if (!this.shouldAnimate) return undefined; if (this.canMove(index)) { const blankIndex = this.gameBoard.indexOf(0); const diff = Math.abs(index - blankIndex); if (diff === 1) { // 左右移动 return TransitionEffect.translate({ x: `${(diff === 1 ? (index > blankIndex ? -1 : 1) : 0) * (this.cellSize + this.cellMargin * 2)}lpx` }).animation({ duration: 100 }); } else if (diff === 3) { // 上下移动 return TransitionEffect.translate({ y: `${(diff === 3 ? (index > blankIndex ? -1 : 1) : 0) * (this.cellSize + this.cellMargin * 2)}lpx` }).animation({ duration: 100 }); } } return undefined; }
build() { Column({ space: 10 }) { // 主容器 // 游戏面板容器 Flex({ wrap: FlexWrap.Wrap, direction: FlexDirection.Row }) { ForEach(this.gameBoard, (item: number, index: number) => { Text(`${item}`) // 显示数字文本 .width(`${this.cellSize}lpx`) // 设置宽度 .height(`${this.cellSize}lpx`) // 设置高度 .margin(`${this.cellMargin}lpx`) // 设置外边距 .fontSize(`${this.cellSize / 2}lpx`) // 设置字体大小 .textAlign(TextAlign.Center) // 文本居中 .backgroundColor(this.gameBoard[index] === 0 ? Color.White : Color.Orange) // 背景颜色 .fontColor(Color.White) // 字体颜色 .borderRadius(5) // 圆角 .visibility(item == 0 ? Visibility.Hidden : Visibility.Visible) // 隐藏空白格 .transition(this.updateAnim(index)) // 设置动画过渡 .onClick(() => { if (this.canMove(index)) { this.moveTile(index); } }); // 点击事件 }) } .width(`${(this.cellSize + this.cellMargin * 2) * 3}lpx`) // 设置容器宽度
// 重新开始按钮 Button('重新开始') .width('50%') // 设置按钮宽度 .height('10%') // 设置按钮高度 .onClick(() => { this.shuffleGameBoard(); // 重新开始游戏 }); } .width('100%') // 设置主容器宽度 .height('100%') // 设置主容器高度 .onTouch((e) => { if (e.type === TouchType.Down && e.touches.length > 0) { // 触摸开始,记录初始位置 this.screenStartX = e.touches[0].x; this.screenStartY = e.touches[0].y; } else if (e.type === TouchType.Up && e.changedTouches.length > 0) { // 当手指抬起时,更新最后的位置 this.lastScreenX = e.changedTouches[0].x; this.lastScreenY = e.changedTouches[0].y; } }) .gesture( SwipeGesture({ direction: SwipeDirection.All }) // 支持方向中 all可以是上下左右 .onAction((_event: GestureEvent) => { const swipeX = this.lastScreenX - this.screenStartX; const swipeY = this.lastScreenY - this.screenStartY;
// 判断滑动方向 let directionText = ''; if (Math.abs(swipeX) > Math.abs(swipeY)) { if (swipeX > 0) { directionText = 'Right'; // 向右滑动 } else { directionText = 'Left'; // 向左滑动 } } else { if (swipeY > 0) { directionText = 'Down'; // 向下滑动 } else { directionText = 'Up'; // 向上滑动 } }
console.info('====滑动方向:', directionText); // console.info('====起点x:', this.screenStartX); // console.info('====起点y:', this.screenStartY); // console.info('====终点x:', this.lastScreenX); // console.info('====终点y:', this.lastScreenY);
// 清除开始位置记录,准备下一次滑动判断 this.screenStartX = 0; this.screenStartY = 0;
this.moveOnSwipe(directionText); // 根据方向移动方块 }) ) }
private moveOnSwipe(direction: string) { const blankIndex = this.gameBoard.indexOf(0); let newIndex = blankIndex;
switch (direction) { case 'Up': newIndex = blankIndex + 3; break; case 'Down': newIndex = blankIndex - 3; break; case 'Left': newIndex = blankIndex + 1; break; case 'Right': newIndex = blankIndex - 1; break; }
if (this.isValidMove(newIndex)) { this.moveTile(newIndex); } }
private isValidMove(newIndex: number): boolean { // 检查新位置是否越界 return newIndex >= 0 && newIndex < this.gameBoard.length; }}
复制代码


用户头像

zhongcx

关注

还未添加个人签名 2024-09-27 加入

还未添加个人简介

评论

发布
暂无评论
鸿蒙开发案例:实现数字华容道游戏_zhongcx_InfoQ写作社区