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

【实现的功能】

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

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

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

【待实现功能】

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

【完整代码】

// 使用装饰器标记Cell类,可能表示该类具有可观测性
@ObservedV2
class Cell {// 使用Trace装饰器标记value属性,可能表示该属性的变化会被追踪@Trace value: number;// 构造函数初始化单元格的值为0constructor() {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或4emptyCells.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); // 添加一个随机方块}))}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/448011.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenAI 公布了其新 o1 模型家族的元提示(meta-prompt)

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

出处不详 取数游戏

目录 取数游戏题目描述背景输入输出数据范围 题解解法优化 打赏 取数游戏 题目描述 背景 两人将 n n n个正整数围成一个圆环&#xff0c;规则如下&#xff1a; 第一名玩家随意选取数字&#xff1b;第二名玩家从与第一名玩家相邻的两个数字中选择一个&#xff1b;而后依次在…

科技云报到:大模型时代下,向量数据库的野望

科技云报到原创。 自ChatGPT爆火&#xff0c;国内头部平台型公司一拥而上&#xff0c;先后发布AGI或垂类LLM&#xff0c;但鲜有大模型基础设施在数据层面的进化&#xff0c;比如向量数据库。 在此之前&#xff0c;向量数据库经历了几年的沉寂期&#xff0c;现在似乎终于乘着Ch…

python 位运算 笔记

起因&#xff0c; 目的: 位运算&#xff0c;令我头疼的地方。算法题里面也是经常见到。 位运算。 按位或&#xff0c;OR, | , 只要有一个为1&#xff0c; 结果就是1&#xff0c;否则为0按位异或&#xff0c;XOR, ^, 2个数不同&#xff0c;结果为1&#xff0c; 否则为0&#…

一文介绍SQL标准1986~2023的演变

SQL标准1986年制定第一版&#xff0c;到最新的2023版&#xff0c;已经有38年的历史&#xff0c;现在依然是计算机非常活跃的语言&#xff0c;50%的程序员都能掌握SQL&#xff0c;数据分析师也是SQL的主要使用人员之一。 从早期的基本语法&#xff0c;到融合了XML、JSON等复杂数…

【Matlab 六自由度机器人】笛卡尔空间规划和关节空间规划(附MATLAB建模代码)

笛卡尔空间规划和关节空间规划 近期更新前言正文1. 笛卡尔空间规划特点&#xff1a;步骤&#xff1a; 2. 关节空间规划特点&#xff1a;步骤&#xff1a; 3. 两种方法的区别4. MATLAB代码&#xff1a;机械臂避障路径规划问题和解答4.1 关节空间规划方法4.2 笛卡尔空间规划方法4…

Java中关于算数运算符的理解

在Java中基本的算数运算符有五类 加减-乘*在编程语言中乘号一律写为 *除/在Java中两个整数相除结果还是整数取余%取得的是两个数相除的余数 这里可以看见&#xff0c;在输出加法和减法时&#xff0c;我在后面多加了一个括号&#xff0c;这是因为运算优先级的原因&#xff0c;加…

105. 从前序与中序遍历序列构造二叉树【 力扣(LeetCode) 】

文章目录 零、LeetCode 原题一、题目描述二、测试用例三、解题思路四、参考代码 零、LeetCode 原题 105. 从前序与中序遍历序列构造二叉树 一、题目描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的…

Hadoop集群安装

集群规划 node01node02node03角色主节点从节点从节点NameNode√DataNode√√√ResourceManager√NodeManager√√√SecondaryNameNode√Historyserver√ 上传安装包到node01 解压到指定目录 tar -zxvf /bigdata/soft/hadoop-3.3.3.tar.gz -C /bigdata/server/ 创建软链接 cd…

基于Spring Boot的医疗病历B2B平台开发策略

第4章 系统设计 4.1 系统总体设计 系统不仅要求功能完善&#xff0c;而且还要界面友好&#xff0c;因此&#xff0c;对于一个成功的系统设计&#xff0c;功能模块的设计是关键。由于本系统可执行的是一般性质的学习信息管理工作&#xff0c;本系统具有一般适用性&#xff0c;其…

49 | 桥接模式:如何实现支持不同类型和渠道的消息推送系统?

上一篇文章我们学习了第一种结构型模式&#xff1a;代理模式。它在不改变原始类&#xff08;或者叫被代理类&#xff09;代码的情况下&#xff0c;通过引入代理类来给原始类附加功能。代理模式在平时的开发经常被用到&#xff0c;常用在业务系统中开发一些非功能性需求&#xf…

Docker consul注册中心

一、consul 1.1、什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。 起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。 直到后来出现了多个节点的分布式架构&#x…

如何看一个flutter项目的具体flutter版本

查看pubspec.lock文件 这个项目实际运行的就是 flutter 3.16.6 版本的

模电板测试分析报告【积分/微分电路】

积分电路常用于波形转换&#xff0c;如将矩形波变三角波。对正弦波积分可以实现相移。 微分电路&#xff1a; 为什么直接串联0.1uF电容到反馈线上去&#xff1a; 整改&#xff1a;这么看的话原理图中C58应该换成电阻的。 积分电路下图中红色的换成电容就可以变成微分电路了。 从…

八、随机名字功能

摘要&#xff1a; XML在C#与Unity3D中的实战运用 - PlaneZhong - 博客园 (cnblogs.com) 读取策划提供的配置文件。 策划提供一份execel文档&#xff0c;程序将它转化为一个配置文件&#xff08;xml&#xff09; 首先&#xff1a; XML是一个可扩展标记的语言 一、转换方法…

VSCode运行QT界面

VSCode用久了,感觉Qt Creator的写起代码来还是不如VSCode得心应手,虽然目前还是存在一些问题,先把目前实现的状况做个记录,后续有机会再进一步优化。 当前方式 通过QtCreator创建一个CMake项目,然后使用CMake的方式在VSCode中进行编译。 claude给出的建议 左上角的名字会…

Node.js管理工具NVM

nvm&#xff08;Node Version Manager&#xff09;是一个用于管理多个 Node.js 版本的工具。以下是 nvm 的使用方法和一些常见命令&#xff1a; 一、安装 nvm 下载 nvm&#xff1a; 地址&#xff1a;https://github.com/coreybutler/nvm-windows/releases访问 nvm 的 GitHub 仓…

【C语言】你不知道的知识小盲区——柔性数组

文章目录 一、什么是柔性数组二、柔性数组的特点三、柔性数组的使用四、柔性数组的优势 一、什么是柔性数组 也许你从来没有听说过柔性数组&#xff08;flexible array&#xff09;这个概念&#xff0c;但是它确实是存在的。在C99标准中&#xff0c;如果结构体的最后一个成员是…

sqli-labs less-26 空格绕过

空格绕过 过滤空格 用Tab代替空格%20 %09 %0a %0b %0c %0d %a0 //() 绕过空格注释符绕过//–%20//#–- -;%00; 空白字符绕过SQLite3 —— 0A,0D,0c,09,20 MYSQL 09,0A,0B,0B,0D,A0,20 PosgressSQL 0A,0D,0C,09,20 Oracle_11g 00,0A,0D,0C,09,20 MSSQL 01,02,03,04,05,06,07,…

[瑞吉外卖]-05菜品模块

文件上传下载 介绍 文件上传也称为upload&#xff0c;是指将本地图片、视频、音频等文件上传到服务器上, 可以供其他用户浏览或下载 前端组件库提供了上传组件&#xff0c;但是底层原理还是基于form表单的文件上传。 服务端要接收客户端上传的文件&#xff0c;通常都会使用Ap…