【C++项目】基于Cocos2d-x 4.0的 植物大战僵尸 Plants vs Zombies

本文详细介绍了使用C++与Cocos2d-x框架开发《植物大战僵尸》游戏的过程,包括项目搭建、游戏逻辑设计及各类游戏元素的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在这里插入图片描述

前言

创建cmake 俩条指令


 cocos new PlantsVsZombies -l cpp -p com.test.games   // 在空文件下输入cmd 

cmake  .. -G "Visual Studio 17 2022" -AWin32 // 在proj.win32 目录下输入cmd

环境

在这里插入图片描述

实现需要的前置知识

  • C++语法: 面向对象 、 stl的vector 和 string 、 auto、指针 , 需要有一定的c++语言基础
  • cocos2d:类的继承、节点和精灵 场景这些继承关系、调度函数、回调函数、一些基本操作,plist 文件的使用。

实现思路

把cocos2d 的部分 和 代码逻辑分开 ,采用面向对象的模式

文件架构

在这里插入图片描述

场景类

场景类:登录、注册、加载、主菜单、帮助、声音设置、游戏选择植物、游戏、胜利、失败、 鼠标层
在这里插入图片描述
界面这里放游戏界面的 , 比较有难度
GameScene.h

#ifndef __GAME_SCENE_H__
#define __GAME_SCENE_H__
#include "Other//PlantsVsZombies.h"
#include "cocos2d.h"
using namespace cocos2d;
#include "ui/CocosGUI.h"
using namespace ui;
class GameScene : public cocos2d::Scene {
public:
    //PlantVsZombies* plants_vs_zombies;
    static cocos2d::Scene* createScene();
    virtual void update(float update_time);
    virtual bool init();
    void init_data();
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    bool onTouchBegan(Touch* touch, Event* unused_event);
    void onTouchMoved(Touch* touch, Event* unused_event);
    void onTouchEnded(Touch* touch, Event* unused_event);
    int row_plant_cnt(int row_); // 获取这一路的植物数量。
    void plant_down(int row_, int col_, PlantType plant_type);
    void plant(int row_, int col_, PlantType plant_type);
    Sprite* mouse_sprite;
    PlantType selete_plant_type;
    // implement the "static create()" method manually
    CREATE_FUNC(GameScene);
    Label* sun_label;
};  

#endif // __HELLOWORLD_SCENE_H__

GameScene.cpp

#include "Scene/GameScene.h"
#include "AudioEngine.h"
#include "Other/Data.h"
#include "Scene/GameOverScene.h"
#include "Scene/GameVictorScene.h"
USING_NS_CC;
PlantVsZombies* plants_vs_zombies = new PlantVsZombies;
static int numofsum = 0; //存储阳光数量

Scene* GameScene::createScene() {
    return GameScene::create();
}

// on "init" you need to initialize your instance
bool GameScene::init() {
    if (!Scene::init()) {
        return false;
    }
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    AudioEngine::stopAll();
    //音乐播放
    AudioEngine::preload("Music/game.mp3");
    auto game_BGM = AudioEngine::play2d("Music/game.mp3", true);


    this->init_data();
    //鼠标 监听事件
    auto listener = EventListenerTouchOneByOne::create();
    listener->onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan, this);
    listener->onTouchMoved = CC_CALLBACK_2(GameScene::onTouchMoved, this);
    listener->onTouchEnded = CC_CALLBACK_2(GameScene::onTouchEnded, this);
    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);

    //创建背景图 并且设置坐标为左起点
    auto background_sprite = Sprite::create("Games/bg1.jpg");
    background_sprite->setPosition(visibleSize.width / 2.0f + 150.0f, visibleSize.height / 2.0f);
    background_sprite->setScale(1.32f);
    this->addChild(background_sprite, 0);

     卡片与阳光框 Sprite对象
    auto card_bar_sprite = Sprite::create("Games/ChooserBackground.png");
    card_bar_sprite->setPosition(Vec2(visibleSize.width / 2.0f - 160.0f, visibleSize.height / 2.0f + 120.0f));
    card_bar_sprite->setScale(1.3f);
    background_sprite->addChild(card_bar_sprite, 1);

    //覆盖初始阳光
    auto card_bar_sprite2 = Sprite::create("Games/sunNum.png");
    card_bar_sprite2->setPosition(Vec2(visibleSize.width / 2.0f - 320.0f, visibleSize.height / 2.0f + 100.0f));
    card_bar_sprite2->setScale(1.3f);
    background_sprite->addChild(card_bar_sprite2, 2);

    // 阳光数量
    sun_label = Label::createWithSystemFont(std::to_string(numofsum), "TypeFace/Marker Felt.ttf", 15);
    sun_label->setPosition(Vec2(visibleSize.width / 2.0f - 320.0f, visibleSize.height / 2.0f + 100.0f));
    background_sprite->addChild(sun_label, 3);
    sun_label->setColor(Color3B::BLACK);
    sun_label->setString(std::to_string(sun_total));   //用于动态修改阳光数量

    // 卡片槽 Menu对象
    // 铲子 Menu对象
   /* auto shovelMenu = MenuItemImage::create("Games/shovel.png", "Games/shovel.png", CC_CALLBACK_1(GameScene::menuCloseCallback, this));
    auto ShovelMenu = Menu::create(shovelMenu, NULL);
    ShovelMenu->setPosition(visibleSize.width * 0.63f, visibleSize.height * 0.92f);
    this->addChild(ShovelMenu, 1);*/
    // 确认 Menu对象

    //场景绑定
    plants_vs_zombies->init_scene(this);
    this->addChild(plants_vs_zombies);

    //汽车创建
    for (int i = 0; i < 5; i++) {
        Car* temp_car = new Car;
        temp_car->sprite_init(Vec2(car_positon[i]));
        temp_car->sprite->setScale(1.8f);
        plants_vs_zombies->addChild(temp_car->sprite);
        temp_car->row = i; //设置为第n行
        car_date.push_back(temp_car);
    }


    SunFlowerCard* card1 = new SunFlowerCard;    //sunflower card
    card1->sprite_init("SunFlower", Vec2(135, 555));
    this->addChild(card1->sprite, 2);
    card_date.push_back(card1);
    PeashooterCard* card2 = new PeashooterCard;   //peashooter card
    card2->sprite_init("PeaShooter", Vec2(185, 555));
    this->addChild(card2->sprite, 2);
    card_date.push_back(card2);
    WallNutCard* card3 = new WallNutCard;     //wallnut card
    card3->sprite_init("WallNut", Vec2(235, 555));
    this->addChild(card3->sprite, 2);
    card_date.push_back(card3);


    auto sp1 = Sprite::create("Games/loading/timerBar2.png");
    auto sp3 = Sprite::create("Games/loading/FlagMeterLevelProgress.png");
    auto load_bar = ProgressTimer::create(Sprite::create("Games/loading/timerBar1.png")); // 加入进度条精灵
    sp1->setScale(2);
    sp3->setScale(2);
    load_bar->setScale(2);
    sp3->setPosition(Vec2(800, 30));
    //三个默认设置 一般不改
    load_bar->setType(ProgressTimer::Type::BAR); //方向
    load_bar->setBarChangeRate(Vec2(1, 0));
    load_bar->setMidpoint(Vec2(0, 0));
    load_bar->runAction(ProgressFromTo::create(75, 0, 100)); // 时间  起点百分百   终点百分比
    load_bar->setPosition(Vec2(800, 15));
    sp1->setPosition(Vec2(800, 15));
    this->addChild(load_bar, 3);
    this->addChild(sp1, 2);
    this->addChild(sp3, 2);
    plants_vs_zombies->create_sun(300, 300);

    /*auto sp_g1 = Sprite::create("Games/loading/LargeWave.png");
    auto sp_g2 = Sprite::create("Games/loading/FinalWave.png");
    sp_g1->setPosition(visibleSize.width / 2, visibleSize.height / 2);
    sp_g2->setPosition(visibleSize.width / 2, visibleSize.height / 2);
    sp_g1->setScale(4);
    sp_g1->setVisible(false);
    this->addChild(sp_g1 , 4);
    sp_g2->setScale(2);
    sp_g1->setVisible(false);
    this->addChild(sp_g2, 4);*/

    this->scheduleUpdate();
    return true;
}

void GameScene::init_data() { 
    auto visibleSize = Director::getInstance()->getVisibleSize();
    //坐标初始化 
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 9; j++) {
            float ii = 0.15f * i + 0.15f, jj = 0.21f + j * 0.09f;
            plant_position[i][j] = Vec2(visibleSize.width * jj, visibleSize.height * ii);
        }
    }
    //僵尸坐标初始  下标代表row   保证起点不同  出发的时间不同
    zombie_positon[0] = Vec2(1400, visibleSize.height * (0.15f));
    zombie_positon[1] = Vec2(1300, visibleSize.height * (0.15f * 1 + 0.17f));
    zombie_positon[2] = Vec2(1000, visibleSize.height * (0.15f * 2 + 0.17f));
    zombie_positon[3] = Vec2(1000, visibleSize.height * (0.15f * 3 + 0.20f));
    zombie_positon[4] = Vec2(1200, visibleSize.height * (0.15f * 4 + 0.20f));
    car_positon[0] = Vec2(visibleSize.width * 0.12f, visibleSize.height * (0.15f));
    car_positon[1] = Vec2(visibleSize.width * 0.12f, visibleSize.height * (0.15f * 1 + 0.17f));
    car_positon[2] = Vec2(visibleSize.width * 0.12f, visibleSize.height * (0.15f * 2 + 0.17f));
    car_positon[3] = Vec2(visibleSize.width * 0.12f, visibleSize.height * (0.15f * 3 + 0.20f));
    car_positon[4] = Vec2(visibleSize.width * 0.12f, visibleSize.height * (0.15f * 4 + 0.20f));
}

bool bg1 = false, bg2 = false;

void GameScene::update(float update_time) {
    
    if (plants_vs_zombies->is_pass_time2 && !bg1) {
        auto visibleSize = Director::getInstance()->getVisibleSize();
        Vec2 origin = Director::getInstance()->getVisibleOrigin();
        bg1 = true;
       /* auto sp_g1 = Sprite::create("Games/loading/LargeWave.png");
        sp_g1->setPosition(visibleSize.width / 2, visibleSize.height / 2);
        sp_g1->setScale(4);
        sp_g1->setOpacity(254);
        this->addChild(sp_g1, 4);
        auto fadeOut = FadeOut::create(1.0f);
        sp_g1->runAction(Sequence::create(DelayTime::create(2),RemoveSelf::create()));*/

        auto game_BGM = AudioEngine::play2d("Music/finalwave.ogg", false);
    }

    if (plants_vs_zombies->is_pass_time3 && !bg2 ) {
        auto visibleSize = Director::getInstance()->getVisibleSize();
        bg2 = true;

        auto game_BGM = AudioEngine::play2d("Music/awooga.ogg", false);
       /* auto sp_g2 = Sprite::create("Games/loading/FinalWave.png");
        sp_g2->setPosition(visibleSize.width / 2, visibleSize.height / 2);
        sp_g2->setScale(2);
        this->addChild(sp_g2, 4);*/
    }

    for (auto& it : card_date) {
        if (it->judge_cd()) { // 如果在cd状态
            it->time_count += update_time;
            if (it->card_cd <= it->time_count) {
                it->set_is_cd(false);
                it->time_count = 0.0;
            }
        }
    }

    if (plants_vs_zombies->is_game_fail == true) { // 判断游戏失败
        zombie_date.clear();
        plant_date.clear();
        bullet_date.clear();
        card_date.clear();
        car_date.clear();
        sun_date.clear();
        for (auto& it : plant_judge_position)
        {
            for (auto& elem : it)
            {
                elem = false;
            }
        }
        //delete plants_vs_zombies;
        sun_total = 50;
        Director::getInstance()->replaceScene(GameOverScene::create());
        plants_vs_zombies = new PlantVsZombies();
    }
    if (plants_vs_zombies->is_game_success == true) { // 判断游戏胜利
        zombie_date.clear();
        plant_date.clear();
        bullet_date.clear();
        card_date.clear();
        car_date.clear();
        sun_date.clear();
        for (auto& it : plant_judge_position)
        {
            for (auto& elem : it)
            {
                elem = false;
            }
        }
        sun_total = 50;
        Director::getInstance()->replaceScene(GameVictorScene::create());
        plants_vs_zombies = new PlantVsZombies();
    }

}

void GameScene::menuCloseCallback(Ref* pSender) {
    Director::getInstance()->end();
}

bool GameScene::onTouchBegan(Touch* touch, Event* unused_event) {
    auto touch_position = touch->getLocation();
    for (auto& it : sun_date) {//这里查看鼠标点击位置是否有阳光
        if (it->judge_delete())	continue;
        double width_ = it->sprite->getContentSize().width / 2;
        double height_ = it->sprite->getContentSize().height / 2;
        Vec2 SunPosition_ = it->sprite->getPosition();
        auto offset_ = SunPosition_ - touch_position;
        if ((fabs(offset_.x) <= width_) && (fabs(offset_.y) <= height_)) {  //说明鼠标点击位置是阳光所在位置
            sun_total += 50;
            it->run_sun_collect_animation();
            this->sun_label->setString(to_string(sun_total));
            return true;
        }
    }

    //这里查看鼠标点击的位置是不是植物卡片的位置
    for (auto& it : card_date) {
        if (it->judge_cd() || mouse_sprite != nullptr) continue;
        double width_ = it->sprite->getContentSize().width / 2;
        double height_ = it->sprite->getContentSize().height / 2;
        Vec2 card_positon = it->sprite->getPosition();
        auto offset_ = card_positon - touch_position;
        if ((fabs(offset_.x) <= width_) && (fabs(offset_.y) <= height_)) {
            switch (it->card_type) {
            case CardType::card_type_Peashooter:
            {
                Peashooter* pe = new Peashooter;
                mouse_sprite = pe->run_plant_animation(it->sprite->getPosition());
                this->addChild(mouse_sprite, 1);
                selete_plant_type = PlantType::Peashooter;
                break;
            }
            case CardType::card_type_SunFlower:
            {
                SunFlower* pe = new SunFlower;
                mouse_sprite = pe->run_plant_animation(it->sprite->getPosition());
                this->addChild(mouse_sprite, 1);
                selete_plant_type = PlantType::SunFlower;
                break;
            }
            case CardType::card_type_WallNut:
            {
                WallNut* pe = new WallNut;
                mouse_sprite = pe->run_plant_animation(it->sprite->getPosition());
                this->addChild(mouse_sprite, 1);
                selete_plant_type = PlantType::WallNut;
                break;
            }
            default: {
                break;
            }
            }
            return true;
        }
    }
    return false;
}

void GameScene::onTouchMoved(Touch* touch, Event* unused_event) {
    if (mouse_sprite != nullptr && selete_plant_type != PlantType::plant_type_none) {
        int row_, col_;
        mouse_sprite->setPosition(touch->getLocation());
        if (plants_vs_zombies->judge_vec2_in_row_col(touch->getLocation(), row_, col_)) { //判断鼠标是否在草地区域
            if (plant_judge_position[row_][col_] == false)	mouse_sprite->setPosition(plant_position[row_][col_]);
            else {
                mouse_sprite->setPosition(touch->getLocation());
            }
        }
        else {
            mouse_sprite->setPosition(touch->getLocation());
        }
    }
}


void GameScene::onTouchEnded(Touch* touch, Event* unused_event) {
    if (mouse_sprite != nullptr && selete_plant_type != PlantType::plant_type_none) {
        int row_, col_;
        if (plants_vs_zombies->judge_vec2_in_row_col(touch->getLocation(), row_, col_)) {//鼠标位置在坑中 
            if (plants_vs_zombies->judge_plant(row_, col_) == false) {
                plant_down(row_, col_, selete_plant_type);
            }
        }
        selete_plant_type = PlantType::plant_type_none;
    }
    this->removeChild(mouse_sprite);
    mouse_sprite = nullptr;
}

//种植 + 判断
void GameScene::plant_down(int row_, int col_, PlantType plant_type_) {
    if (plant_judge_position[row_][col_] == true)	return;

    //根据类型  获取卡片对象
    Card* temp_card = nullptr;
    int idx = -1;
    switch (plant_type_) {
    case PlantType::SunFlower:
    {
        temp_card = card_date[0];  // 太阳花卡片
        idx = 0;
        break;
    }
    case PlantType::Peashooter:
    {
        temp_card = card_date[1]; // 豌豆卡片
        idx = 1;
        break;
    }
    case PlantType::WallNut:
    {
        temp_card = card_date[2]; // 坚果卡片
        idx = 2;
        break;
    }
    default:
        break;
    }
    if (!temp_card->judge_cd()) { //不在冷却时间
        //判断当前的阳光是否足够种植该植物
        if (sun_total >= temp_card->sun_cost) { //阳光足够
            sun_total -= temp_card->sun_cost;
            this->sun_label->setString(to_string(sun_total));
            card_date[idx]->set_is_cd(true); // 设置卡片为cd状态
            card_date[idx]->init_card_cd_animation();
            plant(row_, col_, plant_type_);
            AudioEngine::play2d("Music/plant2.ogg", false);
            return;
        }
    }
    //plant(row_, col_, plant_type_);
}

void GameScene::plant(int row_, int col_, PlantType plant_type) {
    if (plant_judge_position[row_][col_])
    {
        return;
    }
    plant_judge_position[row_][col_] = true;
    //float cow_to_x[9] = { 0.21 , 0.30 , 0.39 , 0.49 , 0.58 , 0.67 , 0.755 , 0.84 , 0.94 };
    //float row_to_y[5] = { 0.15 , 0.30 , 0.46 , 0.63 , 0.79 };
    //int x_position = Director::getInstance()->getVisibleSize().width * cow_to_x[col_];
    //int y_position = Director::getInstance()->getVisibleSize().height * row_to_y[row_];
    Plant* temp_push = NULL;
    switch (plant_type)
    {
    case PlantType::SunFlower:
        temp_push = new SunFlower();
        this->addChild(temp_push->run_plant_animation(plant_position[row_][col_]));
        temp_push->set_plant_type(PlantType::SunFlower);
        break;
    case PlantType::WallNut:
        temp_push = new WallNut();
        this->addChild(temp_push->run_plant_animation(plant_position[row_][col_]));
        temp_push->set_plant_type(PlantType::WallNut);
        break;
    case PlantType::Peashooter:
        temp_push = new Peashooter();
        this->addChild(temp_push->run_plant_animation(plant_position[row_][col_]));
        temp_push->set_plant_type(PlantType::Peashooter);
        break;
    default:
        break;
    }
    plant_date.emplace_back(temp_push);
}


int row_plant_cnt(int row_) {
    int cnt = 0;
    for (auto& it : plant_date) {
        if (it->get_row() != row_) continue;
        else cnt++;
    }
    return cnt;
} // 获取这一路的植物数量。

僵尸类

  • 采用继承, 可以节省很多代码量
    ZombieBase.h
#ifndef ZOMBIEBASE_H_
#define ZOMBIEBASE_H_

#include "cocos2d.h"
#include "Plant/PlantBase.h"
using namespace cocos2d;

enum ZombieType {
	zombie_type_none = -1,
	zombie_type_normal , // 普通僵尸
	zombie_type_conehead,// 锥形头盔僵尸
	zombie_type_buckethead,// 铁桶僵尸
	zombie_type_flag //旗帜僵尸
	// Other Zombie
};

//class Plant;

class Zombie :public Sprite {
public:
	Zombie() = default;
	~Zombie() = default;
	virtual void zombie_init();// 变量初始化
	void sprite_init(const Vec2& position_);//精灵初始化
	virtual void set_scale(float val_);// 设置精灵的大小
	virtual Vec2 get_position() const; 
	virtual double get_x();
	virtual double get_y();
	virtual int get_blood() const;
	virtual int get_attack_time() const;
	virtual int get_attack_num() const;
	virtual double get_move_rate() const;
	//加载动作
	virtual void init_zombie_move_animation();
	virtual void init_zombie_eat_animation();
	virtual void init_zombie_wait_animation();
	virtual void init_zombie_die_animation();
	//运行动作
	virtual bool run_zombie_move_animation();
	virtual void run_zombie_eat_animation();
	virtual void run_zombie_wait_animation();
	virtual void run_zombie_die_animation();
	//设置状态
	virtual bool judge_eat() const;
	virtual bool judge_dead() const;
	virtual bool judge_move() const;
	virtual bool judge_wait() const;

	//变量
	static int zombie_id; // 僵尸编号 唯一性
	double blood;	// 血量
	double  attack_time;// 间隔攻击时间
	double time_count;// 时间计时器  统计使用精灵 到结束
	double attack_num; //攻击值
	float move_rate; // 移速
	
	Vec2 position;//二维位置 变量
	Sprite* sprite;// 精灵变量

	//动作变量 
	Animate* zombie_move_animate;
	Animate* zombie_eat_animate;
	Animate* zombie_wait_animate;
	Animate* zombie_die_animate;

	Vector<SpriteFrame*> zombie_wait;
	Vector<SpriteFrame*> zombie_move;
	Vector<SpriteFrame*> zombie_eat;
	Vector<SpriteFrame*> zombie_die;
	//僵尸类型 enum 结合switch遍历
	ZombieType zombie_type;
	//僵尸状态变量
	bool is_move;//僵尸是否处于行走状态
	bool is_eat;//僵尸是否处于攻击状态
	bool is_dead;//僵尸是否已经死亡
	bool is_wait;//僵尸是否处于等待进攻状态
	int row;
};
#endif // ZOMBIEBASE_H_

ZombieBase.cpp

#include "Zombie/ZombieBase.h"
int Zombie::zombie_id;

void Zombie::zombie_init() {

}// 变量初始化


// 精灵初始化  参数1 精灵图片名字  参数2 : 二维坐标名字
void Zombie::sprite_init( const Vec2& position_) {
	sprite->setPosition(position_);
}

// 设置精灵的大小  参数:缩放倍数
void Zombie::set_scale(float val_) {
	sprite->setScale(val_);
} 

//获取二维坐标
Vec2 Zombie::get_position() const {
	return position; 
}

//获取二维坐标的X
double Zombie::get_x() {
	return sprite->getPositionX();
}


//获取二维坐标的Y
double Zombie::get_y() {
	return sprite->getPositionY();
}

//获取当前僵尸血量
int Zombie::get_blood() const {
	return blood; 
}

//获取当前僵尸的攻击间隔时间
int Zombie::get_attack_time() const {
	return attack_time; 
}

//获取攻击值
int Zombie::get_attack_num() const {
	return attack_num; 
}

//设置移动速度
double Zombie::get_move_rate() const {
	return move_rate; 
}

//virtual void attack_plant(Plant & plant_); //攻击植物 使植物掉血

// 创建 僵尸移动动画
void Zombie::init_zombie_move_animation() {

}

//创建 僵尸吃植物动画
void Zombie::init_zombie_eat_animation() {

}

//创建僵尸等待动画
void Zombie::init_zombie_wait_animation() {

}

//僵尸 死亡动画
void Zombie::init_zombie_die_animation() {
	
} 

//时间开销分析
//就是额外构造俩对象的开销,不用从内存读数据,和原有的代码差不多
bool Zombie::run_zombie_move_animation() {
	if (is_dead) return false ;
	if (sprite == nullptr || zombie_move_animate == nullptr) return false ;
	is_dead = false;
	is_eat = false;
	is_move = true;
	is_wait = false;
	sprite->stopAllActions();
	sprite->runAction(RepeatForever::create(
	Animate::create(Animation::createWithSpriteFrames(zombie_move, 1.0 / 10)))); // 运行移动动画
	sprite->runAction(MoveTo::create(5.0f,Vec2(-100.0,get_y())));  // 可修改移动速度 
}

void Zombie::run_zombie_eat_animation() {
	if (sprite == nullptr || zombie_move_animate == nullptr) return;
	this->is_dead = false;
	this->is_eat = true;
	this->is_move = false;
	this->is_wait = false;
	sprite->stopAllActions();
	sprite->runAction(RepeatForever::create(
		Animate::create(Animation::createWithSpriteFrames(zombie_eat, 1.0 / 4))));;
}

void Zombie::run_zombie_wait_animation() {
	if (is_dead) return;
	if (sprite == nullptr || zombie_move_animate == nullptr) return;
	is_dead = false;
	is_eat = false;
	is_move = false;
	is_wait = true;
	sprite->stopAllActions();
	sprite->runAction(RepeatForever::create(
		Animate::create(Animation::createWithSpriteFrames(zombie_eat, 1.0 / 5))));
}


void Zombie::run_zombie_die_animation() {
	if (sprite == nullptr || zombie_move_animate == nullptr) return;
	//auto delayt = DelayTime::create(3);
	is_dead = true;
	is_eat = false;
	is_move = false;
	is_wait = false;
	sprite->stopAllActions();
	sprite->runAction(
		Animate::create(Animation::createWithSpriteFrames(zombie_die, 1.0 / 2)));
	sprite->runAction(Sequence::create(DelayTime::create(2.0f), Blink::create(2.0f, 5),RemoveSelf::create(), nullptr));
	removeFromParent();

}
//判断状态
bool Zombie::judge_eat() const {
	return is_eat; 
}

bool Zombie::judge_dead() const {
	return is_dead; 
}

bool Zombie::judge_move() const { 
	return is_move; 
}

bool Zombie::judge_wait() const { 
	return is_wait; 
}

  • 普通僵尸实现 其他的僵尸类似
    NormalZombie.h
#ifndef _NOMALZOMBIE_H_
#define _NOMALZOMBIE_H_

#include "cocos2d.h"
#include "Zombie/ZombieBase.h"
using namespace cocos2d;


//class Plant;

class NormalZombie : public Zombie{
public:
	NormalZombie();
	~NormalZombie() = default;

	virtual void update(float update_time); //调度函数  参数调度时间间隔
	virtual void zombie_init();// 变量初始化

	//重写基类动画
	virtual void init_zombie_move_animation() ;
	virtual void init_zombie_eat_animation();
	virtual void init_zombie_wait_animation();
	virtual void init_zombie_die_animation(); //僵尸死亡动画
	static bool is_run_frame;   //用于判断是否加载帧动画了   防止重复加载
	Vector<SpriteFrame*> getAnimation(const char* format, int count);


};
#endif // ZOMBIEBASE_H_

NormalZombie.cpp

#include "cocos2d.h"
#include "Zombie/NormalZombie.h"
#include "AudioEngine.h"
using namespace cocos2d;
bool NormalZombie::is_run_frame  = false; // 已经初始化  构造函数不用

//构造函数  调用 init函数   然后加载四个init动画
NormalZombie::NormalZombie() {
	//类属性初始化
	this->zombie_init();
	this->init_zombie_move_animation();
	this->init_zombie_die_animation();
	this->init_zombie_eat_animation();
	this->init_zombie_wait_animation();
	sprite = Sprite::create();
	sprite->setScale(1.6f);
	sprite->runAction(RepeatForever::create(zombie_wait_animate));
	is_run_frame = true;
	//执行调度器
	this->scheduleUpdate();
}

void NormalZombie::update(float update_time) {
	time_count += update_time;
	
	//auto move_sound = AudioEngine::play2d("Music/.ogg", true);//没有
	//auto eat_sound = AudioEngine::play2d("Music/chomp.ogg", true);
	if (is_dead) {
		this->unscheduleUpdate();
		removeFromParent();
	}
} //调度函数  参数调度时间间隔

void NormalZombie::zombie_init() {
	this->zombie_id = -1; // 僵尸编号 
	this->blood = 10;	// 血量
	this->attack_time = 2.0;// 间隔攻击时间
	this->time_count = 0.0;// 时间计时器  统计使用精灵 到结束
	this->attack_num = 100; //攻击值
	this->move_rate = 0; // 移速 相对于移动时间
	this->zombie_type = ZombieType::zombie_type_normal; //僵尸类型
	this->is_move = false;//僵尸是否处于行走状态
	this->is_eat = false;//僵尸是否处于攻击状态
	this->is_dead = false;//僵尸是否已经死亡
	this->is_wait = false;//僵尸是否处于等待进攻状
}// 变量初始化

void NormalZombie::init_zombie_move_animation() {
	SpriteFrameCache::getInstance()->addSpriteFramesWithFile("ZomBies/NomalZombie/ZombieMove/Zombie_default.plist");
	zombie_move = getAnimation("Zombie%d.png", 30); // 调用加载图片
	sprite = Sprite::createWithSpriteFrame(zombie_move.front());
	auto animation1 = Animation::createWithSpriteFrames(zombie_move, 1.0 / 10); // 缓存时间 约小则加载越快  
	zombie_move_animate = Animate::create(animation1);
}

void NormalZombie::init_zombie_eat_animation() {
	SpriteFrameCache::getInstance()->addSpriteFramesWithFile("ZomBies/NomalZombie/ZombieAttack/ZombieAttack_default.plist");
	zombie_eat = getAnimation("ZombieAttack%d.png", 20); // 调用加载图片
	//sprite = Sprite::createWithSpriteFrame(frames.front());
	auto animation1 = Animation::createWithSpriteFrames(zombie_eat, 1.0 / 4); // 缓存时间 约小则加载越快  
	zombie_eat_animate = Animate::create(animation1);
}

void NormalZombie::init_zombie_die_animation() {
	SpriteFrameCache::getInstance()->addSpriteFramesWithFile("ZomBies/NomalZombie/ZombieDie/ZombieDie_default.plist");
	zombie_die = getAnimation("ZombieDie%d.png", 9); // 调用加载图片
	//sprite = Sprite::createWithSpriteFrame(frames.front());
	auto animation1 = Animation::createWithSpriteFrames(zombie_die, 1.0f / 2); // 缓存时间 约小则加载越快  
	zombie_die_animate = Animate::create(animation1);
}


void NormalZombie::init_zombie_wait_animation() {
	SpriteFrameCache::getInstance()->addSpriteFramesWithFile("ZomBies/NomalZombie/ZombieMove/Zombie_default.plist");
	zombie_wait = getAnimation("Zombie%d.png", 5); // 调用加载图片
	//sprite = Sprite::createWithSpriteFrame(frames.front());
	auto animation1 = Animation::createWithSpriteFrames(zombie_wait, 1.0 / 5); // 缓存时间 约小则加载越快  
	zombie_wait_animate = Animate::create(animation1);
}


Vector<SpriteFrame*> NormalZombie::getAnimation(const char* format, int count) {
	auto spritecache = SpriteFrameCache::getInstance();
	Vector<SpriteFrame*> animFrames;
	char str[100];
	for (int i = 1; i <= count; i++) {
		sprintf(str, format, i);
		animFrames.pushBack(spritecache->getSpriteFrameByName(str));
	}
	return animFrames;
}

植物类

PlantBase.h

#ifndef _PLANTBASE_H_
#define _PLANTBASE_H_

#include "Bullet/Bullet.h"
#include "Card/Card.h"
#include "cocos2d.h"
using namespace cocos2d;
#include <string>

enum class PlantType {
	plant_type_none = -1,
	SunFlower,// 太阳花
	Peashooter,//豌豆射手
	WallNut, //坚果
	CherryBomb, //樱桃炸弹
	Squash // 窝瓜
};

class Plant : public Sprite {//基类
public:
	Plant() = default; //继承后请同样直接在构造函数内部调用init函数,
			  //获取数据函数将不在保证无法访问未初始化数据,该功能由构造函数承担
	~Plant() = default;
	virtual void plant_init(); // 初始化 数据
	virtual Sprite* sprite_init(const Vec2& position_); // 精灵初始化 在初始化精灵前请先初始化动画
	virtual void set_blood(int val); // 设置血量
	virtual void set_time_count(int val);//设置时间间隔
	virtual void add_interval_time(float time_); //设置触发技能频率 
	virtual void set_plant_type(PlantType type);//设置植物的种类
	virtual void set_is_plan();//设置是否种植
	virtual void set_is_shoot(bool val);//设置is_shoot
	virtual void set_scale(float val_);//设置大小
	virtual void set_interval_time(float val);//增加间隔时间
	virtual void set_rate_time(float val);

	virtual Vec2 get_position() const; // 获取坐标
	virtual double get_x() const;  // 获取X 坐标
	virtual double get_y() const; // 获取Y 坐标
	virtual PlantType get_type() const; // 获取植物类型
	virtual int get_blood() const;//获取当前血量
	virtual bool get_is_shoot(); //获取当前is_shoot
	virtual void by_attack(int val_);//遭受攻击后扣除血量
	virtual float get_rate_time();//获取触发速率
	virtual float get_interval_time();//获取间隔时间
	virtual int get_row();//获取列
	virtual int get_line();//获取行

	virtual void init_plant_animation(); // 加载植物动画
	virtual void set_plant_animation(Animate*); //获取植物动画
	virtual Sprite* run_plant_animation(Vec2 position); // 运行植物动画动画
	virtual Animate* get_plant_animate();//获取植物动画

	virtual void load_animation(std::string pre_path, int number, cocos2d::Vector<cocos2d::SpriteFrame*>& res);//加载动画
	//如果想使用该函数,必须保证.plist文件含有后缀_default
	virtual void set_start_frame(SpriteFrame* frame); //设置第一帧
	virtual void set_is_shoot(double delta);
	virtual void do_action();
	virtual Sprite* get_sprite();

protected:

private:
	//变量
	int blood; //生命值
	bool is_plant; // 判断是否种植
	Vec2 position; // 坐标 
	Sprite* sprite; // 精灵
	PlantType plant_type;//植物类型变量
	Animate* plant_animate; //植物动画变量
	Animate* plant_down_animate; // 种植动画
	SpriteFrame* start_frame; //第一帧动画
	float interval_time;//距离上一次发射的时间
public:
	bool is_shoot;//是否发射子弹的标志
	double rate_time; //触发技能速率
	double time_count;// 时间计时器  统计使用精灵 到结束
	int row;
	int line;
};
#endif // _PLANTBASE_H_

PlantBase.cpp

#include "Plant/PlantBase.h"
#include "Other/Data.h"
#include <string>
#include <iostream>
#include <exception>

void Plant::plant_init() {
}// 初始化 数据 为默认值  

void Plant::set_plant_animation(Animate* animation)
{
	this->plant_animate = animation;
	return;
}

Animate* Plant::get_plant_animate()
{
	return this->plant_animate;
}

void Plant::set_blood(int val_)
{
	this->blood = val_;
}

void Plant::set_time_count(int val_)
{
	this->time_count = val_;
}

void Plant::set_plant_type(PlantType p)
{
	this->plant_type = p;
}

void Plant::set_is_plan()
{
	this->is_plant = false;
}

Sprite* Plant::sprite_init(const Vec2& position_) {
	Size show_range = Director::getInstance()->getVisibleSize();
	//float x_to_line[9] = { 0.21 , 0.30 , 0.39 , 0.49 , 0.58 , 0.67 , 0.755 , 0.84 , 0.94 };
	//float y_to_row[5] = { 0.15 , 0.30 , 0.46 , 0.63 , 0.79 };
	//float x_range = show_range.width;
	//float y_range = show_range.height;
	//for(int i = 0 ; i < 9 ; i++)
	//{
	//	if (position_.x <= x_range * x_to_line[i])
	//	{
	//		this->line = i; // 从0开始
	//		break;
	//	}
	//}
	//for (int i = 0; i < 5; i++)
	//{
	//	if (position.y <= y_range * y_to_row[i])
	//	{
	//		this->row = i; // 从0开始
	//		break;
	//	}
	//}

	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			if (plant_position[i][j] == position_)
			{
				this->line = j;
				this->row = i;
			}
		}
	}
	sprite = Sprite::createWithSpriteFrame(this->start_frame);
	sprite->setPosition(position_);
	this->position = position_;
	return this->sprite;
} // 精灵初始化

Vec2 Plant::get_position() const {
	return this->position;
} // 获取坐标


double Plant::get_x() const {
	return this->position.x;
}  // 获取X 坐标



double Plant::get_y() const {
	return this->position.y;
}// 获取Y 坐标


PlantType Plant::get_type() const {
	return this->plant_type;
} // 获取植物类型

int Plant::get_blood() const {
	return this->blood;
}//获取当前血量

//设置攻击间隔  变量time_为攻击时间间隔
void Plant::set_rate_time(float time_) {
	this->rate_time = time_;
}//设置触发技能频率

void Plant::set_interval_time(float time_)
{
	this->interval_time = time_;
}

//我感觉不太对
//提供重载版本
//设置当前植物的精灵大小为val_
void Plant::set_scale(float val_)
{
	this->sprite->setScale(val_);
	return;
}

void Plant::init_plant_animation() {
	//return this->plant_animate;
} //植物动画


void Plant::by_attack(int val_) {
	blood -= val_;
}

void Plant::load_animation(std::string path, int number, cocos2d::Vector<cocos2d::SpriteFrame*>& res)
{
	SpriteFrameCache::getInstance()->addSpriteFramesWithFile(path);
	auto sprite_cache = SpriteFrameCache::getInstance();
	int pos_path = path.size();
	int pos_end = pos_path;
	while (path[pos_path] != '/')
	{
		if (path[pos_path] == '_')
			pos_end = pos_path;
		pos_path--;
	}
	pos_path++;
	std::string pre = path.substr(pos_path, (pos_end - pos_path));

	for (int i = 1; i <= number; i++)
	{
		std::string test = pre + std::to_string(i) + ".png";
		auto temp = sprite_cache->getSpriteFrameByName(pre + std::to_string(i) + ".png");
		res.pushBack(temp);
	}
	return;
}

void Plant::set_is_shoot(bool val)
{
	this->is_shoot = val;
}

bool Plant::get_is_shoot()
{
	return this->is_shoot;
}

void Plant::set_start_frame(SpriteFrame* frame)
{
	this->start_frame = frame;
	return;
}

//间隔时间增加
void Plant::add_interval_time(float val)
{
	this->interval_time += val;
}

//返回间隔时间
float Plant::get_interval_time()
{
	return this->interval_time;
}

float Plant::get_rate_time()
{
	return (float)this->rate_time;
}

void Plant::do_action()
{

}

Sprite* Plant::run_plant_animation(Vec2 position)
{

 this->init_plant_animation();
 auto sptite = this->sprite_init(position);
 sptite->runAction(RepeatForever::create(this->get_plant_animate()));
 this->set_scale(1.85);
 return sprite;
}

int Plant::get_row()
{
	return this->row;
}

int Plant::get_line()
{
	return this->line;
}

void Plant::set_is_shoot(double delta)
{
	this->add_interval_time(delta);
	int temp2 = this->get_interval_time();
	int temp = this->get_rate_time();
	if (this->get_interval_time() > this->get_rate_time())
	{
		this->set_interval_time(0);
		this->set_is_shoot(true);
	}
}

Sprite* Plant::get_sprite()
{
	return this->sprite;
}

SunFlower.h

#ifndef SUNFLOWER_H
#define SUNFLOWER_H


#include "Plant/PlantBase.h"

class SunFlower :public Plant
{
public:
	SunFlower();
	~SunFlower() = default;

	//初始化僵尸的属性(处理精灵 和动画外的 把基类的搬过来)  然后写在构造函数里面
	virtual void plant_init();

	//调度函数  参数:调度时间间隔 
	virtual void update(float update_time);


	 
	virtual void init_plant_animation(); // 种下后的植物动画
	virtual void init_plant_down_animation(); //植物种植动画并且加上声音



	static bool is_run_frame;// 用于判断是否加入帧动画了  防止调度重复加载
};

#endif // !SUNFLOWER_H

SunFlower.cpp

#include "Plant/SunFlower.h"
#include "Other/PlantsVsZombies.h"
#include <functional>
bool SunFlower::is_run_frame = false;//初始化为false 这个不用写init里面

//构造函数  调用init  然后 初始化动作
SunFlower::SunFlower() {
	this->plant_init();
}

//初始化植物的属性(处理精灵 和动画外的 把基类的搬过来)  然后写在构造函数里面

//属性未修改

//在此修改属性
#define SUNFLOWER_BLOOD 300
#define SUNFLOWER_RATE_TIME 10.0
#define SUNFLOWER_TIME_COUNT 0.0

void SunFlower::plant_init() {
	//is_shoot = true;
	this->set_blood(SUNFLOWER_BLOOD);
	this->set_is_plan();
	this->set_plant_type(PlantType::SunFlower);
	this->set_rate_time(SUNFLOWER_RATE_TIME);
	this->set_time_count(SUNFLOWER_TIME_COUNT);
}

//调度函数  参数:调度时间间隔 
void SunFlower::update(float update_time) {
	if (this->get_blood() <= 0)
	{
		//当前植物将被删除
		removeFromParent();
		this->unscheduleUpdate();
	}
}


void SunFlower::init_plant_animation() {
	cocos2d::Vector<cocos2d::SpriteFrame*> res;
	this->load_animation("Plants/SunFlower/SunFlower_default.plist",18, res);
	this->set_start_frame(res.front());
	auto animation = Animation::createWithSpriteFrames(res, 1.0f / 8);
	this->set_plant_animation(Animate::create(animation));
	return;
} // 种下后的植物动画


void SunFlower::init_plant_down_animation() {

} //植物种植动画并且加上声音




Peashooter.h

#ifndef PEASHOOTER_H
#define PEASHOOTER_H


#include "Plant/PlantBase.h"

class Peashooter :public Plant {
public:
	Peashooter();
	~Peashooter() = default;

	//初始化僵尸的属性(处理精灵 和动画外的 把基类的搬过来)  然后写在构造函数里面
	virtual void plant_init();

	//调度函数  参数:调度时间间隔 
	virtual void update(float update_time);

	virtual void init_plant_animation(); // 种下后的植物动画


	static bool is_run_frame;// 用于判断是否加入帧动画了  防止调度重复加载
};

#endif // !SUNFLOWER_H

Peashooter.cpp

#include "Plant/Peashooter.h"
#include "Other/PlantsVsZombies.h"

bool Peashooter::is_run_frame = false;//初始化为false 这个不用写init里面

//构造函数  调用init  然后 初始化动作
Peashooter::Peashooter() {
	this->plant_init();

	//动作加载 
	is_run_frame = true;
	//调度函数 
}

//初始化植物的属性(处理精灵 和动画外的 把基类的搬过来)  然后写在构造函数里面

#define PEASHOOTER_BLOOD 300
#define PEASHOOTER_RATE_TIME 1.5f  
#define PEASHOOTER_TIME_COUNT 0

void Peashooter::plant_init() {
	this->set_blood(PEASHOOTER_BLOOD);
	this->set_is_plan();
	this->set_plant_type(PlantType::WallNut);
	this->set_rate_time(PEASHOOTER_RATE_TIME);
	this->set_time_count(PEASHOOTER_TIME_COUNT);
	this->set_is_shoot(false);
	this->set_interval_time(0);
}

//调度函数  参数:调度时间间隔 
void Peashooter::update(float update_time) {
	if (this->get_blood() < 0)
	{
		//删除植物
		removeFromParent();
		this->unscheduleUpdate();
	}
	else
	{
		this->set_interval_time(update_time);
		if (this->get_interval_time() > this->get_rate_time())
		{
			this->set_interval_time(-this->get_interval_time());
			this->set_is_shoot(true);
		}
	}
}

void Peashooter::init_plant_animation() {

	cocos2d::Vector<cocos2d::SpriteFrame*> res;
	this->load_animation("Plants/Peashooter/Peashooter_default.plist", 13, res);
	this->set_start_frame(res.front());
	auto animation = Animation::createWithSpriteFrames(res, 1.0f / 8);

	this->set_plant_animation(Animate::create(animation));
	// 把动作复制给变量 
} // 植物动画


阳光类

Sun.h

#ifndef _SUN_H_
#define _SUN_H_

#include <iostream>
#include <vector>
using namespace std;
#include "cocos2d.h"
#include "Plant/PlantBase.h"
using namespace cocos2d;

class Sun : public Sprite{
public:
	Sun();
	~Sun() = default;
	void sun_init(); // 初始化
	virtual void update(float update_time); //调度函数 
	void sprite_init( const Vec2& position_); 
	// 精灵初始化 参1:精灵图片名字  参2:设置的二维坐标
	void init_sun_produce_animation(); // ̫太阳生成动画
	void init_sun_collect_animation(); // 太阳收集动画  moveto到初始化的位置
	void run_sun_produce_animation(); // ̫运行太阳生成动画
	void run_sun_collect_animation(); // 运行太阳收集动画  moveto到初始化的位置
	void run_sun_delete_animation();
	void set_is_delete(); // 设置为可删除
	void set_is_produce(); // 设置为可生成  
	bool judge_prodece(); // 判断能否生成
	bool judge_delete(); // 判断能否删除
	int time_count;// 计时器 用于调度函数
	Animate* sun_produce_animate; // 生成动画变量
	MoveTo * sun_collect_animate; // 收集动画变量
	Sprite* sprite;
	Vec2 position;
	int sun_num; // ̫太阳值
	double sun_time; // 太阳保留时间
	Vector<SpriteFrame*> Sun::getAnimation(const char* format, int count);
private:
	bool is_delete; //  可以删除 :true  不可生成 :false
	bool is_produce; //可以生成 : true  不可生成 : false
};

#endif // _SUM_H_

Sun.cpp

#include "Sun.h"

Sun::Sun() {
	//调用init 动作初始化  
	this->init_sun_produce_animation();
	this->sun_init();
	sprite = Sprite::create();
	sprite->runAction(RepeatForever::create(sun_produce_animate));
	this->scheduleUpdate();
}

void Sun::sun_init() {
	sun_num = 50;
	sun_time = 0; //太阳保留时间
	is_delete = false; 
	is_produce = false; 
	time_count = 0;
} // 初始化函数


//修改系统的时间调度函数  
//参数 update_time 是系统调用该函数 的间隔时间
 void Sun::update(float update_time) {
	 //time_count += update_time;
	 if (judge_delete()) {
		 //run_sun_delete_animation();
		 this->unscheduleUpdate();
	 }
 } //调度函数

 // 精灵初始化函数  参1 :图片名字  参2 : 二维坐标  
void Sun::sprite_init(const Vec2& position_) {
	//sprite = Sprite::create("Sums/Sun3.png");
	 sprite->setPosition(position_); // 随机生成
} 
	
 // 创建太阳生成动画  
void Sun::init_sun_produce_animation() {
	SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Plants/Sun/Sun_default.plist"); //22 
	auto frames = getAnimation("Sun%d.png", 8); // 调用加载图片
	sprite = Sprite::createWithSpriteFrame(frames.front());
	auto animation1 = Animation::createWithSpriteFrames(frames, 1.0f / 8); // 缓存时间 约小则加载越快  
	sun_produce_animate = Animate::create(animation1);
}

//创建太阳收集动画  未完善
 void Sun::init_sun_collect_animation() {
	 sun_collect_animate = MoveTo::create(0.5f,Vec2(100,200));
 } 

 void Sun::run_sun_produce_animation() {
	 sprite->runAction(RepeatForever::create(sun_produce_animate));
 } // ̫运行太阳生成动画

 void Sun::run_sun_collect_animation() {
	 this->sprite->runAction(MoveTo::create(2.0, Vec2(67, 560)));
	 auto seq = Sequence::createWithTwoActions(DelayTime::create(2.2f), RemoveSelf::create());
	 this->sprite->runAction(seq);
	 this->is_delete = true;
	 this->unscheduleUpdate();
 }

 void Sun::run_sun_delete_animation() {
	 auto seq = Sequence::createWithTwoActions(Blink::create(2.0, 5), RemoveSelf::create());
	 sprite->runAction(seq);
	 this->is_delete = true;
	 this->unscheduleUpdate();
 }
//设置为可删除
void Sun::set_is_delete() {
	is_delete = true;
}

//设置为可生成
void Sun::set_is_produce() {
	is_produce = true;
}

bool Sun::judge_prodece() {
	return is_produce;
} // 判断能否生成

bool Sun::judge_delete() {
	return is_delete;
} // 判断能否删除


Vector<SpriteFrame*> Sun::getAnimation(const char* format, int count) {
	auto spritecache = SpriteFrameCache::getInstance();
	Vector<SpriteFrame*> animFrames;
	char str[100];
	for (int i = 1; i <= count; i++) {
		sprintf(str, format, i);
		animFrames.pushBack(spritecache->getSpriteFrameByName(str));
	}
	return animFrames;
}

子弹类

Bullet.h


#ifndef BULLET_H
#define BULLET_H

#include "cocos2d.h"
#include "Zombie/ZombieBase.h"
#include "Plant/PlantBase.h"
#include "AudioEngine.h"

using namespace cocos2d;

class Bullet : public Sprite {
public:
	Bullet();//构造函数

	~Bullet() = default;
	virtual void bullet_init();// 初始化函数
	virtual void update(double update_time); // 调度函数 参数:调度间隔时间
	virtual void bullet_move_animation(); //创建子弹移动动画

	//精灵初始化  参1 图片名字  参2:二维坐标
	void sprite_init(const Vec2& position_);
	virtual Vec2 get_position() const; // 获取二维坐标
	virtual double get_x() const; // getPositionX
	virtual double get_y() const;
	virtual void set_crash(); // 设置为判断
	virtual bool judge_crash(); // crash judgement, if ture, delete the bullet
	virtual void set_attack_num(double damage);// 设置子弹伤害
	virtual void delete_sprite(); // 删除精灵 
	int row;
//变量
	Vec2 position; // 二维坐标
	Sprite* sprite; //精灵变量
	double attack_num; // 攻击伤害
	bool is_crash; // 是否碰撞   true:碰撞  false:未碰撞
	Animate* bullet_move_animate; // 子弹移动动作变量
};

#endif // _BULLET_H_


Bullet.cpp


#include "Bullet/Bullet.h"

// 子弹变量 初始化

//构造函数
Bullet::Bullet() {
	//调用init函数
	//调用精灵初始化函数 并且赋值
	sprite = Sprite::create();
	sprite->setScale(2);
	//调用动作初始化函数  并且赋值
	bullet_init();
	this->scheduleUpdate();
}

void Bullet::bullet_init() {
	attack_num = 1; // damage scale
	is_crash = false; // 判断是否碰撞变量 
}

//精灵初始化  参1 精灵图片名字  参2 二维坐标
// 精灵图 使用 豌豆的   大小设置为1  位置
void Bullet::sprite_init(const Vec2& position_) {
	sprite = Sprite::create("Bullet/PeaBullet0.png");
	sprite->setPosition(position_);
	position = position_;
}

//修改系统的时间调度函数  
//参数 update_time 是系统调用该函数 的间隔时间
void Bullet::update(double update_time) {
	//状态 如果is_crash 为true 则删除子弹 并且产生碰撞音效 
} // 

//创建子弹移动动画 
void Bullet::bullet_move_animation() {
	//auto bullet_movement = MoveTo::create(2.0f, Vec2(1000,position.y)); // Position of the leftmost zombie in that row
	//sprite->setScale(1.0f);
	//auto bullet_move_animate = Sequence::create(bullet_callback_audio, bullet_movement, nullptr);
	sprite->runAction(MoveTo::create(4.0f, Vec2(1000, position.y)));
}


//获取二维坐标
Vec2 Bullet::get_position() const {
	return position;
}

//获取精灵 x坐标
double Bullet::get_x() const {
	return sprite->getPositionX();
}

//获取精灵 y 坐标
double Bullet::get_y() const {
	return sprite->getPositionY();
}

// 判断是否碰撞
bool Bullet::judge_crash() {
	return is_crash;
}

//设置碰撞
void Bullet::set_crash() {
	is_crash = true;
}

//设置 攻击伤害   参数:子弹伤害
void Bullet::set_attack_num(double plant_damage) {// damage scale
	this->attack_num = plant_damage;
}

//删除 精灵 is_crash 变量执行  未完善
void Bullet::delete_sprite() {
	if (is_crash) {
		this->removeChild(this, true); // "this" stands for bullet object
	}
}

卡片类

Card.h

#ifndef CARD_H
#define  CARD_H
#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include "Plant/PlantBase.h"

using namespace cocos2d;
using namespace ui;

enum class CardType{
	card_type_none = -1,
	card_type_SunFlower,// 太阳花 0
	card_type_Peashooter,//豌豆射手 1
	card_type_WallNut, //坚果 2
	card_type_CherryBomb, //樱桃炸弹 3
	card_type_Squash // 窝瓜 4
};

//卡片的图像大小为64*90

class Card : public Sprite{
public:
	Card() = default;
	~Card() = default;
	virtual void card_init();//初始化函数
	Sprite* sprite_init(const std::string& name_, const Vec2& position_);
	Sprite* cd_sprite_init(const std::string& name_, const Vec2& position_);
	virtual void init_card_cd_animation(); // 卡片冷却动画
	virtual void init_card_move_animation(const Vec2& destination_); // 卡片点击后卡片移动到已选传送带内 : 选择界面

	virtual void set_is_choose(const bool& state_); //设置状态  参数为状态 
	virtual void set_is_cd(const bool& state_);
	virtual bool judge_choose();
	virtual bool judge_cd();
	
	Rect getRect(); //获取卡片所占范围
	double card_cd; // 卡片冷却时间
	double time_count;// 时间计时器  统计使用精灵 
	int sun_cost; // 太阳值费用
	bool can_choose; //能被选中
	CardType card_type;
	Sprite* sprite; // 固定的精灵
	Sprite* cd_sprite; // 冷却的精灵 
	FadeIn* card_cd_animate; // 卡片冷却动画
	MoveTo* card_move_animate; // 卡片点击后卡片移动到已选传送带内 : 选择界面

	bool is_choose; // 是否被选择了  该参数只用于左上角显示精灵
	bool is_cd; //  判断 是否 要进入cd状态
};
#endif // !CARD_H_

Card.cpp

#include "Card/Card.h"



void Card::card_init() {
}//初始化函数

Sprite* Card::sprite_init(const std::string& name_, const Vec2& position_) {
	sprite = Sprite::create("/Cards/" + name_ + ".png");
	sprite->setPosition(position_);
	return sprite;
} // 精灵初始化


Sprite* Card::cd_sprite_init(const std::string& name_, const Vec2& position_) {
	cd_sprite = Sprite::create("/Cards/" + name_ + ".png");
	cd_sprite->setPosition(position_);
	return cd_sprite;
}

void Card::init_card_cd_animation() {
	
} // 卡片冷却动画

void Card::init_card_move_animation(const Vec2& destination_/*const std::string& name_, const Vec2& destination_*/) {
	auto card_move_to = MoveTo::create(0.1, destination_);
	this->runAction(card_move_to);
} // 卡片点击 移动到  已选框内  选择界面



void Card::set_is_choose(const bool& state_) {
	is_choose = state_;
} // 设置已选 

void Card::set_is_cd(const bool &state_) {
	is_cd = state_;
}

bool Card::judge_choose() { 
	return is_choose;
}
bool Card::judge_cd() {
	return is_cd;
}

by QZP20220815
Rect Card::getRect() {
	float ori_x = this->sprite->getPosition().x - this->sprite->getContentSize().width * this->sprite->getAnchorPoint().x;
	float ori_y = this->sprite->getPosition().y - this->sprite->getContentSize().height * this->sprite->getAnchorPoint().y;
	float w = this->sprite->getContentSize().width;
	float h = this->sprite->getContentSize().height;
	return Rect(ori_x, ori_y, w, h);
	
} //获取卡片所占范围

SunFlower.h

#ifndef SUNFLOWERCARD_H
#define  SUNFLOWERCARD_H
#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include "Card/Card.h"

using namespace cocos2d;
using namespace ui;


//卡片的图像大小为64*90

class SunFlowerCard : public Card{
public:
	SunFlowerCard();
	virtual void update(float update_time); //调度函数
	virtual void card_init();//初始化函数
	virtual void init_card_cd_animation(); // 卡片冷却动画
};
#endif // !CARD_H_

SunFlower.cpp

#include "Card/SunFlowerCard.h"

SunFlowerCard::SunFlowerCard() {
	this->card_init();
	//this->sprite_init(sp_name,position);  初始化 先空着
	//this->init_card_cd_animation(); // 加载动画
	this->scheduleUpdate(); //启动调度函数 
}

//调度函数   参数系统调用该函数的 间隔时间
void SunFlowerCard::update(float update_time) {
	//this->time_count += update_time;
	//if (judge_cd()) { //如果在cd 
	//	this->init_card_cd_animation();  //播放冷却动画
	//	this->set_is_cd(false);
	//	this->time_count = 0;   //开始计时
	//}
	//if (time_count >= card_cd + 0.1) {  //0.1为卡片置为不可用动画的时间
	//	this->can_choose = true;   //冷却动画完成,则卡片置为可选状态
	//} 

	
} //调度函数

void SunFlowerCard::card_init() {
	card_cd = 10; //卡片冷却时间
	time_count = 0;// 时间计时器  统计使用精灵 
	sun_cost = 50; // 太阳值费用
	is_cd = false;//  判断 是否 进入cd状态
	can_choose = true;
	card_type = CardType::card_type_SunFlower;
}//初始化函数

void SunFlowerCard::init_card_cd_animation() {
	//card_cd_animate   给 card 里面对应的动作变量赋值
	/*auto card_cd1 = FadeOut::create(0.1);
	auto card_cd2 = FadeIn::create(card_cd);
	auto card_cd_seq = Sequence::create(card_cd1, card_cd2,nullptr);
	this->sprite->runAction(card_cd_seq);*/
	this->cd_sprite_init("SunFlowerCD", this->sprite->getPosition());
	this->cd_sprite->setColor(Color3B::GRAY);
	auto cd_sp_bar = ProgressTimer::create(this->cd_sprite);
	cd_sp_bar->setPosition(this->sprite->getPosition());  //进度条生成在卡片位置
	cd_sp_bar->setType(ProgressTimer::Type::BAR);
	cd_sp_bar->setBarChangeRate(Vec2(0, 1));
	cd_sp_bar->setMidpoint(Vec2(0, 1));
	cd_sp_bar->runAction(ProgressFromTo::create(this->card_cd, 100, 0)); // 三个参数:时间 起点百分百 终点百分比
	this->sprite->getParent()->addChild(cd_sp_bar, 3);
	//card_cd_animate赋值?
} // 卡片冷却动画

汽车类

Car.h

#ifndef _CAR_H_
#define _CAR_H_
#include "cocos2d.h"
using namespace cocos2d;

class Car : public Sprite{
public:
	Car();
	~Car() = default;
	virtual void update(float update_time);//调度函数
	void sprite_init(const Vec2& position_);
	void init_car_move_animation(); //汽车移动动画
	Sprite* sprite; // 精灵
	MoveTo* car_move_animate;
	virtual Vec2 get_position() const; // 获取坐标xy
	virtual double get_x() const; //相关函数 getPOsitionX / Y
	virtual double get_y() const;
	Vec2 position; // 位置
	bool is_used; // 是否使用
	int row;
};
#endif // _CAR_H_

Car.cpp

#include "Other/Car.h"

//构造函数
Car::Car() {
	//调用init函数
	is_used = false;
	//调用精灵初始化函数 并且赋值
	//sprite = Sprite::create();
	//调用动作初始化函数  并且赋值
	this->scheduleUpdate();// 启动调度函数
}

//修改系统的时间调度函数  
//参数 update_time 是系统调用该函数 的间隔时间
void Car::update(float update_time) {
	//for 循环遍历  判断每个车子是否使用
	//如果使用就删去精灵
	if (is_used) {
		sprite->removeFromParentAndCleanup(true);
		this->unscheduleUpdate(); //关闭调度函数
	}
}

//精灵初始化    参数 二维坐标
void Car::sprite_init(const Vec2& position_) {
	sprite = Sprite::create("Games/car.png");
	sprite->setPosition(position_);
}

Vec2 Car::get_position() const {
	return position;
} // 获取二维坐标Vec2

double Car::get_x() const {
	return sprite->getPositionX();
} //相关函数 getPOsitionX / Y

double Car::get_y() const {
	return sprite->getPositionY();
}

void Car::init_car_move_animation() {
	//精灵动作  移动 该精灵的位置 到地图最右边   
	is_used = true;
	car_move_animate = MoveTo::create(5, Vec2(2000, sprite->getPositionY()));
	sprite->runAction(car_move_animate);
 } //汽车移动动画


数据保存

Data.h

#ifndef DATE_H
#define DATE_H

#include <vector>

#include "Card/Card.h"
#include "Card/PeashooterCard.h"
#include "Card/SunFlowerCard.h"
#include "Card/WallNutCard.h"

#include "Other/Car.h"
#include "Other/Shovel.h"
#include "Other/User.h"

#include "Zombie/ZombieBase.h"
#include "Zombie/BucketheadZombie.h"
#include "Zombie/ConeheadZombie.h"
#include "Zombie/FlagZombie.h"
#include "Zombie/NormalZombie.h"


#include "Plant/PlantBase.h"
#include "Plant/Sun.h"
#include "Plant/Peashooter.h"
#include "Plant/SunFlower.h"
#include "Plant/WallNut.h"
#include "Bullet/Bullet.h"

extern std::vector<Zombie*> zombie_date;//用于保存已经创建的僵尸
extern std::vector<Plant*> plant_date;//用于保存已经创建的植物对象
extern std::vector<Bullet*> bullet_date;//用于保存已经创建的子弹对象
extern std::vector<Card*> card_date;//用于保存已经创建的卡片对象
extern std::vector<Car*> car_date;//用于保存已经创建的车对象
extern std::vector<Sun*> sun_date;//用于保存已经创建的阳光对象

extern int sun_total;//用于显示游戏中的阳光值

extern Vec2 plant_position[5][9];//用于保存各个二维坐标当中,植物的位置,5*9
extern bool plant_judge_position[5][9];//用于保存各个坐标当中,是否存在植物
extern Vec2 card_bar_position[4]; //  用于保存放在卡片栏的位置  暂时只弄四个  用于 图片的移动动画
extern Vec2 card_table_position[4];  // 用于保存卡片选择表的位置  暂时只弄四个 
extern Vec2 zombie_positon[5]; // 用于记录僵尸五个起点
extern Vec2 car_positon[5]; // 用于保存五个车子的位置 
#endif // !DATE_H

Data.cpp

#include "Other/Data.h"
#include <vector>

//保存数据 使用for + auto 进行遍历
std::vector<Zombie*> zombie_date;//用于保存已经创建的僵尸
std::vector<Plant*> plant_date;//用于保存已经创建的植物对象
std::vector<Bullet*> bullet_date;//用于保存已经创建的子弹对象
std::vector<Card*> card_date;//用于保存已经创建的卡片对象
std::vector<Car*> car_date;//用于保存已经创建的车对象
std::vector<Sun*> sun_date;//用于保存已经创建的车对象

int sun_total = 50;//用于显示游戏中的阳光值

Vec2 plant_position[5][9];//用于保存各个二维坐标当中,植物的位置,5*9
bool plant_judge_position[5][9];//用于保存各个坐标当中,是否存在植物
Vec2 card_bar_position[4]; //  用于保存放在卡片栏的位置  暂时只弄四个  用于 图片的移动动画
Vec2 card_table_position[4];  // 用于保存卡片选择表的位置  暂时只弄四个 
Vec2 zombie_positon[5]; // 用于记录僵尸五个起点
Vec2 car_positon[5]; // 用于保存五个车子的位置 

主游戏逻辑

PlantsVsZombies.h

#ifndef _PLANTSVSZOMBIES_H_
#define _PLANTSVSZOMBIES_H_

#include "Other/Data.h"
#include "AudioEngine.h"
#include "cocos2d.h"
#include "ui/CocosGUI.h"
USING_NS_CC;
using namespace cocos2d;
using namespace ui;

// 僵尸vs植物 类  实现 僵尸的攻击  和 植物的各种攻击  和其他操作 
class PlantVsZombies :public Node { 
public:
	PlantVsZombies(); // 构造函数
	~PlantVsZombies() = default; //默认虚构
	virtual void update(float update_time);  //调度函数  计时器 实现第n波进攻  + 植物 (产生攻击 ) + 僵尸产生攻击  +	豌豆检测 + 阳光收集  +游戏失败

	void init_data();//用于初始化 数据 
	void init_scene(Scene* scene_);//用于绑定场景,addChild     游戏场景:new一个节点PvsZ  然后 this->addchild(本节点)

	Zombie* get_row_left_zombie(int row_);//获取在第row路最左边的僵尸  
	Plant* get_row_right_plant(int row_);//获取在第row行最右边的植物

	virtual void peashooter_shoot(Plant* plant_);//豌豆射手发射一个豌豆

	virtual void create_zombie(int row_, ZombieType type_);//在row行创建一个某种类型的僵尸
	virtual void create_plant(int row_, int col_, PlantType type_);//在row行col列创建一个某种类型的植物

	virtual bool judge_plant(int row_, int col_);//用于判断在row行col列是否可以种植植物  需要

	virtual bool judge_vec2_in_row_col(Vec2 vec_position, int& row_, int& col_);//判断这个vec2坐标 鼠标点击的位置是不是在草地区域  结合鼠标使用种植植物 

	virtual void create_sun(int x_,int y_);//生成太阳 在 x y -》写到Vec里面

	Scene* scene;//场景节点  用于添加到游戏场景

	//时间变量  用于控制 对应时间段 僵尸的生成 
	double time1;
	double time2;
	double time3;
	double time4;
	double time5;
	double time6;
	double time7;
	double time8;
	// 判断是否经过了这个时间段了
	bool is_pass_time1; 
	bool is_pass_time2;
	bool is_pass_time3;
	bool is_pass_time4;
	bool is_pass_time5;
	bool is_pass_time6;
	bool is_pass_time7;
	bool is_pass_time8;
	
	double time_count;//用于计时,到达指定时间,生成对应波次的僵尸
	double pruduce_time ; // 用于生成时间间隔  比如能被一个数整除就生成  time_count % produce == 0 

	bool is_game_fail;//判断游戏失败
	bool is_game_success;//判断游戏是否获得胜利
};


#endif // !_PLANTSVSZOMBIES_H_

PlantsVsZombies.cpp

#include "Other/PlantsVsZombies.h"

PlantVsZombies::PlantVsZombies() {
	this->init_data(); // 初始化数据
 // 开启调度函数 
	//this->create_sun(500, 500);

	this->scheduleUpdate();
} // 构造函数

void PlantVsZombies::init_data() {
	time1 = 14;
	time2 = 40;
	time3 = 70;
	time4 = 70;
	time5 = 90;
	time6 = 110;
	time7 = 120;
	time8 = 150;
	// 判断是否经过了这个时间段了
	is_pass_time1 = false;
	is_pass_time2 = false;
	is_pass_time3 = false;
	is_pass_time4 = false;
	is_pass_time5 = false;
	is_pass_time6 = false;
	is_pass_time7 = false;
	is_pass_time8 = false;
	time_count = 0.0;//用于计时,到达指定时间,生成对应波次的僵尸
	pruduce_time = 0.0; // 生成僵尸的时间间隔  比如能被一个数整除就生成  time_count % produce == 0 

	is_game_fail = false;//判断游戏失败
	is_game_success = false;//判断游戏是否获得胜利

	//this->scheduleUpdate();
}//用于初始化 数据 

void PlantVsZombies::update(float update_time) {
	this->pruduce_time += update_time;
	this->time_count += update_time;
	// 第n波进攻
	int rand_row  = 0;
	if (this->time_count >= time1 && !this->is_pass_time1) {
		//生成僵尸
			create_zombie(2, ZombieType::zombie_type_normal);
		this->is_pass_time1 = true;
	}

	if (this->time_count >= time2 && !this->is_pass_time2) {
		//生成僵尸
		create_zombie(2, ZombieType::zombie_type_conehead);
		create_zombie(3, ZombieType::zombie_type_flag);
		create_zombie(4, ZombieType::zombie_type_buckethead);
		this->is_pass_time2 = true;
	}

	if (this->time_count >= time3 && !this->is_pass_time3) {
		create_zombie(2, ZombieType::zombie_type_conehead);
		create_zombie(3, ZombieType::zombie_type_flag);
		create_zombie(4, ZombieType::zombie_type_buckethead);
		create_zombie(1, ZombieType::zombie_type_normal);
		create_zombie(4, ZombieType::zombie_type_normal);
		this->is_pass_time3 = true;
	}

	//if (this->time_count >= time4 && !this->is_pass_time4) {
	//	//生成僵尸
	//	create_zombie(5, ZombieType::zombie_type_normal);
	//	create_zombie(4, ZombieType::zombie_type_normal);
	//	create_zombie(3, ZombieType::zombie_type_flag);
	//	create_zombie(2, ZombieType::zombie_type_buckethead);
	//	create_zombie(1, ZombieType::zombie_type_conehead);
	//	this->is_pass_time4 = true;
	//}

	//if (this->time_count >= time5 && !this->is_pass_time5) {
	//	//生成僵尸
	//	create_zombie(5, ZombieType::zombie_type_normal);
	//	create_zombie(4, ZombieType::zombie_type_normal);
	//	create_zombie(2, ZombieType::zombie_type_buckethead);
	//	create_zombie(1, ZombieType::zombie_type_conehead);
	//	this->is_pass_time5 = true;
	//}

	//if (this->time_count >= time6 && !this->is_pass_time6) {
	//	//生成僵尸
	//	create_zombie(5, ZombieType::zombie_type_normal);
	//	create_zombie(4, ZombieType::zombie_type_normal);
	//	create_zombie(2, ZombieType::zombie_type_buckethead);
	//	create_zombie(1, ZombieType::zombie_type_conehead);
	//	this->is_pass_time6 = true;
	//}

	//if (this->time_count >= time7 && !this->is_pass_time7) {
	//	//生成僵尸
	//	create_zombie(2, ZombieType::zombie_type_normal);
	//	create_zombie(1, ZombieType::zombie_type_normal);
	//	create_zombie(4, ZombieType::zombie_type_buckethead);
	//	create_zombie(5, ZombieType::zombie_type_conehead);
	//	this->is_pass_time7 = true;
	//}

	//if (this->time_count >= time8 && !this->is_pass_time8) {
	//	//生成僵尸
	//	create_zombie(3, ZombieType::zombie_type_flag);
	//	create_zombie(5, ZombieType::zombie_type_conehead);
	//	create_zombie(4, ZombieType::zombie_type_buckethead);
	//	create_zombie(2, ZombieType::zombie_type_buckethead);
	//	create_zombie(1, ZombieType::zombie_type_conehead);
	//	this->is_pass_time8 = true;
	//}
	//遍历死亡
	for (auto& it : zombie_date) {
		if (it->is_dead) continue;
		if (it->blood <= 0) {
			it->run_zombie_die_animation();
			//it->sprite->setPosition(10000, 0);
			it->is_dead = true;
			it->sprite = nullptr;
			//it->sprite->runAction(RemoveSelf::create());
			continue;
		}
		for (auto& ca : car_date) {  // 当车子移动的时候遍历僵尸
			int row_ = ca->row;
			if (fabs(it->get_x() - ca->get_x()) <= 15 && ca->is_used && row_ == it->row) {
				//ca->is_used = true;
				it->blood = 0;
			}
		}
	}

	//遍历所有僵尸
	for (auto& it : zombie_date) {
		it->time_count += update_time;
		if (it->is_dead) {
			continue; // 如果僵尸死了
		}
		Plant* temp_plant = get_row_right_plant(it->row);
		if (temp_plant != nullptr) {
			if (it->time_count >= it->attack_time && fabs(it->get_x() - temp_plant->get_x()) <= 15) {
				if (!it->is_eat) it->run_zombie_eat_animation();
				temp_plant->by_attack(it->attack_num);
				it->time_count = 0.0;
			}
			else {
				if ((!it->judge_move() && fabs(it->get_x() - temp_plant->get_x()) >= 15 ) ) {  
					if(it->judge_eat())it->run_zombie_move_animation();
				}
			}
		}
		else if (it->judge_eat() ) {
				it->run_zombie_move_animation();
			}
	}

	for (auto it = plant_date.begin(); it != plant_date.end();) {
		//遍历植物  执行植物的相应攻击动作
		// 太阳花 就产生 太阳   豌豆就 生成子弹  
		//结合switch
		if ((*it)->get_blood() <= 0)
		{
			plant_judge_position[(*it)->get_row()][(*it)->get_line()] = false;
			(*it)->get_sprite()->removeFromParent();
			(*it)->removeFromParent();
			it = plant_date.erase(it);
			continue;
		}
		(*it)->set_is_shoot(update_time);
		if ((*it)->get_is_shoot() && (get_row_left_zombie((*it)->get_row()) != NULL || (*it)->get_type() == PlantType::SunFlower))
		{
			switch ((*it)->get_type())
			{
			case PlantType::SunFlower:
			{
				(*it)->time_count += update_time;
				if ((*it)->time_count >= (*it)->rate_time) {
					(*it)->time_count = 0.0;
					this->create_sun((*it)->get_x() + 30, (*it)->get_y() + 30);
				}
				break;
			}
			case PlantType::Peashooter:
				this->peashooter_shoot((*it));
				break;
			default:
				break;
			}
		}
		it++;
	}


	for (auto& it : bullet_date) {
		//遍历所有子弹  如果碰撞了 就扣血 + 设置子弹为is_crash
		if (it->judge_crash() || (get_row_left_zombie(it->row) == nullptr && !get_row_left_zombie(it->row)->is_dead)) continue;
		if (get_row_left_zombie(it->row)->get_x() - it->sprite->getPositionX() <= 0) {
			get_row_left_zombie(it->row)->blood -= it->attack_num;
			it->set_crash();
			this->removeChild(it->sprite);
		}
	}

	for (auto& it : car_date) {
		int row_ = it->row;
		Zombie* temp_zombie = get_row_left_zombie(row_);
		if (it->is_used || temp_zombie == nullptr) continue;
		if (fabs(temp_zombie->get_x() - it->get_x()) <= 15) {
			it->init_car_move_animation();
			//it->is_used = true;
			temp_zombie->blood = 0;
		} 
	}

	//执行生成阳光 根据间隔时间生成
	if (pruduce_time >= 15.0) {
		pruduce_time = 0.0;//置零
		int rand_x = rand() % 500 + 100;
		int rand_y = rand() % 600 + 200;
		this->create_sun(rand_x, rand_y);
	}

	//阳光超过指定时间没有收集就删除
	for (auto& it : sun_date) {
		if (it->judge_delete()) continue;
		if (it->sun_time >= 8.0) {
			it->set_is_delete();
			it->run_sun_delete_animation();
		}
		it->sun_time += update_time;
	}

	int zombie_cnt = 0;
	//如果游戏结束了 
	for (auto& it : zombie_date) {
		int x = it->row;
		if (it->judge_dead()) continue; //如果状态为dead
		zombie_cnt++;
		if (it->sprite != nullptr && it->get_x() <= 0 && car_date[x] -> is_used ) {
			is_game_fail = true;
			break;
		}
	}

	if (this->is_pass_time3 == true && zombie_cnt == 0) {
		//如果最后一只僵尸死亡  游戏胜利
		init_data();
		is_game_success = true;
	}
}

void PlantVsZombies::init_scene(Scene* scene_) {
	scene = scene_;
}//用于绑定场景,addChild     游戏场景:new一个节点PvsZ  然后 this->addchild(本节点)

Zombie* PlantVsZombies::get_row_left_zombie(int row_) {
	Zombie* left_zombie = nullptr;
	double left = -1;
	for (auto& it : zombie_date) {
		if (it->row != row_ || it->is_dead) continue;
		if (left == -1 || left >= it->get_x()) {
			left = it->get_x();
			left_zombie = it;
		}
	}
	return left_zombie;
}//获取在第row路最左边的僵尸  


Plant* PlantVsZombies::get_row_right_plant(int row_) {
	Plant* right_plant = nullptr;
	double right = -1.0;
	for (auto& it : plant_date) {
		if (it->row != row_ || it->get_blood() <= 0) continue;
		if (right == -1 || right <= it->get_x()) {
			right = it->get_x();
			right_plant = it;
		}
	}
	return right_plant;
}//获取在第row行最右边的植物

void PlantVsZombies::peashooter_shoot(Plant* plant_) {
	Bullet* temp_bullet = new Bullet;
	temp_bullet->sprite_init(plant_->get_position());
	temp_bullet->bullet_move_animation();
	this->addChild(temp_bullet->sprite, 2);
	temp_bullet->row = plant_->row;
	bullet_date.push_back(temp_bullet);
	temp_bullet->setScale(2);
	plant_->set_is_shoot(false);
}//豌豆射手发射一个豌豆


void PlantVsZombies::create_zombie(int row_, ZombieType type_) {
	Zombie* temp_zombie;
	switch (type_)
	{
	case zombie_type_normal:
	{
		temp_zombie = new NormalZombie();
		break;
	}
	case zombie_type_conehead:
	{
		temp_zombie = new ConeheadZombie();
		break;
	}
	case zombie_type_buckethead:
	{
		temp_zombie = new BucketheadZombie();
		break;
	}
	case zombie_type_flag:
	{
		temp_zombie = new FlagZombie();
		break;
	}
	default:
		break;
	}
	temp_zombie->row = row_;  // 0 1 2 3 4
	temp_zombie->sprite_init(zombie_positon[row_]);
	temp_zombie->run_zombie_move_animation();
	this->addChild(temp_zombie->sprite, 3);
	zombie_date.push_back(temp_zombie);
}//在row行创建一个某种类型的僵尸

void PlantVsZombies::create_plant(int row_, int col_, PlantType type_) {
	plant_judge_position[row_][col_] = true;
}//在row行col列创建一个某种类型的植物

bool PlantVsZombies::judge_plant(int row_, int col_) {
	return plant_judge_position[row_][col_];
}//用于判断在row行col列是否可以种植植物  需要


//判断这个vec2坐标 鼠标点击的位置是不是在草地区域  结合鼠标使用种植植物 
bool PlantVsZombies::judge_vec2_in_row_col(Vec2 vec_position, int& _row, int& _col) {
	//特判  :越界 if(越界) return false
	auto visibleSize = Director::getInstance()->getVisibleSize();
	if ((vec_position.x < visibleSize.width * 0.18) || (vec_position.x > visibleSize.width * 0.95)
		|| (vec_position.y < visibleSize.height * 0.13) || (vec_position.y > visibleSize.height * 0.92))	return false;
	int width_ = 125 / 2;
	int height_ = 103 / 2;
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 9; j++) {
			Vec2 offset = vec_position - plant_position[i][j];
			if ((fabs(offset.x) <= width_) && (fabs(offset.y) <= height_)) {
				_row = i;
				_col = j;
				return true;
			}
		}
	}
	return false;
}

void PlantVsZombies::create_sun(int x_, int y_) {
	Sun* temp_sun = new Sun;
	temp_sun->sprite->setScale(1.5);
	this->addChild(temp_sun->sprite, 5); //添加层数
	sun_date.push_back(temp_sun);
	temp_sun->sprite->setPosition(Vec2(x_ + 30, y_ + 30));
	temp_sun->run_sun_produce_animation();
}//生成太阳 在 x y -》写到Vec里面

总结

用了大概20天的时间,和一群小伙伴完成了这个项目,这个项目代码感觉至少要个4000 , debug 和git推送的冲突解决都让我受益良多,很充实的一段经历,也是我第一次写这么大的项目,大一结束了,暑假过完就大二了,还要继续努力!!
结语 : 天亮未亮,步履不停

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CPT1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值