(八)C++自制植物大战僵尸游戏植物基类讲解

植物大战僵尸游戏开发教程专栏地址icon-default.png?t=N7T8http://t.csdnimg.cn/m0EtD


在植物大战僵尸游戏中,最重要的两个类别就是植物与僵尸。植物可以对僵尸进行攻击,不同的植物攻击方式千差万别,但是不同植物又有许多相同的属性。在基类(父类)中定义植物共有属性,供派生类(子类)继承。

植物状态转换

植物继承关系图

文件位置

植物基类代码文件在Class/Plants文件夹中,详细位置如下图所示。

Plants.h

植物类型定义

首先在植物基类的头文件中使用了枚举定义了植物类型。 植物类型定义如下。

enum class PlantsType
{None = -1,SunFlower,          /* 向日葵 */PeaShooter,         /* 豌豆射手 */WallNut,            /* 坚果墙 */CherryBomb,         /* 樱桃炸弹 */PotatoMine,         /* 土豆雷 */CabbagePult,        /* 卷心菜投手 */Torchwood,          /* 火炬树桩 */Spikeweed,          /* 地刺 */Garlic,             /* 大蒜 */FirePeaShooter,     /* 火焰豌豆射手 */Jalapeno,           /* 火爆辣椒 */AcidLemonShooter,   /* 强酸柠檬射手 */Citron              /* 离子缘 *///...
}; 

植物基类定义

植物基类继承Cocos2d-x中Node类。在基类中定义了大量的属性与方法。植物共有的属性包括阳光数量需求、冷却时间、植物攻击力、植物标签、植物的绘制顺序、植物编号、位置等。详细内容请看下方代码。

class  Plants :public Node
{
public:/***暂停植物所有动作*/static void stopPlantsAllAction();/***创建图片*/virtual Sprite* createPlantImage() = 0;/***创建植物动画*/virtual void createPlantAnimation() = 0;/***判断僵尸与植物之间的相对位置*/virtual void determineRelativePositionPlantsAndZombies();/***检测植物生命情况*/virtual void checkPlantHealthPoint() {}/***设置节点*/virtual void setPlantNode(Node* node) { _node = node; }/***设置位置*/virtual void setPlantPosition(const Vec2& position) { _position = position; }/***设置绘制顺序*/virtual void setPlantLocalZOrder(const int& order) { _zOrder = order; }/***设置植物所在行列*/virtual void setPlantRowAndColumn(const Vec2& rowAndColumn) { _rowAndColumn = rowAndColumn; }/***设置植物是否可见*/virtual void setPlantVisible(const bool visible);/***获取植物类型*/PlantsType getPlantType() const { return _plantsType; }/***设置植物标记*/virtual void setPlantTag(const int& tag) { _plantTag = tag; }/***设置植物生命值*/virtual void setPlantHealthPoint(int healthPoint) { _healthPoint = healthPoint; }/***设置植物大小*/virtual void setPlantScale();/***获取植物标记*/virtual int getPlantTag() const { return _plantTag; }/***获取动画*/SkeletonAnimation* getPlantAnimation() { return _plantAnimation; }/***判断植物是否存活*/virtual bool getPlantIsSurvive() const;/***获取植物生命值*/virtual float getPlantHealthPoint() const { return _healthPoint; }/***@ 1.获取植物所在行列*/virtual Vec2 getPlantRowAndColumn() const { return _rowAndColumn; }/***@ 2.获取植物所在行*/virtual int getPlantRow() const { return _rowAndColumn.x; }/***@ 3.获取植物所在列*/virtual int getPlantColumn() const { return _rowAndColumn.y; }/***获取是否显示*/virtual bool getPlantVisible();/***获取植物是否可以删除*/virtual bool* getPlantIsCanDelete();CC_CONSTRUCTOR_ACCESS:Plants(Node* node = nullptr, const Vec2& position = Vec2::ZERO);~Plants();protected:/***种植植物*/virtual SkeletonAnimation* plantInit(const std::string& plantname, const std::string& animaionname);/***创建植物图片*/virtual void imageInit(const std::string& name, const Vec2& position);/***减少植物生命值*/virtual void reducePlantHealthPoint(int number) { _healthPoint -= number; }/***泥土飞溅动画*/virtual void setPlantSoilSplashAnimation(const float& scale);/***设置植物影子*/virtual void setPlantShadow(const float& scale);/***设置植物受到伤害闪烁*/virtual void setPlantHurtBlink();virtual void setPlantHurtBlink(PlantsType type) const;/***获取僵尸是否在植物前方*/virtual bool getZombieIsTheFrontOfPlant(Zombies* zombie) const;/***获取僵尸是否于植物在同一行*/virtual bool getZombieIsSameLineWithPlant(Zombies* zombie) const;/***获取僵尸是否遇到植物*/virtual bool getzombieIsEncounterPlant(Zombies* zombie) const;/***僵尸吃植物*/virtual void zombieEatPlant(Zombies* zombie);/***僵尸从吃植物中恢复*/virtual void zombieRecoveryMove(Zombies* zombie);private:void setPlantGLProgram();protected:int _sunNeed;                               // 阳光需求int _costGold;                              // 花费金币int _costMasonry;                           // 花费砖石int _combatEffecttiveness;                  // 战斗力int _plantTag;                              // 植物标签int _zOrder;                                // 绘制顺序int _plantNumber;                           // 存储植物编号(唯一性 )bool _isLoop;                               // 是否循环bool _isCanDelete[2];                       // 是否可以删除float _coolDownTime;                        // 冷却时间float _healthPoint;                         // 生命值float _totalHealthPoint;                    // 生命总值Vec2 _position;                             // 位置Vec2 _rowAndColumn;                         // 详细地址,所在行列SkeletonAnimation* _plantAnimation;         // 植物动画Node* _node;                                // 节点Global* _global;                            // 全局变量Sprite* _plantImage;                        // 植物图片Sprite* _plantShadow;                       // 植物影子PlantsType _plantsType;                     // 植物类型SkeletonAnimation* _soilSplashAnimation;    // 泥土飞溅动画static int plantNumber;                     // 植物编号(唯一性)private:static GLProgram* _normalGLProgram;static GLProgram* _highLightGLProgram;GLProgramState* _highLightGLProgramState;float _highLightIntensity;
};

 Plants.cpp

在源文件中实现了头文件件中定义的函数。

构造函数

植物构造函数使用初始化列表的方式将变量进行初始化。

Plants::Plants(Node* node, const Vec2& position) :_node(node)
,	_position(position)
,	_plantShadow(nullptr)
,	_plantImage(nullptr)
,	_plantAnimation(nullptr)
,	_soilSplashAnimation(nullptr)
,   _highLightGLProgramState(nullptr)
,	_sunNeed(NOINITIALIZATION)
,	_coolDownTime(NOINITIALIZATION)
,	_costGold(NOINITIALIZATION)
,	_costMasonry(NOINITIALIZATION)
,	_healthPoint(NOINITIALIZATION)
,	_plantTag(NOINITIALIZATION)
,	_isLoop(true)
,   _isCanDelete{ false,false }
,   _zOrder(0)
,   _highLightIntensity(0.6f)
,   _plantNumber(++plantNumber)
,   _combatEffecttiveness(0)
,   _plantsType(PlantsType::None)
,	_global(Global::getInstance())
{
}

plantInit()函数

函数有两个参数,第一个参数表示文件名称,第二个参数表示动画名称。

SkeletonAnimation* Plants::plantInit(const std::string& plantname, const std::string& animaionname)
{auto iter = _global->userInformation->getAnimationData().find(plantname);if (iter != _global->userInformation->getAnimationData().end())/* 如果可以找到 */{_plantAnimation = SkeletonAnimation::createWithData(iter->second);_plantAnimation->setPosition(_position);_plantAnimation->setAnimation(0, animaionname, _isLoop);_plantAnimation->setLocalZOrder(_zOrder);_plantAnimation->setTag(_plantTag);_plantAnimation->setTimeScale(0.7f + rand() % 4 / 10.f);setPlantGLProgram();return _plantAnimation;}return nullptr;
}

函数根据参数提供的文件名称进行查找,如果没有找到返回空指针(nullptr)表示失败,否则创建东,并根据参数二设置为指定动画。游戏中使用的是Spine骨骼动画,创建完成后会返回SkeletonAnimation的骨骼动画类。动画创建完成后并设置相应属性,如位置、绘制顺序、植物标签(编号)以及植物播放动画播放速率,为了使植物动画看起来并不是完全统一的,所以设置了随机数。

setPlantGLProgram()函数设置了植物动画的shader。植物被僵尸攻击后会出现短暂高亮状态,表示受到攻击。在Cocos2d-x中不能直接调整动画的亮度,所以使用shader实现该功能。


setPlantGLProgram()函数

函数对_normalGLProgram_highLightGLProgram进行了初始化,_normalGLProgram用于渲染正常状态下的动画、_highLightGLProgram用于渲染高亮状态下的动画。

void Plants::setPlantGLProgram()
{if (!_normalGLProgram || !_highLightGLProgram){_normalGLProgram = _plantAnimation->getGLProgram();_highLightGLProgram = Zombies::getHighLight();}
}

Cocos2d-x加载自定义shader的方法如下,使用 GLProgramCache类加载shader文件并使用GLProgram类创建shader,创建好的类放入GLProgramCache中存储,供后续使用。

GLProgram* Zombies::getHighLight()
{auto program = GLProgramCache::getInstance()->getGLProgram("MyHighLightShader");if (!program){program = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert,FileUtils::getInstance()->getStringFromFile("resources/Text/Bloom.fsh").c_str());program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR);program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD);program->link();program->updateUniforms();GLProgramCache::getInstance()->addGLProgram(program, "MyHighLightShader");}return program;
}

determineRelativePositionPlantsAndZombies()函数

函数用于判断植物与僵尸的位置,该函数会被在游戏的每一帧中调用。通过遍历僵尸集合,判断每一只僵尸与该植物的位置关系,如果僵尸移动到植物的攻击范围内,就会更新植物动画并攻击僵尸,同样的,植物进入僵尸攻击范围,僵尸就会吃掉植物。

此函数是一个虚函数,不同的派生类植物可能会重写该函数。在植物基类中,绝大部分的函数都是虚函数,派生类可以对函数进行重写。

void Plants::determineRelativePositionPlantsAndZombies()
{for (auto zombie : ZombiesGroup){zombieEatPlant(zombie);      /* 僵尸吃植物 */zombieRecoveryMove(zombie);  /* 僵尸恢复移动 */}
}

zombieEatPlant()函数

函数有一个参数,传入的是攻击植物的僵尸实例。

僵尸攻击植物,首先要进行各种判断,植物是否存活、僵尸是否存活、植物是否在僵尸的攻击范围内等。如果全部符合要求则改变僵尸动画,并对监听动画,触发条件后使用reducePlantHealthPoint()函数扣除植物血量,并使用setPlantHurtBlink()函数让植物产生短暂高亮状态。

void Plants::zombieEatPlant(Zombies* zombie)
{if (getPlantIsSurvive() && getZombieIsSameLineWithPlant(zombie) && getzombieIsEncounterPlant(zombie)&& /* 植物存活 && 植物与僵尸在同一行 && 僵尸遇到植物 */zombie->getZombieType() != ZombiesType::SnowZombies) /* 僵尸不是雪人僵尸 */{if (zombie->getZombieIsSurvive() && !zombie->getZombieIsEat()){const string eateffect[3] = { "chomp","chomp2","chompsoft" };zombie->setZombieEatPlantNumber(_plantNumber);zombie->setZombieStop();zombie->setZombieIsEat(true);zombie->getZombieAnimation()->setAnimation(1,zombie->getZombieType() == ZombiesType::LmpZombies ? "Zombies_Eat" : rand() % 2 ? "Zombies_Eat" : "Zombies_Eat1", true);zombie->getZombieAnimation()->setEventListener([this, eateffect](spTrackEntry* entry, spEvent* event){if (!strcmp(event->data->name, "eat")){if (event->intValue == 1){reducePlantHealthPoint(100);Bullet::playSoundEffect(eateffect[rand() % 3]);setPlantHurtBlink();}}});}}
}

setPlantHurtBlink()函数

创建Cocos2d-x动画序列不断改变shader中的亮度值_highLightIntensity,实现植物动画高亮闪动状态。

void Plants::setPlantHurtBlink()
{auto action = Repeat::create(Sequence::create(CallFunc::create([this](){_highLightIntensity -= 0.01f;_highLightGLProgramState->setUniformFloat("intensity", _highLightIntensity);}), DelayTime::create(0.005f), nullptr), 50);_plantAnimation->runAction(Sequence::create(CallFunc::create([this](){_plantAnimation->setGLProgram(_highLightGLProgram);_highLightGLProgramState = _plantAnimation->getGLProgramState();_highLightGLProgramState->setUniformFloat("intensity", 0.6f);}), DelayTime::create(0.15f),CallFunc::create([this](){_plantAnimation->setGLProgram(_normalGLProgram);_highLightIntensity = 0.6f;}), nullptr));
}

zombieRecoveryMove()函数

函数有一个参数,传入僵尸实例。如果僵尸正在吃该植物,并且植物死亡,则僵尸会恢复移动。死亡的植物设置为不可见,后续植物将被清除。

void Plants::zombieRecoveryMove(Zombies* zombie)
{if (!getPlantIsSurvive() && zombie->getZombieEatPlantNumber() == _plantNumber &&  /* 植物死亡 && 僵尸是吃的该植物 */zombie->getZombieIsEat() && zombie->getZombieIsStop()) /* 僵尸正在吃植物 && 僵尸正在停止移动 */{setPlantVisible(false);_plantAnimation->stopAllActions();Bullet::playSoundEffect("gulp");zombie->setZombieIsEat(false);if (!zombie->getZombieIsPlayDieAnimation()) /* 僵尸没有播放死亡动画 */{zombie->getZombieAnimation()->setMix("Zombies_Eat", Zombies::getZombieAniamtionName(zombie->getZombieType()), 1.5f);zombie->getZombieAnimation()->addAnimation(1, Zombies::getZombieAniamtionName(zombie->getZombieType()), true);zombie->setZombieCurrentSpeed(zombie->getZombieSpeed());}}
}

getZombieIsSameLineWithPlant()函数

函数有一个参数,传入僵尸实例。通过植物与僵尸的纵坐标进行比较,判断植物与僵尸是否在同一行。

bool Plants::getZombieIsSameLineWithPlant(Zombies* zombie) const
{return fabs(zombie->getZombiePositionY() - 10 - _plantAnimation->getPositionY()) <= 10 ? true : false;
}

getzombieIsEncounterPlant()函数

函数有一个参数,传入僵尸实例。通过植物与僵尸的横坐标进行比较,判断植物与僵尸是否在同一列。

bool Plants::getzombieIsEncounterPlant(Zombies* zombie) const
{return fabs(zombie->getZombiePositionX() - _plantAnimation->getPositionX()) <= 80 ? true : false;
}

getZombieIsTheFrontOfPlant()函数

函数有一个参数,传入僵尸实例。通过植物与僵尸的横坐标进行比较,僵尸的横坐标是否大于植物横坐标,来判断僵尸是否在植物的前方。

bool Plants::getZombieIsTheFrontOfPlant(Zombies* zombie) const
{return zombie->getZombiePositionX() >= _plantAnimation->getPositionX() ? true : false;
}

其他函数

其他函数较为较为简单,这里不一一列举,可以自行查看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/310862.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言基础】:预处理详解(二)

文章目录 一、宏和函数的对比二、#和##运算符2.1 #运算符2.2 ##运算符 三、#undef四、命令行定义五、条件编译六、头文件的包含1. 头文件包含的方式2. 嵌套文件包含 上期回顾&#xff1a; 【C语言基础】&#xff1a;预处理详解(一) 一、宏和函数的对比 宏通常被应有于执行简单…

数图智慧零售解决方案,赋能零售行业空间资源价值最大化

数图智慧零售解决方案 赋能零售行业空间资源价值最大 在激烈的市场竞争中&#xff0c;如何更好地提升空间资源价值&#xff0c;提高销售额&#xff0c;成为行业关注的焦点。近日&#xff0c;NIQ发布的《2024年中国饮料行业趋势与展望》称&#xff0c;“在传统零售业态店内&…

单片机STM32中断与事件的区别

【转】1-单片机STM32---中断与事件的区别 - Engraver - 博客园 (cnblogs.com) 路径不同&#xff0c;处理方式不同&#xff0c;是否有程序不同&#xff0c;是否有cpu参与不同。 事件是比中断更新的升级产物。

3_2Linux中内核级加强型火墙的管理

### 一.Selinux的功能 ### 观察现象 ①当Selinux未开启时 在/mnt中建立文件被移动到/var/ftp下可以被vsftpd服务访问 匿名用户可以通过设置后上传文件 当使用ls -Z /var/ftp查看文件时显示"?" ps auxZ | grep vsftpd 时显示&#xff1a; - root 8546 0.0 0.0 26952 …

【QT+QGIS跨平台编译】181:【QGIS+Qt跨平台编译】—【错误处理:找不到_DEBUGA】

点击查看专栏目录 文章目录 一、找不到_DEBUGA二、原因分析三、错误处理 一、找不到_DEBUGA 报错信息&#xff1a; 二、原因分析 采用了非UNICODE&#xff1a; DEFINES - UNICODE没法识别 _DEBUGA 但可以识别 _DEBUG 三、错误处理 修改 _DEBUGA 为 _DEBUG

简单的车牌号识别

目录 处理流程与界面各接口编写时遇到的一些问题上传图片识别结果标签显示中文 处理流程与界面 首先点击“上传图片”按钮&#xff0c;可以选择文件夹中含有汽车车牌的图片&#xff0c;并显示在“图片框”中。 点击“检测车牌”按钮&#xff0c;会先对“图片框”中即含有汽车车…

【漏洞复现】通天星CMSV6车载视频监控平台inspect_file文件上传漏洞

Nx01 产品简介 通天星车载视频监控平台软件拥有多种语言版本&#xff0c;应用于公交车车载视频监控、校车车载视频监控、大巴车车载视频监控、物流车载监控、油品运输车载监控等公共交通上。 Nx02 漏洞描述 通天星CMSV6车载视频监控平台/inspect_file/upload存在文件上传漏洞&…

阿姨吐槽年轻人卧铺挂帘子不让坐 评论区吵翻天了

近日&#xff0c;网络流传的一段短视频激起了公众的广泛热议。 这段视频展现了一位阿姨与在下铺挂帘子的年轻人之间的冲突。 视频中&#xff0c;阿姨情绪激动&#xff0c;她用镜头对准了那位年轻人&#xff0c;指责他在下铺挂帘子&#xff0c;使得一位70岁的老人无法坐下。 阿姨…

【C++】explicit关键字详解(explicit关键字是什么? 为什么需要explicit关键字? 如何使用explicit 关键字)

目录 一、前言 二、explicit关键字是什么&#xff1f; 三、构造函数还具有类型转换的作用 &#x1f34e;单参构造函数 ✨引出 explicit 关键字 &#x1f34d;多参构造函数 ✨为什么需要explicit关键字&#xff1f; ✨怎么使用explicit关键字&#xff1f; 四、总结 五…

Angular学习第四天--问题记录及父子组件问题

问题一、 拉取完项目&#xff0c;使用npm install命令的时候遇到的。 解决办法&#xff1a; 在查找网上五花八门的解决方案之后&#xff0c;发现都不能解决。 我的解决办法是&#xff1a; 1. 把package-lock.json给删掉&#xff1b; 2. 把package.json中公司自己库的包给删除掉…

C# Solidworks二次开发:模型中实体Entity相关操作API详解

大家好&#xff0c;今天要讲的一些API是关于实体的相关API。 在开发的过程&#xff0c;很多地方会涉及到实体的相关操作&#xff0c;比如通过实体选中节点。下面就直接开始介绍API&#xff1a; &#xff08;1&#xff09;第一个API为Select4&#xff0c;这个API的含义为选中一…

Docker 学习笔记(五):梳理 Docker 镜像知识,附带 Commit 方式提交镜像副本,安装可视化面板 portainer

一、前言 记录时间 [2024-4-10] 前置文章&#xff1a; Docker学习笔记&#xff08;一&#xff09;&#xff1a;入门篇&#xff0c;Docker概述、基本组成等&#xff0c;对Docker有一个初步的认识 Docker学习笔记&#xff08;二&#xff09;&#xff1a;在Linux中部署Docker&…

FluentUI系列 - 1 - 介绍第一个窗口

介绍一个QML的UI库&#xff0c;国人编写&#xff0c;作者也耍知乎。这个UI库确实好用&#xff0c;但是教程基本等于无&#xff0c;个人在使用中顺便记录一下学习内容。这玩意儿也有Pyside6的版本&#xff0c;有需要的可以查看PySide6-FluentUI-QML。 FluentUI库地址​github.c…

00 【哈工大_操作系统】Bochs 汇编级调试方法及指令

本文将介绍一下哈工大李治军老师《操作系统》课程在完成Lab时所使用到的 Bochs 调试工具的使用方法。这是一款汇编级调试工具&#xff0c;打开调试模式非常简单&#xff0c;只需在终端下输入如下指令&#xff1a; 1、bochs 调试基本指令大全 功能指令举例在某物理地址设置断点…

bpftime(为什么要有,介绍,原理图),如何编译运行其代码,示例代码(运行结果+解释+内核层代码,用户层代码分析)

目录 bpftime(开源用户态 eBPF 运行时) 引入 在内核态实现用户态追踪的性能损失 内核空间执行ebpf的弊端 内核态 -> 用户态 介绍 原理图 示例代码 如何编译和运行 编译 运行 运行结果 运行结果 代码分析 .c 源码 语法 #include "malloc.skel.h&…

EPSON开发新IMU产品M-G370PDS改善姿态和震动控制

爱普生IMU于2011年首次推出&#xff0c;已在一系列客户应用中使用&#xff0c;因其出色的性能和质量而享有盛誉。近年来&#xff0c;IMU的使用已经扩展到无人系统测量、航空和水下视频摄影等领域。对更准确的位置和姿态控制的需求不断增长&#xff0c;不仅如此&#xff0c;高效…

【小程序】生成短信中可点击的链接

文章目录 前言一、如何生成链接二、仔细拜读小程序开发文档文档说明1文档说明2 总结 前言 由于线上运营需求&#xff0c;需要给用户发送炮轰短信&#xff0c;用户通过短信点击链接直接跳转进入小程序 一、如何生成链接 先是找了一些三方的&#xff0c;生成的倒是快速&#xf…

DDoS攻击类型与应对措施详解

攻击与防御简介 SYN Flood攻击 原理&#xff1a; SYN Flood攻击利用的是TCP协议的三次握手机制。在正常的TCP连接建立过程中&#xff0c;客户端发送一个SYN&#xff08;同步序列编号&#xff09;报文给服务器&#xff0c;服务器回应一个SYN-ACK&#xff08;同步和确认&#xf…

微信小程序wx.getLocation 真机调试不出现隐私弹窗

在小程序的开发过程中&#xff0c;首页中包含要获取用户地理位置的功能&#xff0c;所以在这里的onLoad&#xff08;&#xff09;中调用了wx.getLocation()&#xff0c;模拟调试时一切正常&#xff0c;但到了真机环境中就隐私框就不再弹出&#xff0c;并且出现了报错&#xff0…

开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition

开源相机管理库Aravis例程学习&#xff08;一&#xff09;——单帧采集single-acquisition 简介源码函数说明arv_camera_newarv_camera_acquisitionarv_camera_get_model_namearv_buffer_get_image_widtharv_buffer_get_image_height 简介 本文针对官方例程中的第一个例程&…