首页 > 其他分享 >C 实现植物大战僵尸(一)

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

时间:2024-12-26 20:31:20浏览次数:6  
标签:PLANT 僵尸 int 植物 大战 WIDTH msg GRASS MARGIN

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)
        //卡片宽度约 65
        putimage(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 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;
}

运行结果

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

运行结果

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

标签:PLANT,僵尸,int,植物,大战,WIDTH,msg,GRASS,MARGIN
From: https://blog.csdn.net/qq_44868502/article/details/144752262

相关文章

  • 【Unity 像素风格植物图标资源包】Vegetation Icons 32×32 Pixel Art 包含大量32x32
    VegetationIcons32×32PixelArt是一款专为Unity开发者设计的像素风格植物图标资源包。该插件包含大量32x32像素大小的植物图标,适用于2D游戏中需要使用植物、树木、花卉等自然元素的场景。无论是为游戏中的UI、物品栏、任务列表还是资源管理系统提供图标,还是作为游戏内环......
  • 植物大战僵尸阳光调整教程
    这是一篇AI文章,其中代码是我两年前所写,但博文本人懒得写了一、引言《植物大战僵尸》作为一款风靡全球的经典塔防游戏,以其丰富的玩法和策略性深受广大玩家喜爱。在游戏中,阳光作为关键资源,用于购买各种植物来抵御僵尸的进攻,其重要性不言而喻。然而,获取阳光的过程有时会受到僵尸的......
  • 【开源免费】基于SpringBoot+Vue.JS植物健康系统(JAVA毕业设计)
    本文项目编号T095,文末自助获取源码\color{red}{T095,文末自助获取源码}......
  • 惊叹!冬至物流大战,哪些办公软件能助力团队知识共享?
    一、前言冬至期间,物流行业面临着巨大的挑战与机遇。电商平台的促销活动让订单如潮水般涌来,物流J人团队需要高效协作,确保每一个包裹都能准确无误且迅速地送达客户手中。在这个关键时期,合适的可视化团队协作办公软件成为了提升工作效率与个人学习效率的得力助手。本文将站在J人......
  • 用Python实现经典游戏——飞机大战(代码附在文末)
    引言在Python中,Pygame是一个非常流行的游戏开发库,适合初学者入门。本文将带你一步步实现一个经典的“飞机大战”游戏,并逐步优化代码,增加关卡系统、难度递增等功能。通过本文,你将学习到如何使用Pygame创建游戏窗口、处理用户输入、实现精灵类、碰撞检测以及游戏逻辑的优化。1......
  • Python-基于Pygame的小游戏(坦克大战-1.0(世界))(一)
    前言:创作背景-《坦克大战》是一款经典的平面射击游戏,最初由日本游戏公司南梦宫于1985年在任天堂FC平台上推出。游戏的主题围绕坦克战斗,玩家的任务是保卫自己的基地,同时摧毁所有敌人的坦克。游戏中有多种地形和敌人类型,玩家可以通过获取道具来强化坦克和基地。此外,游戏还支持......
  • 高级Python游戏开发:创建一款多人对战坦克大战
    在本教程中,我们将用Python的Pygame库开发一款高级的坦克大战游戏。这款游戏支持多人对战、碰撞检测、子弹射击以及地图障碍生成,适合作为学习Python高级游戏开发的练习项目。一、游戏功能概述多人对战模式:玩家可以操作坦克,在同一屏幕上互相攻击。子弹射击:坦克可以发射子......
  • 学生大战期望题目¿h,艰难取胜。(UVA10529)
    /ll某事件\(A\)第一次发生的期望次数\(E(A)=\frac{1}{P(A)}\)。\(\color{Red}({1})\)所以设\(dp_i\)为连续放好\(i\)个的期望。显然有\(dp_0=0\)。由\(\color{Red}({1})\)得到有\(dp_1=\dfrac{1}{1-p_l-p_r}\)。所以同理对于一个骨牌不倒的期望次数为\(\dfra......
  • C# 探险之旅:第五节 - 布尔运算:真假美猴王的逻辑大战
    嘿,勇敢的探险家们!欢迎再次踏上我们的C#奇妙之旅。今天,我们要进入一片神秘而充满逻辑的森林——布尔运算的王国。想象一下,你不仅是一位编码勇士,还是一位判断真假的福尔摩斯,让我们一起揭开“布尔运算”的神秘面纱吧!什么是布尔运算?在编程的世界里,布尔运算就像是侦探手中的放大镜......
  • springboot植物健康系统(代码+数据库+LW)
    摘要随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了植物健康系统的开发全过程。通过分析植物健康系统管理的不足,创建了一个计算机管理植物健康系统的方案。文章介绍了植物健康系统的系统分析部分,包括可行性分析等,系统设计部分......