写点什么

鸿蒙开发案例:HarmonyOS NEXT 语法实现 2048

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

    阅读完需:约 16 分钟

【实现的功能】

• 游戏逻辑:实现了 2048 游戏的核心逻辑,包括初始化游戏盘面、添加随机方块、处理四个方向的滑动操作等。

• UI 展示:构建了游戏的用户界面,显示得分、游戏盘面,并提供了重新开始按钮。

• 用户交互:支持触摸屏上的手势识别,通过滑动手势控制游戏盘面上方块的移动。

【待实现功能】

• 方块移动动画:暂未实现 原理应该是在 UI 的 Text 上设置.translate({ x:, y: })并添加.animation({duration: 200}),然后在逻辑里通过修改 x 或 y 来实现位移动画。研究了一下操作时方块的移动动画,但效果不尽如人意(T_T)。继续努力学习如何实现动画效果。

【完整代码】

// 使用装饰器标记Cell类,可能表示该类具有可观测性@ObservedV2class Cell {  // 使用Trace装饰器标记value属性,可能表示该属性的变化会被追踪  @Trace value: number;
// 构造函数初始化单元格的值为0 constructor() { this.value = 0; }}
// 使用Entry和Component装饰器标记Game2048结构体,可能表示这是程序的入口点,并且该结构体定义了一个组件@Entry@Component // 定义Game2048结构体struct Game2048 { // 使用State装饰器标记状态变量,可能表示这些变量是组件的状态 @State board: Cell[][] = []; // 游戏盘面 @State score: number = 0; // 分数 @State cellSize: number = 200; // 单元格大小 @State cellMargin: number = 5; // 单元格之间的边距 @State screenStartX: number = 0; // 触摸开始时的屏幕X坐标 @State screenStartY: number = 0; // 触摸开始时的屏幕Y坐标 @State lastScreenX: number = 0; // 触摸结束时的屏幕X坐标 @State lastScreenY: number = 0; // 触摸结束时的屏幕Y坐标
// 定义颜色数组 colors: string[] = [ '#CCCCCC', // 0 - 灰色 '#FFC107', // 2 - 黄色 '#FF9800', // 4 - 橙色 '#FF5722', // 8 - 深橙色 '#F44336', // 16 - 红色 '#9C27B0', // 32 - 紫色 '#3F51B5', // 64 - 蓝紫色 '#00BCD4', // 128 - 蓝色 '#009688', // 256 - 深青色 '#4CAF50', // 512 - 绿色 '#8BC34A', // 1024 - 浅绿色 '#CDDC39', // 2048 - 柠檬黄 '#FFEB3B', // 4096 - 淡黄色 '#795548', // 8192 - 棕色 '#607D8B', // 16384 - 深灰色 '#9E9E9E', // 32768 - 灰色 '#000000' // 以上 - 黑色 ];
// 游戏即将出现时执行的方法 aboutToAppear() { this.score = 0; // 重置分数 this.initBoard(); // 重新初始化游戏板 this.addRandomTiles(2); // 添加两个随机方块 }
// 初始化游戏盘面 initBoard() { if (this.board.length == 0) { for (let i = 0; i < 4; i++) { let cellArr: Cell[] = []; for (let j = 0; j < 4; j++) { cellArr.push(new Cell()); // 创建新单元格 } this.board.push(cellArr); // 添加到盘面 } } else { for (let i = 0; i < this.board.length; i++) { for (let j = 0; j < this.board[i].length; j++) { this.board[i][j].value = 0; // 清空单元格 } } } }
// 在盘面上添加指定数量的随机方块 addRandomTiles(count: number) { let emptyCells: object[] = []; for (let row = 0; row < 4; row++) { for (let col = 0; col < 4; col++) { if (this.board[row][col].value === 0) { emptyCells.push(Object({ row: row, col: col })); // 记录空单元格位置 } } }
for (let i = 0; i < count; i++) { if (emptyCells.length > 0) { let randomIndex = Math.floor(Math.random() * emptyCells.length); let obj = emptyCells[randomIndex]; this.board[obj['row']][obj['col']].value = Math.random() < 0.9 ? 2 : 4; // 随机生成2或4 emptyCells.splice(randomIndex, 1); // 移除已使用的空单元格位置 } } }
// 向左滑动 slideLeft() { for (let row = 0; row < 4; row++) { let tempRow: number[] = []; // 临时存储行数据 let merged: boolean[] = new Array(4).fill(false); // 标记是否已经合并过
for (let col = 0; col < 4; col++) { if (this.board[row][col].value !== 0) { tempRow.push(this.board[row][col].value); // 移动非零值 } }
let mergePos = 0; while (mergePos < tempRow.length - 1) { if (tempRow[mergePos] === tempRow[mergePos + 1] && !merged[mergePos]) { tempRow[mergePos] *= 2; // 合并 this.score += tempRow[mergePos]; // 更新分数 merged[mergePos] = true; // 标记已合并 tempRow.splice(mergePos + 1, 1); // 移除合并过的值 } else { mergePos++; } }
while (tempRow.length < 4) { tempRow.push(0); // 填充空位 } for (let col = 0; col < 4; col++) { this.board[row][col].value = tempRow[col]; // 更新盘面 } } }
// 向右滑动 slideRight() { for (let row = 0; row < 4; row++) { let tempRow: number[] = []; // 临时存储行数据 let merged: boolean[] = new Array(4).fill(false); // 标记是否已经合并过
for (let col = 3; col >= 0; col--) { if (this.board[row][col].value !== 0) { tempRow.unshift(this.board[row][col].value); // 移动非零值 } } let mergePos = tempRow.length - 1; while (mergePos > 0) { if (tempRow[mergePos] === tempRow[mergePos - 1] && !merged[mergePos - 1]) { tempRow[mergePos] *= 2; // 合并 this.score += tempRow[mergePos]; // 更新分数 merged[mergePos - 1] = true; // 标记已合并 tempRow.splice(mergePos - 1, 1); // 移除合并过的值 } else { mergePos--; } }
while (tempRow.length < 4) { tempRow.unshift(0); // 填充空位 } for (let col = 0; col < 4; col++) { this.board[row][col].value = tempRow[col]; // 更新盘面 } } }
// 向上滑动 slideUp() { for (let col = 0; col < 4; col++) { let tempCol: number[] = []; // 临时存储列数据 let merged: boolean[] = new Array(4).fill(false); // 标记是否已经合并过
for (let row = 0; row < 4; row++) { if (this.board[row][col].value !== 0) { tempCol.push(this.board[row][col].value); // 移动非零值 } }
let mergePos = 0; while (mergePos < tempCol.length - 1) { if (tempCol[mergePos] === tempCol[mergePos + 1] && !merged[mergePos]) { tempCol[mergePos] *= 2; // 合并 this.score += tempCol[mergePos]; // 更新分数 merged[mergePos] = true; // 标记已合并 tempCol.splice(mergePos + 1, 1); // 移除合并过的值 } else { mergePos++; } }
while (tempCol.length < 4) { tempCol.push(0); // 填充空位 }
for (let newRow = 0; newRow < 4; newRow++) { this.board[newRow][col].value = tempCol[newRow]; // 更新盘面 } } }
// 向下滑动 slideDown() { for (let col = 0; col < 4; col++) { let tempCol: number[] = []; // 临时存储列数据 let merged: boolean[] = new Array(4).fill(false); // 标记是否已经合并过
// 从下往上遍历列 for (let row = 3; row >= 0; row--) { if (this.board[row][col].value !== 0) { tempCol.unshift(this.board[row][col].value); // 移动非零值 } } let mergePos = tempCol.length - 1; while (mergePos > 0) { if (tempCol[mergePos] === tempCol[mergePos - 1] && !merged[mergePos - 1]) { tempCol[mergePos] *= 2; // 合并 this.score += tempCol[mergePos]; // 更新分数 merged[mergePos - 1] = true; // 标记已合并 tempCol.splice(mergePos - 1, 1); // 移除合并过的值 } else { mergePos--; } }
// 如果数组长度小于4,用0填充 while (tempCol.length < 4) { tempCol.unshift(0); // 填充空位 } // 将处理后的数组元素放回到棋盘的对应列中 for (let row = 0; row < 4; row++) { this.board[3 - row][col].value = tempCol[3 - row]; // 注意反转顺序 } } }
// 构建游戏界面 build() { // 布局容器 Column({ space: 10 }) { // 显示得分 Text(`得分: ${this.score}`) .fontSize(24) .margin({ top: 20 })
// 底层背景布局 Flex({ wrap: FlexWrap.Wrap, direction: FlexDirection.Row }) { // 遍历每个单元格 ForEach(this.board.flat(), (cell: Cell, index: number) => { // 显示单元格上的数字 Text(`${cell.value || ''}`) .width(`${this.cellSize}px`) .height(`${this.cellSize}px`) .margin(`${this.cellMargin}px`) .fontSize(`${cell.value >= 100 ? this.cellSize / 3 : this.cellSize / 2}px`) // 根据数字大小调整字体大小 .textAlign(TextAlign.Center) .backgroundColor(this.colors[cell.value == 0?0:Math.floor(Math.log2(cell.value))]) // 设置背景颜色 .fontColor(cell.value === 0 ? '#000' : '#fff') // 设置字体颜色 .borderRadius(5) // 圆角 }) } .width(`${(this.cellSize + this.cellMargin * 2) * 4}px`) // 设置容器宽度
// 重新开始按钮 Button('重新开始').onClick(() => { this.aboutToAppear(); // 重新开始游戏 }) } .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; // 清除开始位置记录,准备下一次滑动判断 this.screenStartX = 0; this.screenStartY = 0; if (Math.abs(swipeX) > Math.abs(swipeY)) { if (swipeX > 0) { this.slideRight(); // 向右滑动 } else { this.slideLeft(); // 向左滑动 } } else { if (swipeY > 0) { this.slideDown(); // 向下滑动 } else { this.slideUp(); // 向上滑动 } } this.addRandomTiles(1); // 添加一个随机方块 }) ) }}
复制代码


用户头像

zhongcx

关注

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

还未添加个人简介

评论

发布
暂无评论
鸿蒙开发案例:HarmonyOS NEXT语法实现2048_zhongcx_InfoQ写作社区