EGE示例程序——2048手机h游戏「EGE示例程序——2048」
EGE专栏:EGE专栏
1. 百度网盘(示例二 2048)
https://pan.baidu.com/s/1BUDGLeenbIxpAfqd1XfNqg
源代码分享于 百度网盘,里面也有程序用到的资源文件,可以查看下载。
2. CSDN(示例二 2048)
https://download.csdn.net/download/qq_39151563/12154829
CSDN资源,设定为0积分,无需积分即可下载。
2048在线游戏链接: https://2048game.com/
2048 游戏规则
2048小游戏相信应该都玩过,规则很简单,上面也贴出了在线游戏链接,可以先玩一玩,了解一下游戏规则。
- 游戏主体为 的格子,每个小格子可以放置一个方块,方块带有一个数字。
- 游戏一开始随机出现两个数字方块,数字为2或4。
- 方块可以 上下左右 四个方向滑动。滑动后,同方向上两个相同的相邻方块会合并成更大的方块,合成后的方块数字是原来两个方块之和, 并且在一次滑动中,一个方块只能和相同的方块合成一次,不能连续合成,且在移动方向前面的两个优先合成。
- 每滑动一次,会在随机的一个空格中生成一个数字(2 或4, 大多是2)
- 如果16个格子都填满且无法滑动,则游戏结束。
计分规则
每合成一个方块加进行加分, 分值 = 合成的方块上的数字。
下面EGE制作的2048界面
1. 素材获取
网上有很多 Android 手机的游戏,想要相关的图片和音乐资源的可以先下载应用安装包,然后用 apktool 解析资源文件,得到里面的素材。
示例程序链接中也放有我从一款2048游戏中解析得到的素材。
2. 素材整理
一般别人做的游戏都比较花里胡哨的,东西很多,可以从中挑选一些自己用到的。有些图片尺寸不对,可以自行用PS, 或者其它的图像处理软件缩放一下,调成合适的尺寸, 并且改成合适文件名。
下面则是我从中挑出的所需要的图片和音乐素材, 并且对图片缩放过,以适配界面尺寸。
素材链接:https://download.csdn.net/download/qq_39151563/12154829
3. 界面设计
这时候考虑窗口的大小,界面的布局,在哪里显示什么内容,界面跳转等等。
再来说一下笔记本显示比例的问题,我笔记本是125%放大显示,估计一般的笔记本都是这样。因为对于笔记本来说,设置为100%的显示比例的时候,界面上的文字和图标真的很小。
所以如果设置EGE窗口是 500x500, 那么你将窗口截屏,会发现截下来的图片,分辨率约为 625 * 625。如果你看到屏幕上某个尺寸挺合适,截图下来后查看分辨率,需要除以缩放比例才能得到原图的大小,以这样的大小绘制,才会得到想要的尺寸。
根据素材所设计的界面布局
4. 功能分析
基本实现
- 方格
- 四方向移动,出现数字的组合
- 移动后随机在一个空格中出现2或4
附加项
- 计分,只出现在合并时
- 游戏结束判断:无法移动
- 移动动画
- 游戏音效
- 游戏自动存档读档
- 游戏重新开始
即仅仅实现基础功能:在 格子中移动合并数字,并随机出现数字,游戏界面只有4x4个格子。不计分,不作游戏结束判断,不存档,无动画效果,无音效。
1. 游戏设计
1.1 数据表示
4x4 格子用二维数组表示即可。
由于方块上数字是 ,是的次方, 所以可以考虑存储 即可, 这样方便编号,特别是图片, 编号 从到,分别对应数字 ,空格用表示。因为元素就是编号,所以绘图时直接根据元素绘制即可。
为什么是1到17?
因为随机出现的数字最大是4, 即 2 的2次方,从4开始,一共能排上16个数字,加上2,那么一共17个数字,即可能出现的最大数字是 。
如下图所示:
上图每个方块所对应的存储数据分别为:
即如果方块的值为 ,那么存储的数值为 。
1.2 图像加载
既然数字 到 , 分别对应编号 到 , 那么图片数据用长度为 的 PIMAGE 数组 blockImgs[] 存储即可, 方块 的图片就存储于blockImgs[n],方块 分别存储于 blockImgs[1], blockImgs[2], … , blockImgs[17], 一共18个,blockImgs[0]不使用。另外还需要存储一张 格子的背景图。
数字对应的图片名字命名格式为 , 存放于 文件夹中。
图片可以使用如下方式获取:
1.3 四方向移动
这是整个游戏最核心的部分。
首先移动需要检测按键,常用 AWDS 和四个方向键。
1.3.1 按键控制移动方向
只需要一个变量 direct来记录移动的方向。
数值 0~3 分别对应:左、上、右、下,这个可以用枚举,宏等进行定义,含义更清晰。
读取后,如果不等于-1,说明按下了代表方向的按键。后面就根据direction的值进行移动。
1.3.2 移动
移动时需要根据移动方向来检测数字,向左移动,那么就要对每一行,从左往右检测,即移动方向和检测方向是相反的,因为在前面的会优先合成。
下图中的左边部分即为左移时的检测顺序,包含初始位置,下一个元素的位置偏移以及下一行(列)的位置偏移。
四个方向,区别就是检测起点不同,检测方向不同,由此可以根据四个方向,得到这些数据。
为检测起点坐标,为当前行(列)中下一个元素的坐标偏移量, 为下一行(列)的坐标偏移量。
所以,对于移动方向direction, 按检测顺序遍历每个元素则为:
这样,就完成了四个方向遍历的统一。
合并问题变换:
这个问题变换为:一个长度为n的数组a,向下标为0的方向移动,忽略值为0的元素,相邻并且相同的元素将合并成一个值为两数之和的元素,并且每个元素只能参与一次合并,多个相同的元素相邻时,下标小的优先合并。
方法一:
因为忽略值为0的元素,所以可以用right表示遍历到的非零元素,0 ~ left - 1 为左边完成移动合并的元素。left 代表可能将要移动到的空位或可能参与下一次合并的元素。
方法二:
直接忽略元素0,只在非零元素间判断,相同则合并,处理完成后,元素中间可能会夹杂许多0,这时再次遍历,像删掉字符串中的某个字符一样,除去元素中间的0。
算法已经实现,然后回到二维数组,分别对每一行或每一列进行合并即可。于是得到下面的移动算法:(使用的是方法一)
move() 函数返回是否有格子发生的变动,这样可以根据是否进行元素移动或合并来决定需不需要添加一个随机数。如果返回 false,即格子没有变动,那么这次移动是无效动作,不需要添加随机数。
emptyBlocks 表示当前的空格数,代码中根据这个值来判断是否还能添加随机数的。每产生一次合并,方块数会少1,所以空格数加1。
1.4 添加随机数
根据空格数 emptyBlock, 生成一个到 empty-1 之间的随机数randEmptyBlock ,然后查找第randEmptyBlock个空格(从0开始编号), 往这个空格里添加一个2或4的方块,出现2的概率应该是大于4的,出现4的情况很少, 这里取 的概率出现数字 , 的概率出现数字 。参数 n 为添加的随机数个数,添加后,空格数 emptyBlock 减少 n,在这个过程中,也要判断空格数是否大于0,没有空格后就无法添加随机数,函数返回。
2. 基础版程序
综合上面,得到下面的代码, 共160行。
图片放在 “https://blog.csdn.net/qq_39151563/article/details/resource/image” 目录,并且数字图片命名格式为 “block_数字.png”, 背景图片命名为 “background.png”。
程序界面截图
使用的素材
界面展示
1. 重新开始
重新开始按钮
在图中添加了重新开始按钮,当检测到鼠标左键点击时,就判断点击位置是否在区域内。
下面代码为判断点击位置是否在按钮区域,因为只有一个按钮,所以直接取了固定值。
鼠标消息处理,判断是否有鼠标点击
点击按钮后标记需要重新开始。
游戏结束后按回车键
在游戏结束后,可以直接按回车键重新开始,不需要用鼠标。游戏没有结束时,防止误碰,不对回车键响应。
重新开始所需要做的工作
重新开始需要把格子清零,空格数emptyBlock 设置为16,本局分数清零,还有结束标记 gameOver 清零,做好后,再做一些其它相关的操作。
2. 添加音效
音效的添加很简单,先用MUSIC类打开音乐文件,然后在合适的时候调用Music.Play(0) 播放即可,因为Music.Play()中插了一个延时,动画会出现一帧的卡顿,如果是在移动动画中播放,延时一帧是可以感知到的卡顿,稍稍有点不流畅。所以可以看情况,决定要不要放音乐。
选择了开始时和合并时播放音乐。
合并音效
合并是在 move() 函数中检测的,用mergeMusic_flag 标记是否有合并。目前是设置为不在播放状态时才重新播放音效。这样的话如果音效放到一半又有其它方块合并,则并不会再次播放。
开始音效
刚打开 和 重新开始 时播放
3. 计分
分数分为 最高分数,当前分数,最大合成数字
因为计分是出现在合并的时候,所以在move() 函数中加入。
合并后计分,分值为合成的数字,同时更新最高分、最大合成数字。
4. 自动存档,读档
数据文件名
读档
不能因为没有记录文件就无法运行,因为程序的运行不需要依赖记录文件。如果没有记录文件,那就自己初始化数据,重新开始。一开始没有运行过的游戏,哪来的记录文件呢?记录应该由程序自己生成,而不是自己手动添加。
如果有记录文件,就读取记录,并且要适当地检查记录数据的正确性。
存档
因为需要退出游戏后保存记录,所以初始化模式需要添加INIT_NOFORCEEXIT ,即关闭窗口后不强制结束程序,以便进行游戏保存工作
为了方便看到保存的游戏数据,所以设置成文本文件格式保存。
5. 游戏结束判断
游戏结束,那必定是在出现随机数后,或者一开始读取的记录就是已经结束的数据。
当空格数 emptyBlock 为 0,并且不存在相邻的格子相同的情况,即为游戏结束,此时结束标记gameOver 置位,并且绘制上gameOver 图片。
6. 添加移动动画(难点)
因为要添加动画,所以要修改 move() 函数,最基本实现中是一次得到最终的移动结果,考虑到各个方块需要移动的距离不一定相同等情况,采用每次只整体移动一格,然后绘制动画的方式。因为最多移动三格,所以遍历三次即可,把要移动的格子作标记。然后在偏移位置绘制相应的图片即可,具体实现看完整代码。
三次遍历,每次遍历之间没什么不同,无法区分是否被合并过,所以要做合并标记,两个数都没合并标记才能合并,因为只能合并一次。在本次移动操作中,方块合并后就不会再移动。
增加了整体移动标记,如果一次检测没有移动,那么直接结束检测。
三次遍历,增加的是检测的工作,因为只有4x4大小,相对窗口几十万个像素的修改来说,无关紧要,耗时部分是动画,动画绘制次数依然不变。
为了方便修改和整理,所以定义了一些宏和全局常量。
由于库本身代码的原因,播放音乐时会有一帧的卡顿,所以示例程序中默认不播放合并音效版,使移动动画更为流畅。==
上海版权声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕,E-mail:xinmeigg88@163.com