在这张图片里面我们看第一列数字{ 2 2 0 4 },这其实就是一个一维数组,然后当玩家按下W或者↑键时我们需要对这个一维数组进行数据处理,就是将{ 2 2 0 4 }变成{ 4 4 0 0 }。
然后我是怎么实现的呢,分两步,先将数字{ 2 2 0 4 }变成{ 2 2 4 0 },然后循环判断相邻的两个数字是否相同,相同就合并。
我们来看一下代码中的实现方式
case 72://上
case'W':
case'w':
//把0向下移动
for (int j = 0; j < 4; j++) //4列分别操作
{
len = 4;
for (int i = 0; i < len - 1; i++)
{
if (map[i][j] == 0)
{
//下面的数字往上移一个格子
for (int k = i; k < 3; k++)
{
map[k][j] = map[k + 1][j];
map[k + 1][j] = 0;
}
len--; //出现一个 0 len减一
i--; //返回原位置检测是否有 0
}
}
for (int i = 0; i < len - 1; i++)
{
if (map[i][j] == 0) break; //第一行为0 后面都是0,不进行操作
if (map[i][j] == map[i + 1][j]) //检测相邻两个数字是否相同,相同就合并
{
map[i][j] *= 2; //合并
//合并之后后面的数字往前移动一位
for (int k = i + 1; k < 3; k++)
{
map[k][j] = map[k + 1][j];
map[k + 1][j] = 0;
}
//进行打分
*score += map[i][j];
}
}
}
break;
别看代码挺长的,其实理解起来特别简单,并且四个方向的代码都是一个模型,复制粘贴,修改点东西,整个游戏的核心代码就掌握了,然后就是界面弄好看点。由于图方便快捷,我没有去找图片资源,所以做的界面可能比较,嗯,垃圾。这个呢仅供学习,别当真,千万别一根筋的说我要通关,我要2048,微信小程序搜索2048,那个界面好看点。
02
游戏三部曲
主函数 int main()
int main()
{
initgraph(MAP_ROW * PIXEL + 100, MAP_COL * PIXEL + 100); //加载窗口
init(map, &score); //初始化地图数据
while (1)
{
DrawMap(map, score); //绘制地图
Play(map, &score); //玩家操作
switch (Judg(map)) //判断输赢 对结果进行匹配
{
case 0://赢
break;
case -1://输
break;
case 1://没有空位置
break;
case 2://还有空位置
break;
default:
break;
}
}
closegraph();
return 0;
}
这个没啥好说的,游戏三部曲
1、加载游戏数据(初始化 init() ;)
2、绘制图形(绘图 DrawMap() ;)
3、玩家操作(数据更新 Play() ;)
初始化 init()
void init(int map[][MAP_COL], int *score)
{
int x, y; //随机位置参数
*score = 0; //对成绩进行初始化
memset(map, 0, sizeof(int)*MAP_ROW*MAP_COL); //初始化地图
//对地图进行初始化
for (int i = 0; i < 2;)
{
x = rand() % 4;
y = rand() % 4;
if (map[x][y] == 0)
{
if (rand() % 9 == 0)
map[x][y] = 4;
else
map[x][y] = 2;
i++;
}
}
}
2048由于游戏简单,没有什么图片资源啊什么的各种资源的加载,所以初始化函数只需要初始化一些基本的数据。
绘图 DrawMap()
void DrawMap(int map[][MAP_COL],int score)
{
wchar_t arr[20]; //临时参数
setbkcolor(RGB(0, 0, 0)); //背景颜色
setlinecolor(RGB(0, 255, 0)); //线条颜色
setlinestyle(PS_SOLID, 3); //线条风格
settextcolor(RGB(0, 255, 0)); //字体颜色
settextstyle(20, 0, L"宋体"); //字体风格 和 字体大小
cleardevice(); //清屏
BeginBatchDraw(); //开始绘制地图
for (int i = 0; i <= 4; ++i)
{
line(PIXEL * i, 0 + 50, PIXEL * i, PIXEL * 4 + 50);//竖线
line(0, PIXEL * i + 50, PIXEL * 4, PIXEL * i + 50);//横线
}
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
wsprintf(arr, L"%d", map[i][j]); //将数字写入字符串
outtextxy(j * PIXEL + 32, i * PIXEL + 50 + 30, arr);//指定位置输出字符串
}
}
//分数
settextcolor(RGB(0, 255, 0)); //字体颜色 红色
wsprintf(arr, L"score: %d", score);
outtextxy(0, 15, arr); //指定位置输出字符串
//最高分
outtextxy(MAP_ROW * PIXEL + 10 , 5, L"最高分:"); //指定位置输出字符串
wsprintf(arr, L"%d", MaxScore);
outtextxy(MAP_ROW * PIXEL + 15 , 25, arr); //指定位置输出字符串
outtextxy(MAP_ROW * PIXEL + 10, 80, L"↑↓←→"); //指定位置输出字符串
outtextxy(MAP_ROW * PIXEL + 10, 120, L"WSAD"); //指定位置输出字符串
outtextxy(MAP_ROW * PIXEL + 10, 160, L"控制游戏"); //指定位置输出字符串
outtextxy(80, 360, L"微信公众号:编程学习基地"); //指定位置输出字符串
EndBatchDraw(); //结束绘制地图
}
绘制地图就是设置一些字体啊,背景颜色啊,然后就是将16宫格画上去,地图里面的数字显示到16宫格上,虽然很繁琐,但是也很简单,调用这些个函数就可以了。
数据更新 Play()
void Play(int map[][MAP_COL], int * score)
{
int len;
switch (getch())
{
case 75://左
case'A':
case'a':
for (int i = 0; i < 4; i++)
{
len = 4;
for (int j = 0; j < len - 1; j++)
{
if (map[i][j] == 0)
{
for (int k = j; k < 3; k++)
{
map[i][k] = map[i][k + 1];
map[i][k + 1] = 0;
}
len--;
j--;
}
}
for (int j = 0; j < 3; j++)
{
if (map[i][j] == 0) break;
if (map[i][j] == map[i][j + 1])
{
map[i][j] *= 2;
for (int k = j + 1; k < 3; k++)
{
map[i][k] = map[i][k + 1];
map[i][k + 1] = 0;
}
*score += map[i][j];
}
}
}
break;
default:break;
}
}
在游戏设计里面已经说得挺明白了,也示例了W和↑键的代码,这里就复制下左和←键的代码凑个牌面。
这里简单提下↑↓←→键的ASCII码值
键 | ASCII |
---|---|
↑ | 72 |
↓ | 80 |
← | 75 |
→ | 77 |
添加数字 Add()
void Add(int map[][MAP_COL])
{
//添加数字
int x, y;
do
{
x = rand() % 4;
y = rand() % 4;
} while (map[x][y] != 0); //找到数字为零的位置
if (rand() % 9 == 0) //设置4出现的概率为1/9
map[x][y] = 4;
else
map[x][y] = 2;
}
2048游戏就是通过没次添加一个数字逼迫玩家消灭数字,函数的功能实现也简单,直接看代码就能看得懂,在这里我就加了个限制来控制2和4出现的概率。
判断输赢 Judg()
int Judg(int map[][MAP_COL])
{
int num = 0; //记录0的个数
int flag = 0; //如果存在一组可以合并的元素 那么flag置为1
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
if (map[i][j] >= 2048) return 0;//赢
if (map[i][j] == 0) num++; //记录0的个数
if (j + 1 < 4 && map[i][j] == map[i][j + 1]) flag = 1;//右边是不是一样
if (i + 1 < 4 && map[i][j] == map[i + 1][j]) flag = 1;//下方是不是一样
}
}
if (num == 0 && flag == 0) return -1; //满了并且不能合并 输
if (num == 0) return 1; //满了 但是可以合并
return 2;
}
判断输赢里面赢最好判断了,出现2048就赢了,输的话就是flag=0,num=0也就是地图中没有0,并且每个数字的左边和下面没有相同的数字就是输,把逻辑整明白,代码写起来就简单。
保存数据 WriteData() ReadData()
void WriteData(int map[][MAP_COL], int score)
{
FILE*fp = fopen("D:\\2048.dat", "wb");
if (fp == NULL) return;
fwrite(map, sizeof(int)*MAP_ROW*MAP_COL, 1, fp);
fwrite(&score, sizeof(int), 1, fp);
fwrite(&MaxScore, sizeof(int), 1, fp);
fclose(fp);
}
void ReadData(int map[][MAP_COL], int* score)
{
FILE*fp = fopen("D:\\2048.dat", "rb");
if (fp==NULL) return;
fread(map, sizeof(int)*MAP_ROW*MAP_COL, 1, fp);
fread(score, sizeof(int), 1, fp);
fread(&MaxScore, sizeof(int), 1, fp);
fclose(fp);
}
这个就是将地图中的数据写入到磁盘中,然后怎么写入的怎么读取,我也就简单地实现了下数据保存功能,详细的文件操作请看
03
游戏优化
1. 用带颜色的图片替代简陋的数字,不图啥,就图界面好看。
2. 可以加载一张图片做背景,可以是你女朋友友的,如果没有,那用你自己的也可以
3. 学过数据结构,知道栈的同学可以实现操作回退功能,这个我准备年后用推箱子来做个实例演示。