1小时开发2048,原来游戏如此简单
本系列教程,想通过一些实际案例,带领大家学习编程。
在学习本课程之前,需要有两个准备,首先,您需要有一点点的编程基础,比如什么是变量、条件判断、循环等。其次,请先玩一玩2048这个游戏,如果没有玩过,是不太容易理解其中的一些算法的。
好了,我当大家已经准备好了,现在开始我们的讲解。讲解分为两大部分,介绍需要了解的材料,以及实际操作过程,其实编程就和小孩搭积木一样,了解好材料一步一步搭建即可。
我能给您的保证就是,通过本文,您一定可以自己手动编码一个2048的游戏,如果不行,请在评论区轰炸我~
材料准备
- 开发环境准备
- 代码基础准备
- 涉及到的样式
1. 开发环境准备
首先,您需要先有一台电脑,如果是手机查看,就算了。。。然后,一个代码编辑器,可以是记事本,也可以是IDEA。这里推荐使用visual studio code编辑器和chrome浏览器。至于visual studio code是啥,请自行百度了。
就是这么简单,开发环境已经准备好了。
2. 代码基础准备
- 完全没有基础的同学,可以先看看w3school关于html的教程、CSS的教程、JS的教程。比较不是仅仅写个hello world,还有需要有一丢丢基础的。
- 教程中,不是使用的原生js,而是基于Jquery进行的编码,使用了一些Jquery的语法,请参考:Jquery的教程
3. 涉及到的样式说明
- width 宽度
- height 高度
- margin 外边距
- background 设置背景
- background-color 设置背景颜色
- color 设置前景色,也就是字体颜色
- border-radius 设置边角圆角幅度
- line-height 设置行号,通常用于上下居中
- text-align 左右对其方式,设置为center,为左右居中
- font-size 字体大小
- float 浮动模式
- .content 代表某个标签下class属性为content,是一个css选择器,以下是例子:
<div class="content"></div>
好了,我们开始进入正题,进入编码环节
正式编码
- 页面基础框架编写
- 样式编写
- 监听键盘事件
- 编写算法逻辑
- 生成随机块
1. 页面基础框架编写
详见上图,这是一个最基础的html代码,分为三块,页面、css样式、js逻辑,请参考图中的注释。直接浏览器打开本文件,即可看到效果。(可按F12,调出chrome的控制台)
再次说明,html基础标签和结构,请参考html的教程
2.样式编写
这里,我们先看标记2的代码,添加了16个div,4*4的游戏中,有16个块。标记1的代码位置,我们设置了宽度和高度,通过float: left; 浮动属性,正好每4个就换行。
然后,我们来修改样式,先修改每个子块背景样式。
这里,我们需要让每个块都有一个间隔,加上margin: 3px,这样每个块的上下左右都有一个3px的边框,从宽度来讲,每个块现在就是3+100+3的宽度了,如果块的宽度还是100,则一横排就装不下4个块,所以我们要减小每个块的宽度,减为94。高度同理。所以我们设置上width: 94px; height: 94px; margin: 3px; 每个小块的背景添加浅一点的颜色,以示区分。然后再对每个小块的边角进行圆角化。具体参考图片里面的代码注释。
然后我们将每个块的文字居中,这里涉及到上下居中和左右居中。分别为:上下居中:line-height: 94px; 和左右居中:text-align: center; 再设置上字体大小。
上下居中特殊一点,设置行高为子div的高度,就可以达到上下居中的效果。
接下里,我们需要对每种块的演示进行下样式设置,在2048里面,有如下块:0,2,4,8,16,32,64,128,256,512,1024,2048,4096(本文仅支持到4086),需要对不同的模块设置下背景色和前景色(字体颜色)。请参考代码:
这里我们设置了item-2到item-4096的12个样式,并且在下面的div上也分别加上,就可以看到拥有不同样式所呈现出来的效果。
到了这一步,我们2048游戏的样式部分就结束了,是不是很简单,哈哈~接下来,进入第三步,监听键盘事件。
3. 监听键盘事件
首先第一步 ,先添加js的启动方法 $(function() {}),不要问为什么,问就是自己百度。
刷新页面,可以看到,在控制台的Console界面,可以看到打印出了:进入启动方法。console.log(str)是js里面在控制台输出文本信息的方法,用于调试比较方便。
在启动方法里面绑定键盘监听事件,这样在按键盘的时候,就可以捕获事件,进行处理,目前是在控制台打印键盘值。38为键盘方向键“向上”的值。我们可以依次试出,37为向左,38为向上,39为向右,40为向下。
然后我们使用switch语法,来进行条件判断,参考如下代码:
发现没有按条件出发,而是连续出发,所有对每个分支,我们都加上break跳出即可。
这下对了,现在按向上键,就仅触发向上的逻辑了。
4. 编写算法逻辑
由于算法逻辑较为复杂,所以我们有分为几个小模块
4.1 通过数组来呈现每个块
4.2 初步实现位移和合并
4.3 实现向上合并(第一种算法)
4.4 实现向下合并(第二种算法)
4.5 实现向左向右合并(第三种算法)
4.1 通过数组来呈现每个块
通过观察,我们发现,整个2048,其实就是一个4*4的二维数组,所以,为了便于运算,我们通过二维数组来表示整个游戏的逻辑
建立数组:[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
有了数组,而页面上,目前我们是写死的div,所以我们需要通过逻辑,运算整个数组,来将页面给画出来。
添加一个drawGame的方法
这里,我们首先在2的位置,添加了一个drawGame的方法,用于刷新绘制界面的。但是光有方法是不行的,还需要被调用才会执行,所以在1位置,我们加上了对在这个方法的调用。
然后,我们用2.1和2.2,分别展示了2种循环的写法,通过循环,我们分别拿到行和列。现在我们就需要通过这个二维数组的值,来绘制我的页面了。
首先,我们删掉之前固定写死的div代码
然后我们通过循环,将div写入进去
通过双层for循环,我们将16个方块都写入到了页面上。那么我们改动一下数组的值,就可以使得对应方格的数字进行变化
非常好,已经快要成功了,现在我们需要做的就是,当值为0的时候,不要显示0,而值不等于0的时候,显示对应的数字和匹配的样式。
好的,我们完成了样式的刷新了,后续每次对数组进行运算更新后,调用drawGame方法,就可以使页面保持最新了。
4.2 初步实现位移和合并
接下来,我们就需要完成合并和位移事件了。先举最简单的例子,将合并与移动进行下说明
先来说位移,将数组重新初始化为一下,仅第二行第一列为2,其余都为0
现在我们按向上的方向键,就需要将这个位置的2移动到第一行第一列去,换个说法,将第二行第一列的值设置为0,第一行第一个值设置为2
好的,我们用代码进行实现
这里说明一下,在JavaScript中,数组下标都是从0开始的,所以第一行第一列是[0][0],第二行第一列是[1][0]
最后需要调用drawGame,确保页面根据数组重新绘制,刷新了。
按了上键后,结果如下。按我的估计,如果之前你没有怎么开发过的话,应该比较兴奋了,页面已经开始随着你的操作有了反馈了,对于心态上,这个是个很大的进步。我当年就有这样的感觉。
位移体验过了,我们又来体验下合并
重新初始化一下数组,是的第一行第一列为2,第二行第一列为2,其余为0。然后我们按上键,将第二行第一列合并到第一行第一列去,换句话说,就是第二行第一列变为0,第一行第一列变为4。看代码:
好的,我们现在按向上键,是不是就合并了呀,是不是 很简单呢。
其实编程就是这样,分为运算和绘制,运算位置、大小、颜色、形状等等,然后将结果绘制出来。
4.3 实现向上合并(第一种算法)
好拉,简单的说完了,我们开始这个游戏的算法。下面我会举例3种算法,分别在不同的方向上实现。
先说第1种算法,逻辑比较简单,写出来比较啰嗦。分为三步,第一步去0位移,第二步合并,第三步再次去0位移。
先来说合并,2048游戏是一个往一个方向合并,如何值相同就合并的游戏。我们来分析下有几种情况
第一种情况,相邻两个是相同的,xx这种类型,有三种情况xxab,axxb,abxx
第二种情况,间隔一个0,然后相等的,x0x这种类型,有两种情况x0xa,ax0x
第三种情况,间隔两个0,然后相等的,就一种,x00x
其实就可以一种一种的处理
xxab,结果是:(2x)ab0
axxb,结果有两种情况:a(2x)b0,(2x)b00,举例:4228结果4480,0228结果4800,主要看a等不等于0
abxx,结果有三种情况:ab(2x)0,a(2x)00,(2x)000,主要看a和b等不等于0,4822结果为4840,0822结果为8400,4022结果为4400,0022结果为4000
......
其余情况都一样,一种一种穷举出来即可。
由于该方案实在是太啰嗦 了,我的第一个方案,在此基础上做了一点点优化,什么优化呢?先进行去0位移操作,将中间的0去掉,再进行合并,就可以将很多情况合并处理了。
比如:ax0x,先进行去0位移,则变成了axx0,再比如0x0x,去0后,就变成了xx00了
所以经过处理,所有可能出现合并的情况就变成了xxab,axxb,abxx,情况了。但是合并后,中间会出现0,比如2448合并后就变成了2808,然后位移获取就应该是2880,最终2448变为2880,最后再次进行去0位移。
所以分为三步,第一步去0位移,第二步合并,第三步再次去0位移
去0算法
这里去0算法,是从后面开始去的,自行思考,如果从前面开始去的话,会有什么问题呢。
好了,这里去0算法,仅仅只是去掉了第一列的0,其余三列的0也需要去掉。所以加一个循环,没必要重复写4遍,请看代码:
按上键后,结果为
好,然后是合并算法
由于先进行了去0位移,所以合并的情况就变为3种情况了,请直接参考图中的注释进行理解。
合并后结果如图,第2列的情况是022(16),先通过去0位移,变为22(16)0,然后合并变为40(16)0,那么我们再继续一次去0位移,就可以达到最终效果了
最终,我们第一个算法就完成了,大家可以直接依葫芦画瓢,将其余三个方向都搞定了。
但是本人觉得,这样写太啰嗦了,所以有了第二个简化一些的方法
4.4 实现向下合并(第二种算法)
优化版去0一位:
第一个算法,核心是如果这个位置为0,则后面的值全部往前移
第二个算法,核心是如果这个位置为0,则往后面找一个非0的值,放到这个0这来。所以要往后一个一个判断是不是非0的。
具体看算法和注释。
第一个算法,一共47行代码,第二个算法33行(去掉注释行和空行),代码又花了四分之一。
4.5 实现向左向右合并(第三种算法)
下面来最后一个算法,我称呼为直接算法(好直接的名字)。
算法核心是这样的,请注意理解:
当前位判断是否为0,如果为0,则后续的所有位往前移动,由于向前移动了了,所以序号要从当前为从新算,所有有个 i--
如果当前位判断不为0,则有后续有3种情况
1.后续位为0,则继续往后找
2.后续位不为0,且也不等于当前位,则不管了,直接结束循环。因为有外层循环,会循环到这一个位置的,所以不用再做处理了。
3.后续位等于当前位,则直接让当前位乘以2,后续位变为0,然后就结束循环,继续外层循环即可。
看算法:
这里给出了具体算法,请根据注释自行思考,如果看不懂,则直接用第一种算法吧。
本文算法不是核心,仅仅是通过不同算法,给大家一些提示而已,不用强求。主要还是思考整个游戏开发的思路。算法都可以慢慢优化的。
对于向下的,也是通过第三种算法,可以完成,看代码:
最后,通过上下左右随机的按,结果就是:
好了,整个游戏的算法基本完成了。
5. 生成随机块
接下来就是每次点击,需要随机生成一个新的块。
刚刚提到,随机,这里就要用到一个随机数:parseInt(Math.random() * 4)
Math.random()会生成一个0到1之间的一个随机浮点小数,乘以4后取整,就可以获取一个随机的0,1,2,3的随机整数了,而这个正好是我需要数组下标。
先编写方法randomItem,看到这个代码,可以随机获取到一个块,让其置为2。
但是有可能当前位置本来就有非0的值呢,所以我们加一个判断,如果当前位置不为0,则重新获取,由于是随机的,概率来说,一定会在有限次内获取到非0的空位的。
加上了判断之后,我们也加上对于生成随机块的触发条件,给一个思考,如果randomItem方法,加载drawGame方法的最后触发呢,效果是啥样的,有什么为题呢。
好了,现在还有最后两个问题没有解决
1.随机生成,应该有一定概率生成4。
2.如果都生成完了呢,全部填满了呢,就会出现怎么随机都找不到一个不为0的,那么就死循环了。
所以需要加一个判断
ok,基于此,我们的教程就写完了。大家可以尽情按照自己的方法玩耍了。
欢迎各位留言~~原创不易,也请大家多多转发,将这么好的教程让更多人看到。后续将推出更多其他详细案例讲解。
发表评论 (审核通过后显示评论):