这是一个贪吃蛇大作战类游戏,修改特性为 AI 不互杀;
该程序有四个类:蛇基类 SnakeBase,玩家类 Player,AI 类,Game 类;SnakeBase 和 AI 均继承自 SnakeBase,SnakeBase 提供基础接口;
Game 提供数据初始化以及游戏主循环。
程序设计之初,采用“实运行”方式,即所有动画均在地图 imgMap 上真实绘制,这样却有一些严重的问题,需要不断地记录节点背景以恢复节点经过的地方等。
后修改为“虚运行”方式,判断位置是否可视后直接绘至窗口内,这样大大降低时间开销,同时不会因为蛇靠近而产生不可擦除的颜色。
至于蛇运行,采用位置继承,即节点下一个位置为上一个节大流量卡点的位置,但是会发现跨度太大,解决办法是在这个跨度中插入适当帧数。
执行效果如下:
完整的游戏源代码如下:
/* 程序名称: 贪吃蛇大作战 简介: 简单模拟了游戏 贪吃蛇大作战 程序运行中可以通过 ESC 直接退出 左键或右键可以加速移动,加速会消耗长度 环境: VS2019 + EasyX_20190529(beta) 作者: Teternity(QQ:1926594835) */ ////////// 头文件 大流量卡////////// #include <easyx.h> #include <conio.h> #include <cstdio> #include <ctime> #include <cmath> #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; i大流量卡nt 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); constCOLORREF 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, inty, 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 = 50 + rand() % 50; 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 = newNode; newNode->lastNode = temp; newNode->x = t大流量卡emp->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 >= no大流量卡deSize *-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(t大流量卡emp->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; inti =大流量卡 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;}N大流量卡ode;bool isDead; static int Count; Node* headNode, * tailNode; }; int SnakeBase::Count = 0; ////////// Player 类 ////////// class Player :public SnakeBase { public: // 构造函数 Player() :SnakeBase() { SetImage(playerColor0, playerColor1, playerColor2); intheadX = rand() % (imgMap->getwidt大流量卡h() – 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 <= ySi大流量卡de || 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, sh大流量卡ort& 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* pla大流量卡yer) { 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() – xSi大流量卡de *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() % Scree大流量卡nWidth) * (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) { doub大流量卡le 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); d大流量卡x =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(stepL大流量卡en *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 + nod大流量卡eSize *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 = HSVto大流量卡RGB(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; intdx, 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); setbkcol大流量卡or(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 (intj = 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 (inti 大流量卡=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(“左键或右键加速”)); out大流量卡textxy((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) { deletetemp->ais; t大流量卡emp = 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); Se大流量卡tWorkingImage(); 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 = newAI(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; }大家赶紧去动手试试吧!
此外,我也给大家分享我收集的其他资源,从最零基础开始的教程到C语言C++项目案例,帮助大家在学习C语言的道路上披荆斩棘!
编程学习书籍分享:
编程学习视频分享:
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!
对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】大流量卡一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!
友情提醒: 请添加客服微信进行免费领取流量卡!
QQ交流群:226333560 站长微信:qgzmt2
原创文章,作者:sunyaqun,如若转载,请注明出处:https://www.dallk.cn/33484.html