目录

  • 前言
  • 概述
  • 产生新格子
    • 随机生成值为2或4的格子
    • 判断格子之间是否可合并
    • 通过滑动移动格子
  • 判断游戏是否结束
  • 更新当前分数
  • 重新开始游戏
  • 游戏完整代码
    • index.js
    • index.hml
    • index.css
  • 结语

前言

此前在观看完张荣超老师的“从零开发鸿蒙小游戏app”(2048小游戏)这一视频后,我们写下了视频中已实现功能的学习笔记----“从零开发鸿蒙小游戏app学习笔记 (1)”。对于视频中未提及部分,我们根据张荣超老师提供的源代码进行了学习,在此写下2048小游戏剩余功能的学习笔记。
“从零开发鸿蒙小游戏app学习笔记 (1)”的链接如下:
https://blog.csdn.net/dioda1/article/details/109695154
原视频地址如下:https://edu.51cto.com/center/course/lesson/index?id=658343
一起学习的小伙伴:
Zy82243480
wx15820482064
Lingzijiandevx
pp459816283

概述

上一篇学习笔记完成了:
“创建项目与分析”
“页面布局”
“方格绘制”
“数字输入与背景填充”
“滑动事件的实现”的部分内容。

本篇学习笔记将介绍:
“产生新格子”
“判断游戏是否结束”
“更新当前分数”
“重新开始游戏”
“游戏完整代码”

产生新格子

随机生成值为2或4的格子

随机生成2或4的格子主要分为两步来完成:
第一步,判断所有格子中空余格子存在的位置

addTwoOrFourToGrids() {let array = [];for (let row = 0; row < 4; row++) {for (let column = 0; column < 4; column++) {if (grids[row][column] == 0) {array.push([row, column])}}}// array: [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3]]

第二步:随机挑选任意空余格子,为其随机赋值为2或4

      // Math.random(): [0, 1)之间的小数// Math.random() * 16: [0, 16)之间的小数// Math.floor(x): 小于等于x的最大整数// Math.floor(Math.random() * 16):[0, 15]之间的整数// Math.floor(Math.random() * array.length):[0, array.length-1]之间的整数let randomIndex = Math.floor(Math.random() * array.length);let row = array[randomIndex][0];let column = array[randomIndex][1];if (Math.random() < 0.8) {grids[row][column] = 2;} else {grids[row][column] = 4;}},

判断格子之间是否可合并

判断格子之间是否可以合并(此处以向左滑动为例),我们可以对每行都进行如下的循环:
把每行非零的元素加入一个队列里面,得到队列之后,如果第i个元素和第i+1个元素相同,则说明可以相加,就把元素移到左边,把相邻元素记为0。

for (let i = 0; i < 4; i++) {if (grids[row][column] != 0) {array.push(grids[row][column]);}column += step;//step用于控制入表时序号的大小,如果为1,则入表后的坐标和原坐标一样,若为-1则入表下标等于4减去坐标}for (let i = 0; i < array.length - 1; i++) {if (array[i] == array[i + 1]) {array[i] += array[i + 1];this.updateCurrentScores(array[i]);array[i + 1] = 0;i++;}}

对于其他三个方向都是大同小异,如果向右或者向下,要先把column记为3,把step赋值为-1,用来控制判断顺序。

通过滑动移动格子

移动滑块,是通过触屏滑动来移动格子,当在滑动方向上的同一行或者同一列上,除了零外最相近的两个值相同的时候这两个数按加法合并。
1.创建一个全是0的4*4的矩阵,然后判断是左右方向的还是上下方向的。
2.若判断为左右方向,再判断方向是左还是右,当左的时候令step=1以及column=0,以使每一行上从左往右把除零之外的值放进空数组中,当方向为右的时候与之相反。
3.然后使用上面步骤得到的数组判断上面步骤得到的数组相邻的两个数是否相同。若相同就相加合并,得到一个新的数组。
4.然后再将第二步得到的新数组直接在最右端,或者最左端直接输入。
5.触屏方向的上下和触屏方向的左右原理相似。

changeGrids(direction) {
let newGrids = [[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]];
//判断是左右方向还是上下方向

      if (direction == 'left' || direction == 'right') {//判断是左还是右let step = 1;for (let row = 0; row < 4; row++) {let array = [];let column = 0;if (direction == 'right') {column = 3;}//将除了零的外的数放进空数组里for (let i = 0; i < 4; i++) {if (grids[row][column] != 0) {array.push(grids[row][column]);}column += step;}//判断是否相同并合并for (let i = 0; i < array.length - 1; i++) {if (array[i] == array[i + 1]) {array[i] += array[i + 1];this.updateCurrentScores(array[i]);array[i + 1] = 0;i++;}}//将数组里的值输入column = 0;if (direction == 'right') {column = 3;}for (const elem of array) {if (elem != 0) {newGrids[row][column] = elem;column += step;}}}} else if (direction == 'up' || direction == 'down') {//上下跟左右的步骤一样let step = 1;if (direction == 'down') {step = -1;}for (let column = 0; column < 4; column++) {let array = [];let row = 0;if (direction == 'down') {row = 3;}for (let i = 0; i < 4; i++) {if (grids[row][column] != 0) {array.push(grids[row][column]);}row += step;}for (let i = 0; i < array.length - 1; i++) {if (array[i] == array[i + 1]) {array[i] += array[i + 1];this.updateCurrentScores(array[i]);array[i + 1] = 0;i++;}}row = 0;if (direction == 'down') {row = 3;}for (const elem of array) {if (elem != 0) {newGrids[row][column] = elem;row += step;}}}}return newGrids;},

判断游戏是否结束

游戏结束,需要满足两个条件:

  1. 没有空格子(即值为0的格子)
  2. 相邻的格子都不相同

只有同时满足上述两个条件,游戏才会结束。
我们可以添加两个函数isGridsFull()和isGridsNotMergeable()来作为判断依据:

    isGridsFull() {if (grids.toString().split(",").indexOf("0") == -1) {return true;} else {return false;}},isGridsNotMergeable() {for (let row = 0; row < 4; row++) {for (let column = 0; column < 4; column++) {if (column < 3) {if (grids[row][column] == grids[row][column + 1]) {return false;}} }}return true;},

而在游戏结束时,游戏界面的颜色会发生变化,故我们在原有颜色的基础上,再添加一组配色相对较浅的配色,具体配色如下:

const THEME = {normal: {"0": "#CDC1B4","2": "#EEE4DA","4": "#EDE0C8","8": "#F2B179","16": "#F59563","32": "#F67C5F","64": "#F65E3B","128": "#EDCF72","256": "#EDCC61","512": "#99CC00","1024": "#83AF9B","2048": "#0099CC","2or4": "#645B52","others": "#FFFFFF"},faded: {"0": "#D4C8BD","2": "#EDE3DA","4": "#EDE1D1","8": "#F0CBAA","16": "#F1BC9F","32": "#F1AF9D","64": "#F1A08B","128": "#EDD9A6","256": "#F6E5B0","512": "#CDFF3F","1024": "#CADCD4","2048": "#75DBFF","2or4": "#645B52","others": "#FFFFFF"}
};
var colors = THEME.normal;

更新当前分数

此处分数的计算方法是合成方块对应的数字的和,例如只有两个2的格子合成4,那么得分增加4。
在js文件中添加函数:

 updateCurrentScores(gridNum) {this.currentScores += gridNum;},

并将这个函数加到上一步的changegrid()函数的函数。

重新开始游戏

直接上代码,注释写在里面

restartGame(){this.currentScores = 0;     //重置当前分为0this.isShow = false;        //将是否显示“游戏结束”选为否colors =THEME.normal;       //背景色调回正常游戏版本this.initGrids();         //初始化数组this.addTwoOrFourToGrids(); //随机生成一个格子,重复两遍此操作this.addTwoOrFourToGrids();this.drawGrids();            //绘制格子内容}
}

游戏完整代码

index.js

var grids;
var context;const THEME = {normal: {"0": "#CDC1B4","2": "#EEE4DA","4": "#EDE0C8","8": "#F2B179","16": "#F59563","32": "#F67C5F","64": "#F65E3B","128": "#EDCF72","256": "#EDCC61","512": "#99CC00","1024": "#83AF9B","2048": "#0099CC","2or4": "#645B52","others": "#FFFFFF"},faded: {"0": "#D4C8BD","2": "#EDE3DA","4": "#EDE1D1","8": "#F0CBAA","16": "#F1BC9F","32": "#F1AF9D","64": "#F1A08B","128": "#EDD9A6","256": "#F6E5B0","512": "#CDFF3F","1024": "#CADCD4","2048": "#75DBFF","2or4": "#645B52","others": "#FFFFFF"}
};
var colors = THEME.normal;const SIDELEN = 70;
const MARGIN = 5;export default {data: {bestScores: 9818,currentScores: 0,isShow: false},onInit() {this.initGrids();this.addTwoOrFourToGrids();this.addTwoOrFourToGrids();},initGrids() {grids = [[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0]];},addTwoOrFourToGrids() {let array = [];for (let row = 0; row < 4; row++) {for (let column = 0; column < 4; column++) {if (grids[row][column] == 0) {array.push([row, column])}}}// array: [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3]]// Math.random(): [0, 1)之间的小数// Math.random() * 16: [0, 16)之间的小数// Math.floor(x): 小于等于x的最大整数// Math.floor(Math.random() * 16):[0, 15]之间的整数// Math.floor(Math.random() * array.length):[0, array.length-1]之间的整数let randomIndex = Math.floor(Math.random() * array.length);let row = array[randomIndex][0];let column = array[randomIndex][1];if (Math.random() < 0.8) {grids[row][column] = 2;} else {grids[row][column] = 4;}},onReady() {context = this.$refs.canvas.getContext('2d');},onShow() {this.drawGrids();},drawGrids() {for (let row = 0; row < 4; row++) {for (let column = 0; column < 4; column++) {let gridStr = grids[row][column].toString();context.fillStyle = colors[gridStr];let leftTopX = column * (MARGIN + SIDELEN) + MARGIN;let leftTopY = row * (MARGIN + SIDELEN) + MARGIN;context.fillRect(leftTopX, leftTopY, SIDELEN, SIDELEN);context.font = "24px HYQiHei-65S";if (gridStr != "0") {if (gridStr == "2" || gridStr == "4") {context.fillStyle = colors["2or4"];} else {context.fillStyle = colors["others"];}let offsetX = (4 - gridStr.length) * (SIDELEN / 8);let offsetY = (SIDELEN - 24) / 2;context.fillText(gridStr, leftTopX + offsetX, leftTopY + offsetY);}}}},swipeGrids(event) {let newGrids = this.changeGrids(event.direction);if (newGrids.toString() != grids.toString()) {grids = newGrids;this.addTwoOrFourToGrids();this.drawGrids();if (this.isGridsFull() == true && this.isGridsNotMergeable() == true) {colors = THEME.faded;this.drawGrids();this.isShow = true;}}},isGridsFull() {if (grids.toString().split(",").indexOf("0") == -1) {return true;} else {return false;}},isGridsNotMergeable() {for (let row = 0; row < 4; row++) {for (let column = 0; column < 4; column++) {if (column < 3) {if (grids[row][column] == grids[row][column + 1]) {return false;}}if (row < 3) {if (grids[row][column] == grids[row + 1][column]) {return false;}}}}return true;},changeGrids(direction) {let newGrids = [[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0]];if (direction == 'left' || direction == 'right') {let step = 1;if (direction == 'right') {step = -1;}for (let row = 0; row < 4; row++) {let array = [];let column = 0;if (direction == 'right') {column = 3;}for (let i = 0; i < 4; i++) {if (grids[row][column] != 0) {array.push(grids[row][column]);}column += step;}for (let i = 0; i < array.length - 1; i++) {if (array[i] == array[i + 1]) {array[i] += array[i + 1];this.updateCurrentScores(array[i]);array[i + 1] = 0;i++;}}column = 0;if (direction == 'right') {column = 3;}for (const elem of array) {if (elem != 0) {newGrids[row][column] = elem;column += step;}}}} else if (direction == 'up' || direction == 'down') {let step = 1;if (direction == 'down') {step = -1;}for (let column = 0; column < 4; column++) {let array = [];let row = 0;if (direction == 'down') {row = 3;}for (let i = 0; i < 4; i++) {if (grids[row][column] != 0) {array.push(grids[row][column]);}row += step;}for (let i = 0; i < array.length - 1; i++) {if (array[i] == array[i + 1]) {array[i] += array[i + 1];this.updateCurrentScores(array[i]);array[i + 1] = 0;i++;}}row = 0;if (direction == 'down') {row = 3;}for (const elem of array) {if (elem != 0) {newGrids[row][column] = elem;row += step;}}}}return newGrids;},updateCurrentScores(gridNum) {this.currentScores += gridNum;},restartGame() {this.currentScores = 0;this.isShow = false;colors = THEME.normal;this.initGrids();this.addTwoOrFourToGrids();this.addTwoOrFourToGrids();this.drawGrids();}
}

index.hml

<div class="container"><text class="scores">最高分:{{bestScores}}</text><text class="scores">当前分:{{currentScores}}</text><stack class="stack"><canvas class="canvas" ref="canvas" onswipe="swipeGrids"></canvas><div class="subcontainer" show="{{isShow}}"><text class="gameover">游戏结束</text></div></stack><input type="button" value="重新开始" class="btn" onclick="restartGame"/>
</div>

index.css

.container {flex-direction: column;justify-content: center;align-items: center;width: 454px;height: 454px;
}
.scores {font-size: 18px;text-align: center;width: 300px;height: 20px;letter-spacing: 0px;margin-top: 10px;
}
.stack{width: 305px;height: 305px;margin-top: 10px;
}
.canvas {width: 305px;height: 305px;background-color: #BBADA0;
}
.subcontainer {width: 305px;height: 305px;justify-content: center;align-items: center;background-color: transparent;
}
.gameover {font-size: 38px;color: black;
}
.btn {width: 150px;height: 30px;background-color: #AD9D8F;font-size: 24px;margin-top: 10px;
}

结语

以上就是我们学习完2048小游戏的笔记,希望能对大家有帮助,也烦请各路大神斧正。也期待张荣超老师对于2048小游戏的完整讲解视频,张老师发布后我们会第一时间附上链接供大家学习。同时也十分感谢欧js师兄、李cz老师、王bh老师的引路,让我们有机会接触到这个HarmonyOS这个潜力无限的领域。大家一起加油!祝大家敲出改变世界的代码!

深鸿会深大小组学习笔记:第二周,从零开发鸿蒙小游戏2048app(下)相关推荐

  1. qml自学笔记------自己写类似于劲舞团的按键小游戏(下)

    接上篇<qml自学笔记------自己写类似于劲舞团的按键小游戏(中)> 第四部分 PauseButton.qml 和 RestartButton.qml 第四部分其实就是两个按键,一个是 ...

  2. 【学习周报】深度学习笔记第二周

    学习目标: 吴恩达深度学习课程week2 学习内容: 梯度下降法(Gradient Descent) 计算图(Computation Graph) 逻辑回归中的梯度下降(Logistic Regres ...

  3. WPF学习笔记-第二周【基本笔刷】

    书接上回,这一次,讲的是WPF中的基本笔刷,由于是菜鸟,就不多说了,继续帖示例代码:) 第一部份 代码 第二章 基本笔刷 第一个 示例 VaryTheBackgroud P38  1     #reg ...

  4. java学习笔记第二周(一)

    目录 一. 面向对象学习 1.快速熟悉面向对象 1.1表格结构和类结构 1.2表格的动作和类的方法 2.面向过程和面向对象思想 2.1面向过程和面向对象的区别 3.对象和类的详解 3.1类和对象的概念 ...

  5. Unity学习笔记第二章:如何创建一个2D游戏

    ps:发文章只是为了稳固自己的学习记录一下,如果有什么错误麻烦多指教 目前学习到了自己的第一个2D游戏,记录一下大概流程以及Sprite的操作 1:地形的创建设置以及Sprite的操作: 创建地形这块 ...

  6. Java学习笔记(十六)—— 开发个小项目(GoBang4.0)

    接十二,今天重点搞定简单基础版本的AI下棋. 画个大纲(跟随慢慢开发过程不断完善) 1.用户 两个用户对战  一黑一白 用户可以是人,也可以是AI.对战模式支持人人,人机,机机. 属性 本次比赛执棋颜 ...

  7. 软件构造学习笔记-第二周

    本周课程把第六章测试的内容提前讲了一部分,主要为实验1服务,讲了有关测试的概念.作用和基本方法."测试优先"的思想是非常重要的,根据spec写出简单而全面的测试,在方法/类完成后第 ...

  8. 2、python学习笔记第二课:python开发环境

    python常用的开发环境: IDLE: Pycharm: wingIDLE: Eclipse: Ipython; 交互环境:

  9. 虚幻4学习笔记(8)动手制作一个小游戏

    动手制作一个小游戏 新节点介绍 前期准备 搭建场景 门蓝图 灯蓝图 创建关卡蓝图 B站UP谌嘉诚课程:https://www.bilibili.com/video/BV164411Y732 新节点介绍 ...

  10. 大数据学习笔记:Hadoop生态系统

    文章目录 一.Hadoop是什么 二.Hadoop生态系统图 三.Hadoop生态圈常用组件 (一)Hadoop (二)HDFS (三)MapReduce (四)Hive (五)Hbase (六)Zo ...

最新文章

  1. python中什么是字符举例说明_第20p,什么是字符串?Python中的str
  2. error while loading shared libraries的解決方法
  3. python的接口实现zope.interface示例
  4. PAT甲级 1017 Queueing at Bank
  5. python 删除满足条件的行
  6. mysql学习之三:sql语句学习
  7. 2022年流行的Java框架有哪些?
  8. puzzle(102)数独
  9. 使用Cadence组件进行Pspice仿真
  10. 100多本炒股电子书网盘自由下载!
  11. 26 | 产品安全方案:如何降低业务对黑灰产的诱惑?
  12. 使用eclipse编写第一个Java程序及运行(超详细)
  13. 微信公众平台接入token验证失败php,PHP开发公众号token验证失败是什么意思?其中一个原因 筋斗云网络...
  14. 程序集(dll) 安装到 GAC 程序集添加到VS引用开窗(转)
  15. 苹果个人开发者账号升级为公司开发者教程
  16. 无聊科技正经事周刊(第3期):美团的推荐算法,是在玩火吗?
  17. 服务器和交换机物理连接_交换机发生网络通信故障怎么解决?
  18. java利用正则表达式提取字符串中的整数和小数部分
  19. 混合云管平台排名您知道吗?看这里
  20. RK3128-android7.1-nt68661

热门文章

  1. 虚拟均衡器:Producers Vault Baby Bass for Mac
  2. 一款自动生成唯一头像的开源代码库
  3. vs2010最佳配色选择_2010年代35部最佳电影
  4. 怎么在安卓手机上阅读txt小说,小说阅读器推荐
  5. mtk屏幕背光默认时间修改
  6. db2 jdbc驱动参数_db2的jdbc驱动安装及例子
  7. 区块链实战超级账本视频教程|区块链视频教程
  8. 【静态ip保姆级教程他来了】
  9. 数学建模 ---斯皮尔曼相关系数
  10. 老外挑战360加固--实战分析(很详细)