2048小游戏(C语言实现)
前言
本次实验跟随https://www.bilibili.com/video/BV1BC4y1H7jz/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click做的练习,感兴趣的可以看看
2048小游戏(C语言实现)
在完成2048小游戏之前,我们要先学会导入图片,导入了图片才会变得好看。
导入图片
#include <stdio.h>
#include <stdlib.h>
// 导入图片资源的头文件是
#include <graphics.h>
int main() {
// 首先初始化图形窗口,并设置大小
initgraph(800, 400);
// 声明图片变量
IMAGE background;
// 加载图片资源到loadimage中(注意,一般"./rec.jpg"就可,如果显示重载错误,请在前面加L)
loadimage(&background, L"./rec.jpg", 800, 400);
// putimage显示图片
putimage(0, 0, &background);
// 避免闪屏
getchar();
// 关闭图形窗口
closegraph();
system("pause");
return 0;
}
./rec.jpg是自己导入到main.c当前目录下的文件
在加载图片到loadimage中时,如果产生两个重载的错误,请在前面加L(在网上查找了很久)
或者将字符集改成多字节字符集
显示结果如下:
批量加载图片
正如我们所知,2048有很多图片需要加载,我们不可能每次加载图片都重复写一样的函数,于是就批量加载图片减少代码量
// 存放12张图片
IMAGE img[12];
// 定义图片
int imgIndex[12] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 };
// 加载资源
void loadResource() {
for (i = 0; i < 12; i++) {
//批量加载
//加入名字
char filename[20] = "";
sprintf(filename, "%d.bmp", imgIndex[i]);
loadimage(img + i, filename);
}
}
这样就可以显示所有的图片了。
设置背景
现在就开始设置我们的背景了,我们的2048游戏是4×4的格式的背景,如下图:
前面空一行写标题,后面是4×4格式的图案
// 定义一个4×4的地图格式
int map[4][4] = { 0 };
// 设置背景
void drawMap() {
setbkcolor(RGB(244, 215, 215)); //设置颜色
cleardevice(); //刷新文字颜色
settextcolor(WHITE); //设置字体颜色
settextstyle(35, 0, "楷体"); //设置文字格式
outtextxy(50, 10, "2048游戏"); //输出文字
// 根据二维数组的值贴图片
int x, y, k;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
x = 60 * j;
y = 60 * i + 60;
// 求出贴哪一张图片
for (k = 0; k < 12; k++) {
if (imgIndex[k] == map[i][j]) {
break;
}
}
//贴相应图片
putimage(x, y, img + k);
}
}
}
随机生成数字
每次按键上下左右时都会随机生成数字
首先扫描整个地图,查找所有空白位置,并将其存储在 empty
数组中。然后,函数随机选择一个空白位置,并在该位置上生成一个数字(2或4)。在生成数字时,使用 rand() % 2 ? 2 : 4
表达式可以使生成的数字以50%的概率为2或4。
void randIntNum() {
int i, j, count = 0;
int empty[16][2]; //用于存储空白位置的数组
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (map[i][j] == 0) { //如果该位置上的数字为0,则认为该位置为空白位置
empty[count][0] = i;
empty[count][1] = j;
count++;
}
}
}
if (count == 0) //如果没有空白位置,则不需要生成数字
return;
int index = rand() % count; //在空白位置中随机选择一个位置
int x = empty[index][0];
int y = empty[index][1];
map[x][y] = rand() % 2 ? 2 : 4; //在选定的位置上生成一个数字(2或4)
}
获取用户的按键
用户可以通过键盘上下左右操作游戏
// 获取用户的按键
void keyDown() {
char key = _getch(); //用来接收用户的按键
switch (key) {
case 'W':
case 'w':
case 72: //小键盘的上
randIntNum();
break;
case 'S':
case 's':
case 80: //小键盘的下
randIntNum();
break;
case 'A':
case 'a':
case 75: //小键盘的左
randIntNum();
break;
case 'D':
case 'd':
case 77: //小键盘的右
randIntNum();
break;
}
}
向上移动代码:
int moveUp() {
int row, col, flag = 0, i, j;
for (col = 0; col < 4; col++) {
//将非0元素移到上侧
for (i = 0; i < 3; i++) {
if (map[i][col] == 0) {
for (j = i + 1; j <= 3; j++) {
if (map[j][col] != 0) {
map[i][col] = map[j][col];
map[j][col] = 0;
flag = 1;
break;
}
}
}
}
//合并相同元素
for (row = 0; row < 3; row++) {
if (map[row][col] == map[row + 1][col]) {
map[row][col] += map[row + 1][col];
map[row + 1][col] = 0;
flag = 1;
}
}
//再次将非0元素移到上侧
for (i = 0; i < 3; i++) {
if (map[i][col] == 0) {
for (j = i + 1; j <= 3; j++) {
if (map[j][col] != 0) {
map[i][col] = map[j][col];
map[j][col] = 0;
flag = 1;
break;
}
}
}
}
}
if (flag)
return 0;
else
return 4;
}
向下移动代码:
int moveDown() {
int row, col, flag = 0, i, j;
for (col = 0; col < 4; col++) {
//将非0元素移到下侧
for (i = 3; i > 0; i--) {
if (map[i][col] == 0) {
for (j = i - 1; j >= 0; j--) {
if (map[j][col] != 0) {
map[i][col] = map[j][col];
map[j][col] = 0;
flag = 1;
break;
}
}
}
}
//合并相同元素
for (row = 3; row > 0; row--) {
if (map[row][col] == map[row - 1][col]) {
map[row][col] += map[row - 1][col];
map[row - 1][col] = 0;
flag = 1;
}
}
//再次将非0元素移到下侧
for (i = 3; i > 0; i--) {
if (map[i][col] == 0) {
for (j = i - 1; j >= 0; j--) {
if (map[j][col] != 0) {
map[i][col] = map[j][col];
map[j][col] = 0;
flag = 1;
break;
}
}
}
}
}
if (flag)
return 0;
else
return 4;
}
向左移动代码:
int moveLeft() {
int row, col, flag = 0, i, j;
for (row = 0; row < 4; row++) {
//将非0元素移到左侧
for (i = 0; i < 3; i++) {
if (map[row][i] == 0) {
for (j = i + 1; j <= 3; j++) {
if (map[row][j] != 0) {
map[row][i] = map[row][j];
map[row][j] = 0;
flag = 1;
break;
}
}
}
}
//合并相同元素
for (col = 0; col < 3; col++) {
if (map[row][col] == map[row][col + 1]) {
map[row][col] += map[row][col + 1];
map[row][col + 1] = 0;
flag = 1;
}
}
//再次将非0元素移到左侧
for (i = 0; i < 3; i++) {
if (map[row][i] == 0) {
for (j = i + 1; j <= 3; j++) {
if (map[row][j] != 0) {
map[row][i] = map[row][j];
map[row][j] = 0;
flag = 1;
break;
}
}
}
}
}
if (flag)
return 0;
else
return 4;
}
向右移动代码:
int moveRight() {
int row, col, flag = 0, i, j;
for (row = 0; row < 4; row++) {
//将非0元素移到右侧
for (i = 3; i > 0; i--) {
if (map[row][i] == 0) {
for (j = i - 1; j >= 0; j--) {
if (map[row][j] != 0) {
map[row][i] = map[row][j];
map[row][j] = 0;
flag = 1;
break;
}
}
}
}
//合并相同元素
for (col = 3; col > 0; col--) {
if (map[row][col] == map[row][col - 1]) {
map[row][col] += map[row][col - 1];
map[row][col - 1] = 0;
flag = 1;
}
}
//再次将非0元素移到右侧
for (i = 3; i > 0; i--) {
if (map[row][i] == 0) {
for (j = i - 1; j >= 0; j--) {
if (map[row][j] != 0) {
map[row][i] = map[row][j];
map[row][j] = 0;
flag = 1;
break;
}
}
}
}
}
if (flag)
return 0;
else
return 4;
}
总代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <graphics.h>
#include <conio.h>
#include <time.h>
// 存放12张图片
IMAGE img[12];
// 定义图片
int imgIndex[12] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 };
// 循环变量遍历
int i, j;
// 加载资源
void loadResource() {
for (i = 0; i < 12; i++) {
//批量加载
//加入名字
char filename[20] = "";
sprintf(filename, "%d.bmp", imgIndex[i]);
loadimage(img + i, filename);
}
}
// 定义一个4×4的地图格式
int map[4][4] = { 0 };
// 设置背景
void drawMap() {
setbkcolor(RGB(244, 215, 215)); //设置颜色
cleardevice(); //刷新文字颜色
settextcolor(WHITE); //设置字体颜色
settextstyle(35, 0, "楷体"); //设置文字格式
outtextxy(50, 10, "2048游戏"); //输出文字
// 根据二维数组的值贴图片
int x, y, k;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
x = 60 * j;
y = 60 * i + 60;
// 求出贴哪一张图片
for (k = 0; k < 12; k++) {
if (imgIndex[k] == map[i][j]) {
break;
}
}
//贴相应图片
putimage(x, y, img + k);
}
}
}
// 随机生成数字
void randIntNum() {
int i, j, count = 0;
int empty[16][2]; //用于存储空白位置的数组
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (map[i][j] == 0) { //如果该位置上的数字为0,则认为该位置为空白位置
empty[count][0] = i;
empty[count][1] = j;
count++;
}
}
}
if (count == 0) //如果没有空白位置,则不需要生成数字
return;
int index = rand() % count; //在空白位置中随机选择一个位置
int x = empty[index][0];
int y = empty[index][1];
map[x][y] = rand() % 2 ? 2 : 4; //在选定的位置上生成一个数字(2或4)
}
int moveRight() {
int row, col, flag = 0, i, j;
for (row = 0; row < 4; row++) {
//将非0元素移到右侧
for (i = 3; i > 0; i--) {
if (map[row][i] == 0) {
for (j = i - 1; j >= 0; j--) {
if (map[row][j] != 0) {
map[row][i] = map[row][j];
map[row][j] = 0;
flag = 1;
break;
}
}
}
}
//合并相同元素
for (col = 3; col > 0; col--) {
if (map[row][col] == map[row][col - 1]) {
map[row][col] += map[row][col - 1];
map[row][col - 1] = 0;
flag = 1;
}
}
//再次将非0元素移到右侧
for (i = 3; i > 0; i--) {
if (map[row][i] == 0) {
for (j = i - 1; j >= 0; j--) {
if (map[row][j] != 0) {
map[row][i] = map[row][j];
map[row][j] = 0;
flag = 1;
break;
}
}
}
}
}
if (flag)
return 0;
else
return 4;
}
int moveLeft() {
int row, col, flag = 0, i, j;
for (row = 0; row < 4; row++) {
//将非0元素移到左侧
for (i = 0; i < 3; i++) {
if (map[row][i] == 0) {
for (j = i + 1; j <= 3; j++) {
if (map[row][j] != 0) {
map[row][i] = map[row][j];
map[row][j] = 0;
flag = 1;
break;
}
}
}
}
//合并相同元素
for (col = 0; col < 3; col++) {
if (map[row][col] == map[row][col + 1]) {
map[row][col] += map[row][col + 1];
map[row][col + 1] = 0;
flag = 1;
}
}
//再次将非0元素移到左侧
for (i = 0; i < 3; i++) {
if (map[row][i] == 0) {
for (j = i + 1; j <= 3; j++) {
if (map[row][j] != 0) {
map[row][i] = map[row][j];
map[row][j] = 0;
flag = 1;
break;
}
}
}
}
}
if (flag)
return 0;
else
return 4;
}
int moveUp() {
int row, col, flag = 0, i, j;
for (col = 0; col < 4; col++) {
//将非0元素移到上侧
for (i = 0; i < 3; i++) {
if (map[i][col] == 0) {
for (j = i + 1; j <= 3; j++) {
if (map[j][col] != 0) {
map[i][col] = map[j][col];
map[j][col] = 0;
flag = 1;
break;
}
}
}
}
//合并相同元素
for (row = 0; row < 3; row++) {
if (map[row][col] == map[row + 1][col]) {
map[row][col] += map[row + 1][col];
map[row + 1][col] = 0;
flag = 1;
}
}
//再次将非0元素移到上侧
for (i = 0; i < 3; i++) {
if (map[i][col] == 0) {
for (j = i + 1; j <= 3; j++) {
if (map[j][col] != 0) {
map[j][col] = map[j][col];
map[j][col] = 0;
flag = 1;
break;
}
}
}
}
}
if (flag)
return 0;
else
return 4;
}
int moveDown() {
int row, col, flag = 0, i, j;
for (col = 0; col < 4; col++) {
//将非0元素移到下侧
for (i = 3; i > 0; i--) {
if (map[i][col] == 0) {
for (j = i - 1; j >= 0; j--) {
if (map[j][col] != 0) {
map[i][col] = map[j][col];
map[j][col] = 0;
flag = 1;
break;
}
}
}
}
//合并相同元素
for (row = 3; row > 0; row--) {
if (map[row][col] == map[row - 1][col]) {
map[row][col] += map[row - 1][col];
map[row - 1][col] = 0;
flag = 1;
}
}
//再次将非0元素移到下侧
for (i = 3; i > 0; i--) {
if (map[i][col] == 0) {
for (j = i - 1; j >= 0; j--) {
if (map[j][col] != 0) {
map[i][col] = map[j][col];
map[j][col] = 0;
flag = 1;
break;
}
}
}
}
}
if (flag)
return 0;
else
return 4;
}
// 获取用户的按键
void keyDown() {
char key = _getch(); //用来接收用户的按键
switch (key) {
case 'W':
case 'w':
case 72: //小键盘的上
randIntNum();
moveUp();
drawMap();
break;
case 'S':
case 's':
case 80: //小键盘的下
randIntNum();
moveDown();
drawMap();
break;
case 'A':
case 'a':
case 75: //小键盘的左
randIntNum();
moveLeft();
drawMap();
break;
case 'D':
case 'd':
case 77: //小键盘的右
randIntNum();
moveRight();
drawMap();
break;
}
}
int main() {
// 程序显示图片
loadResource();
initgraph(60*4, 60*5);
drawMap();
while (1) {
keyDown();
drawMap();
}
// 初始化图形窗口,并设置大小
// 声明图片变量
// 加载图片资源到loadImage中
// putImage显示图片
//initgraph(800, 400);
IMAGE background;
//loadimage(&background, "./rec.jpg", 800, 400);
//putimage(0, 0, &background);
// 避免闪屏
getchar();
closegraph();
system("pause");
return 0;
}
运行结果
最后可以更加优化,例如添加分数上去等,以后有时间再优化吧。