一.项目介绍

目前只完成了

/*
* 开发日志
* 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;

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐