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

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 编程

image-20241226092139164

遇到的小问题 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)//卡片宽度约 65putimage(338 + i * 65, 6, &imgCards[i]);
}int main()
{gameInit();updateWindow();system("pause");return 0;
}

运行结果

image-20241226104554542

遇到的小问题 图片加载不出来,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 65int 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;
}

运行结果

image-20241226135201780

遇到的小问题 拖动植物位置不对,检查渲染当前拖动植物的参数坐标,以及监听鼠标点击事件时,坐标需要设置

四 实现植物种植和摇摆

#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) / 9int 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;
}

运行结果

image-20241226200606328

遇到的小问题

注意以下关于行列的偏移

//计算第几行
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 函数,提高程序运行效率;先渲染种植植物,再渲染拖动植物,提高游戏观感

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/496206.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL创建和操纵表

本文介绍创建、更改和删除表的基本知识。 1. 创建表 SQL 不仅用于表数据操纵&#xff0c;而且还用来执行数据库和表的所有操作&#xff0c;包括表本身的创建和处理。一般有两种创建表的方法&#xff1a; 多数DBMS 都具有交互式创建和管理数据库表的工具&#xff1b;表也可以…

Java开发经验——数据库开发经验

摘要 本文主要介绍了Java开发中的数据库操作规范&#xff0c;包括数据库建表规范、索引规约、SQL规范和ORM规约。强调了在数据库设计和操作中应遵循的最佳实践&#xff0c;如字段命名、数据类型选择、索引创建、SQL语句编写和ORM映射&#xff0c;旨在提高数据库操作的性能和安…

NTLM 中继到 LDAP 结合 CVE-2019-1040 接管全域

目录 LDAP中继 LDAP签名 CVE-2019-1040 NTLM MIC 绕过漏洞 漏洞背景 漏洞利用链 利用方式 1&#xff1a;配置基于资源的约束委派-攻击域控 利用方式 2&#xff1a;攻击 Exchange Exchange windows permissions 组介绍 复现 LDAP中继 LDAP&#xff08;轻量级目录访问协…

如何通过采购管理系统实现智能化采购?

随着人工智能、大数据等技术的快速发展&#xff0c;采购管理逐步迈入智能化时代。智能化采购不仅提升了效率&#xff0c;还为企业提供了更精准的采购决策支持。本文将从智能化采购的优势出发&#xff0c;探讨采购管理系统如何助力企业实现这一目标。 文中用到的采购管理系统&a…

STM32学习(一)

STM32是什么 STM32是意法半导体&#xff08;ST&#xff09;公司基于ARM Cortex-M内核开发的32位微控制器‌。他的名字是由&#xff1b;意大利SGS&法国Thomson共同研制的&#xff0c;中国人民取两家公司名称的首字母于是ST就是这样来的&#xff0c;M是单片机M-Micro Contro…

C# 读取多种CAN报文文件转换成统一格式数据,工具类:CanMsgRead

因为经常有读取CAN报文trace文件的需求&#xff0c;而且因为CAN卡不同、记录软件不同会导致CAN报文trace文件的格式都有差异。为了方便自己后续开发&#xff0c;我写了一个CanMsgRead工具类&#xff0c;只要提供CAN报文路径和CAN报文格式的选项即可将文件迅速读取转换为统一的C…

hiprint结合vue2项目实现静默打印详细使用步骤

代码地址是&#xff1a;vue-plugin-hiprint: hiprint for Vue2/Vue3 ⚡打印、打印设计、可视化设计器、报表设计、元素编辑、可视化打印编辑 本地安装包地址&#xff1a;electron-hiprint 发行版 - Gitee.com 1、先安装hipint安装包在本地 2、项目运行npm&#xff08;socket.…

WPF+MVVM案例实战与特效(四十七)-实现一个路径绘图的自定义按钮控件

文章目录 1、案例效果2、创建自定义 PathButton 控件1、定义 PathButton 类2、设计样式与控件模板3、代码解释3、控件使用4、直接在 XAML 中绑定命令3、源代码获取4、总结1、案例效果 2、创建自定义 PathButton 控件 1、定义 PathButton 类 首先,我们需要创建一个新的类 Pat…

《传染病与人类历史》传染病如何推动人类历史进程

《传染病与人类历史》传染病如何推动人类历史进程 Epidemics: The Impact of Germs and Their Power Over Humanity Joshua S. Loomis&#xff08;约书亚S卢米斯&#xff09;美国&#xff0c;教授&#xff0c;微生物学家。主要教授微生物学、遗传学、免疫学、细胞生物学与传染病…

如何检查交叉编译器gcc工具链里是否有某个库(以zlib库和libpng库为例)

freetype 依赖于 libpng&#xff0c;libpng 又依赖于 zlib&#xff0c;所以我们应该&#xff1a;先编译 安装 zlib&#xff0c;再编译安装 libpng&#xff0c;最后编译安装 freetype。 但是&#xff0c;有些交叉编译器工具链里已经有 zlib库和freetype&#xff0c;所以我们需要…

MySql详细教程-从入门到进阶(超实用)

基础篇 通用语法及分类 DDL: 数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库、表、字段&#xff09;DML: 数据操作语言&#xff0c;用来对数据库表中的数据进行增删改DQL: 数据查询语言&#xff0c;用来查询数据库中表的记录DCL: 数据控制语言&#xff0c;用…

Burp炮台实现(动态ip发包)

基本步骤 1.使用 zmap 爬取大量代理ip 2.使用py1脚本初步筛选可用ip 3.利用py2脚本再次筛选对目标网站可用ip&#xff08;不带payload安全检测&#xff09; 4.配置 burp 插件并加载收集到的代理池 5.加载payload&#xff0c;开始爆破 Zmap kali安装 sudo apt update apt …

海外招聘丨 苏黎世联邦理工学院—机器学习在社会和政治科学中的应用博士后

雇主简介 苏黎世联邦理工学院是世界领先的科技大学之一。我们以优质的教育、尖端的基础研究和将新知识直接转化为社会而闻名。来自 120 多个国家的 30,000 多名学生认为我们的大学是一个鼓励独立思考和激励卓越的环境的地方。 我们位于欧洲中心&#xff0c;但与世界各地建立联…

【微信小程序】3|首页搜索框 | 我的咖啡店-综合实训

首页-搜索框-跳转 引言 在微信小程序中&#xff0c;首页的搜索框是用户交互的重要入口。本文将通过“我的咖啡店”小程序的首页搜索框实现&#xff0c;详细介绍如何在微信小程序中创建和处理搜索框的交互。 1. 搜索函数实现 onClickInput函数在用户点击搜索框时触发&#x…

VS Code AI开发之Copilot配置和使用详解

随着AI开发工具的迅速发展&#xff0c;GitHub Copilot在Cursor、Winsuf、V0等一众工具的冲击下&#xff0c;推出了免费版本。接下来&#xff0c;我将为大家介绍GitHub Copilot的配置和使用方法。GitHub Copilot基于OpenAI Codex模型&#xff0c;旨在为软件开发者提供智能化的代…

表达式语句、复合语句和空语句

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;表达式语句、复合语句和空语句 发布时间&#xff1a;2024.12.26 隶属专栏&#xff1a;C语言 目录 1. 表达式语句定义作用常见类型赋值语句函数调用语句 2. 复合语句定义作用变量作用域 3. 空语句定义作用 1. 表达式…

数学建模助力干细胞研究,配体纳米簇如何影响干细胞命运

大家好&#xff01;今天来了解一篇关于对干细胞行为的可逆调控的研究——《Modularity-based mathematical modeling of ligand inter-nanocluster connectivity for unraveling reversible stem cell regulation》发表于《Nature Communications》。这项研究利用图论对细胞外基…

HTMLCSS:超炫丝滑的卡片水波纹效果

这段代码创建了一个卡片&#xff0c;卡片上有三个波动效果&#xff0c;这些波动效果通过 CSS 的keyframes 动画实现&#xff0c;创建了一个旋转的动画效果。这种效果适用于创建动态的视觉效果&#xff0c;例如音乐播放器的封面、动态背景或其他需要动态效果的界面元素。 演示效…

pytorch MoE(专家混合网络)的简单实现。

专家混合&#xff08;Mixture of Experts, MoE&#xff09;是一种深度学习模型架构&#xff0c;通常用于处理大规模数据和复杂任务。它通过将输入分配给多个专家网络&#xff08;即子模型&#xff09;&#xff0c;然后根据门控网络&#xff08;gating network&#xff09;的输出…

K8s证书过期

part of the existing bootstrap client certificate is expired: 2023-11-27 12:44:12 0000 UTC 查看运行日志&#xff1a; journalctl -xefu kubelet 重新生成证书&#xff1a; #重新生成证书 kubeadm alpha certs renew all #备份旧的配置文件 mv /etc/kubernetes/*.conf…