一、概述
2048小游戏是一款休闲益智类小游戏,游戏在一个4×4的方格上进行,在这里会不断产生方块,方块的初始值为2,玩家通过合并、消除方块使得方块的数值越来越大,最终合成2048的方块来获取胜利,游戏的核心功能逻辑简单易实现,通过C语言的函数功能,同时配合easyx图形库美化游戏界面,一个简单的2048小游戏即可实现。
二、各部分功能实现
对主要功能进行分析后,将其分成了六个函数如下:
1. 随机数产生函数
用于产生整数2或4,这里设置产生2的概率为90%,产生4的概率为10%
void createNum(){while (1){int ret = 0;int x = rand() % MAX_SIZE; //创建一个随机数对MAX_SIZE 取余结果坐标为0,1,2,3int y = rand() % MAX_SIZE; //创建一个随机数对MAX_SIZE 取余结果坐标为0,1,2,3if (map[x][y] == 0) { //查看随机的坐标是否等于0if (rand() % 10 == 1) //判断随机数取10余数是否为1,来限制4出现的几率为10%{map[x][y] = 4;}else{map[x][y] = 2; //否则2出现的几率为90% }break;}}}
2. 游戏初始化函数
初始化游戏,重置游戏方格区域的数据,并在两个随机位置产生2或4,概率同上,由此不难发现,该函数只需要在重置方格数据和生成两个随机位置后,调用以上随机数产生函数即可
void gameInit(){for (int i = 0; i < MAX_SIZE; i++) {for (int j = 0; j < MAX_SIZE; j++) {map[i][j] = 0; //遍历所有方格并初始化为0}}score = 0; //得分初始化为0//设置随机数种子srand(GetTickCount());createNum();createNum();}
3. 游戏界面函数(核心)
由于应用了easyx图形库,因此该函数的主要功能就是使用easy图形库的各种命令来优化图形界面,例如打印界面,设置各个区域以及不同数值方块相对应的颜色,打印得分区域和新游戏按钮等
void gameView(){//打印方格、设置颜色、打印方格值for (int i = 0; i < MAX_SIZE; i++) { //遍历所以格子起点for (int j = 0; j < MAX_SIZE; j++) { //遍历所以格子起点int x = j * GRID_W + (j + 1) * INTERVAL; //起点X坐标int y = i * GRID_W + (i + 1) * INTERVAL; //起点y坐标 int index = (int)log2((double)map[i][j]);if (map[i][j] == 0) { //判断当前格子的数字是否为0COLORREF gcolor = arr[0]; //若为0赋值数组中颜色setfillcolor(gcolor); //设置空方格时的颜色solidroundrect(x, y, x + GRID_W, y + GRID_W, 5, 5); //创建空方格}else { //除方格值为0时,其余情况下打印值COLORREF gcolor = arr[index];setfillcolor(gcolor); //设置不同方格值时的颜色solidroundrect(x, y, x + GRID_W, y + GRID_W, 5, 5); //创建不同颜色的方格settextstyle(50, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //更改文字颜色大小if (map[i][j] < 8) settextcolor(RGB(119, 110, 101)); //设置16以下文字的颜色else settextcolor(WHITE); //16以上文字的颜色char snum[10] = ""; //创建一维数组sprintf(snum, "%d", map[i][j]); //将数字转化成字符串int ntw = textwidth(snum); //求输出的字的宽int nth = textheight(snum); //求输出的字的高outtextxy(x + (100 - ntw) / 2, y + (100 - nth) / 2, snum); //将文字输出//绘制方格值 }}}//将得分转化为字符串打印在得分板上char sscore[10]; //创建数组sprintf(sscore, "%d", score); //将数字转化成字符串int stw = textwidth(sscore);int sth = textheight(sscore);//数字的宽settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //设置分数的大小的字体settextcolor(WHITE); //分数的颜色setbkmode(TRANSPARENT); //透明背景//将分数打印至得分板区域RECT r = { 530, 65, 670, 100 };HRGN rgn = CreateRectRgn(530, 65, 670, 100); setcliprgn(rgn); //设置裁剪区clearcliprgn(); //每轮结束后,清空裁减区,重新打印分数(避免分数打印堆积)setcliprgn(NULL);drawtext(sscore, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //在对应区域打印分数}
4. 移动函数(核心)
游戏的核心逻辑部分,接收到玩家的键盘指令后,需要做出相对应的移动逻辑,并且根据方格区域不同的情况要做出不同的反应。例如所要移动的方向上无相同值方块可合并所有方块都到达边界无法前进,此时做出的回应是不执行任何命令,等待下一个键盘指令;而前方若有可合并方块,则需要改变前方可合并方块的数值,使其翻倍,并使得距离所要移动方向更远的方块消失,而若方块朝向索要移动的方向上无其他方块,则需使其向着该方向移动至边界,而若同一行内有三或四个相同数值的方块,则需要先从靠近所移动方向的一侧开始遍历合并,并且每个方块每回合只能合并一次,合并后还需要继续前进直到遇到其他方块或边界,为此,将移动部分拆分为四个函数,根据不同情况产生不同的指令,其根本逻辑都是一样的,只是跟据移动方向的不同会改变遍历的初始位置和方向,此处只展示上移函数,其余移动函数请见完整代码部分
void moveUp(){bool isMoved = 0;//是否移动成功for (int i = 0; i < MAX_SIZE; i++)//遍历列{int now = 0;//标尺位,每一列遍历时都将标尺位调至第一行for (int next = 1; next < MAX_SIZE; next++)//遍历行,next是now后面的数字{//判断next处是不是0,意思就是除了第一行其他位置是否有数字存在if (map[next][i] != 0)//next处不是0{if (map[now][i] == 0)//出现数字的前方路径上全是0{map[now][i] = map[next][i];//将第一行变成该数字,map[next][i] = 0;//该数字原本的位置置为0isMoved = 1;//成功移动数字,随机在空格位置刷新2或4}else if (map[now][i] == map[next][i])//顺序遍历,标尺位数字和出现数字相同,且中间路径上没有数字{map[now][i] *= 2;//将数字合并,标尺位数字变为原来的二倍score += map[now][i];//方块合成后的得分map[next][i] = 0;//释放原来的数字now++;//标尺位向后一行isMoved = 1;//成功移动数字,随机在空格处刷新2或4}else//标尺位和数字不相等,那就将数字移动到标尺位的后一行,如果本身就在标尺位的后一行,依然可以赋值,但不删除原位置{map[now + 1][i] = map[next][i];//将与标尺位不相同的数字移动到标尺位的后一行if (now + 1 != next)//如果数字就在标尺位的后一行,他俩相邻,就不用执行这一步,因为now+1和next原本就是一个位置他俩原本的数字就相等//如果数字和标尺位中间有空格,就要执行这一步,因为原本的now+1是0,而next原本的数字是一个不为0的数,他俩不相等,要执行这一步进行 next原本位置的删除{map[next][i] = 0;//删除isMoved = 1;//成功移动数字刷新2或4}now++;//标尺位向后一行}}}}if (isMoved){createNum(); //判定是否产生了移动,若是,则随机空白位置生成一个整数2或4}gameView(); //打印一次游戏界面}
5. 胜负判定函数
根据当前方格区域的方块数值分布情况来确定胜利或失败,若生成了2048的方块,则游戏胜利,返回数值1;若方格填满且所有方块无法继续合并免责判定游戏失败,返回数值2;否则判定胜负未定,返回数值3;若胜负已定,则会调用胜利/失败界面函数
int isWin(){int ret = 2; //定义判别变量,初始状态为失败for (int i = 0; i < MAX_SIZE; i++) //循环遍历每个数字 {for (int j = 0; j < MAX_SIZE; j++){if (map[i][j] == 2048) {ret = 1;break;} //如果有数字2048,改变判别变量,判定为胜利,终中断循环if (map[i][j] == 0) {ret = 0;break;} //如果还有空方格,说明此时仍可移动,改变判别变量,判定为胜负未定,终止循环if (map[i][j] == map[i][j + 1] || map[i][j] == map[i + 1][j]) {if (i < 3 && j < 3) {ret = 0;break;}}if (i == 3 && j != 3 && map[i][j] == map[i][j + 1]) {ret = 0;break;}if (j == 3 && i != 3 && map[i][j] == map[i + 1][j]) {ret = 0;break;} //如果在不符合前两个if的情形下,且对任意在游戏区域内的方块,其右侧和下侧的方块至少有一个与其相等,则改变判别变量判定为胜负未定,中断循环}}return ret; //否则判定游戏失败}
6. 胜利/失败界面函数
根据胜负判定函数的返回值来打印游戏胜利/失败界面
void winOrLoseView(int det){//接收isWin返回的参数,若为1打印胜利字样,为2打印失败字样HWND window = GetHWnd(); //获取窗口句柄if (det == 1) //胜利判定{MessageBox(window, "You won the game!", "提示", MB_OK); //打印提示gameInit(); //新开始游戏}else if (det == 2){ //失败判定MessageBox(window, "Game over!", "提示", MB_OK); //打印提示gameInit(); //新开始游戏}}
7. 定义主函数
int main(void){//创建游戏窗口,尺寸为500 * 700initgraph(GAME_ZONE + 200, GAME_ZONE); //创建游戏窗口//设置背景(方格间距)颜色 setbkmode(TRANSPARENT); //设置窗口为透明色setbkcolor(INTERVRL_COLOR); //设置背景颜色cleardevice(); //清除背景//打印游戏右侧区域并设置颜色setfillcolor(RIGHT_RECTANGLE); //设置矩形填充颜色solidrectangle(GAME_ZONE, 0, GAME_ZONE + 200, GAME_ZONE); //创建游戏额外区域的矩形//打印并设置积分板颜色,并打印对应字体setfillcolor(SCORE_COLOR); //设置游戏界面右侧的颜色solidroundrect(530, 40, 670, 100, 5, 5); //得分板的左上和右下坐标分别为(530,40), (670,100) 创建得分板settextcolor(RGB(238, 228, 218)); //设置字体颜色settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //设置文字大小和字体outtextxy(565, 40, "SCORE"); //出入文字//打印New Gme按钮以及字体并设置字体颜色和格式setfillcolor(NEWGAME_COLOR); //设置新游戏按钮区域背景颜色solidroundrect(525, 160, 675, 210, 5, 5); //New Game区域的左上和右下坐标分别为(525,160), (675,210) int btw = textwidth("New Game"); //文字宽距离按钮居中更多距离int bth = textheight("New Game"); //文字高距离按钮居中更多距离settextcolor(WHITE); //设置文字颜色为白色settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //设置文字的字体和大小outtextxy(525 + (150 - btw) / 2, 160 + (50 - bth) / 2, "New Game"); //将文字居中放在矩形中gameInit();gameView(); //调用函数ExMessage m; //定义接收键鼠输入的变量//开始游戏,置于恒定循环内BeginBatchDraw(); //开始批量绘图,防止游戏过程中闪屏while (1){gameView();//每轮开始时,进行一次胜负判定,若胜负未定则继续游戏,否则调用胜利/失败界面函数int det = isWin();if (det != 0) winOrLoseView(det);//接收键鼠输入m = getmessage(EX_MOUSE | EX_KEY);switch (m.message){case WM_KEYDOWN: //如果键盘输入wasd或上下左右键,则做出相对应移动指令if (m.vkcode == VK_UP || m.vkcode == 'w' || m.vkcode == 'W') moveUp();if (m.vkcode == VK_DOWN || m.vkcode == 's' || m.vkcode == 'S')moveDown();if (m.vkcode == VK_LEFT || m.vkcode == 'a' || m.vkcode == 'A')moveLeft();if (m.vkcode == VK_RIGHT || m.vkcode == 'd' || m.vkcode == 'D')moveRight();break;case WM_LBUTTONDOWN: //若点击了New game按钮所在区域,则调用gameInit函数,即初始化游戏数据并打印游戏界面if (m.x >= 525 && m.x <= 675 && m.y >= 160 && m.y < 210) {gameInit();break;}}FlushBatchDraw(); //结束批量绘图}getchar();return 0;}
三、完整代码
#include <stdio.h>#include <easyx.h> //easyx图形库头文件#include <stdlib.h>#include <math.h>#define GRID_W 100 //格子宽度(正方形边长)#define INTERVAL 20 //格子间距#define MAX_SIZE 4 //每行每列各自的最大数量#define GAME_ZONE MAX_SIZE * GRID_W + (MAX_SIZE + 1) * INTERVAL //游戏格子区域的边长(实际为500)//枚举出游戏中用到的颜色enum color{BLANK_GRID = RGB(215, 203, 190), //空格子颜色INTERVRL_COLOR = RGB(187, 173, 160), //间距颜色,在函数中设定为背景颜色RIGHT_RECTANGLE = RGB(250, 248, 219), //窗口右侧矩形区域颜色,即除格子和间隔以外的颜色SCORE_COLOR = RGB(187, 173, 160), //计分板颜色NEWGAME_COLOR = RGB(143, 122, 102), //新游戏按钮颜色TWO_ONE = RGB(238, 228, 218), //方格值为2的颜色TWO_TWO = RGB(237, 224, 200), //方格值为4的颜色TWO_THREE = RGB(242, 177, 121), //方格值为8的颜色TWO_FOUR = RGB(245, 149, 99), //方格值为16的颜色TWO_FIVE = RGB(246, 124, 95), //方格值为32的颜色TWO_SIX = RGB(246, 94, 59), //方格值为64的颜色TWO_SEVEN = RGB(237, 207, 114), //方格值为128的颜色TWO_EIGHT = RGB(237, 204, 97), //方格值为256的颜色TWO_NINE = RGB(237, 200, 80), //方格值为512的颜色TWO_TEN = RGB(237, 197, 63), //方格值为1024的颜色TWO_ELEVEN = RGB(237, 194, 46), //方格值为2048的颜色};color arr[12] = { BLANK_GRID, TWO_ONE, TWO_TWO, TWO_THREE, TWO_FOUR, TWO_FIVE, TWO_SIX, TWO_SEVEN, TWO_EIGHT, TWO_NINE, TWO_TEN, TWO_ELEVEN, }; //枚举颜色数组int map[MAX_SIZE][MAX_SIZE]; //定义一个数组,用于存储游戏区域数据,全局变量自动初始化为零int score; //定义全局变量得分值,会被打印在得分板上//随机数产生函数void createNum();//游戏初始化函数void gameInit();//游戏界面函数void gameView();//游戏移动函数void moveUp(); //上移void moveDown(); //下移void moveLeft(); //左移void moveRight(); //右移//胜负判定函数int isWin();//胜利/失败界面函数void winOrLoseView(int det);//定义主函数int main(void){//创建游戏窗口,尺寸为500 * 700initgraph(GAME_ZONE + 200, GAME_ZONE); //创建游戏窗口//设置背景(方格间距)颜色 setbkmode(TRANSPARENT); //设置窗口为透明色setbkcolor(INTERVRL_COLOR); //设置背景颜色cleardevice(); //清除背景//打印游戏右侧区域并设置颜色setfillcolor(RIGHT_RECTANGLE); //设置矩形填充颜色solidrectangle(GAME_ZONE, 0, GAME_ZONE + 200, GAME_ZONE); //创建游戏额外区域的矩形//打印并设置积分板颜色,并打印对应字体setfillcolor(SCORE_COLOR); //设置游戏界面右侧的颜色solidroundrect(530, 40, 670, 100, 5, 5); //得分板的左上和右下坐标分别为(530,40), (670,100) 创建得分板settextcolor(RGB(238, 228, 218)); //设置字体颜色settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //设置文字大小和字体outtextxy(565, 40, "SCORE"); //出入文字//打印New Gme按钮以及字体并设置字体颜色和格式setfillcolor(NEWGAME_COLOR); //设置新游戏按钮区域背景颜色solidroundrect(525, 160, 675, 210, 5, 5); //New Game区域的左上和右下坐标分别为(525,160), (675,210) int btw = textwidth("New Game"); //文字宽距离按钮居中更多距离int bth = textheight("New Game"); //文字高距离按钮居中更多距离settextcolor(WHITE); //设置文字颜色为白色settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //设置文字的字体和大小outtextxy(525 + (150 - btw) / 2, 160 + (50 - bth) / 2, "New Game"); //将文字居中放在矩形中gameInit();gameView(); //调用函数ExMessage m; //定义接收键鼠输入的变量//开始游戏,用一个恒定循环定义BeginBatchDraw(); //开始批量绘图,防止游戏过程中闪屏while (1){gameView();//每轮开始时,进行一次胜负判定,若胜负未定则继续游戏,否则调用胜利/失败界面函数int det = isWin();if (det != 0) winOrLoseView(det);//接收键鼠输入m = getmessage(EX_MOUSE | EX_KEY);switch (m.message){case WM_KEYDOWN: //如果键盘输入wasd或上下左右键,则做出相对应移动指令if (m.vkcode == VK_UP || m.vkcode == 'w' || m.vkcode == 'W') moveUp();if (m.vkcode == VK_DOWN || m.vkcode == 's' || m.vkcode == 'S')moveDown();if (m.vkcode == VK_LEFT || m.vkcode == 'a' || m.vkcode == 'A')moveLeft();if (m.vkcode == VK_RIGHT || m.vkcode == 'd' || m.vkcode == 'D')moveRight();break;case WM_LBUTTONDOWN: //若点击了New game按钮所在区域,则调用gameInit函数,即初始化游戏数据并打印游戏界面if (m.x >= 525 && m.x <= 675 && m.y >= 160 && m.y < 210){gameInit();break;}}FlushBatchDraw(); //结束批量绘图}getchar();return 0;}void createNum(){//代码实现while (1){int ret = 0;int x = rand() % MAX_SIZE; //创建一个随机数对MAX_SIZE 取余结果坐标为0,1,2,3int y = rand() % MAX_SIZE; //创建一个随机数对MAX_SIZE 取余结果坐标为0,1,2,3if (map[x][y] == 0) { //查看随机的坐标是否等于0if (rand() % 10 == 1) //判断随机数取10余数是否为1,来限制4出现的几率为10%{map[x][y] = 4;}else{map[x][y] = 2; //否则2出现的几率为90% }break;}}}void gameInit(){//代码实现for (int i = 0; i < MAX_SIZE; i++) {for (int j = 0; j < MAX_SIZE; j++) {map[i][j] = 0; //遍历所有方格并初始化为0}}score = 0; //得分初始化为0//设置随机数种子srand(GetTickCount());createNum();createNum();}void gameView(){//代码实现//打印方格、设置颜色、打印方格值for (int i = 0; i < MAX_SIZE; i++) { //遍历所以格子起点for (int j = 0; j < MAX_SIZE; j++) { //遍历所以格子起点int x = j * GRID_W + (j + 1) * INTERVAL; //起点X坐标int y = i * GRID_W + (i + 1) * INTERVAL; //起点y坐标 int index = (int)log2((double)map[i][j]);if (map[i][j] == 0) { //判断当前格子的数字是否为0COLORREF gcolor = arr[0]; //若为0赋值数组中颜色setfillcolor(gcolor); //设置空方格时的颜色solidroundrect(x, y, x + GRID_W, y + GRID_W, 5, 5); //创建空方格}else { //除方格值为0时,其余情况下打印值COLORREF gcolor = arr[index];setfillcolor(gcolor); //设置不同方格值时的颜色solidroundrect(x, y, x + GRID_W, y + GRID_W, 5, 5); //创建不同颜色的方格settextstyle(50, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //更改文字颜色大小if (map[i][j] < 8) settextcolor(RGB(119, 110, 101)); //设置16以下文字的颜色else settextcolor(WHITE); //16以上文字的颜色char snum[10] = ""; //创建一维数组sprintf(snum, "%d", map[i][j]); //将数字转化成字符串int ntw = textwidth(snum); //求输出的字的宽int nth = textheight(snum); //求输出的字的高outtextxy(x + (100 - ntw) / 2, y + (100 - nth) / 2, snum); //将文字输出//绘制方格值 }}}//将得分转化为字符串打印在得分板上char sscore[10]; //创建数组sprintf(sscore, "%d", score); //将数字转化成字符串int stw = textwidth(sscore);int sth = textheight(sscore);//数字的宽settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //设置分数的大小的字体settextcolor(WHITE); //分数的颜色setbkmode(TRANSPARENT); //透明背景//将分数打印至得分板区域RECT r = { 530, 65, 670, 100 };HRGN rgn = CreateRectRgn(530, 65, 670, 100);setcliprgn(rgn); //设定裁剪区,为计分板区域clearcliprgn(); //每轮游戏结束后,清空计分板,重新打印分数setcliprgn(NULL);drawtext(sscore, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //在指定区域打印分数,设置居中}void moveUp(){//代码实现bool isMoved = 0;//是否移动成功for (int i = 0; i < MAX_SIZE; i++)//遍历列{int now = 0;//标尺位,每一列遍历时都将标尺位调至第一行for (int next = 1; next < MAX_SIZE; next++)//遍历行,next是now后面的数字{//判断next处是不是0,意思就是除了第一行其他位置是否有数字存在if (map[next][i] != 0)//next处不是0{if (map[now][i] == 0)//出现数字的前方路径上全是0{map[now][i] = map[next][i];//将第一行变成该数字,map[next][i] = 0;//该数字原本的位置置为0isMoved = 1;//成功移动数字,随机在空格位置刷新2或4}else if (map[now][i] == map[next][i])//顺序遍历,标尺位数字和出现数字相同,且中间路径上没有数字{map[now][i] *= 2;//将数字合并,标尺位数字变为原来的二倍score += map[now][i];//方块合成后的得分map[next][i] = 0;//释放原来的数字now++;//标尺位向后一行isMoved = 1;//成功移动数字,随机在空格处刷新2或4}else//标尺位和数字不相等,那就将数字移动到标尺位的后一行,如果本身就在标尺位的后一行,依然可以赋值,但不删除原位置{map[now + 1][i] = map[next][i];//将与标尺位不相同的数字移动到标尺位的后一行if (now + 1 != next)//如果数字就在标尺位的后一行,他俩相邻,就不用执行这一步,因为now+1和next原本就是一个位置他俩原本的数字就相等//如果数字和标尺位中间有空格,就要执行这一步,因为原本的now+1是0,而next原本的数字是一个不为0的数,他俩不相等,要执行这一步进行 next原本位置的删除{map[next][i] = 0;//删除isMoved = 1;//成功移动数字刷新2或4}now++;//标尺位向后一行}}}}if (isMoved){createNum(); //判定是否产生了移动,若是,则随机空白位置生成一个整数2或4}gameView(); //打印一次游戏界面}void moveDown(){//代码实现bool isMoved = 0;for (int i = 0; i < MAX_SIZE; i++)//遍历列{int now = MAX_SIZE - 1;for (int next = MAX_SIZE - 2; next >= 0; next--)//遍历行{if (map[next][i] != 0){if (map[now][i] == 0){map[now][i] = map[next][i];map[next][i] = 0;isMoved = 1;}else if (map[now][i] == map[next][i]){map[now][i] *= 2;score += map[now][i];map[next][i] = 0;isMoved = 1;now--;}else {map[now - 1][i] = map[next][i];if (now - 1 != next){map[next][i] = 0;isMoved = 1;}now--;}}}}if (isMoved){createNum();}gameView(); //打印一次游戏界面}void moveLeft(){//代码实现bool isMoved = 0;//是否移动成功for (int i = 0; i < MAX_SIZE; i++)//遍历行{int now = 0;for (int next = 1; next < MAX_SIZE; next++)//遍历列{//判断next处是不是0if (map[i][next] != 0){if (map[i][now] == 0){map[i][now] = map[i][next];map[i][next] = 0;isMoved = 1;}else if (map[i][now] == map[i][next]){map[i][now] *= 2;score += map[i][now];map[i][next] = 0;now++;isMoved = 1;}else{map[i][now + 1] = map[i][next];if (now + 1 != next){map[i][next] = 0;isMoved = 1;}now++;}}}}if (isMoved){createNum();}gameView(); //打印一次游戏界面}void moveRight(){//代码实现bool isMoved = 0;//是否移动成功for (int i = 0; i < MAX_SIZE; i++)//遍历行{int now = MAX_SIZE - 1;for (int next = MAX_SIZE - 2; next >= 0; next--)//遍历列{//判断next处是不是0if (map[i][next] != 0){if (map[i][now] == 0){map[i][now] = map[i][next];map[i][next] = 0;isMoved = 1;}else if (map[i][now] == map[i][next]){map[i][now] *= 2;score += map[i][now];map[i][next] = 0;now--;isMoved = 1;}else{map[i][now - 1] = map[i][next];if (now - 1 != next){map[i][next] = 0;isMoved = 1;}now--;}}}}if (isMoved){createNum();}gameView(); //打印一次游戏界面}int isWin(){//代码实现int ret = 2; //定义判别变量,初始状态为游戏失败for (int i = 0; i < MAX_SIZE; i++) //循环遍历每个数字 {for (int j = 0; j < MAX_SIZE; j++){if (map[i][j] == 2048) {ret = 1;break;} //如果有数字2048,改变判别变量,判定为胜利,终中断循环if (map[i][j] == 0) {ret = 0;break;} //如果还有空方格,说明此时仍可移动,改变判别变量,判定为胜负未定,终止循环if (map[i][j] == map[i][j + 1] || map[i][j] == map[i + 1][j]) {if (i < 3 && j < 3) {ret = 0;break;}}if (i == 3 && j != 3 && map[i][j] == map[i][j + 1]) {ret = 0;break;}if (j == 3 && i != 3 && map[i][j] == map[i + 1][j]) {ret = 0;break;} //如果在不符合前两个if的情形下,且对任意在游戏区域内的方块,其右侧和下侧的方块至少有一个与其相等,则改变判别变量判定为胜负未定,中断循环}}return ret; //否则判定游戏失败}void winOrLoseView(int det){//代码实现//接收isWin返回的参数,若为1打印胜利字样,为2打印失败字样HWND window = GetHWnd(); //获取窗口句柄if (det == 1) //胜利判定{MessageBox(window, "You won the game!", "提示", MB_OK); //打印提示gameInit(); //新开始游戏}else if (det == 2){ //失败判定MessageBox(window, "Game over!", "提示", MB_OK); //打印提示gameInit(); //新开始游戏}}
成品示例:
四、总结
可以看出,整个游戏从构思倒实现还是比较简单的,总代码量也只有450行左右,只是一个功能比较简略、仅仅实现了核心功能和一点点拓展功能的版本,实际上该游戏的可扩展之处还有很多,例如音乐、主菜单、游戏设置、图形界面字体的优化、游戏的特效以及设置更多游戏模式等,这代表着实现从深度到广度的延伸,使得游戏更具有可玩性,广大开发者可自行构思、创造。