【C / EasyX】十字消除游戏的实现方法
发表于2018-07-17
十字消除游戏是以前百度上火过一段时间的一个Flash小游戏,本篇文章要教给大家的就是如何实现这样一个十字消除游戏。
游戏逻辑比较简单,是我初次接触EasyX图形库后使用EasyX写的第一个i程序。
游戏开始后,在棋盘中随机生成成对的随即颜色的方块,预留一些空白位置。玩家点击空白位置,如果所在位置延伸出的十字线上,能遇到相同颜色的方块,则消除之,累加分数。消除一对方块会恢复一定时间,不进行任何操作时间条会自动减少,直至耗尽游戏结束。如果棋盘上剩余方块少于一定数量,会继续随机生成一定数量的新方块。
效果图如下:
源代码如下:
///////////////////////////////////////////////////////// // 程序名称:AyaCrossX(十字消除游戏) // 编译环境:Visual C++ 6.0 / 2012,EasyX 2013霜降版 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <conio.h> #include <math.h> #include <time.h> //EasyX图形库 #include <graphics.h> //////////////////////////////以下是 宏定义参数 ////////////////////////////// #define WIN_WIDTH 600 //屏幕宽度 #define WIN_HEIGHT 430 //屏幕高度 #define WIN_BLANKTOP 45 //屏幕顶部空边 #define WIN_BLANK 25 //屏幕边缘留空 #define G_BN 20 //方块横向数量 #define G_BM 12 //方块纵向数量 #define G_BW 20 //方块宽度 #define G_BS 8 //方块间隔宽度 #define G_BSELECT 3 //方块选中大小 #define G_FLOATW 4 //选中浮动大小 #define G_BDWO 1 //消失初始大小 #define G_BDW 6 //消失大小 #define G_CN 10 //颜色数量 #define G_BUL 25 //方块增量程度 #define C_BLOCKPANEL RGB(255, 255, 221) //底板颜色 #define C_HPBAR RGB(255, 170, 255) //HP条颜色 //////////////////////////////以下是数据结构定义////////////////////////////// //方块结构 struct Block{ COLORREF color; //颜色 float FLOAT; //浮动大小 float DIS; //消除延迟 bool isDIS; //是否消失中 bool isClick; //是否被点击 }; //点结构 struct Point{ int x; int y; }; //矩形结构 struct Rect{ int x; int y; int w; int h; }; //////////////////////////////以下是 全局变量 ////////////////////////////// int GamePhase = 0; //游戏阶段 0 准备 1 进行 2 结束 3 帮助界面 int isShowFPS = 0; //是否显示帧数 int mouseX; //鼠标位置坐标X int mouseY; //鼠标位置坐标Y int time_max; //总游戏时间 int time_now; //当前剩余时间 int level; //等级 int score; //分数 bool isMouseDown; //鼠标按下 Block block[G_BM][G_BN]; //方块数组 COLORREF Defcolor[G_CN]; //候选颜色数组 //////////////////////////////以下是 函数声明 ////////////////////////////// float GetFPS(); //获取帧数 void HpSleep(DWORD ms); //绝对延时 Point GetMouseLocal(int x, int y); //获取鼠标所在坐标 void AddBlock(int n); //添加指定个数的未选中方块 void Init(); //初始化 void DisCheck(int x, int y); //检测指定位置的消除 int CountBlock(); //统计方块个数 void Manager(); //逻辑处理 void Draw(); //绘图处理 void StartDraw(); //游戏开始绘图 void GamingDraw(); //游戏进行中绘图 void EndDraw(); //游戏结束绘图 void HelpDraw(); //游戏帮助绘图 void EndFrame(); //帧结束处理 bool IsInRect(int x, int y, Rect r); //是否在矩形内 //////////////////////////////以下是辅助功能函数////////////////////////////// //计算画面FPS(每秒帧数) float GetFPS() { #define FPS_COUNT 8 static int i = 0; static DWORD oldTime = GetTickCount(); static float fps; if (i > FPS_COUNT) { i = 0; int newTime = GetTickCount(); int elapsedTime = newTime - oldTime; fps = FPS_COUNT / (elapsedTime / 1000.0f); oldTime = newTime; } i++; return fps; } //绝对延时 void HpSleep(DWORD ms) { static clock_t oldclock = clock(); // 静态变量,记录上一次 tick oldclock += ms * CLOCKS_PER_SEC / 1000; // 更新 tick if (clock() > oldclock) // 如果已经超时,无需延时 oldclock = clock(); else while(clock() < oldclock) // 延时 Sleep(1); // 释放 CPU 控制权,降低 CPU 占用率 } //获取鼠标选中点 Point GetMouseLocal(int x, int y) { Point p; x = (x - WIN_BLANK) / (G_BW + G_BS); y = (y - WIN_BLANK - WIN_BLANKTOP) / (G_BW + G_BS); if (x < 0 || x >= G_BN) x = -1; if (y < 0 || y >= G_BM) y = -1; p.x = x; p.y = y; return p; } //是否在矩形内 bool IsInRect(int x, int y, Rect r) { return ((x >= r.x && x <= r.w) && (y >= r.y && y <= r.h)); } //添加制定个数的未选中方块 void AddBlock(int n) { //随机生成方块 int num = 0, x1, y1, x2, y2; int num_max = n; while(num < num_max) { x1 = rand() % G_BN; y1 = rand() % G_BM; x2 = rand() % G_BN; y2 = rand() % G_BM; if(block[y1][x1].isClick && block[y2][x2].isClick) { COLORREF color = Defcolor[rand() % G_CN]; block[y1][x1].color = color; block[y1][x1].isClick = false; block[y1][x1].FLOAT = 0; block[y1][x1].DIS = 0; block[y1][x1].isDIS = false; block[y2][x2].color = color; block[y2][x2].isClick = false; block[y2][x2].FLOAT = 0; block[y2][x2].DIS = 0; block[y2][x2].isDIS = false; num += 2; } } } //初始化 void Init() { //预置颜色 Defcolor[0] = RGB(255, 153, 0); Defcolor[1] = RGB(204, 102, 0); Defcolor[2] = RGB(27, 118, 255); Defcolor[3] = RGB(255, 136, 255); Defcolor[4] = RGB(204, 204, 102); Defcolor[5] = RGB(104, 204, 204); Defcolor[6] = RGB(255, 125, 125); Defcolor[7] = RGB(204, 104, 204); Defcolor[8] = RGB(190, 190, 190); Defcolor[9] = RGB(0, 204, 0); //初始化时间 time_max = 10000; time_now = 10000; //初始化分数 score = 0; //置全选中 for(int i = 0; i < G_BM; i++) { for(int j = 0; j < G_BN; j++) { block[i][j].isClick = true; block[i][j].FLOAT = 0; block[i][j].DIS = 0; } } //添加方块 AddBlock(G_BN * G_BM * 2 / 3); //清除鼠标消息队列 FlushMouseMsgBuffer(); } //统计方块个数 int CountBlock() { int n = 0; for(int i = 0; i < G_BN; i++) { for(int j = 0; j < G_BM; j++) { if(!block[j][i].isClick) n++; } } return n; } //检测指定位置消除 void DisCheck(int x, int y) { //记录待消除的方块 Block* b[4]; int i, j, n = 0; //向左 for(i = x; i >= 0; i--) { if(!block[y][i].isClick) { b[n] = &block[y][i]; n++; break; } } //向右 for(i = x; i < G_BN; i++) { if(!block[y][i].isClick) { b[n] = &block[y][i]; n++; break; } } //向上 for(j = y; j >= 0; j--) { if(!block[j][x].isClick) { b[n] = &block[j][x]; n++; break; } } //向下 for(j = y; j < G_BM; j++) { if(!block[j][x].isClick) { b[n] = &block[j][x]; n++; break; } } //消除 for(i = 0; i < n; i++) { for(j = i + 1; j < n; j++) { if(b[i]->color == b[j]->color) { if(!b[i]->isDIS){ b[i]->DIS = G_BDWO; b[i]->isDIS = true; } if(!b[j]->isDIS){ b[j]->DIS = G_BDWO; b[j]->isDIS = true; } } } } isMouseDown = false; } //逻辑处理 void Manager() { //如果在游戏中 if(GamePhase == 1) { Point p = GetMouseLocal(mouseX, mouseY); int x = p.x, y = p.y; //如果选中 if(p.x != -1 && p.y != -1) { //如果单击 if(isMouseDown && block[y][x].isClick) { //触发消除检测 DisCheck(x, y); } } //添加新方块添加随机个数新方块 if(CountBlock() < G_BN * G_BM / 3) { AddBlock((rand() % 15 + 15) * 2); //时间恢复 time_now += 500; if(time_now > time_max) time_now = time_max; } } } //开始绘图 void StartDraw() { settextstyle(52, 30, _T("Impact")); //标题 settextcolor(RGB(255, 17, 102)); outtextxy(143, 93, _T("AyaCrossX")); settextcolor(RGB(255, 153, 238)); outtextxy(140, 90, _T("AyaCrossX")); //版本 settextstyle(22, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(340, 153, _T("v0.91 By:ls9512")); //开始游戏按钮 Rect r; r.x = 240; r.y = 220; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //开始游戏按键响应 if(isMouseDown) { Init(); GamePhase = 1; isMouseDown = false; } } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("PLAY GAME")); //帮助游戏按钮 r.x = 240; r.y = 270; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //帮助按键响应 if(isMouseDown) GamePhase = 3; } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("HELP INFO")); //退出游戏按钮 r.x = 240; r.y = 320; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //退出按键响应 if(isMouseDown) exit(0); } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("EXIT GAME")); //说明 settextstyle(16, 5, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(175, 400, _T("Programing By VC++ & EasyX Date:2013.11.01")); } //游戏中绘图 void GameingDraw() { int x, y; COLORREF c, c2; //时间条 x = WIN_BLANK; y = WIN_BLANK; int HPBARW = (G_BW + G_BS) * G_BN - 200; setlinecolor(RGB(255, 17, 255)); setfillcolor(WHITE); fillrectangle(x, y, x + HPBARW, y + 10); setfillcolor(C_HPBAR); fillrectangle(x, y, x + (int)(HPBARW * (1.0 * time_now / time_max)), y + 10); //底板 setlinecolor(RGB(255, 25, 22)); setfillcolor(C_BLOCKPANEL); x = WIN_BLANK - G_BS; y = WIN_BLANK + WIN_BLANKTOP - G_BS; fillrectangle(x, y, x + (G_BW + G_BS) * G_BN + G_BS, y + (G_BW + G_BS) * G_BM + G_BS); //画方块 for(int i = 0; i < G_BM; i++) { for(int j = 0; j < G_BN; j++) { if(!block[i][j].isClick) { c = block[i][j].color; c = RGB(GetRValue(c) - (BYTE)G_BUL, GetGValue(c) - (BYTE)G_BUL, GetBValue(c) - (BYTE)G_BUL); int fw = (int)block[i][j].FLOAT; int fd = (int)block[i][j].DIS; setlinecolor(c); setfillcolor(block[i][j].color); x = j * (G_BW + G_BS) + WIN_BLANK - fw - fd; y = i * (G_BW + G_BS) + WIN_BLANK + WIN_BLANKTOP - fw - fd; fillrectangle(x, y, x + G_BW + 2 * (fw + fd), y + G_BW + 2 * (fw + fd)); //浮动缩减 if(block[i][j].FLOAT > 0) block[i][j].FLOAT -= 0.4f; //消失延迟 if(block[i][j].isDIS) { if(block[i][j].DIS < G_BDW) block[i][j].DIS += 0.4f; else { //消除 block[i][j].isClick = true; //加分 score += 100; time_now += 25; if (time_now > time_max) time_now = time_max; } } } } } //获取鼠标位置 Point p = GetMouseLocal(mouseX, mouseY); //画出鼠标光标 if(p.x != -1 && p.y != -1) { c = RED; x = p.x, y = p.y; int fw; if(block[y][x].isClick) fw = 0; else fw = (int)block[y][x].FLOAT; x = x * (G_BW + G_BS) + WIN_BLANK - G_BSELECT - fw; y = y * (G_BW + G_BS) + WIN_BLANK + WIN_BLANKTOP - G_BSELECT -fw; //选中方块加亮 if(!block[p.y][p.x].isClick) { //置浮动大小 block[p.y][p.x].FLOAT = G_FLOATW; c2 = block[p.y][p.x].color; } else { c2 = C_BLOCKPANEL; } setlinecolor(c); setfillcolor(c2); fillrectangle(x, y, x + G_BW + 2 * (G_BSELECT + fw), y + G_BW + 2 * (G_BSELECT + fw)); } //画出分数 settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(155, 51, 68)); TCHAR b[10]; #if _MSC_VER > 1200 _stprintf_s(b, _T("%d"), score); #else _stprintf(b, _T("%d"), score); #endif outtextxy(390, 15, _T("分数:")); settextcolor(RGB(255, 51, 68)); outtextxy(440, 16, b); } //游戏结束绘图 void EndDraw() { //标题 settextstyle(52, 30, _T("Impact")); settextcolor(RGB(255, 17, 102)); outtextxy(143, 103, _T("GAME OVER")); settextcolor(RGB(215, 193, 238)); outtextxy(140, 100, _T("GAME OVER")); //版本 settextstyle(34, 12, _T("Verdana")); settextcolor(RGB(255, 51, 68)); TCHAR b[10]; #if _MSC_VER > 1200 _stprintf_s(b, _T("%d"), score); #else _stprintf(b, _T("%d"), score); #endif outtextxy(232, 173, _T("分数:")); outtextxy(290, 173, b); //重新开始游戏按钮 Rect r; r.x = 240; r.y = 240; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //开始游戏按键响应 if(isMouseDown) { Init(); GamePhase = 1; } } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T(" REPLAY")); //退出 r.x = 240; r.y = 290; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //退出按键响应 if(isMouseDown) exit(0); } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("EXIT GAME")); } //游戏帮助绘图 void HelpDraw() { //标题 settextstyle(52, 30, _T("Impact")); settextcolor(RGB(255, 17, 152)); outtextxy(233, 33, _T("HELP")); settextcolor(RGB(215, 193, 238)); outtextxy(230, 30, _T("HELP")); //底板 setlinecolor(RGB(255, 17, 204)); setfillcolor(RGB(255, 221, 255)); fillrectangle(60, 100, 550, 280); //版本 settextstyle(18, 10, _T("黑体")); settextcolor(RGB(245, 126, 92)); outtextxy(70, 110, _T("十字消除是一款经典消除类小游戏。")); outtextxy(70, 145, _T("【玩法】")); outtextxy(80, 170, _T("1.点击空白处,所在横纵十字线上同色方块会被消除。")); outtextxy(80, 195, _T("2.消除方块会累加得分,并恢复一定量的时间.")); outtextxy(80, 220, _T("3.方块数量少于某数值会出现新方块并回复时间.")); outtextxy(80, 245, _T("4.时间耗尽时则游戏结束.")); //返回 Rect r; r.x = 240; r.y = 360; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //返回按键响应 if(isMouseDown) GamePhase = 0; } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 9, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("BACK MENU")); } //输出帧数 void DrawFPS() { //输出帧数 if(isShowFPS) { settextcolor(RED); settextstyle(14, 0, _T("宋体")); TCHAR s[5]; #if _MSC_VER > 1200 _stprintf_s(s, _T("%.1f"), GetFPS()); #else _stprintf(s, _T("%.1f"), GetFPS()); #endif outtextxy(0, 0, s); } } //绘图 void Draw() { //清屏 setlinecolor(WHITE); setfillcolor(WHITE); fillrectangle(0, 0, WIN_WIDTH, WIN_HEIGHT); switch(GamePhase) { case 0: StartDraw(); break; case 1: GameingDraw(); break; case 2: EndDraw(); break; case 3: HelpDraw(); break; } DrawFPS(); } //帧结束处理 void EndFrame() { time_now -= 2; //游戏结束 if(time_now <= 0 && GamePhase == 1) { GamePhase = 2; } } //主入口函数 int main() { //置随机数种子 srand((unsigned)time(NULL)); //初始化设备,加载图片 initgraph(WIN_WIDTH, WIN_HEIGHT); //设置窗口标题 SetWindowText(GetHWnd(), _T("AyaCrossX v0.91 By:ls9512")); cleardevice(); //设置黑色背景 setbkmode(TRANSPARENT); settextcolor(BLACK); //开启双缓冲,防止闪屏 BeginBatchDraw(); // 鼠标消息变量 MOUSEMSG mmsg; //初始化 Init(); while(true) { //处理鼠标消息 while(MouseHit()) { mmsg = GetMouseMsg(); switch(mmsg.uMsg) { case WM_MOUSEMOVE: mouseX = mmsg.x; mouseY = mmsg.y; break; case WM_LBUTTONDOWN: isMouseDown = true; break; case WM_LBUTTONUP: isMouseDown = false; break; } } //逻辑处理 Manager(); //绘图处理 Draw(); //显示缓存的绘制内容 FlushBatchDraw(); //帧结束处理 EndFrame(); //延迟,帧数控制 HpSleep(18); } // 关闭 EndBatchDraw(); closegraph(); return 0; }
来自:https://blog.csdn.net/ls9512/article/details/46803749