
easyx高仿贪吃蛇(附源码)
发布日期:2021-05-10 05:17:17
浏览次数:21
分类:精选文章
本文共 17889 字,大约阅读时间需要 59 分钟。
easyx高仿贪吃蛇
已经一周没有发新文章啦,看着这点击量掉的不是一般的快啊!这样可不行,所以,今天我就发一篇来啦!
游戏介绍
这是一个贪吃蛇大作战类游戏,修改特性为 AI 不互杀;
该程序有四个类:蛇基类 SnakeBase,玩家类 Player,AI 类,Game 类;
SnakeBase 和 AI 均继承自 SnakeBase,SnakeBase 提供基础接口; Game 提供数据初始化以及游戏主循环。程序设计之初,采用“实运行”方式,即所有动画均在地图 imgMap 上真实绘制,
这样却有一些严重的问题,需要不断地记录节点背景以恢复节点经过的地方等。后修改为“虚运行”方式,判断位置是否可视后直接绘至窗口内,
这样大大降低时间开销,同时不会因为蛇靠近而产生不可擦除的颜色。至于蛇运行,采用位置继承,即节点下一个位置为上一个节点的位置,但是会发现跨度太大,
解决办法是在这个跨度中插入适当帧数。功能实现
- 支持16个方向的移动
- 自动彩豆生成
- 碰撞检测
- 全自动电脑AI移动、攻击、拾取食物
- 地图边界
- 地图网格
- 长度、击杀显示
- 鼠标控制
- 高仿原版贪吃蛇界面
效果截图
编译系统
Visual Studio 2017上编译通过,需安装easyx.
Visual Studio下载: https://visualstudio.microsoft.com/zh-hans/ easyX下载: https://easyx.cn/ easyX教程: https://docs.easyx.cn/en-us/intro
代码部分
作者: https://codebus.cn/teternity/
// 头文件 //#include#include #include #include #include #pragma comment( lib, "MSIMG32.LIB")// 窗口大小 //const short ScreenWidth = 640;const short ScreenHeight = 480;// 全局变量 //long ret = 0;MOUSEMSG msg;const short gap = 20;const short xSide = ScreenWidth / 2 + gap;const short ySide = ScreenHeight / 2 + gap;IMAGE* imgMap = new IMAGE(ScreenWidth * 4, ScreenHeight * 4);const double PI = 3.1415926;const short nodeSize = 17;const short nodeGap = 16;const short stepLen = 4;const short frame = 4;const short snakeSpecies = 20;int killCount = 0;int mapX = 0;int mapY = 0;typedef struct _FOOD{ int x; int y; int r; COLORREF c;}Food;Food* food;short nFood = 520;IMAGE imgFood(nodeSize, nodeSize);const COLORREF mapMainColor = WHITE;const COLORREF playerColor0 = RGB(120, 0, 0);const COLORREF playerColor1 = RGB(200, 0, 0);const COLORREF playerColor2 = RGB(255, 255, 0);const COLORREF mapLineColor = RGB(225, 225, 225);// 透明贴图函数:(更多内容请参考官网)// 参数:// x, y: 目标贴图位置// srcimg: 源 IMAGE 对象指针。NULL 表示默认窗体// dstimg: 目标 IMAGE 对象指针。NULL 表示默认窗体// transparentcolor: 透明色。srcimg 的该颜色并不会复制到 dstimg 上,从而实现透明贴图void putTimage(int x, int y, IMAGE* srcimg, IMAGE* dstimg = NULL, UINT transparentcolor = 0){ HDC dstDC = GetImageHDC(dstimg); HDC srcDC = GetImageHDC(srcimg); int w = srcimg->getwidth(); int h = srcimg->getheight(); // 使用 Windows GDI 函数实现透明位图 TransparentBlt(dstDC, x, y, w, h, srcDC, 0, 0, w, h, transparentcolor);}// 蛇基类 //class SnakeBase{ public: // 构造函数 SnakeBase() { Count++; isDead = false; length = 10 + rand() % 10; nNode = length / 5; maxNode = 9999; imgHead = imgNode = imgTail = nullptr; headNode = tailNode = nullptr; nodeMsg = nullptr; } // 析构函数 virtual ~SnakeBase() { Count--; Node* temp = headNode; while (temp != nullptr) { headNode = headNode->nextNode; delete temp; temp = headNode; } delete imgHead, imgNode, imgTail; if (nodeMsg != nullptr) delete[] nodeMsg; } // 设置image void SetImage(COLORREF headColor, COLORREF nodeColor, COLORREF tailColor) { imgHead = new IMAGE(nodeSize, nodeSize); imgNode = new IMAGE(nodeSize, nodeSize); imgTail = new IMAGE(nodeSize, nodeSize); SetWorkingImage(imgHead); setfillcolor(headColor); solidcircle(nodeSize / 2, nodeSize / 2, nodeSize / 2); setfillcolor(tailColor); solidcircle(nodeSize / 2, nodeSize / 2, 2); SetWorkingImage(imgNode); setfillcolor(nodeColor); solidcircle(nodeSize / 2, nodeSize / 2, nodeSize / 2); SetWorkingImage(imgTail); setfillcolor(tailColor); solidcircle(nodeSize / 2, nodeSize / 2, nodeSize / 2); SetWorkingImage(); } // 创建各节点 void Creat(int headX, int headY) { headNode = new Node; headNode->lastNode = nullptr; headNode->nextNode = nullptr; headNode->x = headX; headNode->y = headY; Node* temp = headNode; for (int i = 0; i < nNode - 1; i++) { Node* newNode = new Node; newNode->lastNode = temp; newNode->x = temp->x; newNode->y = temp->y; temp->nextNode = newNode; temp = newNode; } temp->nextNode = nullptr; tailNode = temp; temp = nullptr; } // 绘制蛇身 void ShowBody() { int n = nNode % 2; Node* temp = tailNode; while (temp != headNode) { if (temp->x - nodeSize / 2 + mapX >= nodeSize * -1 && temp->x - nodeSize / 2 + mapX <= ScreenWidth && temp->y - nodeSize / 2 + mapY >= nodeSize / -2 && temp->y - nodeSize / 2 + mapY <= ScreenHeight) putTimage(temp->x - nodeSize / 2 + mapX, temp->y - nodeSize / 2 + mapY, n % 2 == 0 ? imgNode : imgTail); temp = temp->lastNode; n++; } if (temp->x - nodeSize / 2 + mapX >= nodeSize * -1 && temp->x - nodeSize / 2 + mapX <= ScreenWidth && temp->y - nodeSize / 2 + mapY >= nodeSize / -2 && temp->y - nodeSize / 2 + mapY <= ScreenHeight) putTimage(temp->x - nodeSize / 2 + mapX, temp->y - nodeSize / 2 + mapY, imgHead); temp = nullptr; } // 刷新数据 void FlushData(short& n, int& dx, int& dy) { if (n == frame) { nodeMsg = new POINT[nNode]; Node* temp = headNode; int i = 0; while (temp != nullptr) { nodeMsg[i].x = temp->x; nodeMsg[i].y = temp->y; temp = temp->nextNode; i++; } } Node* temp = tailNode; int i = nNode - 2; while (temp != headNode) { if (n == 1) { temp->x = nodeMsg[i].x; temp->y = nodeMsg[i].y; } else { temp->x += int(stepLen * cos(atan2(nodeMsg[i].y - temp->y, nodeMsg[i].x - temp->x))); temp->y += int(stepLen * sin(atan2(nodeMsg[i].y - temp->y, nodeMsg[i].x - temp->x))); } temp = temp->lastNode; i--; } temp = nullptr; headNode->x += dx; headNode->y += dy; if (n == 1) { delete[] nodeMsg; nodeMsg = nullptr; } } // 据长度添加节点 void SetNode(int ex = 0) { int n = nNode; length += ex; n = length / 5 - n; if (n > 0) { while (nNode < maxNode && n != 0) { n--; nNode++; Node* newNode = new Node; newNode->lastNode = tailNode; newNode->nextNode = nullptr; newNode->x = tailNode->x; newNode->y = tailNode->y; tailNode->nextNode = newNode; tailNode = newNode; newNode = nullptr; } } else if (n < 0) { while (nNode > 10 && n != 0) { n++; nNode--; Node* temp = tailNode; tailNode = tailNode->lastNode; tailNode->nextNode = nullptr; delete temp; temp = nullptr; } if (nNode == 10) length = 50; } } // 吃到食物 bool GetFood(int k, int x, int y) { int len = nodeGap + 5; if (x - headNode->x < len && headNode->x - x < len && y - headNode->y < len && headNode->y - y < len) { food[k].x = rand() % (imgMap->getwidth() - xSide * 2) + xSide; food[k].y = rand() % (imgMap->getheight() - ySide * 2) + ySide; food[k].r = rand() % 2 + 3; food[k].c = HSVtoRGB(float(rand() % 360), rand() % 1000 / 2000.0f + 0.5f, rand() % 1000 / 2000.0f + 0.5f); return true; } return false; }protected: long length; int nNode; int maxNode; IMAGE* imgHead, *imgNode, *imgTail; POINT* nodeMsg;public: // 蛇节点 typedef struct _NODE { int x; int y; struct _NODE* lastNode; struct _NODE* nextNode; }Node; bool isDead; static int Count; Node* headNode, *tailNode;};int SnakeBase::Count = 0;// Player 类 //class Player :public SnakeBase{ public: // 构造函数 Player() :SnakeBase() { SetImage(playerColor0, playerColor1, playerColor2); int headX = rand() % (imgMap->getwidth() - xSide * 2 - nodeSize * 10) + xSide + nodeSize; int headY = rand() % (imgMap->getheight() - ySide * 2 - nodeSize * 12) + ySide + nodeSize; Creat(headX, headY); nt = frame; } // 是否死亡 void IsDead() { if (headNode->x <= xSide || headNode->x >= imgMap->getwidth() - 1 - xSide || headNode->y <= ySide || headNode->y >= imgMap->getheight() - 1 - ySide) isDead = true; else { double radian = atan2(headNode->y - headNode->nextNode->y, headNode->x - headNode->nextNode->x); int x = ScreenWidth / 2 + int(nodeSize / 2 * cos(radian)); int y = ScreenHeight / 2 + int(nodeSize / 2 * sin(radian)); COLORREF c = getpixel(x, y); if (!(c == mapLineColor || c == mapMainColor)) isDead = true; else { for (int i = 1; i < 8; i++) { x = ScreenWidth / 2 + int(nodeSize / 2 * cos(radian)); y = ScreenHeight / 2 + int(nodeSize / 2 * sin(radian)); c = getpixel(x, y); if (!(c == mapLineColor || c == mapMainColor)) { isDead = true; break; } x = ScreenWidth / 2 + int(nodeSize / 2 * cos(radian)); y = ScreenHeight / 2 + int(nodeSize / 2 * sin(radian)); c = getpixel(x, y); if (!(c == mapLineColor || c == mapMainColor)) { isDead = true; break; } } } } } // 移动 void Move(int& ex, int& mapX, int& mapY, short& ddx, short& ddy) { int dx = int(stepLen * cos(atan2(ddy, ddx))); int dy = int(stepLen * sin(atan2(ddy, ddx))); mapX -= dx; mapY -= dy; FlushData(nt, dx, dy); for (int k = 0; k < nFood; k++) if (GetFood(k, food[k].x, food[k].y)) ex += food[k].r / 2; nt--; if (nt <= 0) { nt = frame; while (MouseHit()) msg = GetMouseMsg(); ddx = msg.x - ScreenWidth / 2; ddy = msg.y - ScreenHeight / 2; SetNode(ex); ex = 0; } } // 打印成绩 void Print() { settextcolor(BLACK); settextstyle(20, 0, _T("微软雅黑")); TCHAR str[32] = { 0 }; _stprintf_s(str, _T("长度:%d"), length); outtextxy(5, 5, str); _stprintf_s(str, _T("节点:%d"), nNode); outtextxy(5, 25, str); _stprintf_s(str, _T("击杀:%d"), killCount); outtextxy(5, 45, str); }public: short nt;};// AI 类 //class AI :public SnakeBase{ public: // 构造函数 AI(Player* player) { p = player; COLORREF c0 = HSVtoRGB(float(rand() % 360), 1.0f, 0.4f); COLORREF c1 = HSVtoRGB(float(rand() % 360), 1.0f, 0.8f); COLORREF c2 = HSVtoRGB(float(rand() % 360), 1.0f, 1.0f); SetImage(c0, c1, c2); int headX = 0; int headY = 0; do { headX = rand() % (imgMap->getwidth() - xSide * 2 - nodeSize * 3) + xSide + nodeSize; headY = rand() % (imgMap->getheight() - ySide * 2 - nodeSize * 5) + ySide + nodeSize; } while (IsInPlayer(headX, headY)); Creat(headX, headY); nt = frame; minLine = 3 * frame; curLine = minLine + (rand() % 12) * frame; ddx = (rand() % ScreenWidth) * (rand() % 1000 < 500 ? 1 : -1); ddy = (rand() % ScreenHeight) * (rand() % 1000 < 500 ? 1 : -1); dx = int(stepLen * cos(atan2(ddy, ddx))); dy = int(stepLen * sin(atan2(ddy, ddx))); isFast = false; ct = 0; exp = 0; } // 是否死亡 bool IsDead(int& ex) { if (headNode->x <= xSide || headNode->x >= imgMap->getwidth() - 1 - xSide || headNode->y <= ySide || headNode->y >= imgMap->getheight() - 1 - ySide) return true; else { bool is_dead = IsInPlayer(headNode->x, headNode->y); if (is_dead) { killCount++; ex += nNode; } return is_dead; } } // 位置是否接触 player bool IsInPlayer(int headX, int headY) { Node* temp = p->headNode; while (temp != nullptr) { if (headX - temp->x < nodeSize && temp->x - headX < nodeSize && headY - temp->y < nodeSize && temp->y - headY < nodeSize) return true; temp = temp->nextNode; } return false; } // 移动 void Move(int& ex) { if (curLine <= 0) { double rad = atan2(ddy, ddx); isFast = rand() % 1200 < 100 ? true : false; curLine = minLine + (rand() % 21) * frame; do { ddx = (rand() % ScreenWidth + 40) * (rand() % 1000 < 500 ? 1 : -1); ddy = (rand() % ScreenHeight + 30) * (rand() % 1000 < 500 ? 1 : -1); } while (fabs(atan2(ddy, ddx) - rad) > PI / 6 * 5); dx = int(stepLen * cos(atan2(ddy, ddx))); dy = int(stepLen * sin(atan2(ddy, ddx))); } FlushData(nt, dx, dy); for (int k = 0; k < nFood; k++) if (GetFood(k, food[k].x, food[k].y)) exp += food[k].r / 2; nt--; curLine--; if (nt <= 0) { nt = frame; if (rand() % 1000 < 120) { SetNode(rand() % 5 + exp); exp = 0; } if (rand() % 1000 < 80) curLine = 0; if (rand() % 1000 < 900 && curLine > 0) // 90% 概率避免出界 { int deadX = headNode->x + int((rand() % 50 + nodeGap) * cos(atan2(ddy, ddx))); int deadY = headNode->y + int((rand() % 50 + nodeGap) * sin(atan2(ddy, ddx))); if (deadX <= xSide || deadX >= imgMap->getwidth() - 1 - xSide) { ddx *= -1; ddy += rand() % 30 + 30; } if (deadY <= ySide || deadY >= imgMap->getheight() - 1 - ySide) { ddy *= -1; ddx += rand() % 40 + 40; } dx = int(stepLen * cos(atan2(ddy, ddx))); dy = int(stepLen * sin(atan2(ddy, ddx))); } if (rand() % 1000 < rand() % 400 + 400 && curLine > 0) // 40%-80%(也可以理解为60%) 概率躲避 player { int deadX = headNode->x + int((nodeGap + 5) * cos(atan2(ddy, ddx))); int deadY = headNode->y + int((nodeGap + 5) * sin(atan2(ddy, ddx))); if (IsInPlayer(deadX, deadY)) { ddx = -1 * ddx + rand() % 40 + 40; ddy = -1 * ddy + rand() % 30 + 30; } dx = int(stepLen * cos(atan2(ddy, ddx))); dy = int(stepLen * sin(atan2(ddy, ddx))); } } isDead = IsDead(ex); if (isDead) { int n = nNode / 2; Node* temp = headNode->nextNode->nextNode; while (n--) { for (int k = 0; k < nFood; k++) { if (food[k].x < -1 * mapX - nodeSize * 2 || food[k].x > -1 * mapX + nodeSize * 2 + ScreenWidth || food[k].y < -1 * mapY - nodeSize * 2 || food[k].y > -1 * mapY + nodeSize * 2 + ScreenHeight) { food[k].x = temp->x + (rand() % 5 + 3) * (rand() % 100 < 50 ? 1 : -1); food[k].y = temp->y + (rand() % 5 + 3) * (rand() % 100 < 50 ? 1 : -1); food[k].r = nodeSize / 2; food[k].c = HSVtoRGB(float(rand() % 360), rand() % 1000 / 2000.0f + 0.5f, rand() % 1000 / 2000.0f + 0.5f); break; } else if (k == nFood - 1) { n = 0; break; } } temp = temp->nextNode; } temp = nullptr; } }private: short nt; int minLine; int curLine; int ddx, ddy; int dx, dy; Player* p;public: bool isFast; clock_t ct; int exp;};// 游戏主体类 //class Game{ public: // 构造函数 Game() { flushTime = 32; setbkmode(TRANSPARENT); BeginBatchDraw(); SetWorkingImage(imgMap); setlinecolor(mapLineColor); setfillcolor(mapMainColor); setbkcolor(BROWN); cleardevice(); solidrectangle(xSide, ySide, imgMap->getwidth() - 1 - xSide, imgMap->getheight() - 1 - ySide); for (int i = gap + xSide; i < imgMap->getwidth() - xSide; i += gap) line(i, ySide, i, imgMap->getheight() - 1 - ySide); for (int j = gap + ySide; j < imgMap->getheight() - ySide; j += gap) line(xSide, j, imgMap->getwidth() - 1 - xSide, j); SetWorkingImage(); player = new Player; mapX = -1 * (player->headNode->x - ScreenWidth / 2); mapY = -1 * (player->headNode->y - ScreenHeight / 2); ai = new Ai; ai->ais = new AI(player); Ai* temp = ai; for (int i = 1; i < 15; i++) { temp->next = new Ai; temp = temp->next; temp->ais = new AI(player); } temp->next = ai; temp = nullptr; food = new Food[nFood]; for (int k = 0; k < nFood; k++) { food[k].x = rand() % (imgMap->getwidth() - xSide * 2) + xSide; food[k].y = rand() % (imgMap->getheight() - ySide * 2) + ySide; food[k].r = rand() % 2 + 3; food[k].c = HSVtoRGB(float(rand() % 360), rand() % 1000 / 2000.0f + 0.5f, rand() % 1000 / 2000.0f + 0.5f); } Draw(); outtextxy((ScreenWidth - textwidth(_T("左键或右键加速"))) / 2, ScreenHeight / 4 * 3 + 25, _T("左键或右键加速")); outtextxy((ScreenWidth - textwidth(_T("按任意键继续"))) / 2, ScreenHeight / 4 * 3 + 50, _T("按任意键继续")); FlushBatchDraw(); ret = _getwch(); } // 析构函数 ~Game() { EndBatchDraw(); delete imgMap; delete player; if (ai != nullptr) { Ai* temp = ai->next; while (temp != ai) { delete temp->ais; temp = temp->next; } delete ai->ais; delete ai; } } // 绘制食物 void DrawFood() { for (int k = 0; k < nFood; k++) { if (food[k].x - 9 + mapX >= 0 && food[k].x - 9 + mapX <= ScreenWidth && food[k].y - 9 + mapY >= 0 && food[k].y - 9 + mapY <= ScreenHeight) { if (food[k].r < 6) { setfillcolor(food[k].c); solidcircle(food[k].x + mapX, food[k].y + mapY, food[k].r); } else { SetWorkingImage(&imgFood); cleardevice(); setfillcolor(food[k].c); solidcircle(nodeSize / 2, nodeSize / 2, nodeSize / 2); setfillcolor(BLACK); solidcircle(nodeSize / 2, nodeSize / 2, nodeSize / 5); SetWorkingImage(); putTimage(food[k].x - nodeSize / 2 + mapX, food[k].y - nodeSize / 2 + mapY, &imgFood); } } } } // 绘制界面 void Draw() { putimage(mapX, mapY, imgMap); DrawFood(); Ai* temp = ai->next; while (temp != ai) { temp->ais->ShowBody(); temp = temp->next; } temp->ais->ShowBody(); player->IsDead(); player->ShowBody(); player->Print(); FlushBatchDraw(); } // 运行 void Running() { short ddx = 0; short ddy = 0; time_t ct = clock() - time_t(100); Ai* temp = ai->next; while (temp != ai) { temp->ais->ct = clock(); temp = temp->next; } temp->ais->ct = clock(); temp = temp->next; int ex = 0; bool isFast = false; while (!(GetAsyncKeyState(VK_ESCAPE) & 0x8000) && !player->isDead) { if (GetAsyncKeyState('P') & 0x8000) ret = _getwch(); if (clock() - ct > flushTime) { ct = clock(); isFast = (msg.mkLButton || msg.mkRButton) ? true : false; player->Move(ex, mapX, mapY, ddx, ddy); if (isFast) { player->Move(ex, mapX, mapY, ddx, ddy); if (rand() % 1000 < 100) ex -= 2; } Draw(); } if (clock() - temp->ais->ct > flushTime && !player->isDead) { temp->ais->ct = clock(); temp->ais->Move(ex); if (temp->ais->isDead) { delete temp->ais; temp->ais = new AI(player); temp->ais->ct = clock(); } else if (temp->ais->isFast) { temp->ais->Move(ex); if (temp->ais->isDead) { delete temp->ais; temp->ais = new AI(player); temp->ais->ct = clock(); } } ai = ai->next; temp = temp->next; } } }private: int flushTime; Player* player; typedef struct _AI { AI* ais; struct _AI* next; }Ai; Ai* ai;};// 主函数 //int main(){ initgraph(ScreenWidth, ScreenHeight); srand((unsigned)time(NULL)); Game game; game.Running(); settextcolor(BLACK); settextstyle(50, 0, _T("微软雅黑")); outtextxy((ScreenWidth - textwidth(_T("游戏结束"))) / 2, ScreenHeight / 3, _T("游戏结束")); settextstyle(20, 0, _T("微软雅黑")); outtextxy((ScreenWidth - textwidth(_T("按任意键退出"))) / 2, ScreenHeight / 4 * 3 + 50, _T("按任意键退出")); FlushBatchDraw(); while (_kbhit()) ret = _getwch(); ret = _getwch(); closegraph(); return 0;}
赞!👍
https://blog.csdn.net/wangxun20081008/article/details/116170414
发表评论
最新留言
路过,博主的博客真漂亮。。
[***.116.15.85]2025年04月17日 08时10分56秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
第三周总结
2021-05-09
Spark学习之SparkStreaming
2021-05-09
AlwaysOn配置时在连接步骤时报错(35250)
2021-05-09
排序算法之总结
2021-05-09
微软云Linux服务器 Mysql、tomcat远程连接错误解决办法
2021-05-09
Java基础知识回顾之六 ----- IO流
2021-05-09
React 函数式组件的 Ref 和子组件访问(useImperativeHandle)
2021-05-09
Python数据分析(二): Numpy技巧 (2/4)
2021-05-09
09 . Python3之常用模块
2021-05-09
Python学习笔记-StatsModels 统计回归(3)模型数据的准备
2021-05-09
Velocity.js初步
2021-05-09
Ubuntu16.04中php如何切换版本
2021-05-09
nginx上配置phpmyadmin
2021-05-09
HustOJ二次开发之修改数据库连接池
2021-05-09
SpringBoot之整合Dubbo
2021-05-09
Drools之关键字及错误信息
2021-05-09
h5做的app和原生app的区别
2021-05-09
嘿!为你的应用创建滚动日志吧?
2021-05-09