一个专业的程序原该养成的好习惯(分析能力)《2048》游戏分析

一、游戏分析
《2048》是一款比较流行的数字游戏,其作者Gabriele Cirulli (加布里埃尔斯路理)目前居住在意大利。他在2014年3月最先将 2048 的开源版本放到 Github 上,由此引发了风靡全球的狂潮,而其当时年仅20岁。
这款游戏的玩法很简单,每次可以选择上下左右移动,每移动一次,所有的数字方块都会往移动的方向靠拢,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢、相撞时会相加。在所有方格未被占满且无法移动之前合成出2048,则游戏胜利,否则失败

游戏初始界面:

游戏开始后,可以按键盘的上下左右键进行移动,数字方块会向移动方向靠拢,并生成新方块

数字方块移动期间碰撞会将相同方块合并成新方块,并加分

数字方块移动期间碰撞会将相同方块合并成新方块,并加分

直到生成2048或无法移动、碰撞为止,游戏结束,可以重新开始游戏或退出游戏,重新开始则更新最高分

游戏过程中可以按F1重新开始游戏

游戏过程中可以按ESC退出游戏

二、开发过程
软件的开发过程可以分为:
1.确定需求(软件功能的文字描述)
2048的功能如下:

1).开始游戏会在4*4的方格中,随机产生两个数字方块2或4
2).每次可以用键盘上下左右移动,每移动一次,所有的数字方块都会往移动的方向靠拢,系统也会在空白的地方随机出现一个数字方块2或4。如果无任何方块移动,则不产生数字方块。
3).相同数字的方块在靠拢、相撞时会相加。
4).不断的叠加最终拼凑出2048这个数字就算成功。如果在凑出2048之前,方块占满所有方格,不能再移动或相加,则游戏结束
5).游戏有计分功能,每次发生方块相撞相加,就会加分,分值为当前方块数的数值,显示在界面上,动态居中显示
6).游戏胜利或结束后更新最高分,可以退出游戏或重新开始
7).游戏过程中可以按F1重新开始游戏,可以按esc退出游戏

2.业务需求分析(找出对象,与对象之间的关系。本游戏中对象关系如下)
|–StartGame(游戏主窗口)
|–Game_2048(2048游戏面板类)
|–Background 背景类
|–GameSerivce 游戏功能类
|–Data 分数类
|–Resource 资源加载类

3.类的设计
要创建对象,就先要定义出类。找出对象中的数据,根据对象定义类。

a.游戏启动类StartGame
用于设置游戏主界面,加载游戏显示内容Game_2048。

b.游戏资源类Resources
Resources类用于提供游戏需要使用到所有文件的引用,包括方块2~方块2048、背景和次背景、得分和最高分计分板、分数数字的png图片文件
|–Resources
|–img_num 分数数字
|–img_bg 背景
|–img_fg 次背景方格
|–img_score 得分
|–img_highScore 最高得分
|–img_2 方块2
|–img_4 方块4
|–img_8 方块8
|–img_16 方块16
|–img_32 方块32
|–img_64 方块64
|–img_128 方块128
|–img_256 方块256
|–img_512 方块512
|–img_1024 方块1024
|–img_2048 方块2048
|–getImg(String) 加载图片

c.游戏分数类Data
Data类用于储存游戏中的分数信息,并定义用于返回和修改其中私有数据成员的公共接口方法,同时定义了绘制得分、最高分计分板、分数数字的显示分数的功能
|–Data
|–SCORE_X 得分计分板图片的原点x坐标
|–SCORE_Y 得分计分板图片的原点y坐标
|–HIGHTSCORE_X 最高分数计分板的原点x坐标
|–HIGHTSCORE_Y 最高分数计分板的原点y坐标
|–SIZE_NUM 数字图片中一个数字占的像素大小是21(宽高都是)
|–SIZE_SCORE 分数与最高分图片宽度 都是140像素
|–score 当前得分
|–hightScore 最高分
|–drawData(Graphics) 实现计分板功能,包括绘制计分板背景图和得分、最高分数据显示功能
|–drawScore(Graphics) 显示得分数据
|–drawHightScore(Graphics)

d.游戏背景类Background
在指定的坐标绘制游戏的背景和次背景图。
|–Background
|–FG_X 次背景方格原点x值
|–FG_Y 次背景方格原点y值
|–SIZE 一个次背景方格大小
|–drawBackground(Graphics) 画背景和次方格背景
|–drawBlock(Graphics,int,int,int) 根据图片索引画数字图片
|–drawNumPic(Graphics,Image,int,int)画出图片数字

e.GameService类用于绘制主要功能的方法,以及实现游戏主要功能需要调用的一些接口方法:
数据成员
用于对应4*4方格的存储数字图片的索引编号的二维数组 int[][] gameMap;
保存需要显示的分数数据 Data data;
开始游戏start();
生成一个新方块newBlock();
绘制游戏主功能GamePaint()
根据参数画数字方格图片deawNumPic
向左移动moveLeft();
向右移动moveRight();
向上移动moveUp();
向下移动moveDown();
向上消除removeUp();
向下消除removeDown();
向左消除removeLeft();
向右消除removeRight()。
游戏结束或胜利的处理isGameOver();
是否满屏isFull();
2048是否达成 is2048();
是否还可以移动或消除canRemove();
重新开始restart();
计分功能bonus()
刷新最高分功能refreshHighscore();

f.游戏显示功能类Game_2048
游戏显示面板,用于调用背景类Background的drawBackground和游戏显示服务类GameService的gamePaint显示功能来显示游戏画面内容
接收从键盘输入按键键值,并根据不同的键值调用不同的方法;
按←键,键值为37,调用GameService类中的moveleft();和removeLeft();方法实现向左移动和向左消除;根据返回值isMove和isRemove判断移动是否有效,决定是否生成新的方块;
按↑(键值为38)、→(键值为39)、↓(键值为40)键同理;
按F1,键值为112,可以在游戏中途重新开始;
按Esc,键值为27,可在游戏中途退出。

4.开发过程
步骤一:新建工程和包
新建名为JAVA_2048的工程,然后在工程下的src目录下新建包
步骤二:新建StartGame类
该类的作用是用于加载游戏并启动
步骤三:新建Game_2048类
此类是游戏显示面板,用于显示游戏画面内容,需要继承(JPanel),也是一个监听键盘操作的监听器,
需要实现KeyListener接口
步骤四:拷贝Background类、Resource类、Data
Background类是背景类
Resource类是资源类,作用是加载资源
Data类,该类是用于封装计分板数据与计分板计分功能
将res资源文件夹放到Java_2048工程的根目录下
步骤五:重构StartGame类和Game_2048类,加载背景图片
在Game_2048类中添加成员Background,并定义无参构造方法,初始化成员Background
在StartGame类中添加加载游戏面版的代码
步骤六:
测试背景图是否绘制成功
步骤七:新建GameService类,用于实现游戏主要显示功能和业务处理功能
新建GameService类,并定义成员Data data和int[][] gameMap.
其中data是计分功能类的对象,gameMap是用于存储游戏中对应的4*4
方格的数字图片的索引编号的。游戏中数字图片2~2048共有11种,对应
存储到gamMap中的值为1~11,也就是数字图片2对应存储为数值1,以
此类推。如果某位置没有图片,则存数值0。如下图中的数字图片,
对应gameMap中的值为:
{{2,0,0,0},
{0,0,0,1},
{0,0,0,0,},
{0,0,0,0}}

初始化成员信息
画出面板上的内容:分数和方格中的数字图片

步骤八:重构Game_2048,实现画计分板功能
在Game_2048类中添加GameService成员,并调用GameService的gamePaint方法
运行StartGame的main方法测试

步骤九:实现游戏启动功能,随机生成2个数字图片
在游戏主功能类GameService中定义start和newBlock方法
start方法用于启动游戏后的操作,将gameMap初始化和将data中的score和hightScore清零,并且随机位置生成两个数字图片。
完成GameService的start方法,并在该类的构造方法中调用,随该类创建而启动游戏
newBlock方法用于在4*4的方格内随机生成方块2或4,如果位置上存在方块则再生成,直到不重复,并需要将生成的方块所代表的数字图片索引编号存入gameMap中。为了降低游戏难度,生成方块4的概率约1/8

步骤十:重构GameService的gamePaint功能,实现画数字方块的功能
数字方块图片与GameSerivce的gameMap是对应关系,gameMap中
存储了游戏主界面上的所有数字方块图案索引编号,如果某个位置没有
数字方块图案,则存储0,否则存储1~11。
画数字方块的功能,首先需要遍历二维数组gameMap的内容,并将值
一一取出来判断,根据值画对应的图片。
其次,画图片需要定位图片原点的x和y值位置。数字图片都是100100
像素大小,次背景方格是400
400像素,刚好放入一个背景方格占满。
图片原点x值为:FG_X+i100(FG_X为背景方格原点x值,i为该行第几个格子,格子从0开始)
图片原点y值为:FG_Y+j
100(FG_Y为背景方格原点x值,j为该列第几个格子,格子从0开始)
运行StartGame的main方法,测试游戏第九步和第十步实现的功能

步骤十一:添加游戏移动功能
在GameService类中添加moveLeft、moveRight、moveDown、moveUp功能:

步骤十二:处理键盘按下事件
重构StartGame,添加监听功能

步骤十三:测试键盘响应功能
运行游戏,分别测试上下左右小键盘、esc和F1按键

步骤十四:实现左移操作
键盘按下的处理流程(以按了小键盘左箭头为例):按了键盘左键,则需要调用GameService中的
moveLeft和removeLeft,同时根据返回值来判断是否有移动或消除,如果有则生成新的方块。移动后重画游戏界面
运行游戏,测试左移操作

步骤十五:实现GameService左移方法moveLeft
左移操作流程:
1).遍历每一行,边界条件为0~3
2).遍历行当中的每一个格子,边界条件为1~3(为0的格子不需要移动)
3).每一个格子需要检查是否有数字方格图片并且左边的格子是否有
数字方格图片,如果都满足,则可以进行左移操作。左移操作实际
就是操作gameMap中的数据,将当前格子位置对应的图片索引的
值赋值给左边的格子位置,并将当前格子索引值赋值为0。
4).当前格子变为左边的格子,更改移动标记
5).再循环3、4的过程,直到3的判断条件不满足,则表示该格子不
能左移动了

步骤十六:实现右移功能
重构GameService的keyPressed方法和GameService的moveRight方法,实现右移,
右移的流程:
1.遍历每一行,边界条件为0~3(i)
2.遍历行当中的每一个格子,边界条件为2~0(j,值为3的格子不需
要移动,且从值为2的格子开始右移动)
3.每一个格子需要检查是否有数字方格图片并且右边的格子是否没有
数字方格图片,如果都满足,则可以进行右移操作。右移操作实际
就是操作gameMap中的数据,将当前格子位置对应的图片索引的
值赋值给右边的格子位置,并将当前格子索引值赋值为0。
4.当前格子变为右边的格子,更改移动标记(下一个标记要小于2)
5.再循环3、4的过程,直到3的判断条件不满足,则表示该格子不能
右移动了

步骤十七:实现下移和上移操作
下移与上移操作,流程与之前的左右移动操作类似,只是在下移和上移时是修改gameMap[i][j]中的i值这点不一样。
下移流程:
1.遍历每一行,边界条件为2~0(i值,为3的格子不需
要移动,且从值为2的格子开始下移动)
2.遍历行当中的每一个格子,边界条件为0~3(j值)
3.每一个格子需要检查是否有数字方格图片并且下边的格子是否没有
数字方格图片,如果都满足,则可以进行下移操作。下移操作实际
就是操作gameMap中的数据,将当前格子位置对应的图片索引的
值赋值给下边的格子位置,并将当前格子索引值赋值为0。
4.当前格子变为下边的格子,更改移动标记
5.再循环3、4的过程,直到3的判断条件不满足,则表示该格子不能
下移动了

上移流程:
1.遍历每一行,边界条件为1~3(i值,为0的格子不需要移动)
2.遍历行当中的每一个格子,边界条件为0~3(j值)
3.每一个格子需要检查是否有数字方格图片并且上边的格子是否没有
数字方格图片,如果都满足,则可以进行上移操作。上移操作实际
就是操作gameMap中的数据,将当前格子位置对应的图片索引的
值赋值给上边的格子位置,并将当前格子索引值赋值为0。
4.当前格子变为上边的格子,更改移动标记
5.再循环3、4的过程,直到3的判断条件不满足,则表示该格子不能
上移动了

步骤十八:实现消除功能的左消除
左消除,当前的格子都会将右边的格子左移并尝试合并,并且最后图片数字会移动到最左边,所以需要从最左边的图片数字开始消除,流程如下(左消除之前已经调用
左移动,格子内的图片数字会先移动到左边):
1.遍历每一行,边界条件为0~3(i值)
2.遍历行当中的每一个格子,边界条件为0~2(j值,为3的格子不需
要尝试合并,右边没有格子了,且从值为0的格子开始左消)
3.每一个格子需要检查是否有数字方格图片并且右边的格子图片索
引是否有与当前数字方格图片索引值相同,如果都满足,则可以进
行左消操作。左消操作实际就是操作gameMap中的数据,将当前
格子位置对应的图片索引的值增加1,并将当右边格子索引值赋值
为0。
4.当前格子变为右边的格子,更改消除标记

运行游戏,测试左消功能:
按小键盘左键,触发的操作有1.左移操作 2.左消除操作

步骤十九:实现右消除、上消除、下消除功能
右消除,键盘右方向键触发,左边的图片尝试往右边合并 因为在右消除之前已经调用了右移动,所以,格子都已经右移到右边。流程:
1.遍历每一行,边界条件为0~3(i值)
2.遍历行当中的每一个格子,边界条件为3~1(j值,为0的格子不需
要尝试合并,左边没有格子了,且从值为3的格子开始右消)
3.每一个格子需要检查是否有数字方格图片并且左边的格子图片索
引是否有与当前数字方格图片索引值相同,如果都满足,则可以进
行右消操作。右消操作实际就是操作gameMap中的数据,将当前
格子位置对应的图片索引的值增加1,并将当左边格子索引值赋值
为0。
4.当前格子变为左边的格子,更改消除标记

上消除,键盘上方向键触发,下边的图片尝试往上边合并 因为在上消除之前已经调用了上移动,所以,格子都已经上移到上边。流程:
1.遍历每一行,边界条件为0~2(i值,为3的格子不需
要尝试合并,下边没有图片了,且从值为0的格子开始上消)
2.遍历行当中的每一个格子,边界条件为0~3(j值)
3.每一个格子需要检查是否有数字方格图片并且下边的格子图片索
引是否有与当前数字方格图片索引值相同,如果都满足,则可以进
行上消操作。上消操作实际就是操作gameMap中的数据,将当前
格子位置对应的图片索引的值增加1,并将当下边格子索引值赋值
为0。
4.当前格子变为下边的格子,更改消除标记

下消除,键盘下方向键触发,上边的图片尝试往下边合并 因为在下消除之前已经调用了下移动,所以,格子都已经下移到下边。流程:
1.遍历每一行,边界条件为3~1(i值,为0的格子不需
要尝试合并,上边没有图片了,且从值为3的格子开始下消)
2.遍历行当中的每一个格子,边界条件为0~3(j值)
3.每一个格子需要检查是否有数字方格图片并且上边的格子图片索
引是否有与当前数字方格图片索引值相同,如果都满足,则可以进
行下消操作。下消操作实际就是操作gameMap中的数据,将当前
格子位置对应的图片索引的值增加1,并将当上边格子索引值赋值
为0。
4.当前格子变为上边的格子,更改消除标记

测试右消除功能

测试上消除功能:

测试下消除

步骤二十:实现计分功能
计分功能在游戏开始运行后,发生消除则计分,分数增量为消除后产生的数字图片的面值,计算出分数后,
需要画在分数面板上,并且实现动态居中的效果。
1).分析分值:4是2的2次方、8是2的3次方、16是2的4次方,
而在gameMap中正好存储的数字图片对应的索引编号,图片4是
数值2,图片8是数值3。通过Math.pow(2,num)公式则可以计算出
消除后产生的分值(num为图片索引值)。
2).将该值累加进Data的score成员中
3).在GameService类中添加计分方法bonus,实现计分功能,
4).测试计分功能

步骤二十一:实现更新最高分功能
游戏胜利或结束后,如果得分比最高分高,则更新最高分。
重构GameService类,添加refreshHighScore方法

步骤二十二:实现游戏结束功能
游戏结束包括两种情况:合成出2048游戏胜利、占满了所有格子且不能移动或合并游戏失败
流程:
1).判断是否游戏失败(所有格子都占满了,且不能移动或合并)
a.如果是游戏失败,则弹出窗口,显示游戏结束,并显示得分
b.刷新最高分
c.窗口选项可以选择重新开始游戏或结束游戏。重新开始游戏则将当前得分清零,初始化gameMap数组,并生成两个新的方块图片。结束游戏则退出并关闭窗口。
2).判断是否游戏胜利
a.如果是,则弹出窗口,提示达成2048
b.刷新最高分。
c.窗口选项可以选择重新开始还是结束游戏。重新开始游戏则将当前得分清零,初始化gameMap数组,并生成两个新的方块图片。结束游戏则退出并关闭窗口。

A.重构GameService类,添加isFull方法和canMove方法,用于判断是否占满所有方格,并且不能移动
B.添加refreshHighScore方法
C.添加restart方法和isGameOver方法
D.添加is2048方法,用来判断是否达成2048
E.给isGameOver方法添加判断游戏胜利功能
F.keyPressed方法,增加判断游戏结束功能
G.测试更新最高分和游戏结束功能

步骤二十三:实现Esc退出游戏和F1重新开始游戏功能

2048扩展功能:
实现最高分的文件存取:

步骤二十四:在结束游戏或退出游戏的时候将最高分保存或更新到当前项目的src的score.txt中
1.新建score.txt文件(存放hightScore数据)
2.新建一个操作类的ScoreUtil.java,添加保存、更新、查询方法
3.在结束游戏或退出游戏的时候,先调用查询方法查看文件中是否有数据,如果不存在就调用保存
如果存在则将数据取出判断是否需要更新最高分,在需要的情况下,调用
更新方法将数据更新

步骤二十五:在启动游戏的时候读取最高分数据在窗口上显示
在启动游戏的时候读取文件,将最高分数读取出来,放入Data类的hightScore
成员中,并且画出最高分

猜你喜欢

转载自blog.csdn.net/qq_42902470/article/details/85100545