C 实现植物大战僵尸(一)
对应资源链接,C语言项目:完整版植物大战僵尸
以下内容为个人实现版,与原 UP 主项目代码内容有收支,进步了些可读和简便性
一 创建主场景
安装 easyx 库,easyx 官网
主场景代码
- #include <stdio.h>
- #include <graphics.h> // 引用图形库头文件
- /*
- 开发日志
- 1、创建新项,导入素材,实现开始游戏场景
- */
- #define WIN_WIDTH 900
- #define WIN_HIGHT 600
- IMAGE imgBg; //背景图片
- void gameInit()
- {
- //加载背景图片
- loadimage(&imgBg, "res/map0.jpg");
-
- //创建游戏图形窗口
- initgraph(WIN_WIDTH, WIN_HIGHT);
- }
- void updateWindow()
- {
- //渲染背景图至窗口
- putimage(0, 0, &imgBg);
- }
- int main()
- {
- gameInit();
- updateWindow();
- system("pause");
- return 0;
- }
复制代码 设置项目字符集
VS中多字节字符集和UNICODE字符集的使用说明,如果要兼容 C 编程,只能使用多字节字符集。这里的兼容 C 编程,主要就是指 WindowsAPI 编程
遇到的小问题 VS 无法打开 .exe 文件举行写入
通常是因为上次运行的进程未关闭,导致文件仍被占用。当尝试运行或重新编译一个程序时,如果上一次天生的 exe 文件仍在运行,VS2022 无法对其举行写入操纵
二 实现植物卡牌
- #include <stdio.h>
- #include <graphics.h> // 引用图形库头文件
- #include "tools.h"
- /*
- 开发日志
- 1、创建新项,导入素材,实现开始游戏场景
- 2、实现游戏顶部工具栏和植物卡牌
- */
- #define WIN_WIDTH 900
- #define WIN_HIGHT 600
- enum PLANT_CARDS{ PEA, SUNFLOWER, PLANT_CNT}; //使用 PLANT_CNT 统计 PLANT 总数
- IMAGE imgBg; //背景图片
- IMAGE imgBar; //工具栏图片
- IMAGE imgCards[PLANT_CNT]; //植物卡片
- void gameInit()
- {
- //加载背景图片
- loadimage(&imgBg, "res/map0.jpg");
- loadimage(&imgBar, "res/bar5.png");
- //加载植物卡片
- char name[64];
- for (int i = 0;i < PLANT_CNT;++i)
- {
- //获取植物卡片相对路径名称
- sprintf(name, "res/Cards/card_%d.png", i + 1);
- loadimage(&imgCards[i], name);
- }
- //创建游戏图形窗口
- initgraph(WIN_WIDTH, WIN_HIGHT);
- }
- void updateWindow()
- {
- //渲染背景图至窗口
- putimage(0, 0, &imgBg);
- putimagePNG(250, 0, &imgBar);
- //渲染植物卡牌
- for (int i = 0;i < PLANT_CNT;++i)
- //卡片宽度约 65
- putimage(338 + i * 65, 6, &imgCards[i]);
- }
- int main()
- {
- gameInit();
- updateWindow();
- system("pause");
- return 0;
- }
复制代码 运行结果
遇到的小问题 图片加载不出来,DEBUG 图片的相对路径
三 实现植物的选择和拖动
- #include <stdio.h>
- #include <graphics.h> // 引用图形库头文件
- #include "tools.h"
- /*
- 开发日志
- 1、创建新项,导入素材,实现开始游戏场景
- 2、实现游戏顶部工具栏和植物卡牌
- 3、实现植物的选择和拖动
- */
- #define WIN_WIDTH 900
- #define WIN_HIGHT 600
- #define MAX_PICTURE_NUM 20
- #define PIC_LEFT_MARGIN 338
- #define PIC_WIDTH 65
- int currX = 0, currY = 0, currIndex = -1;
- enum PLANT_CARDS{ PEA, SUNFLOWER, PLANT_CNT}; //使用 PLANT_CNT 统计 PLANT 总数
- IMAGE imgBg; //背景图片
- IMAGE imgBar; //工具栏图片
- IMAGE imgCards[PLANT_CNT]; //植物卡片
- /* 这里也可使用二维数组, 但会存在浪费空间的问题 */
- IMAGE* imgPlant[PLANT_CNT][MAX_PICTURE_NUM]; //动态植物素材
- bool fileExist(const char* name)
- {
- FILE* file = NULL;
- if (file = fopen(name,"r"))
- fclose(file);
- return file == NULL ? false : true;
- }
- void gameInit()
- {
- //加载背景图片
- loadimage(&imgBg, "res/map0.jpg");
- loadimage(&imgBar, "res/bar5.png");
- //加载植物卡片
- char name[64];
- //将二维指针数组内存空间置零
- memset(imgPlant, 0, sizeof(imgPlant));
- for (int i = 0; i < PLANT_CNT; ++i)
- {
- //获取植物卡片相对路径名称
- sprintf(name, "res/Cards/card_%d.png", i + 1);
- loadimage(&imgCards[i], name);
- for (int j = 0;i < MAX_PICTURE_NUM; ++j)
- {
- //获取动态植物素材相对路径名称
- sprintf(name, "res/Plants/%d/%d.png", i, j + 1);
- if (fileExist(name)) {
- imgPlant[i][j] = new IMAGE;
- loadimage(imgPlant[i][j], name);
- }
- else break;
- }
- }
- //创建游戏图形窗口
- initgraph(WIN_WIDTH, WIN_HIGHT, 1);
- }
- void updateWindow()
- {
- //使用双缓冲, 解决输出窗口闪屏
- BeginBatchDraw();
- //渲染背景图至窗口
- putimage(0, 0, &imgBg);
- putimagePNG(250, 0, &imgBar);
- //渲染植物卡牌
- for (int i = 0;i < PLANT_CNT;++i)
- putimage(PIC_LEFT_MARGIN + i * PIC_WIDTH, 6, &imgCards[i]);
-
- //渲染 当前拖动的植物
- if (currIndex >= 0)
- {
- IMAGE* currImage = imgPlant[currIndex][0];
- putimagePNG(currX - currImage->getwidth() / 2,
- currY - currImage->getheight() / 2, currImage);
- }
- EndBatchDraw(); //结束双缓冲
- }
- void userClick()
- {
- ExMessage msg; //创建消息体
- /* 拖动需先左键点击再拖动 */
- static int status = 0; //种植植物必须先选中再拖动
- if (peekmessage(&msg)) //该函数用于获取一个消息,并立即返回
- {
- if (msg.message == WM_LBUTTONDOWN) //鼠标点击
- {
- if (msg.x > PIC_LEFT_MARGIN &&
- msg.x < PIC_LEFT_MARGIN + PLANT_CNT * PIC_WIDTH &&
- msg.y < 96)
- {
- currX = msg.x, currY = msg.y;
- currIndex = (msg.x - PIC_LEFT_MARGIN) / PIC_WIDTH;
- status = 1;
- }
- }
- else if (msg.message == WM_MOUSEMOVE && status == 1) //鼠标拖动
- {
- currX = msg.x, currY = msg.y; //记录当前拖动位置
- }
- else if (msg.message == WM_LBUTTONUP) //鼠标抬起
- {
- }
- }
- }
- int main()
- {
- gameInit();
- while (1)
- {
- userClick(); //监听窗口鼠标事件
- updateWindow(); //更新窗口视图
- }
- system("pause");
- return 0;
- }
复制代码 运行结果
遇到的小问题 拖动植物位置不对,检查渲染当前拖动植物的参数坐标,以及监听鼠标点击变乱时,坐标需要设置
四 实现植物莳植和摇晃
- #include <stdio.h>
- #include <graphics.h> // 引用图形库头文件
- #include "tools.h"
- /*
- 开发日志
- 1、创建新项,导入素材,实现开始游戏场景
- 2、实现游戏顶部工具栏和植物卡牌
- 3、实现植物的选择和拖动
- 4、实现植物的种植和摇摆
- */
- #define WIN_WIDTH 900
- #define WIN_HIGHT 600
- #define MAX_PICTURE_NUM 20
- #define PIC_LEFT_MARGIN 338
- #define PIC_WIDTH 65
- #define GRASS_LEFT_MARGIN 252
- #define GRASS_TOP_MARGIN 82
- #define GRASS_GRID_ROW 5
- #define GRASS_GRID_COL 9
- #define GRASS_GRID_HIGHT 98 // (570 - 82) / 5
- #define GRASS_GRID_WIDTH 81 //(984 - 252) / 9
- int currX = 0, currY = 0, currIndex = -1;
- enum PLANT_CARDS{ PEA, SUNFLOWER, PLANT_CNT}; //使用 PLANT_CNT 统计 PLANT 总数
- IMAGE imgBg; //背景图片
- IMAGE imgBar; //工具栏图片
- IMAGE imgCards[PLANT_CNT]; //植物卡片
- /* 这里也可使用二维数组, 但会存在浪费空间的问题 */
- IMAGE* imgPlant[PLANT_CNT][MAX_PICTURE_NUM]; //动态植物素材
- typedef struct Plant
- {
- int type; //植物类型, -1 表示草地
- int frameId; //表示植物摆动帧
- }Plant;
- Plant plants[GRASS_GRID_ROW][GRASS_GRID_COL];
- bool fileExist(const char* name)
- {
- FILE* file = NULL;
- if (file = fopen(name,"r"))
- fclose(file);
- return file == NULL ? false : true;
- }
- void gameInit()
- {
- //加载背景图片
- loadimage(&imgBg, "res/map0.jpg");
- loadimage(&imgBar, "res/bar5.png");
- //加载植物卡片
- char name[64];
- //将二维指针数组内存空间置零
- memset(imgPlant, 0, sizeof(imgPlant));
- memset(plants, -1, sizeof(plants));
- for (int i = 0; i < PLANT_CNT; ++i)
- {
- //获取植物卡片相对路径名称
- sprintf(name, "res/Cards/card_%d.png", i + 1);
- loadimage(&imgCards[i], name);
- for (int j = 0;i < MAX_PICTURE_NUM; ++j)
- {
- //获取动态植物素材相对路径名称
- sprintf(name, "res/Plants/%d/%d.png", i, j + 1);
- if (fileExist(name)) {
- imgPlant[i][j] = new IMAGE;
- loadimage(imgPlant[i][j], name);
- }
- else break;
- }
- }
- //创建游戏图形窗口
- initgraph(WIN_WIDTH, WIN_HIGHT, 1);
- }
- void updateWindow()
- {
- //使用双缓冲, 解决输出窗口闪屏
- BeginBatchDraw();
- //渲染背景图至窗口
- putimage(0, 0, &imgBg);
- putimagePNG(250, 0, &imgBar);
- //渲染植物卡牌
- for (int i = 0;i < PLANT_CNT;++i)
- putimage(PIC_LEFT_MARGIN + i * PIC_WIDTH, 6, &imgCards[i]);
- //渲染种植植物
- for (int i = 0; i < GRASS_GRID_ROW; ++i)
- {
- for (int j = 0; j < GRASS_GRID_COL; ++j)
- {
- if (plants[i][j].type >= 0)
- {
- putimagePNG(GRASS_LEFT_MARGIN + j * GRASS_GRID_WIDTH + 5, //微调植物种植位置
- GRASS_TOP_MARGIN + i * GRASS_GRID_HIGHT + 10,
- imgPlant[plants[i][j].type][plants[i][j].frameId]);
- }
- }
- }
- //渲染当前拖动的植物
- if (currIndex >= 0)
- {
- IMAGE* currImage = imgPlant[currIndex][0];
- putimagePNG(currX - currImage->getwidth() / 2,
- currY - currImage->getheight() / 2, currImage);
- }
- EndBatchDraw(); //结束双缓冲
- }
- void userClick()
- {
- ExMessage msg; //创建消息体
- /* 拖动需先左键点击再拖动 */
- static int status = 0; //种植植物必须先选中再拖动
- if (peekmessage(&msg)) //该函数用于获取一个消息,并立即返回
- {
- if (msg.message == WM_LBUTTONDOWN) //鼠标点击
- {
- if (msg.x > PIC_LEFT_MARGIN &&
- msg.x < PIC_LEFT_MARGIN + PLANT_CNT * PIC_WIDTH &&
- msg.y < 96)
- {
- currX = msg.x, currY = msg.y;
- currIndex = (msg.x - PIC_LEFT_MARGIN) / PIC_WIDTH;
- status = 1;
- }
- }
- else if (msg.message == WM_MOUSEMOVE && status == 1) //鼠标拖动
- {
- currX = msg.x, currY = msg.y; //记录当前拖动位置
- }
- else if (msg.message == WM_LBUTTONUP) //鼠标抬起
- {
- //当植物拖到至草地位置终止, 则种植植物
- if (msg.x >= GRASS_LEFT_MARGIN &&
- msg.x <= GRASS_LEFT_MARGIN + GRASS_GRID_COL * GRASS_GRID_WIDTH &&
- msg.y >= GRASS_TOP_MARGIN &&
- msg.y <= GRASS_TOP_MARGIN + GRASS_GRID_ROW * GRASS_GRID_HIGHT)
- {
- int x = (msg.y - GRASS_TOP_MARGIN) / GRASS_GRID_HIGHT; //计算第几行
- int y = (msg.x - GRASS_LEFT_MARGIN) / GRASS_GRID_WIDTH; //计算第几列
-
- //未点击植物或当前位置已种植过植物,则不种植植物
- if (plants[x][y].type < 0 && status == 1)
- {
- plants[x][y].type = currIndex;
- plants[x][y].frameId = 0;
- }
- //printf("x = %d y = %d \n", x, y); -- DEBUG
- }
- status = 0, currIndex = -1; //停止拖动当前植物
- }
- }
- }
- void updateGame()
- {
- //遍历种植植物数组, 更新摆动帧
- for (int i = 0; i < GRASS_GRID_ROW; ++i)
- {
- for (int j = 0; j < GRASS_GRID_COL; ++j)
- {
- if (plants[i][j].type >= 0)
- {
- if (imgPlant[plants[i][j].type][++plants[i][j].frameId] == NULL) //把 ++plants[i][j].frameId 合并在一条语句中
- plants[i][j].frameId = 0;
- }
- }
- }
- }
- int main()
- {
- gameInit();
- updateWindow(); //窗口视图展示
- int timer = 0; //用以计时 20 毫秒更新一次
- while (1)
- {
- userClick(); //监听窗口鼠标事件
- timer += getDelay();
- if (timer > 20)
- {
- updateWindow(); //更新窗口视图
- updateGame(); //更新游戏动画帧
- timer = 0;
- }
- }
- system("pause");
- return 0;
- }
复制代码 运行结果
遇到的小问题
注意以下关于行列的偏移
- //计算第几行
- int x = (msg.y - GRASS_TOP_MARGIN) / GRASS_GRID_HIGHT;
- //计算第几列
- int y = (msg.x - GRASS_LEFT_MARGIN) / GRASS_GRID_WIDTH;
复制代码 以及渲染莳植植物时
- x = GRASS_LEFT_MARGIN + j * GRASS_GRID_WIDTH;
- y = GRASS_TOP_MARGIN + i * GRASS_GRID_HIGHT;
复制代码 非常容易搞混
小技巧
利用 timer 替换 Sleep 函数,进步程序运行服从;先渲染莳植植物,再渲染拖动植物,进步游戏观感
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |