龙空技术网

【项目实战】轻松实现C/C++大作业:贪吃蛇大作战游戏!

C语言编程 217

前言:

目前兄弟们对“c语言第三次作业”都比较关心,咱们都需要剖析一些“c语言第三次作业”的相关文章。那么小编在网摘上搜集了一些对于“c语言第三次作业””的相关资讯,希望朋友们能喜欢,兄弟们一起来学习一下吧!

这是一个贪吃蛇大作战类游戏,修改特性为 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;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 = 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 = 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;}

大家赶紧去动手试试吧!

此外,我也给大家分享我收集的其他资源,从最零基础开始的教程到C语言C++项目案例,帮助大家在学习C语言的道路上披荆斩棘!

编程学习书籍分享:

编程学习视频分享:

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!

标签: #c语言第三次作业