【2048小游戏】——原生js爬坑之封装行的移动算法&事件
引言:2048小游戏的核心玩法是移动行,包括横行和纵行,玩家可以选择4个方向,然后所有行内的数字就会随着行的移动而向特定的方向移动。这个行的移动是一个需要重复调用的算法,所以这里就要将一行的移动算法封装,循环调用给所用行,这样便实现了所有行的调用。

| 一、一行的左移 |
- 关键逻辑算法 伪代码 ↓
- c从0开始,遍历当前行每个元素
- 找c右侧下一个不为0的位置nextc
- 如果找到 → 如果c位置的值是0,将nextc位置的值赋值给c位置,将nextc位置的值置为0,c留在原地;否则如果c位置的值等于nextc位置的值,将c位置的值*2,将nextc位置的值置为0
- 否则(如果没找到) → 退出循环
- 坑:2048游戏的规则,如果一个数字在发生一次替换(比如左移)之后没有合并即*2,那么,它还有一次机会和相反方向的数字进行合并(还可以向右移动),这就要求如果c的位置为0,那么除了数值的替换程序之外,还应该把c位置留在原地,方便之前的数字再回来
- 解决:因为每次循环都会使c++,+1,要让c留在原地,保持不变 → 用c--,和c++抵消,保持不变
| 二、所有行的左移 |
- 思路:将一行的左移封装成一个算法,循环调用给所有行;同时为了函数的功能集中,把找c右侧下一个不为0的位置nextc的循环也封装为一个小函数
- 创建关键函数 ↓
- moveLeft() 左移所有行
- moveLeftInRow(r) 左移第r行 → 一行左移的算法封装
- getNextcInRow(r,c) 找r行c列右侧下一个不为0的位置nextc → 循环,找到返回位置nextc,没找到返回-1
- 2048游戏规则:每一次控制移动都要随机生成一个2或4,但反过来,一定要发生了移动才能生成2或4,只是按键控制了移动,但没有发生移动,那就不生成。
- 坑:要判定控制移动是否发生了真实的移动
- 解决:在控制移动前将data转为字符串保存在before中,在控制移动后将data转为字符串保存在after中,然后再判断before是否等于after,如果不等于,重绘页面,生成2或4,如果等于,不重绘,也不生成。
- 坑:如果先刷新了页面,再生成一个2或4,这样页面就看不到这个2或4了
- 解决:先生成一个2或4,然后再刷新页面

| 三、键盘按下事件 |
- 事件:用户在页面上,用鼠标触发的行为的变化
- 为页面添加 键盘按下事件 处理函数 → document.onkeydown=function(){ }
- 识别按键 ↓
- 原理:每一个键盘的按键下面都藏着一个唯一的编号(虚拟的)
- 通过判断每一个按键的键盘号的不同,做不同的事情(执行不同的移动控制函数)
- 上下左右的键盘号 keyCode
- 左 37
- 上 38
- 右 39
- 下 40
- 关键代码 ↓
//为当前页面添加键盘按下事件处理函数
document.onkeydown = function(e){
//判断按键号:
switch (e.keyCode){
case 37://左
moveLeft();
break;
case 38://上
moveUp();
break;
case 39://右
moveRight();
break;
case 40://下
moveDown();
break;
}
}
| 四、游戏得分Score |
- 游戏的得分,应该作为游戏的一个重要属性保存下来 → var score = 0;初始值为0
- 在每一次游戏开始的时候,即start()起始,都应该将得分归零 → score=0;
- 2048游戏规则:每一次发生数字合并都会增加分值,分值数=合并后的显示数字=合并前的数字*2
- data[r][c]*=2; → 将要合并的当前格数字*2
- score+=data[r][c]; → 将data中的数值赋给score
- 在updateView(){}元素填写页面的函数方法中,设置id为score的span的内容为score
- var span = document.getElementById("score");
- span.innerHTML=score;
| 五、游戏移动完整代码 |
var game={
RN:4,
CN:4,
data:null,
score:0,
state:1,
GAMEOVER:0,
RUNNING:1,
//启动游戏
start:function(){
this.state=this.RUNNING;
this.score=0;
this.data=[];
for(var r=0;r<this.RN;r++){
this.data[r]=[];
for(var c=0;
c<this.CN;
this.data[r][c]=0,c++);
}
this.randomNum();
this.randomNum();
this.updateView();
//为页面绑定键盘按下事件
document.onkeydown=function(e){
switch(e.keyCode){
case 37: this.moveLeft();break;
case 38: this.moveUp();break;
case 39: this.moveRight();break;
case 40: this.moveDown();break;
}
}.bind(this);/*this是start方法的this*/
},
move:function(callback){
var before=String(this.data);
callback();//this->window
var after=String(this.data);
if(before!=after){
this.randomNum();
if(this.isGameOver()){
this.state=this.GAMEOVER;
}
this.updateView();
}
},
isGameOver:function(){
for(var r=0;r<this.RN;r++){
for(var c=0;c<this.CN;c++){
if(this.data[r][c]==0){return false;}
else if(c<this.CN-1
&&this.data[r][c]==this.data[r][c+1]){
return false;
}
else if(r<this.RN-1
&&this.data[r][c]==this.data[r+1][c]){
return false;
}
}
}
return true;
},
moveLeft:function(){
this.move(function(){
for(var r=0;r<this.RN;r++){
this.moveLeftInRow(r);
}
}.bind(this));/*this是moveLeft方法的this*/
},
moveLeftInRow:function(r){
for(var c=0;c<this.CN-1;c++){
var nextc=this.getNextInRow(r,c);
if(nextc==-1){break;}
else{
if(this.data[r][c]==0){
this.data[r][c]=this.data[r][nextc];
this.data[r][nextc]=0;
c--;
}else if(this.data[r][c]
==this.data[r][nextc]){
this.data[r][c]*=2;
this.score+=this.data[r][c];
this.data[r][nextc]=0;
}
}
}
},
getNextInRow:function(r,c){
c++;
for(;c<this.CN;c++){
if(this.data[r][c]!=0){
return c;
}
}
return -1;
},
moveRight:function(){
this.move(function(){
for(var r=0;r<this.RN;r++){
this.moveRightInRow(r);
}
}.bind(this));
},
moveRightInRow:function(r){
for(var c=this.CN-1;c>0;c--){
var prevc=this.getPrevInRow(r,c);
if(prevc==-1){break;}
else{
if(this.data[r][c]==0){
this.data[r][c]=this.data[r][prevc];
this.data[r][prevc]=0;
c++;
}else if(this.data[r][c] ==this.data[r][prevc]){
this.data[r][c]*=2;
this.score+=this.data[r][c];
this.data[r][prevc]=0;
}
}
}
},
getPrevInRow:function(r,c){
c--;
for(;c>=0;c--){
if(this.data[r][c]!=0){
return c;
}
}
return -1;
},
moveUp:function(){
this.move(function(){
for(var c=0;c<this.CN;c++){
this.moveUpInCol(c);
}
}.bind(this));
},
moveUpInCol:function(c){
for(var r=0;r<this.RN-1;r++){
var nextr=this.getNextInCol(r,c);
if(nextr==-1){break;}
else{
if(this.data[r][c]==0){
this.data[r][c]=this.data[nextr][c];
this.data[nextr][c]=0;
r--;
}else if(this.data[r][c]
==this.data[nextr][c]){
this.data[r][c]*=2;
this.score+=this.data[r][c];
this.data[nextr][c]=0;
}
}
}
},
getNextInCol:function(r,c){
r++;
for(;r<this.RN;r++){
if(this.data[r][c]!=0){
return r;
}
}
return -1;
},
moveDown:function(){
this.move(function(){
for(var c=0;c<this.CN;c++){
this.moveDownInCol(c);
}
}.bind(this));
},
moveDownInCol:function(c){
for(var r=this.RN-1;r>0;r--){
var prevr=this.getPrevInCol(r,c);
if(prevr==-1){break;}
else{
if(this.data[r][c]==0){
this.data[r][c]=this.data[prevr][c];
this.data[prevr][c]=0;
r++;
}else if(this.data[r][c]
==this.data[prevr][c]){
this.data[r][c]*=2;
this.score+=this.data[r][c];
this.data[prevr][c]=0;
}
}
}
},
getPrevInCol:function(r,c){
r--;
for(;r>=0;r--){
if(this.data[r][c]!=0)
return r;
}
return -1;
},
//将数组中每个元素更新到页面的div中
updateView:function(){
for(var r=0;r<this.RN;r++){
for(var c=0;c<this.CN;c++){
var div= document.getElementById("c"+r+c);
if(this.data[r][c]!=0){
div.innerHTML=this.data[r][c];
div.className="cell n"+this.data[r][c];
}else{//否则
div.innerHTML="";
div.className="cell";
}
}
}
//找到id为score的元素,设置其内容为score属性
document.getElementById("score")
.innerHTML=this.score;
//如果游戏状态为结束
if(this.state==this.GAMEOVER){
document.getElementById("gameover")
.style.display="block";
document.getElementById("final")
.innerHTML=this.score;
}else{
document.getElementById("gameover").style.display="none";
}
},
randomNum:function(){
while(true){
var r=Math.floor(Math.random()*(this.RN));
var c=Math.floor(Math.random()*(this.CN));
if(this.data[r][c]==0){
this.data[r][c]=Math.random()<0.5?2:4;
break;
}
}
}
};
game.start();
注:转载请注明出处
【2048小游戏】——原生js爬坑之封装行的移动算法&事件的更多相关文章
- 【2048小游戏】——CSS/原生js爬坑之纯CSS模态对话框&游戏结束
引言:2048小游戏的结束界面,使用纯CSS制作模态对话框,一般做模态对话框都会使用BootStrap自带的模态对话框组件方便使用,但在制作要运行在移动端的小项目时,就不能使用BootStrap,因为 ...
- 【2048小游戏】——原生js爬坑之遍历算法显示二维数组内容
引言:做2048小游戏会将横纵方向的数字内容,存储在一个二维数组中,要将这个二维数组中的内容显示在页面上,就一定要用遍历算法来实现了. 一.二维数组存储 首先考虑用二维数组存储所有行数,列数 ...
- 【京东详情页】——原生js爬坑之二级菜单
一.引言 做京东详情页仿写的时候,要用原生js实现顶部菜单的二级菜单显示与隐藏事件的触发. 过程中遇到了一个坑,在这里与大家分享.要实现的效果如下: 二.坑 谁触发事件?显示.隐藏二级菜单 ...
- 【京东详情页】——原生js爬坑之放大镜
一.引言 在商城的详情页中,放大镜的功能是很常见的.这里京东详情页就要做一个仿放大镜的效果,预览如下: 二.实现原理 实际上,放大镜的实现是单纯用几个div,鼠标移入其中一个小图div,触发事件显示另 ...
- 【京东详情页】——原生js爬坑之标签页
一.引言 要做详情页的商品评价等5个li的标签页转换,效果如下: 二.实现原理 有一个特别的地方:上面五个li,但下面只有四个容器(table/div). 设计的目的:无论点哪个li,只有前四个div ...
- js、jQuery实现2048小游戏
2048小游戏 一.游戏简介: 2048是一款休闲益智类的数字叠加小游戏 二. 游戏玩法: 在4*4的16宫格中,您可以选择上.下.左.右四个方向进行操作,数字会按方向移动,相邻的两个数字相同就会合 ...
- 用js实现2048小游戏
用js实现2048小游戏 笔记仓库:https://github.com/nnngu/LearningNotes 1.游戏简介 2048是一款休闲益智类的数字叠加小游戏.(文末给出源代码和演示地址) ...
- 使用JS实现2048小游戏
JS实现2048小游戏源码 效果图: 代码如下,复制即可使用: (适用浏览器:360.FireFox.Chrome.Opera.傲游.搜狗.世界之窗. 不支持Safari.IE8及以下浏览器.) &l ...
- 基于jQuery的2048小游戏设计(网页版)
上周模仿一个2048小游戏,总结一下自己在编写代码的时候遇到的一些坑. 游戏规则:省略,我想大部分人都玩过,不写了 源码地址:https://github.com/xinhua6/2048game.g ...
随机推荐
- Leetcode 514.自由之路
自由之路 视频游戏"辐射4"中,任务"通向自由"要求玩家到达名为"Freedom Trail Ring"的金属表盘,并使用表盘拼写特定关键词 ...
- 共鸣(resonance)
共鸣(resonance) 题目描述 GHQ通过在24区引起基因组共鸣,从而引发了第二次失落的圣诞. 24区的地图可以视为一个二维平面.GHQ在24区布置了m架发射塔,而葬仪社也建立了n个据点.要阻止 ...
- 洛谷P1816 忠诚
P1816 忠诚 569通过 1.5K提交 题目提供者该用户不存在 标签云端 难度普及+/提高 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 主席树的常数貌似大于线段树… TL ...
- Linux基础值定时任务
Linux计划任务:列行公事 在Linux中,通过crontab与at这两个来实现这些功能 at:是一个可以处理仅执行一次就结束的指令 crontab:把你指定的工作或任务,按照你设定的周期一直循环执 ...
- filesystem
1 tmpfs 以下来源于维基百科: tmpfs是类Unix系统上暂存档存储空间的常见名称,通常以挂载文件系统方式实现,并将数据存储在易失性存储器而非永久存储设备中.和RAM disk的概念近似,但后 ...
- 在npm当中发布自己的包的方法
首先需要一个注册一个npm账号,注意,必须验证邮箱,不然是无法发布包的!下面是当时的报错 接着在你需要发布的包的文件夹下面打开你的cmd或者其他的命令行输入工具 输入 npm init 初始化你的 ...
- mybatis 从数据库查询的信息不完整解决办法
List<Product> products = productService.getProductListWithPage(productQuery); 今天碰到一个很奇怪的现象,上面的 ...
- 什麼是 usb upstream port
主機USB埠是定義為USB纜線的上行端(Upstream)或「A」接頭,即PC端.而裝置USB埠是定義為USB纜線的下行端(Downstream)或「B」接頭,即行動產品端. Reference ht ...
- [Oracle] Lock&Latch梳理
Oracle lock&latch 1. 概述 4种锁机制 lock latch pin mutex 保证资源在并发访问和修改时不被破坏 锁类型 行为 持有时间 级别 保护类型 lock 队列 ...
- 在 IntelliJ IDEA 中配置 JSF 开发环境的入门详解
JSF 作为 JavaEE 官方标准,在了解并掌握其基本开发技术后,对于功能要求较高.业务流程复杂的各种现代 Web 应用程序开发将会成为非常合适且强大的高效率开发利器.JSF 的开发环境搭建涉及到在 ...