C语言实现植物大战僵尸
一.项目介绍
目前只完成了
/*
* 开发日志
* 1.创建新项目(空白项目模板)使用VS2019
* 2.导入素材
* 3.实现最开始的游戏场景
* 4.实现游戏顶部的工具栏
* 5.实现工具栏中的植物卡牌
* 6.植物卡牌的选择与拖动
* 7.植物的种植
* 8.植物的摇摆
* 9.制作启动菜单
* 10.创建随机阳光
* 11.收集阳光,显示阳光值
* 12.创建僵尸
*/
在创建僵尸这里出现了bug,僵尸每走到草坪中间就会消失,目前bug
还没解决,等以后有时间了再完成后续工作。
二.创建主场景
新建项目--植物大战僵尸
导入素材(素材会新发一个文件)
新建res文件,将解压后的素材移入res中
把解压后的素材中的两个工具文件放到外面
安装EasyX图形库
1.打开官网EasyX Graphics Library for C++
2.点击“下载 EasyX”
3.直接打开安装包,跟随指令安装即可
4.判断是否安装成功
打开VS
新建一个“空项目”或者“控制台项目”
输入:
#include <graphics.h> // 引用图形库头文件
#include <conio.h>
int main()
{
initgraph(640, 480); // 创建绘图窗口,大小为 640x480 像素
circle(200, 200, 100); // 画圆,圆心(200, 200),半径 100
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
}
黑窗口上出现了一个圆,代表安装成功
实现最开始的游戏场景:
#include<stdio.h>
#include <graphics.h> // easyx图形库的头文件,需要安装easyx图形库
#define WIN_WIDTH 900 //窗口宽度
#define WIN_HEIGHT 600 //窗口高度
IMAGE imgBg; //表示背景图片
//游戏初始化
void gameInit() {
/*加载游戏背景图片
* 背景图片有两种加载方式
* 1.直接把背景图片打印到窗口里,这种方式会比较慢
* 2.把背景图片先放到内存变量里面去,再从变量里面显示就很快
* 使用第二种方式
*/
//把字符集修改为”多字节字符集“
loadimage(&imgBg, "res/bg.jpg"); //背景图片加载到变量 ""里是素材的路径
//创建游戏的图形窗口
initgraph(WIN_WIDTH, WIN_HEIGHT);
}
//更新窗口
void updateWindow() {
putimage(0,0,&imgBg); //把图片渲染出来
}
int main()
{
gameInit();
updateWindow();
system("pause");
return 0;
}
三.实现植物卡牌
将两个工具导入项目中,使用其接口
#include<stdio.h>
#include <graphics.h> // easyx图形库的头文件,需要安装easyx图形库
#include"tools.h"
#define WIN_WIDTH 900 //窗口宽度
#define WIN_HEIGHT 600 //窗口高度
enum {WAN_DOU , XIANG_RI_KUI , ZHI_WU_COUNT};
IMAGE imgBg; //表示背景图片
IMAGE imgBar; //植物卡牌工具栏
IMAGE imgCards[ZHI_WU_COUNT]; //植物卡牌
//游戏初始化
void gameInit() {
/*加载游戏背景图片
* 背景图片有两种加载方式
* 1.直接把背景图片打印到窗口里,这种方式会比较慢
* 2.把背景图片先放到内存变量里面去,再从变量里面显示就很快
* 使用第二种方式
*/
//把字符集修改为”多字节字符集“
loadimage(&imgBg, "res/bg.jpg"); //背景图片加载到变量 ""里是素材的路径
loadimage(&imgBar, "res/bar.png");
//初始化植物卡牌
char name[64];
for (int i = 0; i < ZHI_WU_COUNT; i++) {
//生成植物卡牌的文件名
sprintf_s(name, sizeof(name), "res/Cards/card_%d.png", i + 1);
loadimage(&imgCards[i], name);
}
//创建游戏的图形窗口
initgraph(WIN_WIDTH, WIN_HEIGHT);
}
//更新窗口
void updateWindow() {
putimage(0,0,&imgBg); //把图片渲染出来
//putimage(250,0,&imgBar);
putimagePNG(250, 0, &imgBar); //背景透明化
for (int i = 0; i < ZHI_WU_COUNT; i++) {
int x = 338 + i * 65;
int y = 6;
putimage(x, y, &imgCards[i]);
}
}
四.实现植物的选择与拖动
关掉SDL检查
IMAGE imgCards[ZHI_WU_COUNT]; //植物卡牌
IMAGE* imgZhiWu[ZHI_WU_COUNT][20];
int curX, curY; //当前选中的植物,在移动过程中的位置
int curZhiWu; // 0 : 没有选中, 1 :选中了第一种植物
bool fileExist(const char* name) {
FILE* fp = fopen(name, "r");
if (fp == NULL) {
return false;
}
else {
fclose(fp);
return true;
}
}
//游戏初始化
void gameInit() {
/*加载游戏背景图片
* 背景图片有两种加载方式
* 1.直接把背景图片打印到窗口里,这种方式会比较慢
* 2.把背景图片先放到内存变量里面去,再从变量里面显示就很快
* 使用第二种方式
*/
//把字符集修改为”多字节字符集“
loadimage(&imgBg, "res/bg.jpg"); //背景图片加载到变量 ""里是素材的路径
loadimage(&imgBar, "res/bar.png");
memset(imgZhiWu, 0, sizeof(imgZhiWu));//初始化
//初始化植物卡牌
char name[64];
for (int i = 0; i < ZHI_WU_COUNT; i++) {
//生成植物卡牌的文件名
sprintf_s(name, sizeof(name), "res/Cards/card_%d.png", i + 1);
loadimage(&imgCards[i], name);
for (int j = 0; j < 20; j++) {
sprintf_s(name, sizeof(name), "res/zhiwu/%d/%d.png", i,j + 1);
//先判断这个文件是否存在
if (fileExist(name)) { //文件存在,然后加载
imgZhiWu[i][j] = new IMAGE; //分配内存
loadimage(imgZhiWu[i][j], name);
}
else
{
break;
}
}
}
curZhiWu = 0; //初始化
//创建游戏的图形窗口
initgraph(WIN_WIDTH, WIN_HEIGHT,1);
}
//更新窗口
void updateWindow() {
BeginBatchDraw(); //开始缓冲
putimage(0,0,&imgBg); //把图片渲染出来
//putimage(250,0,&imgBar);
putimagePNG(250, 0, &imgBar); //背景透明化
for (int i = 0; i < ZHI_WU_COUNT; i++) {
int x = 338 + i * 65;
int y = 6;
putimage(x, y, &imgCards[i]);
}
//渲染 拖动过程中的植物
if (curZhiWu > 0) {
putimage(curX, curY, imgZhiWu[curZhiWu - 1][0]);
}
EndBatchDraw(); //结束缓冲
}
//点击
void userClick() {
ExMessage msg;
static int status = 0; //判断拖动状态还是选择状态
if (peekmessage(&msg)) {//判断有没有消息
if (msg.message == WM_LBUTTONDOWN) {//如果是左键按下
//判断单击位置对不对,只有点击卡牌区域才响应
if (msg.x > 338 && msg.x < 338 + 65 * ZHI_WU_COUNT && msg.y < 96) {
int index = (msg.x - 338) / 65;//判断点击的是哪张卡牌
status = 1; //选择状态
curZhiWu = index + 1;
}
}
else if (msg.message == WM_MOUSEMOVE && status == 1) { //如果是鼠标移动
curX = msg.x;
curY = msg.y;
}
else if (msg.message == WM_LBUTTONUP) { //如果左键抬起来
}
}
}
可以拖动,但是有两个问题
1.拖动的植物是黑色背景,太丑陋
渲染改成PNG版本的:
//渲染 拖动过程中的植物
if (curZhiWu > 0) {
putimage(curX, curY, imgZhiWu[curZhiWu - 1][0]);
}
改成
//渲染 拖动过程中的植物
if (curZhiWu > 0) {
putimagePNG(curX, curY, imgZhiWu[curZhiWu - 1][0]);
}
2.光标显示在植物的左上角,应该让它显示在植物的正中央
渲染的位置作下改动
if (curZhiWu > 0) {
putimagePNG(curX, curY, imgZhiWu[curZhiWu - 1][0]);
}
改成:
//渲染 拖动过程中的植物
if (curZhiWu > 0) {
IMAGE* img = imgZhiWu[curZhiWu - 1][0];
putimagePNG(curX - img->getwidth() / 2, curY - img->getheight() / 2, img);
}
五.实现植物的种植
int curX, curY; //当前选中的植物,在移动过程中的位置
int curZhiWu; // 0 : 没有选中, 1 :选中了第一种植物
struct zhiwu {
int type; //0:没有植物; 1: 第一种植物
int frameIndex; //序列帧的序号
};
struct zhiwu map[3][9]; //地图
bool fileExist(const char* name) {
FILE* fp = fopen(name, "r");
if (fp == NULL) {
return false;
}
else {
fclose(fp);
return true;
}
}
//游戏初始化
void gameInit() {
/*加载游戏背景图片
* 背景图片有两种加载方式
* 1.直接把背景图片打印到窗口里,这种方式会比较慢
* 2.把背景图片先放到内存变量里面去,再从变量里面显示就很快
* 使用第二种方式
*/
//把字符集修改为”多字节字符集“
loadimage(&imgBg, "res/bg.jpg"); //背景图片加载到变量 ""里是素材的路径
loadimage(&imgBar, "res/bar.png");
memset(imgZhiWu, 0, sizeof(imgZhiWu));//初始化
memset(map, 0, sizeof(map));
//初始化植物卡牌
char name[64];
for (int i = 0; i < ZHI_WU_COUNT; i++) {
//生成植物卡牌的文件名
sprintf_s(name, sizeof(name), "res/Cards/card_%d.png", i + 1);
loadimage(&imgCards[i], name);
for (int j = 0; j < 20; j++) {
sprintf_s(name, sizeof(name), "res/zhiwu/%d/%d.png", i,j + 1);
//先判断这个文件是否存在
if (fileExist(name)) { //文件存在,然后加载
imgZhiWu[i][j] = new IMAGE; //分配内存
loadimage(imgZhiWu[i][j], name);
}
else
{
break;
}
}
}
curZhiWu = 0; //初始化
//创建游戏的图形窗口
initgraph(WIN_WIDTH, WIN_HEIGHT,1);
}
//更新窗口
void updateWindow() {
BeginBatchDraw(); //开始缓冲
putimage(0,0,&imgBg); //把图片渲染出来
//putimage(250,0,&imgBar);
putimagePNG(250, 0, &imgBar); //背景透明化
for (int i = 0; i < ZHI_WU_COUNT; i++) {
int x = 338 + i * 65;
int y = 6;
putimage(x, y, &imgCards[i]);
}
//渲染 拖动过程中的植物
if (curZhiWu > 0) {
IMAGE* img = imgZhiWu[curZhiWu - 1][0];
putimagePNG(curX - img->getwidth() / 2, curY - img->getheight() / 2, img);
}
//种下植物
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 9; j++) {
if (map[i][j].type > 0) {
int x = 256 + j * 81;
int y = 179 + i * 102 + 14;
int zhiWuType = map[i][j].type - 1;
int index = map[i][j].frameIndex;
putimagePNG(x, y, imgZhiWu[zhiWuType][index]);
}
}
}
EndBatchDraw(); //结束缓冲
}
//点击
void userClick() {
ExMessage msg;
static int status = 0; //判断拖动状态还是选择状态
if (peekmessage(&msg)) {//判断有没有消息
if (msg.message == WM_LBUTTONDOWN) {//如果是左键按下
//判断单击位置对不对,只有点击卡牌区域才响应
if (msg.x > 338 && msg.x < 338 + 65 * ZHI_WU_COUNT && msg.y < 96) {
int index = (msg.x - 338) / 65;//判断点击的是哪张卡牌
status = 1; //选择状态
curZhiWu = index + 1;
}
}
else if (msg.message == WM_MOUSEMOVE && status == 1) { //如果是鼠标移动
curX = msg.x;
curY = msg.y;
}
else if (msg.message == WM_LBUTTONUP) { //如果左键抬起来
if (msg.x > 256 && msg.y > 179 && msg.y < 489) {
int row = (msg.y - 179) / 102;
int col = (msg.x - 256) / 81;
//printf("%d,%d\n", row, col);
if (map[row][col].type == 0) {
map[row][col].type = curZhiWu;
map[row][col].frameIndex = 0;
}
}
curZhiWu = 0;
status = 0;
}
}
}
六.实现植物的摇摆
void updateGame() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 9; j++) {
if (map[i][j].type > 0) {
map[i][j].frameIndex++;
int zhiWuType = map[i][j].type - 1;
int index = map[i][j].frameIndex;
if (imgZhiWu[zhiWuType][index] == NULL) {
map[i][j].frameIndex = 0;
}
}
}
}
}
七.优化游戏循环和游戏渲染顺序
不使用sleep(),使游戏循环衔接的很好。
int timer = 0;
bool flag = true;
while (1) {
userClick();
timer += getDelay();
if (timer > 20) {
flag = true;
timer = 0;
}
if (flag) {
flag = false;
updateWindow();
updateGame();
}
八.制作启动菜单
//启动菜单
void startUI() {
IMAGE imgBg, imgMenu1, imgMenu2;
loadimage(&imgBg, "res/menu.png");
loadimage(&imgMenu1, "res/menu1.png");
loadimage(&imgMenu2, "res/menu2.png");
int flag = 0;
while (1) {
BeginBatchDraw();
putimage(0, 0, &imgBg);
putimagePNG(474, 75, flag ? &imgMenu2 : &imgMenu2);
ExMessage msg;
if (peekmessage(&msg)) {
if (msg.message == WM_LBUTTONDOWN &&
msg.x > 474 && msg.x < 474 + 300 && msg.y>75 && msg.y < 75 + 140) {
flag = 1;
}
else if (msg.message == WM_LBUTTONUP && flag ) {
return;
}
}
EndBatchDraw();
}
}
九.创建随机阳光
//阳光
/*
* 阳光旋转着落下
其动画通过播放很多张旋转的图片来实现,图片循环着播放,显示
阳光是垂直飘下的,只需确定y轴坐标即可,x轴坐标不变
*/
struct sunshineBall {
int x, y; //阳光球在飘落过程中的位置变化
int frameIndex; //当前显示的图片帧的序号
int destY; //飘落的目标位置的y坐标
int used; //是否在使用
};
/*
* 预先准备一个阳光池,需要生产阳光时,从里面取出一个阳光,用完之后,再归还进池子
*/
//阳光池
struct sunshineBall balls[10];
//存储阳光球照片的数组
IMAGE imgSunshineBall[29];
//创建阳光
void createSunshine() {
static int count = 0; //函数的调用次数
static int fre = 400;
count++;
if (count >= fre) { //调用xx次才生成阳光
fre = 200 + rand() % 200;
count = 0;
//从阳光池中取一个可以使用的
int ballMax = sizeof(balls) / sizeof(balls[0]);
int i;
for (int i = 0; i < ballMax && balls[i].used; i++);
if (i >= ballMax) {
return;
}
balls[i].used = true;
balls[i].frameIndex = 0;
balls[i].x = rand() % (900 - 260); //260..900 阳光飘落的区域
balls[i].y = 60;
balls[i].destY = 200 + (rand() % 4) * 90;
}
十.显示随机阳光
//更新阳光状态
void updateSunshine() {
int ballMax = sizeof(balls) / sizeof(balls[0]);
for (int i = 0; i < ballMax; i++) {
if (balls[i].used) {
balls[i].frameIndex = (balls[i].frameIndex + 1) % 29;
if (balls[i].timer == 0) {
balls[i].y += 2;
}
if (balls[i].y >= balls[i].destY) {
//balls[i].used = false;
balls[i].timer++;
if (balls[i].timer > 100) {//定时器加到100时,才让阳光球消失,模拟阳光在草坪上停留的时间
balls[i].used = false;
}
}
}
}
}
十一.收集阳光,显示阳光值
//设置阳光初始值
int sunshine;
//设置字体
LOGFONT f;
gettextstyle(&f);
f.lfHeight = 30;
f.lfWeight = 15;
strcpy(f.lfFaceName, "Segoe UI Black");
f.lfQuality = ANTIALIASED_QUALITY;//抗锯齿效果
settextstyle(&f);
setbkmode(TRANSPARENT);
setcolor(BLACK);
//收集阳光
void collectSunshine(ExMessage* msg) {
int count = sizeof(balls) / sizeof(balls[0]);
int w = imgSunshineBall[0].getwidth();
int h = imgSunshineBall[0].getheight();
for (int i = 0; i < count; i++) {
if (balls[i].used) {
int x = balls[i].x;
int y = balls[i].y;
if (msg->x > x && msg->x<x + w &&
msg->y>y && msg->y < y + h) {
balls[i].used = false;
sunshine += 25;
//播放音效
mciSendString("play res/bg.MP3", 0, 0, 0);
}
}
}
}
十二.创建僵尸
//定义僵尸
struct zm {
int x, y;
int frameIndex;
bool used;
int speed;
};
struct zm zms[10]; //定义10个僵尸
//僵尸图片数组
IMAGE imgZM[22];
//创建僵尸
void createZM() {
static int zmFre = 500;
static int count = 0;
count++;
if (count > zmFre) {
count = 0;
zmFre = rand() % 200 + 300;
int i = 0;
int zmMax = sizeof(zms) / sizeof(zms[0]);
for (int i = 0; i < zmMax && zms[i].used; i++);
if (i < zmMax) {
zms[i].used = true;
zms[i].x = WIN_WIDTH;
zms[i].y = 172 + (1 + rand() % 3) * 100;
zms[i].speed = 1;
}
}
}
//更新僵尸的状态
void updateZM() {
int zmMax = sizeof(zms) / sizeof(zms[0]);
//更新僵尸的位置
for (int i = 0; i < zmMax; i++) {
if (zms[i].used) {
zms[i].x -= zms[i].speed;
if (zms[i].x < 170) {
printf("GAME OVER\n");
MessageBox(NULL, "over", "over", 0); //待优化
exit(0); //待优化
}
}
}
}
//绘制僵尸
void drawZM() {
int zmCount = sizeof(zms) / sizeof(zms[0]);
for (int i = 0; i < zmCount; i++) {
if (zms[i].used) {
IMAGE* img = &imgZM[zms[i].frameIndex];
putimagePNG(
zms[i].x,
zms[i].y - img->getheight(),
img);
}
}
}
已编写出的源码:
/*
* 开发日志
* 1.创建新项目(空白项目模板)使用VS2019
* 2.导入素材
* 3.实现最开始的游戏场景
* 4.实现游戏顶部的工具栏
* 5.实现工具栏中的植物卡牌
* 6.植物卡牌的选择与拖动
* 7.植物的种植
* 8.植物的摇摆
* 9.制作启动菜单
* 10.创建随机阳光
* 11.收集阳光,显示阳光值
* 12.创建僵尸
*/
#include<stdio.h>
#include <graphics.h> // easyx图形库的头文件,需要安装easyx图形库
#include<time.h>
#include"tools.h"
#include<mmsystem.h> //播放音效
#pragma comment(lib,"winmm.lib");
#define WIN_WIDTH 900 //窗口宽度
#define WIN_HEIGHT 600 //窗口高度
enum {WAN_DOU , XIANG_RI_KUI , ZHI_WU_COUNT};
IMAGE imgBg; //表示背景图片
IMAGE imgBar; //植物卡牌工具栏
IMAGE imgCards[ZHI_WU_COUNT]; //植物卡牌
IMAGE* imgZhiWu[ZHI_WU_COUNT][20];
int curX, curY; //当前选中的植物,在移动过程中的位置
int curZhiWu; // 0 : 没有选中, 1 :选中了第一种植物
struct zhiwu {
int type; //0:没有植物; 1: 第一种植物
int frameIndex; //序列帧的序号
};
struct zhiwu map[3][9]; //地图
//阳光
/*
* 阳光旋转着落下
其动画通过播放很多张旋转的图片来实现,图片循环着播放,显示
阳光是垂直飘下的,只需确定y轴坐标即可,x轴坐标不变
*/
struct sunshineBall {
int x, y; //阳光球在飘落过程中的位置变化
int frameIndex; //当前显示的图片帧的序号
int destY; //飘落的目标位置的y坐标
int used; //是否在使用
int timer;//定时器
};
/*
* 预先准备一个阳光池,需要生产阳光时,从里面取出一个阳光,用完之后,再归还进池子
*/
//阳光池
struct sunshineBall balls[10];
//存储阳光球照片的数组
IMAGE imgSunshineBall[29];
//设置阳光初始值
int sunshine;
//定义僵尸
struct zm {
int x, y;
int frameIndex;
bool used;
int speed;
};
struct zm zms[10]; //定义10个僵尸
//僵尸图片数组
IMAGE imgZM[22];
bool fileExist(const char* name) {
FILE* fp = fopen(name, "r");
if (fp == NULL) {
return false;
}
else {
fclose(fp);
return true;
}
}
//游戏初始化
void gameInit() {
/*加载游戏背景图片
* 背景图片有两种加载方式
* 1.直接把背景图片打印到窗口里,这种方式会比较慢
* 2.把背景图片先放到内存变量里面去,再从变量里面显示就很快
* 使用第二种方式
*/
//把字符集修改为”多字节字符集“
loadimage(&imgBg, "res/bg.jpg"); //背景图片加载到变量 ""里是素材的路径
loadimage(&imgBar, "res/bar.png");
memset(imgZhiWu, 0, sizeof(imgZhiWu));//初始化
memset(map, 0, sizeof(map));
//初始化植物卡牌
char name[64];
for (int i = 0; i < ZHI_WU_COUNT; i++) {
//生成植物卡牌的文件名
sprintf_s(name, sizeof(name), "res/Cards/card_%d.png", i + 1);
loadimage(&imgCards[i], name);
for (int j = 0; j < 20; j++) {
sprintf_s(name, sizeof(name), "res/zhiwu/%d/%d.png", i,j + 1);
//先判断这个文件是否存在
if (fileExist(name)) { //文件存在,然后加载
imgZhiWu[i][j] = new IMAGE; //分配内存
loadimage(imgZhiWu[i][j], name);
}
else
{
break;
}
}
}
curZhiWu = 0; //初始化
sunshine = 150; //设置阳光初始值
memset(balls, 0, sizeof(balls));
for (int i = 0; i < 29; i++) {
sprintf_s(name, sizeof(name), "res/sunshine/%d.png", i + 1);
loadimage(&imgSunshineBall[i], name);
}
//配置随机种子
srand(time(NULL));
//创建游戏的图形窗口
initgraph(WIN_WIDTH, WIN_HEIGHT,1);
//设置字体
LOGFONT f;
gettextstyle(&f);
f.lfHeight = 30;
f.lfWeight = 15;
strcpy(f.lfFaceName, "Segoe UI Black");
f.lfQuality = ANTIALIASED_QUALITY;//抗锯齿效果
settextstyle(&f);
setbkmode(TRANSPARENT);
setcolor(BLACK);
//僵尸数据初始化
memset(zms, 0, sizeof(zms));
for (int i = 0; i < 22; i++) {
sprintf_s(name, sizeof(name), "res/zm/%d.png", i + 1);
loadimage(&imgZM[i], name);
}
}
//绘制僵尸
void drawZM() {
int zmCount = sizeof(zms) / sizeof(zms[0]);
for (int i = 0; i < zmCount; i++) {
if (zms[i].used) {
IMAGE* img = &imgZM[zms[i].frameIndex];
putimagePNG(
zms[i].x,
zms[i].y - img->getheight(),
img);
}
}
}
//更新窗口
void updateWindow() {
BeginBatchDraw(); //开始缓冲
putimage(0,0,&imgBg); //把图片渲染出来
//putimage(250,0,&imgBar);
putimagePNG(250, 0, &imgBar); //背景透明化
for (int i = 0; i < ZHI_WU_COUNT; i++) {
int x = 338 + i * 65;
int y = 6;
putimage(x, y, &imgCards[i]);
}
//种下植物
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 9; j++) {
if (map[i][j].type > 0) {
int x = 256 + j * 81;
int y = 179 + i * 102 + 14;
int zhiWuType = map[i][j].type - 1;
int index = map[i][j].frameIndex;
putimagePNG(x, y, imgZhiWu[zhiWuType][index]);
}
}
}
//渲染 拖动过程中的植物
if (curZhiWu > 0) {
IMAGE* img = imgZhiWu[curZhiWu - 1][0];
putimagePNG(curX - img->getwidth() / 2, curY - img->getheight() / 2, img);
}
int ballMax = sizeof(balls) / sizeof(balls[0]);
for (int i = 0; i < ballMax; i++) {
if (balls[i].used) {
IMAGE* img = &imgSunshineBall[balls[i].frameIndex];
putimagePNG(balls[i].x, balls[i].y, img);
}
}
//渲染出阳光值
char scoreText[8];
sprintf_s(scoreText, sizeof(scoreText), "%d", sunshine);
outtextxy(276, 67, scoreText);
//绘制僵尸
drawZM();
EndBatchDraw(); //结束缓冲
}
//收集阳光
void collectSunshine(ExMessage* msg) {
int count = sizeof(balls) / sizeof(balls[0]);
int w = imgSunshineBall[0].getwidth();
int h = imgSunshineBall[0].getheight();
for (int i = 0; i < count; i++) {
if (balls[i].used) {
int x = balls[i].x;
int y = balls[i].y;
if (msg->x > x && msg->x<x + w &&
msg->y>y && msg->y < y + h) {
balls[i].used = false;
sunshine += 25;
//播放音效
mciSendString("play res/sunshine.mp3", 0, 0, 0);
}
}
}
}
//点击
void userClick() {
ExMessage msg;
static int status = 0; //判断拖动状态还是选择状态
if (peekmessage(&msg)) {//判断有没有消息
if (msg.message == WM_LBUTTONDOWN) {//如果是左键按下
//判断单击位置对不对,只有点击卡牌区域才响应
if (msg.x > 338 && msg.x < 338 + 65 * ZHI_WU_COUNT && msg.y < 96) {
int index = (msg.x - 338) / 65;//判断点击的是哪张卡牌
status = 1; //选择状态
curZhiWu = index + 1;
}
else
{
//是否在收集阳光
collectSunshine(&msg);
}
}
else if (msg.message == WM_MOUSEMOVE && status == 1) { //如果是鼠标移动
curX = msg.x;
curY = msg.y;
}
else if (msg.message == WM_LBUTTONUP) { //如果左键抬起来
if (msg.x > 256 && msg.y > 179 && msg.y < 489) {
int row = (msg.y - 179) / 102;
int col = (msg.x - 256) / 81;
//printf("%d,%d\n", row, col);
if (map[row][col].type == 0) {
map[row][col].type = curZhiWu;
map[row][col].frameIndex = 0;
}
}
curZhiWu = 0;
status = 0;
}
}
}
//创建阳光
void createSunshine() {
static int count = 0; //函数的调用次数
static int fre = 400;
count++;
if (count >= fre) { //调用xx次才生成阳光
fre = 200 + rand() % 200;
count = 0;
//从阳光池中取一个可以使用的
int ballMax = sizeof(balls) / sizeof(balls[0]);
int i = 0;
for (int i = 0; i < ballMax && balls[i].used; i++);
if (i >= ballMax) {
return;
}
balls[i].used = true;
balls[i].frameIndex = 0;
balls[i].x = 260 + rand() % (900 - 260); //260..900 阳光飘落的区域
balls[i].y = 60;
balls[i].destY = 200 + (rand() % 4) * 90;
balls[i].timer = 0;
}
}
//更新阳光状态
void updateSunshine() {
int ballMax = sizeof(balls) / sizeof(balls[0]);
for (int i = 0; i < ballMax; i++) {
if (balls[i].used) {
balls[i].frameIndex = (balls[i].frameIndex + 1) % 29;
if (balls[i].timer == 0) {
balls[i].y += 2;
}
if (balls[i].y >= balls[i].destY) {
//balls[i].used = false;
balls[i].timer++;
if (balls[i].timer > 100) {//定时器加到100时,才让阳光球消失,模拟阳光在草坪上停留的时间
balls[i].used = false;
}
}
}
}
}
//创建僵尸
void createZM() {
static int zmFre = 500;
static int count = 0;
count++;
if (count > zmFre) {
count = 0;
zmFre = rand() % 200 + 300;
int i = 0;
int zmMax = sizeof(zms) / sizeof(zms[0]);
for (int i = 0; i < zmMax && zms[i].used; i++);
if (i < zmMax) {
zms[i].used = true;
zms[i].x = WIN_WIDTH;
zms[i].y = 172 + (1 + rand() % 3) * 100;
zms[i].speed = 1;
}
}
}
//更新僵尸的状态
void updateZM() {
int zmMax = sizeof(zms) / sizeof(zms[0]);
//更新僵尸的位置
for (int i = 0; i < zmMax; i++) {
if (zms[i].used) {
zms[i].x -= zms[i].speed;
if (zms[i].x < 170) {
printf("GAME OVER\n");
MessageBox(NULL, "over", "over", 0); //待优化
exit(0); //待优化
}
}
}
}
void updateGame() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 9; j++) {
if (map[i][j].type > 0) {
map[i][j].frameIndex++;
int zhiWuType = map[i][j].type - 1;
int index = map[i][j].frameIndex;
if (imgZhiWu[zhiWuType][index] == NULL) {
map[i][j].frameIndex = 0;
}
}
}
}
createSunshine(); //创建阳光
updateSunshine();//更新阳光状态
createZM(); //创建僵尸
updateZM();//更新僵尸的状态
}
//启动菜单
void startUI() {
IMAGE imgBg, imgMenu1, imgMenu2;
loadimage(&imgBg, "res/menu.png");
loadimage(&imgMenu1, "res/menu1.png");
loadimage(&imgMenu2, "res/menu2.png");
int flag = 0;
while (1) {
BeginBatchDraw();
putimage(0, 0, &imgBg);
putimagePNG(474, 75, flag ? &imgMenu2 : &imgMenu2);
ExMessage msg;
if (peekmessage(&msg)) {
if (msg.message == WM_LBUTTONDOWN &&
msg.x > 474 && msg.x < 474 + 300 && msg.y>75 && msg.y < 75 + 140) {
flag = 1;
}
else if (msg.message == WM_LBUTTONUP && flag ) {
return;
}
}
EndBatchDraw();
}
}
int main(void)
{
gameInit();
startUI();
int timer = 0;
bool flag = true;
while (1) {
userClick();
timer += getDelay();
if (timer > 20) {
flag = true;
timer = 0;
}
if (flag) {
flag = false;
updateWindow();
updateGame();
}
//Sleep(10);
}
system("pause");
return 0;
}
C语言实现植物大战僵尸相关推荐
- 易语言c编译程序集,植物大战僵尸。易语言.版本 2.程序集 窗口程序集1.程序集...
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 植物大战僵尸.易语言 .版本 2 .程序集 窗口程序集1 .程序集变量 a, 整数型 .程序集变量 b, 整数型 .程序集变量 c, 整数型 .程序集变量 ...
- C语言也能写植物大战僵尸
不少同学都玩过<植物大战僵尸>,最近PopCap公司又带来了新版的消息,这次高兴的轮到Xbox的用户了,日前PopCap公司公布了<植物大战僵尸>XBLA版的截图,这个版本的& ...
- C语言手写-植物大战僵尸
植物大战僵尸,是一个非常经典的小游戏,初学者从零开始,开发一个自己的植物大战僵尸,还是非常值得期待的!可以作为自己的课设,也可以用来快速提升自己的项目开发能力. 项目效果(详细视频教程-下载素材-点这 ...
- 女友让我破解植物大战僵尸!我干脆撸了一款一样的....翻身舔狗把歌唱呀
今天给大家分享的开源项目可以说非常适合入门,还比较好玩,更是一个有故事的项目.既能满足想学习的读者,又能满足那些喜欢八卦的读者. 提到植物大战僵尸相信大部分读者都不陌生,可以说是塔防类游戏的鼻祖.就鸟 ...
- 基于python开发植物大战僵尸
目录 摘要 2 一, 引言 3 1.1中国游戏产业的现状 3 1.2中国游戏产业的未来发展局势 4 1.3植物大战僵尸游戏的发展状况 4 二.系统结构 5 2.1 Python3.8.2 IDLE 简 ...
- 前女友让我撸个植物大战僵尸,我一怒之下把代码开源了...
今天给大家分享的开源项目可以说非常适合入门,还比较好玩,更是一个有故事的项目.既能满足想学习的读者,又能满足那些喜欢八卦的读者. 提到植物大战僵尸相信大部分读者都不陌生,可以说是塔防类游戏的鼻祖.就鸟 ...
- java设计建议植物大战僵尸_基于Java的游戏设计之植物大战僵尸
植物大战僵尸这款游戏相信大家或多或少都玩过,那么大家有没有想过自己尝试着做一下植物大战僵尸的游戏设计呢.本文将基于Java语言为大家展示如何开发出植物大战僵尸游戏的简易版本,主要内容包括规则.对象.功 ...
- 植物大战僵尸1辅助,JS版
植物大战僵尸1辅助,js版 使用简易引擎 + js开发,植物大战僵尸1单击版游戏辅助工具 本程序源代码:https://gitee.com/yisin/zwdzjs1tool 本软件使用[简易引擎JS ...
- 植物大战僵尸java 僵尸_生存僵尸启示录:文字,路径和基本动画
植物大战僵尸java 僵尸 本系列的第一篇文章介绍了SVG的基础知识,这是HTML5的一个被忽视,未被重视的方面. 尽管不同的浏览器以不同的方式实现SVG,但是上一指南介绍了如何创建文本,合并SVG图 ...
最新文章
- 如何下载图片新闻并将其写入文件
- 让您的Eclipse具有千变万化的外观
- Android 性能优化——布局优化
- 详解Python中的JSON以及在Python中使用JSON
- 红米Note 7 Pro在印度首销迅速售罄
- vue开发看这篇文章就够了 vue知识大全
- django分页-Paginator类
- JavaScript之局部变量和局部函数
- 中专生计算机职业素养论文,中职学生的职业素养
- 初识马尔科夫链,原来是这样的
- HT6221发送红外HS0038解码程序
- 每周推荐短视频:道哥表达了对自动驾驶技术的感恩之情
- 硬件探索——数字钟的设计与制作
- WDF框架系列:同步域,运行级别
- android kodi,XBMC之Android硬解之路
- 打印一本200页的书要多少钱?
- 通过微信公众号远程控制设备STM32+NB模组方案
- html在图片上半透明磨砂,有没有办法在HTML内容上实现磨砂玻璃浮动div(类IO7)...
- Airtest+Poco+Pytest框架搭建1
- 三星note升级android9,Verizon版三星Note 9正式升级Android 10
热门文章
- 典型相关分析(Canonical Correlation Analysis,CCA)原理及Python、MATLAB实现
- 【转载】——Samba
- 【C++入门必看】C++从0到1入门编程
- 基于PyTorch实现PointNet++
- 初次办流量卡,怎么才能区分是不是物联卡?
- C语言malloc()和free()函数
- 士兵队列训练问题(队列--先进先出)
- 前端知识——js部分
- 北大软微19计算机考研招生,19年北大软微初试高分学长学姐经验贴
- 2021年哈工程计算机考研经验分享(从初试到录取)