基于嵌入式linux开发的“2048”游戏综合设计

学号:17020110019    姓名:高少魁

【嵌牛导读】本设计将之前提到的在FPGA开发平台上设计的游戏2048移植到了嵌入式开发平台上,利用基于qemu开源软件的虚拟 mini2440开发板,使用 Framebuffer图形界面编程,屏幕上显示棋盘以及数字的图片、用户的分数、游戏胜利/失败的提示信息等。该游戏可以检测用户输入的内容 进行各种操作,如上下左右移动、重启游戏、退出、直接取得游戏胜利等。该程序图形界面风格良好,用户交互操作简单。

【嵌牛鼻子】嵌入式linux开发    FrameBuffer编程    虚拟机    arm-C语言编译工具

【嵌牛正文】

一、主要设计思路

该设计采用自顶向下、模块化的设计思路:将程序划分成独立命名并可以独立访问的模板,每个模块将完成一个子功能把这些模块集合起来构成一个整体,可以完成制定的功能,满足用户的需求 。如果大型程序仅由一个 main函数组成, 它将很难被人所理解。采用模块化原理,可以使软件结构更加清晰,方便我们设计与理解。由于程序错误通常在有关模块以及他们之间的接口之中,因此模块化使软件更加容易测试和调试,有助于提高软件的可靠性;程序的变动往往只涉及少数几个模块因此模块化可以提高程序的可修改性;模块化还有助于软件开发的组织管理,一个复杂的程序分成多个模块后,开发难度大大减小。

二、项目目标

1、游戏核心层

此部分主要完成对棋盘的操作,可以进行

⚫ 对棋盘中的数字 进行上下左右移动,并合并相邻的数字

⚫ 在随机位置随机产生新的数字 2或 4,以防止棋盘数字不同从而无法合并的情况

⚫ 在游戏进行过程中 不断地 检查游戏状态 ,游戏失败、胜利都会被检测出来。

2、屏幕操作层

此部分主要是根据用户输入内容 ,调用游戏核心层代码进行一些对虚拟液晶 屏幕的操作

⚫ 初始化屏幕,设置背景底色, 初始化游戏棋盘以及分数

⚫ 对用户每次输入的指令内容进行判断,并调用Framebuffer层在屏幕上刷新游戏棋盘和游戏分数

⚫ 根据游戏核心层中的游戏状态来打印相关的信息,如游戏结束/游戏胜利等。

3、FrameBuffer层

此部分是程序的最底层,直接操作 Framebuffer指针 来在液晶屏幕上绘制图形或图片,他可以进行:

⚫ 根据棋盘和分数的内容调用数字和分数的图像数组( 数组在头文件中);

⚫ 在指定位置画出指定大小的图像;

三、采用的主要方法

该程序使用 C语言 完成开发。采用面向过程、模块化的方法,将这些需要完成的项封装成一个个的函数,函数之间互相调用, 最后完成整个目标。由于2048游戏的棋盘中有 4×4=16个数字,因此可以将游戏核心层简化为对一个二维数组数组gameBoard[4][4]的操作,循环遍历元素进行比较、合并、判断胜负等。为了避免程序调用参数过多消除一些代码的冗余度,本设计将一些经常操作的变量 定义为全局变量,如游戏棋盘数组 gameBoard[4][4]、游戏分数 gameScore、 帧缓冲指针 fbp、 可变/固定屏幕信息 vinfo/finfo等 。

为了使游戏界面美观简洁,本设计利用了 2048游戏官网 https://play2048.co 中的各个数字的图片,使用 Photoshop制作了 16张 96×96的 图片 以及一些提示信息和现实得分的数字图片,如下图:

程序使用的各个数字图像
程序显示得分的数字图片

四、模块设计

1、主程序模块

此模块的函数为main函数。

主程序模块负责一些设备初始化的操作 ,首先打开帧缓冲设备,并读取固定屏幕信息和可变屏幕信息,之后通过 mmap函数映射屏幕缓冲区到用户地址空间,之后就可以通过指针 fbp对屏幕进行操作了。接下来调用屏幕操作层的函数 iniScreen来初始化屏幕,调用 gameAllDisplay显示所有数字,停止 3秒后清零,再调用游戏核心层的函数 gameInitialize初始化游戏 。 接下来程序将进入一个不断读取用户输入键值的一个循环, 将键值传入游戏核心层的 gameRefresh函数 刷新 4××4数组 同时 屏幕操作层 将得到的结果显示在屏幕中,直到游戏结束(胜利或失败)。

2、游戏核心模块

游戏核心模块主要涉及对数组4×4数组 gameBoard[4][4]的处理, 进入游戏后,首先会显示 2048棋盘中所有可能出现的数字( 包括大于 2048的数字 )),即2、 4、 8……65536,之后 延时 3秒后正式进入游戏 此部分的函数为 gameInitialize。 同时棋盘上 的值刷新为初始值,即有两个随机 位置上的“ 2 或“ 4 此部分的函数为 addRandomNumber 随机数由 rand函数和 srand函数生成,通过判别随机数除以 10的余数是否为 0来判断生成 2或 4),并且 分数清零。

此部分的核心函数为gameRefresh,通过 判断 主程序模块传入的键值调用 moveUp、 moveDown、 moveLeft、 moveRight进行上下左右的移动操作,之后调用addRandomNumber获得下一个随机数与随机生成位置,然后通过子函数 checkWin和 chechGameOver来判断游戏是否胜利或结束,若未结束 则 调用屏幕操作模块 的 refreshBoard和 refreshScore来 在屏幕上刷新内容。

游戏核心模块部分设置了getEmptyBoxCount函数来获得棋盘空格也就是gameBoard[4][4]中 数字 0 的个数 checkWin函数通过扫描棋盘 ,若发现数字2048 则游戏胜利,调用屏幕操作模块的 drawString函数显示获胜信息 checkGameOver函数通过扫描棋盘中每一个元素与它相邻的元素是否相同来判断游戏是否结束,如果结束就调用 drawString函数来显示游戏结束信息。

3、屏幕操作模块

之前说明过,由于本程序为轻量级程序为了减小代码的冗余度帧缓冲 fbp指针等变量为全局变量 ,本模块的函数都是通过 fbp指针来改变屏幕内容的。IniScreen函数通过对指针进行写操作来设置屏幕颜色, gameAlldisplay函数可以显示所有数字以及设置的分数,休眠 3秒后正式开始游戏。而 refreshBoard和refreshScore都是读取全局变量 gameBoard[4][4]和 gameScore,之后将要画的 数字 取出来,调用 Framebuffer层的 getBoxNumber和 getScoreNumber函数来获得图片数组,再调用 drawPicture函数在屏幕上画出图像。同样, drawString函数也调用了 drawPicture函数,通过传进来的游戏状态来画出不同的提示信息图像 。

4、FrameBuffer模块

此模块的 函数有: getBoxNumber、 getScoreNumber、 drawPicture。getBoxNumber和 getScoreNumber函 数 都是基于 case语句,通过判断全局变量的内容来返回图像的,返回的图像格式 为 const unsigned char 。而函数drawString函数就是通过上层传入的信息这些信息包括图片在屏幕上要画的位置 xpos ypos,图像的行数 row和列数 col,以及图像数组 Image 利用 写 fbp指针来在屏幕上画图的。

五、调试运行结果

本设计主要是基于 arm-linux开发板进行开发,因此使用了编译工具 arm-linux-gnueabi-gcc进行编译 。刚开始只设计了游戏核心程序部分,因此 在 PC Linux上依旧可以运行,所有画图部分均在屏幕上打印出来 ,这部分使用了gcc和gdb进行编译和调试,后续加上了画图部分后直接在 arm-linux上运行 ,通过观察程序运行结果和虚拟液晶显示屏上的内容来进行调试 。

游戏最终运行时的截图如下:

游戏正常运行界面

其中,左侧为虚拟的液晶显示屏 ,右侧为 arm-linux开发板的 console窗口,在窗口输入命令,如 wasd 即可以操作游戏 ,同时 console窗口会打印出当前的分数。

游戏胜利时的界面
游戏结束界面

如右图,最后一次输入的命令为d即向右移动,此时棋盘上不会有相邻且相同的数 ,因此判定游戏结束 ,显示标语 GAME OVER。

你可能感兴趣的:(基于嵌入式linux开发的“2048”游戏综合设计)