C 实现植物大战僵尸(一)

打印 上一主题 下一主题

主题 859|帖子 859|积分 2577

C 实现植物大战僵尸(一)

对应资源链接,C语言项目:完整版植物大战僵尸
以下内容为个人实现版,与原 UP 主项目代码内容有收支,进步了些可读和简便性
一 创建主场景

安装 easyx 库,easyx 官网
主场景代码
  1. #include <stdio.h>
  2. #include <graphics.h>        // 引用图形库头文件
  3. /*
  4.     开发日志
  5.     1、创建新项,导入素材,实现开始游戏场景
  6. */
  7. #define WIN_WIDTH 900
  8. #define WIN_HIGHT 600
  9. IMAGE imgBg; //背景图片
  10. void gameInit()
  11. {
  12.     //加载背景图片
  13.     loadimage(&imgBg, "res/map0.jpg");
  14.    
  15.     //创建游戏图形窗口
  16.     initgraph(WIN_WIDTH, WIN_HIGHT);
  17. }
  18. void updateWindow()
  19. {
  20.     //渲染背景图至窗口
  21.     putimage(0, 0, &imgBg);
  22. }
  23. int main()
  24. {
  25.     gameInit();
  26.     updateWindow();
  27.     system("pause");
  28.     return 0;
  29. }
复制代码
设置项目字符集
VS中多字节字符集和UNICODE字符集的使用说明,如果要兼容 C 编程,只能使用多字节字符集。这里的兼容 C 编程,主要就是指 WindowsAPI 编程

遇到的小问题 VS 无法打开 .exe 文件举行写入
通常是因为上次运行的进程未关闭,导致文件仍被占用。当尝试运行或重新编译一个程序时,如果上一次天生的 exe 文件仍在运行,VS2022 无法对其举行写入操纵‌
二 实现植物卡牌

  1. #include <stdio.h>
  2. #include <graphics.h>        // 引用图形库头文件
  3. #include "tools.h"
  4. /*
  5.     开发日志
  6.     1、创建新项,导入素材,实现开始游戏场景
  7.     2、实现游戏顶部工具栏和植物卡牌
  8. */
  9. #define WIN_WIDTH 900
  10. #define WIN_HIGHT 600
  11. enum PLANT_CARDS{ PEA, SUNFLOWER, PLANT_CNT}; //使用 PLANT_CNT 统计 PLANT 总数
  12. IMAGE imgBg; //背景图片
  13. IMAGE imgBar; //工具栏图片
  14. IMAGE imgCards[PLANT_CNT]; //植物卡片
  15. void gameInit()
  16. {
  17.     //加载背景图片
  18.     loadimage(&imgBg, "res/map0.jpg");
  19.     loadimage(&imgBar, "res/bar5.png");
  20.     //加载植物卡片
  21.     char name[64];
  22.     for (int i = 0;i < PLANT_CNT;++i)
  23.     {
  24.         //获取植物卡片相对路径名称
  25.         sprintf(name, "res/Cards/card_%d.png", i + 1);
  26.         loadimage(&imgCards[i], name);
  27.     }
  28.     //创建游戏图形窗口
  29.     initgraph(WIN_WIDTH, WIN_HIGHT);
  30. }
  31. void updateWindow()
  32. {
  33.     //渲染背景图至窗口
  34.     putimage(0, 0, &imgBg);
  35.     putimagePNG(250, 0, &imgBar);
  36.     //渲染植物卡牌
  37.     for (int i = 0;i < PLANT_CNT;++i)
  38.         //卡片宽度约 65
  39.         putimage(338 + i * 65, 6, &imgCards[i]);
  40. }
  41. int main()
  42. {
  43.     gameInit();
  44.     updateWindow();
  45.     system("pause");
  46.     return 0;
  47. }
复制代码
运行结果

遇到的小问题 图片加载不出来,DEBUG 图片的相对路径
三 实现植物的选择和拖动

  1. #include <stdio.h>
  2. #include <graphics.h>        // 引用图形库头文件
  3. #include "tools.h"
  4. /*
  5.     开发日志
  6.     1、创建新项,导入素材,实现开始游戏场景
  7.     2、实现游戏顶部工具栏和植物卡牌
  8.     3、实现植物的选择和拖动
  9. */
  10. #define WIN_WIDTH 900
  11. #define WIN_HIGHT 600
  12. #define MAX_PICTURE_NUM 20
  13. #define PIC_LEFT_MARGIN 338
  14. #define PIC_WIDTH 65
  15. int currX = 0, currY = 0, currIndex = -1;
  16. enum PLANT_CARDS{ PEA, SUNFLOWER, PLANT_CNT}; //使用 PLANT_CNT 统计 PLANT 总数
  17. IMAGE imgBg; //背景图片
  18. IMAGE imgBar; //工具栏图片
  19. IMAGE imgCards[PLANT_CNT]; //植物卡片
  20. /* 这里也可使用二维数组, 但会存在浪费空间的问题 */
  21. IMAGE* imgPlant[PLANT_CNT][MAX_PICTURE_NUM]; //动态植物素材
  22. bool fileExist(const char* name)
  23. {
  24.     FILE* file = NULL;
  25.     if (file = fopen(name,"r"))
  26.         fclose(file);
  27.     return file == NULL ? false : true;
  28. }
  29. void gameInit()
  30. {
  31.     //加载背景图片
  32.     loadimage(&imgBg, "res/map0.jpg");
  33.     loadimage(&imgBar, "res/bar5.png");
  34.     //加载植物卡片
  35.     char name[64];
  36.     //将二维指针数组内存空间置零
  37.     memset(imgPlant, 0, sizeof(imgPlant));
  38.     for (int i = 0; i < PLANT_CNT; ++i)
  39.     {
  40.         //获取植物卡片相对路径名称
  41.         sprintf(name, "res/Cards/card_%d.png", i + 1);
  42.         loadimage(&imgCards[i], name);
  43.         for (int j = 0;i < MAX_PICTURE_NUM; ++j)
  44.         {
  45.             //获取动态植物素材相对路径名称
  46.             sprintf(name, "res/Plants/%d/%d.png", i, j + 1);
  47.             if (fileExist(name)) {
  48.                 imgPlant[i][j] = new IMAGE;
  49.                 loadimage(imgPlant[i][j], name);
  50.             }
  51.             else break;
  52.         }
  53.     }
  54.     //创建游戏图形窗口
  55.     initgraph(WIN_WIDTH, WIN_HIGHT, 1);
  56. }
  57. void updateWindow()
  58. {
  59.     //使用双缓冲, 解决输出窗口闪屏
  60.     BeginBatchDraw();
  61.     //渲染背景图至窗口
  62.     putimage(0, 0, &imgBg);
  63.     putimagePNG(250, 0, &imgBar);
  64.     //渲染植物卡牌
  65.     for (int i = 0;i < PLANT_CNT;++i)
  66.         putimage(PIC_LEFT_MARGIN + i * PIC_WIDTH, 6, &imgCards[i]);
  67.    
  68.     //渲染 当前拖动的植物
  69.     if (currIndex >= 0)
  70.     {
  71.         IMAGE* currImage = imgPlant[currIndex][0];
  72.         putimagePNG(currX - currImage->getwidth() / 2,
  73.             currY - currImage->getheight() / 2, currImage);
  74.     }
  75.     EndBatchDraw(); //结束双缓冲
  76. }
  77. void userClick()
  78. {
  79.     ExMessage msg; //创建消息体
  80.     /* 拖动需先左键点击再拖动 */
  81.     static int status = 0; //种植植物必须先选中再拖动
  82.     if (peekmessage(&msg)) //该函数用于获取一个消息,并立即返回
  83.     {
  84.         if (msg.message == WM_LBUTTONDOWN) //鼠标点击
  85.         {
  86.             if (msg.x > PIC_LEFT_MARGIN &&
  87.                 msg.x < PIC_LEFT_MARGIN + PLANT_CNT * PIC_WIDTH &&
  88.                 msg.y < 96)
  89.             {
  90.                 currX = msg.x, currY = msg.y;
  91.                 currIndex = (msg.x - PIC_LEFT_MARGIN) / PIC_WIDTH;
  92.                 status = 1;
  93.             }
  94.         }
  95.         else if (msg.message == WM_MOUSEMOVE && status == 1) //鼠标拖动
  96.         {
  97.             currX = msg.x, currY = msg.y; //记录当前拖动位置
  98.         }
  99.         else if (msg.message == WM_LBUTTONUP) //鼠标抬起
  100.         {
  101.         }
  102.     }
  103. }
  104. int main()
  105. {
  106.     gameInit();
  107.     while (1)
  108.     {
  109.         userClick(); //监听窗口鼠标事件
  110.         updateWindow(); //更新窗口视图
  111.     }
  112.     system("pause");
  113.     return 0;
  114. }
复制代码
运行结果

遇到的小问题 拖动植物位置不对,检查渲染当前拖动植物的参数坐标,以及监听鼠标点击变乱时,坐标需要设置
四 实现植物莳植和摇晃

  1. #include <stdio.h>
  2. #include <graphics.h>        // 引用图形库头文件
  3. #include "tools.h"
  4. /*
  5.     开发日志
  6.     1、创建新项,导入素材,实现开始游戏场景
  7.     2、实现游戏顶部工具栏和植物卡牌
  8.     3、实现植物的选择和拖动
  9.     4、实现植物的种植和摇摆
  10. */
  11. #define WIN_WIDTH 900
  12. #define WIN_HIGHT 600
  13. #define MAX_PICTURE_NUM 20
  14. #define PIC_LEFT_MARGIN 338
  15. #define PIC_WIDTH 65
  16. #define GRASS_LEFT_MARGIN 252
  17. #define GRASS_TOP_MARGIN 82
  18. #define GRASS_GRID_ROW 5
  19. #define GRASS_GRID_COL 9
  20. #define GRASS_GRID_HIGHT 98  // (570 - 82) / 5
  21. #define GRASS_GRID_WIDTH 81  //(984 - 252) / 9
  22. int currX = 0, currY = 0, currIndex = -1;
  23. enum PLANT_CARDS{ PEA, SUNFLOWER, PLANT_CNT}; //使用 PLANT_CNT 统计 PLANT 总数
  24. IMAGE imgBg; //背景图片
  25. IMAGE imgBar; //工具栏图片
  26. IMAGE imgCards[PLANT_CNT]; //植物卡片
  27. /* 这里也可使用二维数组, 但会存在浪费空间的问题 */
  28. IMAGE* imgPlant[PLANT_CNT][MAX_PICTURE_NUM]; //动态植物素材
  29. typedef struct Plant
  30. {
  31.     int type;     //植物类型, -1 表示草地
  32.     int frameId;  //表示植物摆动帧
  33. }Plant;
  34. Plant plants[GRASS_GRID_ROW][GRASS_GRID_COL];
  35. bool fileExist(const char* name)
  36. {
  37.     FILE* file = NULL;
  38.     if (file = fopen(name,"r"))
  39.         fclose(file);
  40.     return file == NULL ? false : true;
  41. }
  42. void gameInit()
  43. {
  44.     //加载背景图片
  45.     loadimage(&imgBg, "res/map0.jpg");
  46.     loadimage(&imgBar, "res/bar5.png");
  47.     //加载植物卡片
  48.     char name[64];
  49.     //将二维指针数组内存空间置零
  50.     memset(imgPlant, 0, sizeof(imgPlant));
  51.     memset(plants, -1, sizeof(plants));
  52.     for (int i = 0; i < PLANT_CNT; ++i)
  53.     {
  54.         //获取植物卡片相对路径名称
  55.         sprintf(name, "res/Cards/card_%d.png", i + 1);
  56.         loadimage(&imgCards[i], name);
  57.         for (int j = 0;i < MAX_PICTURE_NUM; ++j)
  58.         {
  59.             //获取动态植物素材相对路径名称
  60.             sprintf(name, "res/Plants/%d/%d.png", i, j + 1);
  61.             if (fileExist(name)) {
  62.                 imgPlant[i][j] = new IMAGE;
  63.                 loadimage(imgPlant[i][j], name);
  64.             }
  65.             else break;
  66.         }
  67.     }
  68.     //创建游戏图形窗口
  69.     initgraph(WIN_WIDTH, WIN_HIGHT, 1);
  70. }
  71. void updateWindow()
  72. {
  73.     //使用双缓冲, 解决输出窗口闪屏
  74.     BeginBatchDraw();
  75.     //渲染背景图至窗口
  76.     putimage(0, 0, &imgBg);
  77.     putimagePNG(250, 0, &imgBar);
  78.     //渲染植物卡牌
  79.     for (int i = 0;i < PLANT_CNT;++i)
  80.         putimage(PIC_LEFT_MARGIN + i * PIC_WIDTH, 6, &imgCards[i]);
  81.     //渲染种植植物
  82.     for (int i = 0; i < GRASS_GRID_ROW; ++i)
  83.     {
  84.         for (int j = 0; j < GRASS_GRID_COL; ++j)
  85.         {
  86.             if (plants[i][j].type >= 0)
  87.             {
  88.                 putimagePNG(GRASS_LEFT_MARGIN + j * GRASS_GRID_WIDTH + 5, //微调植物种植位置
  89.                             GRASS_TOP_MARGIN + i * GRASS_GRID_HIGHT + 10,
  90.                             imgPlant[plants[i][j].type][plants[i][j].frameId]);
  91.             }
  92.         }
  93.     }
  94.     //渲染当前拖动的植物
  95.     if (currIndex >= 0)
  96.     {
  97.         IMAGE* currImage = imgPlant[currIndex][0];
  98.         putimagePNG(currX - currImage->getwidth() / 2,
  99.             currY - currImage->getheight() / 2, currImage);
  100.     }
  101.     EndBatchDraw(); //结束双缓冲
  102. }
  103. void userClick()
  104. {
  105.     ExMessage msg; //创建消息体
  106.     /* 拖动需先左键点击再拖动 */
  107.     static int status = 0; //种植植物必须先选中再拖动
  108.     if (peekmessage(&msg)) //该函数用于获取一个消息,并立即返回
  109.     {
  110.         if (msg.message == WM_LBUTTONDOWN) //鼠标点击
  111.         {
  112.             if (msg.x > PIC_LEFT_MARGIN &&
  113.                 msg.x < PIC_LEFT_MARGIN + PLANT_CNT * PIC_WIDTH &&
  114.                 msg.y < 96)
  115.             {
  116.                 currX = msg.x, currY = msg.y;
  117.                 currIndex = (msg.x - PIC_LEFT_MARGIN) / PIC_WIDTH;
  118.                 status = 1;
  119.             }
  120.         }
  121.         else if (msg.message == WM_MOUSEMOVE && status == 1) //鼠标拖动
  122.         {
  123.             currX = msg.x, currY = msg.y; //记录当前拖动位置
  124.         }
  125.         else if (msg.message == WM_LBUTTONUP) //鼠标抬起
  126.         {
  127.             //当植物拖到至草地位置终止, 则种植植物
  128.             if (msg.x >= GRASS_LEFT_MARGIN &&
  129.                 msg.x <= GRASS_LEFT_MARGIN + GRASS_GRID_COL * GRASS_GRID_WIDTH &&
  130.                 msg.y >= GRASS_TOP_MARGIN &&
  131.                 msg.y <= GRASS_TOP_MARGIN + GRASS_GRID_ROW * GRASS_GRID_HIGHT)
  132.             {
  133.                 int x = (msg.y - GRASS_TOP_MARGIN) / GRASS_GRID_HIGHT;  //计算第几行
  134.                 int y = (msg.x - GRASS_LEFT_MARGIN) / GRASS_GRID_WIDTH; //计算第几列
  135.    
  136.                 //未点击植物或当前位置已种植过植物,则不种植植物
  137.                 if (plants[x][y].type < 0 && status == 1)
  138.                 {
  139.                     plants[x][y].type = currIndex;
  140.                     plants[x][y].frameId = 0;
  141.                 }
  142.                 //printf("x = %d  y = %d \n", x, y); -- DEBUG
  143.             }
  144.             status = 0, currIndex = -1; //停止拖动当前植物
  145.         }
  146.     }
  147. }
  148. void updateGame()
  149. {
  150.     //遍历种植植物数组, 更新摆动帧
  151.     for (int i = 0; i < GRASS_GRID_ROW; ++i)
  152.     {
  153.         for (int j = 0; j < GRASS_GRID_COL; ++j)
  154.         {
  155.             if (plants[i][j].type >= 0)
  156.             {
  157.                 if (imgPlant[plants[i][j].type][++plants[i][j].frameId] == NULL) //把 ++plants[i][j].frameId 合并在一条语句中
  158.                     plants[i][j].frameId = 0;
  159.             }
  160.         }
  161.     }
  162. }
  163. int main()
  164. {
  165.     gameInit();
  166.     updateWindow(); //窗口视图展示
  167.     int timer = 0; //用以计时 20 毫秒更新一次
  168.     while (1)
  169.     {
  170.         userClick(); //监听窗口鼠标事件
  171.         timer += getDelay();
  172.         if (timer > 20)
  173.         {
  174.             updateWindow(); //更新窗口视图
  175.             updateGame(); //更新游戏动画帧
  176.             timer = 0;
  177.         }
  178.     }
  179.     system("pause");
  180.     return 0;
  181. }
复制代码
运行结果

遇到的小问题
注意以下关于行列的偏移
  1. //计算第几行
  2. int x = (msg.y - GRASS_TOP_MARGIN) / GRASS_GRID_HIGHT;
  3. //计算第几列
  4. int y = (msg.x - GRASS_LEFT_MARGIN) / GRASS_GRID_WIDTH;
复制代码
以及渲染莳植植物时
  1. x = GRASS_LEFT_MARGIN + j * GRASS_GRID_WIDTH;
  2. y = GRASS_TOP_MARGIN + i * GRASS_GRID_HIGHT;
复制代码
非常容易搞混
小技巧
利用 timer 替换 Sleep 函数,进步程序运行服从;先渲染莳植植物,再渲染拖动植物,进步游戏观感

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

尚未崩坏

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表