EasyX 是针对 C++ 的图形库,可以帮助 C/C++ 初学者快速上手图形和游戏编程。通过查阅Easyx的官方文档我们可以很快速的上手并编写一些小游戏,如贪吃蛇、连连看等。本文章实现用C语言基于Easyx库,编写一个简单的连连看小游戏。
代码部分
先是一些头文件、宏定义等,方便后续修改游戏相关设置
#include<time.h>#define MAP_SIZE 10 //每一行有多少张图片#define IMG_SIZE 39 //每张图片的宽度#define WIDTH (MAP_SIZE +2) * IMG_SIZE //窗口的宽度#define ANIMAL_NUM 42 //图片的总个数int map[MAP_SIZE + 2][MAP_SIZE + 2];int way[MAP_SIZE + 2][MAP_SIZE + 2];IMAGE img_bk;IMAGE img_Animal[ANIMAL_NUM][2];struct Index{int row;int col;}begin = { 1, -1 }, end = { -1, -1 };enum Cur{BEGIN,END};int step = 0;Cur state = BEGIN;
这部分是游戏初始化的函数
void gameInit(){//设置随机数种子srand((unsigned)time(NULL));//用不同的数据表示不同的图片,每种图片来10个,一共需要10种图片for (int i = 1; i <= MAP_SIZE; i++){for (int k = 1; k <= MAP_SIZE; k++){map[i][k] = i; //10个i}}//加载图片loadimage(&img_bk,"./ref/bk.jpg",WIDTH+80,WIDTH+50); //后两个参数用来调整背景图片的位置IMAGE animal;loadimage(&animal, "./ref/animal.bmp"); //bmp//如何切割图片SetWorkingImage(&animal); //设置工作区for (int i = 0; i < ANIMAL_NUM; i++){for (int k = 0; k < 2; k++){getimage(&img_Animal[i][k], k * IMG_SIZE, i * IMG_SIZE, IMG_SIZE, IMG_SIZE);}}SetWorkingImage(); //恢复默认工作区//打乱数组排列,让图片也随机排列for (int i = 1; i <= MAP_SIZE; i++){for (int k = 1; k <= MAP_SIZE; k++){//随即找到两个游戏区域的下标位置,然后交换值 随机函数 rand()int x = rand() % MAP_SIZE + 1;int y= rand() % MAP_SIZE + 1;int _swap = map[i][k];map[i][k] = map[x][y];map[x][y] = _swap;}}}
我们使用一个二维数组来存储图片的位置信息,为了使每次运行程序时图片的排列方式不同,因此设置了一个随机数种子。
游戏图形的绘制
void gameDraw(){putimage(0, 0, &img_bk);for (int i = 1; i <= MAP_SIZE; i++){for (int k = 1; k <= MAP_SIZE; k++){if (map[i][k] != 0){putimage(k * IMG_SIZE, i * IMG_SIZE, &img_Animal[map[i][k]][1], SRCAND); //原图putimage(k * IMG_SIZE, i * IMG_SIZE, &img_Animal[map[i][k]][0], SRCPAINT); //掩码图}}}}
获取鼠标消息,获取点击的两个位置的下标
void mouseEvent(){if (MouseHit()){MOUSEMSG msg = GetMouseMsg();//判断鼠标点击的位置(begin,end)if (msg.uMsg == WM_LBUTTONDOWN && state == BEGIN){//把鼠标的坐标转成对应的数组的下标begin.col = msg.x / IMG_SIZE;begin.row = msg.y / IMG_SIZE;end.col = -1;end.row = -1;state = END; //改变状态}else if (msg.uMsg == WM_LBUTTONDOWN && state == END){end.col = msg.x / IMG_SIZE;end.row = msg.y / IMG_SIZE;state = BEGIN; //改变状态}if (msg.uMsg == WM_LBUTTONDOWN){printf("begin(%d,%d) end(%d,%d)\n", begin.col, begin.row, end.col, end.row);}}}
判断某个位置是否有图片
bool isBlock(int row, int col){if (row < 0 || col < 0 || row >= MAP_SIZE + 2 || col >= MAP_SIZE + 2)return 1;return map[row][col] + way[row][col];}
在控制台打印出二维数组(为了后续观察寻路器的运作)
void showMap(){for (int i = 0; i < MAP_SIZE + 2; i++){for (int k = 0; k < MAP_SIZE + 2; k++){printf("%-2d ", map[i][k]);}printf("\n");}}
判断鼠标点击的起点和终点是否一致
bool same(int begin_x, int begin_y, int end_x, int end_y) {if (begin_x < 0 || begin_y < 0 || end_x < 0 || end_y < 0 || end_x >= MAP_SIZE + 2 || end_y >= MAP_SIZE + 2 || begin_x >= MAP_SIZE + 2 || begin_y >= MAP_SIZE + 2)return false;if (begin_x == end_x && begin_y == end_y)return true;return false;}
判断是否满足消除条件
bool can_pass(int begin_x, int begin_y, int end_x, int end_y) {if (state != BEGIN || begin_x < 0 || begin_y < 0 || end_x < 0 || end_y < 0 || end_x >= MAP_SIZE + 2 || end_y >= MAP_SIZE + 2 || begin_x >= MAP_SIZE + 2 || begin_y >= MAP_SIZE + 2)return false;if (begin_x == end_x && begin_y == end_y)return false;if (map[begin.row][begin.col] != map[end.row][end.col])return false;return true;}
使用A*寻路算法进行递归寻路消除
bool can(int begin_x, int begin_y, int end_x, int end_y) {printf("step:%d (%d,%d)->(%d,%d)\n", step, begin_x, begin_y, end_x, end_y);if (step++ > 2000) //当找路太多了return false;if (begin_x < 0 || begin_y < 0 || end_x < 0 || end_y < 0 || end_x >= MAP_SIZE + 2 || end_y >= MAP_SIZE + 2 || begin_x >= MAP_SIZE + 2 || begin_y >= MAP_SIZE + 2)return false;if (same(begin_x, begin_y, end_x, end_y) || same(begin_x - 1, begin_y, end_x, end_y) || same(begin_x + 1, begin_y, end_x, end_y) || same(begin_x, begin_y - 1, end_x, end_y) || same(begin_x, begin_y + 1, end_x, end_y))return true;if (!isBlock(begin_x - 1, begin_y)) //1{way[begin_x - 1][begin_y] = 1;if (can(begin_x - 1, begin_y, end_x, end_y))return true;way[begin_x - 1][begin_y] = 0;}if (!isBlock(begin_x, begin_y - 1)) //3{way[begin_x][begin_y - 1] = 1;if (can(begin_x, begin_y - 1, end_x, end_y))return true;way[begin_x][begin_y - 1] = 0;}if (!isBlock(begin_x + 1, begin_y)) //2{way[begin_x + 1][begin_y] = 1;if (can(begin_x + 1, begin_y, end_x, end_y))return true;way[begin_x + 1][begin_y] = 0;}if (!isBlock(begin_x, begin_y + 1)) //4{way[begin_x][begin_y + 1] = 1;if (can(begin_x, begin_y + 1, end_x, end_y))return true;way[begin_x][begin_y + 1] = 0;}return false;}
主函数部分
int main(){//首先需要我们自己创建一个图形窗口initgraph(WIDTH, WIDTH, SHOWCONSOLE);gameInit();showMap();while (1){//开始双缓冲绘图BeginBatchDraw();gameDraw();//结束双缓冲绘图EndBatchDraw();if (can_pass(begin.row, begin.col, end.row, end.col)){memset(way, 0, sizeof(way));way[begin.row][begin.col] = 1;step = 0;printf("inininininin");if (can(begin.row, begin.col, end.row, end.col)){map[begin.row][begin.col] = 0;map[end.row][end.col] = 0;begin.row = -1;begin.col = -1;end.row = -1;end.col = -1;}}mouseEvent();}getchar();return 0;}
使用双缓冲绘图防止闪烁
运行结果
(背景图片和连连看内容图片可以自行更换,这里背景我随便放了一张球星照片,注意连连看图片要同时制作其掩码图以实现透明效果)