桌面山寨版2048—游戏逻辑篇之移动方块的框架

昨天看到博客(www.richinmemory.com)的流量统计,居然还有一位朋友评论了,感动的满眼都是泪啊!谢谢支持啊!为了使互动的朋友更方便的互动,今天我加了个能用微博等帐号登录评论的插件。需要源码的朋友可以直接发信到我的邮箱。猛戳之后(www.richinmemory.com)若觉得还过得去,可以尝试收藏啊,亲。有朝一日有幸流量稳定了,我就开始放弃这边更新了,不过这个肯定还要很久很久才能达到。

二、桌面山寨版2048—游戏逻辑篇之移动方块

这个小游戏的基本逻辑就是:合并同类项。玩家通过上下左右键操纵游戏方块,然后操纵方向上所有相邻相同的数字的方块会被合并,合并之后方块上的数字会被更新并且改变颜色。

对于逻辑推理这件事,我一直深深相信的就是“从1到2再到无穷”的方式,这还是我高中老师教会我的方法,对于任何一个有规则但是复杂的事务,先从简单的开始总是不会错的。于是,首先考虑只有两个方块在一个方向进行操作(我选择的是“下”这个方向)的时候。根据在界面篇中的原则,所有的游戏方块都是被保存在一个ItemBox的数组里并且在程序的一开始就进行了初始化,所以,现在要处理的逻辑就是如何控制每个方格里方块的显示与否。

如果只有两个游戏方块时,用户按下了“下”方向键,要考虑的情况有两种:一是两个游戏方块不在一列,二是两个游戏方块位于一列。首先思考一下情况一的行为模式应该是:两个游戏方块都应该向下移动直到它们碰到了游戏区域的边框。这个逻辑并不难实现,最简单的办法,采用一个循环,遍历所有的方格,如果当前方格的bshow属性是false,那么不进行操作,否则,获取当前方格的位置信息(坐标),文本,颜色。将当前列的最后一行的方格赋予相同的文本和颜色,同时将当前游戏方块的信息清空(方块颜色设置为背景色,文本清空),刷新界面,这样就可以造成当前游戏方格“移动”到最后一行的假象。当一个方向做成功之后,另外三个方向只要以此类推就可以了。虽然说就这个逻辑和最终的逻辑还相差十万八千里,甚至对于整个游戏逻辑来说甚至是不正确的。但是如果没有学会加法,那么你怎么也不会学会三重积分吧。

忘记上面一段扯的废话,现在来考虑情况二,如果两个方块位于一列,就要考虑到合并的情况(先假设两个方块的文本是一样的)。这时按下方向键,正确的期望结果是两个方块合并并且位于当前列的最后一行,这里的做法思路有太多了。我最最最开始的第一脑想法就是从(0,0)位置的方块开始遍历,如果bshow是false,就不用做任何操作,如果不是,首先检查当前列最后一行的方块bshow的属性,如果是false,那么和上面情况一样,设置当前列的最后一行的方块和当前方块信息一致并清空当前方块信息。否则,则说明需要合并,这时只要将当前列最后一行方块升一级(比如”2->4”)并清空当前方块信息就可以了。如此,两个方块的移动和合并已经做完了。可以按照这个思路先先出个代码试试。

现在已经考虑完“1”的情况了,下面要开始进一步,考虑“2”的情况了。当两个初始方块合并完毕以后,开始思考又多生成了一个新的方块该怎么办。这里也有好几种情况需要被考虑。

第一种,第一次出现的两个方块已经合并,新的方块出现并且和被合并的方块不在一列上(还是先只考虑一个方向上的情况)。这种最简单,也就是两个不在一列的方块而已,上面已经说过了。

第二种,同样是最初出现的两个方块已经合并,新的方块与旧的方块在同一列中。这时新出现的方块和已经合并的方块文字不一样,不可能发生合并(暂时先从最简单的情况开始)。所以这时的思路是上面的一个扩展,也是从左上角的位置进行遍历,依次判断每个方块的bshow是否是true,如果是,那么这个移动方式就有点不同了,由于目前已知最后一行是已经合并的方块,所以你知道当前这个方块应该在倒数第二行上出现。但是如果我们不知道当前列最后一个bshow为false的行是多少该怎么办?怎么使用程序来找到呢?由于我们知道当前位置的纵坐标横坐标,所以从最后一行开始,依次向上遍历,如果遇到bshow为false就立马退出循环并且记录下当前的行坐标。这样就能完成上面所说的任务,有了这个坐标就可以知道当前的游戏块应该放在哪个位置,其余的操作就和前面所说的一致就可以了。

第三种,最初的两个方块没有合并,那么这种情况肯定和前面只有两个方块之中的某一种是一样的。

现在我们已经处理了”2”的情况,那么这种”2”的情况能不能推及到无穷呢?如果这时又出现了一个新的方块,那么情况能不能直接套用上面的思路呢?稍微想一想,总感觉可能可以又可能不可以,因为“无穷”的情况实在是太多了。我总结了下,用一个图表示,虽然在实际情况中不可能同时出现这四列,但是四列单独出来,都是可能出现的:

第一列和第四列的情况最简单,直接移动合并就可以,具体步骤前面已经描述过了。

第二列,需要判断出同一列的下一行的文字和当前的文字不相同,只能移动不能发生合并。

第三列,有两个能合并的地方,列1和列2的“2”可以合并,列3和列4的“4”可以合并,而且合并后,两个“2”合并出的“4”要显示在第三行,“4”合并出来的“8”要出现在第四行。

人脑思维分析完了,现在就要转换成为电脑的思维考虑如何用程序实现。按照前面的思维模式下来,从左上角第一个游戏方块开始进行遍历,如果遇到当前行的bshow是true,取得当前游戏方块的列标,从当前列最后一行开始,依次判断当前的bshow是否是false,如果是,记录下当前的行序号并且返回。获得这个返回的行序号之后,判断这个行序号的下一行(当然是同一列)和当前行的游戏方块文字是否一致(有点绕,意思你懂的就行),如果一致,那么要考虑合并,并且重新设置信息。

考虑到这里,貌似觉得逻辑都屡顺了,我当时反正就是这么想的。于是第一次我写的代码是这样的,GetCoordsFromIndex(i,it)是我写的一个函数,目的是根据当前游戏方块的序号得到游戏方块的纵坐标和横坐标,这个使用除法和取余操作很容易做到:


    if(nChar == VK_DOWN)
{
for(int i=0;i<nTotalGrids-1;i++)
{
if(m_itemBoxArray[i].bShow)
{
GetCoordsFromIndex(i,it);
for(int j=nTotalGrids - (m_nRowAndCol-it.nColIndex);j>i;j-=m_nRowAndCol)
{
// 找到当前列bshow为false的最大行序号
if(!m_itemBoxArray[j].bShow)
{
m_itemBoxArray[i].bShow = false;
m_itemBoxArray[j].bShow = true;
m_itemBoxArray[j].strItemText = m_itemBoxArray[i].strItemText;
m_itemBoxArray[i].strItemText = _T("");
GetCoordsFromIndex(j,it);
break;
}
}
//合并
if ( it.nRowIndex < m_nRowAndCol -1 )
{
ItemBox itCurrent = m_itemBoxArray[GetIndexFromCoords(it)];
++it.nRowIndex;
ItemBox itNextRow = m_itemBoxArray[GetIndexFromCoords(it)];

if ( itNextRow.bShow &&
itNextRow.strItemText == itCurrent.strItemText
)
{
m_itemBoxArray[GetIndexFromCoords(it)].strItemText = m_arrStrItemTexts[GetIndex(itNextRow.strItemText)+1];
--it.nRowIndex;
m_itemBoxArray[GetIndexFromCoords(it)].bShow = false;
m_itemBoxArray[GetIndexFromCoords(it)].strItemText = _T("");
}
}
}
}
}

但是一测试,发现貌似情况没有向我想像的方向走啊。我观察到了一个现象,那就是按照这个思路,上图列3的行为完全不正确。然后我又回到了这个代码,毕竟走到这一步,从代码的角度出发更容易找出问题所在,这也不违背数学归纳法的原则。如果按照这个代码,列3这种情况就会出现这样一种情况,由于我们是从左上角开始遍历的,那么第一行的2和第二行的2合并之后成为第二行的4,遍历继续,当遍历到第三行的4时,决定与第四行的4进行合并,这样就形成了第四行的8,但是此时,第二行的4应该移动到第三行。然而,按照我们的代码,我们已经遍历过了第二行,不可能再回去了,所以就造成了在错误,就会造成合并的4和合并的8分别在第二行和第四行,第三行空出来了,这明显是不正确的。如何解决这个问题呢?通过分析可以发现,这个问题就是因为我们的遍历顺序有问题,那么就简单了,在向下这方向上,只要从最后一个游戏方格向第一个方格进行遍历这个问题就迎刃而解,具体逻辑同志们可以再思考思考。

这样之后,带着洋洋得意的心情再次编译,尝试逻辑是否正确,结果发现还是太naive,问题又出现了,如果同一列出现了4个”2“,这时按一下”下“方向键,这4个”2“会直接合并成一个”8“,根据原游戏的规则,这也是不对的,毕竟我们是山寨,山寨就得有点诚意,不能乱窜改原先的游戏规则。这个问题又如何解决呢?首先得找出这个问题出现的原因,仔细思考下不难发现,原来游戏的规则是一次操作中,合并过的游戏方块不可以再次合并,那么,这个问题很容易解决,使用在封装中的另一个成员变量bJoin,标识已经合并的方块。所以最终”下“这个方向的代码如下所示:


    if(nChar == VK_DOWN)
{
for(int i=nTotalGrids-1;i>=0;i--) //从后向前遍历
{
if(m_itemBoxArray[i].bShow)
{
GetCoordsFromIndex(i,it);
// 查询同一列的bshow为false的最大行序号
for(int j=nTotalGrids - (m_nRowAndCol-it.nColIndex);j>i;j-=m_nRowAndCol)
{
if(!m_itemBoxArray[j].bShow)
{
m_itemBoxArray[i].bShow = false;
m_itemBoxArray[j].bShow = true;
m_itemBoxArray[j].strItemText = m_itemBoxArray[i].strItemText;
m_itemBoxArray[i].strItemText = _T("");
GetCoordsFromIndex(j,it);
break;
}
}
// 合并
if ( it.nRowIndex < m_nRowAndCol -1 )
{
ItemBox itCurrent = m_itemBoxArray[GetIndexFromCoords(it)];
++it.nRowIndex;
ItemBox itNextRow = m_itemBoxArray[GetIndexFromCoords(it)];

if ( itNextRow.bShow &&
itNextRow.strItemText == itCurrent.strItemText &&
!itNextRow.bJoin // 判断当前块有无被合并过
)
{
m_itemBoxArray[GetIndexFromCoords(it)].strItemText = m_arrStrItemTexts[GetIndex(itNextRow.strItemText)+1];
m_nScore += 2*pow(2.0, GetIndex(itNextRow.strItemText)+1 );
m_itemBoxArray[GetIndexFromCoords(it)].bJoin = true; // 设置当前块已经被合并
--it.nRowIndex;
m_itemBoxArray[GetIndexFromCoords(it)].bShow = false;
m_itemBoxArray[GetIndexFromCoords(it)].strItemText = _T("");
}
}
}
}
}

其余三个方向的代码依”下“方向类推,但要注意遍历顺序,具体代码可进入博客,那里有我的邮箱,若需要源代码,可以留言或者发信给我。到这里,对于一个完整的游戏,逻辑只能说完成了一个框架,还不能成为一个合格的山寨品,所以请看下一篇”逻辑面之缓缓出现的细节像风叶“。

桌面山寨版2048—游戏逻辑篇之移动方块的框架

时间: 2024-08-18 22:51:06

桌面山寨版2048—游戏逻辑篇之移动方块的框架的相关文章

2048游戏逻辑

math.randomseed(os.time()) local function printGrid(grid) local celllen = 8 -- one cell have character count local gridStrLines = {} table.insert(gridStrLines,"\n-------------------------------------") for i,row in ipairs(grid) do local line = {

Android项目开发实战-2048游戏

<2048>是一款比较流行的数字游戏,最早于2014年3月20日发行.原版2048首先在GitHub上发布,原作者是Gabriele Cirulli,后被移植到各个平台.这款游戏是基于<1024>和<小3传奇>的玩法开发而成的新型数字游戏.游戏源地址:http://gabrielecirulli.github.io/2048/ 1.新建android项目game2048 修改activity_main.xml文件 <LinearLayout xmlns:andro

极客学院Android之2048游戏开发全过程

2048完整开发 课1.游戏2048玩法介绍 同一条线上的相同数字折叠 课2.创建2048游戏项目 修改布局 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:lay

Cocos2d-x 3.x版2048游戏开发

今天给大家介绍如何快速开发2048这样一款休闲游戏,理解整个2048游戏的开发流程,从中你将可以学习到以下内容: 2048游戏的逻辑 Cocos2d-x中上下左右手势的识别 游戏中卡片类的创建 添加卡片到游戏中 游戏中的逻辑实现 游戏中随机卡片的生成 游戏结束判断 游戏分数的添加 游戏美化 笔者的开发环境:    Cocos2d-x 3.1.1(开发引擎)    Visual Studio 2012(Win32)    Xcode 5.1(Mac系统下) 这里注明一下,本教程来自极客学院,小巫对

网页版《2048游戏》教程 - 完成游戏逻辑

1.     捕获键盘事件 <2048>游戏的操作主要是依靠键盘的上.下.左.右来完成,首先我们需要在game.js文件中捕获键盘响应的事件. $(document).keydown(function (event) { switch (event.keyCode) { case 37://left break; case 38://up break; case 39://right break; case 40://down break; default : break; } }); 键盘事

基于MFC对话框的2048游戏

在之前一篇<简单数字拼板游戏学习>基础上修改,地址:http://www.cnblogs.com/fwst/p/3706483.html 运行效果: (4 X 4) (7 X 7) (1)已完成 2048 游戏基本功能,需要解决的几个关键问题是 a. 首先是数据结构.先定义矩形类,然后定义矩形类对象的二维数组,长度由宏定义,可修改,即可自定义成N*N的游戏.这样游戏就是由N*N个矩形对象组成. b. 然后是游戏逻辑处理,也是最重要的一部分.方向键的响应.键盘上下左右四个方向键的逻辑一样,代码部

Web版2048游戏制作

写在前面 工作之余参与了<慕课网2048游戏制作>的学习视频,视频断断续续看完了,游戏也制作成功了.因为其他的事情也没来的及总结,一拖时间也就过去了,整理磁盘的时候发现了2048源码,思考一下还是将之push到github上了(后面贴出),也顺便写篇总结,回顾一下,同时也谢谢liuyubobobo老师的授课. 不过源码在手,不运行下玩玩,怎么说的过去呢,哈哈!下面是游戏截图. 大图是PC端运行的效果,小图则是在iphone5s上的运行效果(Chrome浏览器debug,具体如何做参考文章:ht

MFC实现2048游戏(一)

MFC先实现2048游戏: 总共有两个场景,欢迎场景与游戏场景,在MFC下面,一个场景也就是一个对话框: 第一步: 实现欢迎场景: 很简单的插入一个对话框:防止两个按钮,一个进入游戏,一个退出游戏,其他界面上自己可以加载图片好看一点: 这个类为:CStartDlg 因为我们运行程序首先出来的是欢迎界面,所以在2048App当中要把主对话框改为 CStartDlg 第二步:实现游戏场景: 这里界面几乎没有设置,界面部分没有发时间去弄,就是简单的数组数字显示:主要是弄游戏逻辑,下一篇讲游戏的逻辑!

Android 2048游戏开发

根据教程写的简单的2048游戏. 极客学院教程地址:http://www.jikexueyuan.com/course/43.html 我的源代码地址:https://github.com/myCodingTrip/2048Game 项目有3个类. Card extends FrameLayout{ private int num; private TextView label; public Card(Context context) public int getNum() public vo