一.项目介绍

目前只完成了

/*
* 开发日志
* 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语言实现植物大战僵尸相关推荐

  1. 易语言c编译程序集,植物大战僵尸。易语言.版本 2.程序集 窗口程序集1.程序集...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 植物大战僵尸.易语言 .版本 2 .程序集 窗口程序集1 .程序集变量 a, 整数型 .程序集变量 b, 整数型 .程序集变量 c, 整数型 .程序集变量 ...

  2. C语言也能写植物大战僵尸

    不少同学都玩过<植物大战僵尸>,最近PopCap公司又带来了新版的消息,这次高兴的轮到Xbox的用户了,日前PopCap公司公布了<植物大战僵尸>XBLA版的截图,这个版本的& ...

  3. C语言手写-植物大战僵尸

    植物大战僵尸,是一个非常经典的小游戏,初学者从零开始,开发一个自己的植物大战僵尸,还是非常值得期待的!可以作为自己的课设,也可以用来快速提升自己的项目开发能力. 项目效果(详细视频教程-下载素材-点这 ...

  4. 女友让我破解植物大战僵尸!我干脆撸了一款一样的....翻身舔狗把歌唱呀

    今天给大家分享的开源项目可以说非常适合入门,还比较好玩,更是一个有故事的项目.既能满足想学习的读者,又能满足那些喜欢八卦的读者. 提到植物大战僵尸相信大部分读者都不陌生,可以说是塔防类游戏的鼻祖.就鸟 ...

  5. 基于python开发植物大战僵尸

    目录 摘要 2 一, 引言 3 1.1中国游戏产业的现状 3 1.2中国游戏产业的未来发展局势 4 1.3植物大战僵尸游戏的发展状况 4 二.系统结构 5 2.1 Python3.8.2 IDLE 简 ...

  6. 前女友让我撸个植物大战僵尸,我一怒之下把代码开源了...

    今天给大家分享的开源项目可以说非常适合入门,还比较好玩,更是一个有故事的项目.既能满足想学习的读者,又能满足那些喜欢八卦的读者. 提到植物大战僵尸相信大部分读者都不陌生,可以说是塔防类游戏的鼻祖.就鸟 ...

  7. java设计建议植物大战僵尸_基于Java的游戏设计之植物大战僵尸

    植物大战僵尸这款游戏相信大家或多或少都玩过,那么大家有没有想过自己尝试着做一下植物大战僵尸的游戏设计呢.本文将基于Java语言为大家展示如何开发出植物大战僵尸游戏的简易版本,主要内容包括规则.对象.功 ...

  8. 植物大战僵尸1辅助,JS版

    植物大战僵尸1辅助,js版 使用简易引擎 + js开发,植物大战僵尸1单击版游戏辅助工具 本程序源代码:https://gitee.com/yisin/zwdzjs1tool 本软件使用[简易引擎JS ...

  9. 植物大战僵尸java 僵尸_生存僵尸启示录:文字,路径和基本动画

    植物大战僵尸java 僵尸 本系列的第一篇文章介绍了SVG的基础知识,这是HTML5的一个被忽视,未被重视的方面. 尽管不同的浏览器以不同的方式实现SVG,但是上一指南介绍了如何创建文本,合并SVG图 ...

最新文章

  1. 如何下载图片新闻并将其写入文件
  2. 让您的Eclipse具有千变万化的外观
  3. Android 性能优化——布局优化
  4. 详解Python中的JSON以及在Python中使用JSON
  5. 红米Note 7 Pro在印度首销迅速售罄
  6. vue开发看这篇文章就够了 vue知识大全
  7. django分页-Paginator类
  8. JavaScript之局部变量和局部函数
  9. 中专生计算机职业素养论文,中职学生的职业素养
  10. 初识马尔科夫链,原来是这样的
  11. HT6221发送红外HS0038解码程序
  12. 每周推荐短视频:道哥表达了对自动驾驶技术的感恩之情
  13. 硬件探索——数字钟的设计与制作
  14. WDF框架系列:同步域,运行级别
  15. android kodi,XBMC之Android硬解之路
  16. 打印一本200页的书要多少钱?
  17. 通过微信公众号远程控制设备STM32+NB模组方案
  18. html在图片上半透明磨砂,有没有办法在HTML内容上实现磨砂玻璃浮动div(类IO7)...
  19. Airtest+Poco+Pytest框架搭建1
  20. 三星note升级android9,Verizon版三星Note 9正式升级Android 10

热门文章

  1. 典型相关分析(Canonical Correlation Analysis,CCA)原理及Python、MATLAB实现
  2. 【转载】——Samba
  3. 【C++入门必看】C++从0到1入门编程
  4. 基于PyTorch实现PointNet++
  5. 初次办流量卡,怎么才能区分是不是物联卡?
  6. C语言malloc()和free()函数
  7. 士兵队列训练问题(队列--先进先出)
  8. 前端知识——js部分
  9. 北大软微19计算机考研招生,19年北大软微初试高分学长学姐经验贴
  10. 2021年哈工程计算机考研经验分享(从初试到录取)