林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka
本文从设计模式中的策略模式入手,主讲了飞机大战中英雄飞机切换不同的子弹。这里分为三种子弹。第一种:每次发一个子弹,垂直发射;第二种:每次发两个子弹,两个都是垂直发射:第三种;每次发三个子弹,两边的子弹有一定的角度,而中间的子弹垂直发射;设计模式是游戏开发经常用到的思想,建议有兴趣的同学可以好好研究下!好了,下面开始吧。
效果如下:
Cocos2d-x版本:3.4
工程环境:VS30213
一、策略模式(Stragegy Pattern)
1、简介
Strategy模式也叫策略模式是行为模式之一,它对一系列的算法加以封装,为所有算法定义一个抽象的算法接口,并通过继承该抽象算法接口对所有的算法加以封装和实现,具体的算法选择交由客户端决定(策略)。Strategy模式主要用来平滑地处理算法的切换 。
2、意图
定义一系列的算法,把它们一个个封装起来,并且它们可相互替换。使得算法可独立于使用它的客户而变化。
3、适用性
- 如果一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
- 一个系统需要动态的在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多态性原则,客户端可以选择使用任何一个具体算法类,并只持有一个数据类型是抽象算法的对象。
- 一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的和复杂的只与算法有关的数据。
- 如果一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移到它们各自的Strategy类中以代替这些条件语句。
更多详细的策略模式看这里吧:24天学会设计模式------策略模式
二.子弹类与子弹管理类的代码编写
首先来看下本文的类UML图:
这里的思路是把子弹发射当成是一个函数,基类子弹中定义成虚函数,然后实现类子弹中HeroBulletOne、HeroBulletTwo、HeroBulletThree分别实现不同的发射功能。然后在子弹管理类中HeroBulletLayer中有一个私有的成员变量BulletStyle *mBulletStyle.当需要切换不同的子弹时,就将new不同的HeroBulletOne或HeroBulletTwo或HeroBulletThree,赋给mBulletStyle。
下面我们来看看代码吧!!!
2.1 首先是子弹的基类:
BulletStyle.h,这里注意到virtual void shootBullet(float dt){}为虚函数,表示这里要根据的子类不同,发射不同的子弹数目;
/** *功能 创建子弹的基类 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #pragma once #ifndef __BulletStyle_H__ #define __BulletStyle_H__ #include "cocos2d.h" USING_NS_CC; class BulletStyle : public cocos2d::Node{ public: ~BulletStyle(); /** * 移除所有的东西 */ void removeAllObject(); /** *移除超出屏幕可视范围的子弹或者碰撞后的子弹清除 *@param pNode 要删除的子弹 */ void removeBullet(Node* pNode); /** *根据传入的飞机,子弹跟随发射 *@param plane为传入飞机,可为英雄飞机或敌机 */ virtual void createBullet(Node* plane); /** *发射子弹,在其中进行子弹的渲染和子弹的飞行动作,默认为单子弹 *@param dt子弹间隔发时间 */ virtual void shootBullet(float dt){} protected: //子弹容器 Vector <Sprite *> vecBullet; //批次渲染节点 SpriteBatchNode* bulletBatchNode; //传入的飞机 Node* plane; }; #endif
实现文件BulletStyle.cpp
/** *功能 创建子弹的基类 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #include "BulletStyle.h" BulletStyle::~BulletStyle(){ //removeAllObject(); } /** * 移除所有的东西 */ void BulletStyle::removeAllObject(){ bulletBatchNode->removeAllChildren(); vecBullet.clear(); this->removeAllChildren(); } /** * 移除子弹,将子弹从容器中移除,同时也从SpriteBatchNode中移除 */ void BulletStyle::removeBullet(Node* pNode) { if (NULL == pNode) { return; } Sprite* bullet = (Sprite*)pNode; bulletBatchNode->removeChild(bullet, true); vecBullet.eraseObject(bullet); } /** *根据传入的飞机,子弹跟随发射 *@param plane为传入飞机,可为英雄飞机或敌机 */ void BulletStyle::createBullet(Node* plane){ this->plane = plane; //创建BatchNode节点 bulletBatchNode = SpriteBatchNode::create("bullet1.png"); this->addChild(bulletBatchNode); //每隔0.2S调用一次发射子弹函数 schedule(schedule_selector(BulletStyle::shootBullet), 0.2f);//注意,这里的发射方法留给子类来实现!!! }
2.2 只发射一个子弹的类
HeroBulletOne.h,注意,直接继承
/** *功能 每次只发射一个子弹 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #pragma once #ifndef __HeroBulletOne_H__ #define __HeroBulletOne_H__ #include "cocos2d.h" #include "BulletStyle.h" USING_NS_CC; class HeroBulletOne : public BulletStyle { public: virtual void shootBullet(float dt); }; #endif
实现文件:
/** *功能 每次只发射一个子弹 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #include "HeroBulletOne.h" void HeroBulletOne::shootBullet(float dt) { Size winSize = Director::getInstance()->getWinSize(); auto PlanePos = plane->getPosition(); //从缓存中创建子弹 auto spritebullet = Sprite::createWithTexture(bulletBatchNode->getTexture()); //将创建好的子弹添加到BatchNode中进行批次渲染 bulletBatchNode->addChild(spritebullet); //将创建好的子弹添加到容器 vecBullet.pushBack(spritebullet); Point bulletPos = (Point(PlanePos.x, PlanePos.y + plane->getContentSize().height / 2 + 20)); spritebullet->setPosition(bulletPos); spritebullet->setScale(0.8f); float flyVelocity = 500;//运行速度,可以自己控制,每秒所走的像素 float flyLen = winSize.height - PlanePos.y; float realFlyDuration = flyLen / flyVelocity;//实际飞行的时间 //子弹运行的距离和时间,从飞机处开始运行到屏幕顶端 auto actionMove = MoveTo::create(realFlyDuration, Point(bulletPos.x, winSize.height)); //子弹执行完动作后进行函数回调,调用移除子弹函数 auto actionDone = CallFuncN::create( CC_CALLBACK_1(HeroBulletOne::removeBullet, this)); //子弹开始跑动 Sequence* sequence = Sequence::create(actionMove, actionDone, NULL); spritebullet->runAction(sequence); }
2.3 发射二个子弹的类
HeroBulletTwo.h,注意,直接继承
/** *功能 每次发射二个子弹 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #pragma once #ifndef __HeroBulletTwo_H__ #define __HeroBulletTwo_H__ #include "cocos2d.h" #include "BulletStyle.h" USING_NS_CC; class HeroBulletTwo : public BulletStyle { public: virtual void shootBullet(float dt); }; #endif
实现文件:
/** *功能 每次发射二个子弹 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #include "HeroBulletTwo.h" void HeroBulletTwo::shootBullet(float dt) { Size winSize = Director::getInstance()->getWinSize(); auto PlanePos = plane->getPosition(); //从缓存中创建子弹 auto spritebullet1 = Sprite::createWithTexture(bulletBatchNode->getTexture()); auto spritebullet2 = Sprite::createWithTexture(bulletBatchNode->getTexture()); //将创建好的子弹添加到BatchNode中进行批次渲染 bulletBatchNode->addChild(spritebullet1); bulletBatchNode->addChild(spritebullet2); //将创建好的子弹添加到容器 vecBullet.pushBack(spritebullet1); vecBullet.pushBack(spritebullet2); Point bulletPos1 = (Point(PlanePos.x - plane->getContentSize().width / 4, PlanePos.y + plane->getContentSize().height / 2+10 )); Point bulletPos2 = (Point(PlanePos.x + plane->getContentSize().width / 4, PlanePos.y + plane->getContentSize().height / 2+10)); spritebullet1->setPosition(bulletPos1); spritebullet1->setScale(0.8f); spritebullet2->setPosition(bulletPos2); spritebullet2->setScale(0.8f); float flyVelocity = 500;//运行速度,可以自己控制,每秒所走的像素 float flyLen = winSize.height - PlanePos.y; float realFlyDuration = flyLen / flyVelocity;//实际飞行的时间 //子弹运行的距离和时间,从飞机处开始运行到屏幕顶端 auto actionMove1 = MoveTo::create(realFlyDuration, Point(bulletPos1.x, winSize.height)); auto actionMove2 = MoveTo::create(realFlyDuration, Point(bulletPos2.x, winSize.height)); //子弹执行完动作后进行函数回调,调用移除子弹函数 auto actionDone = CallFuncN::create( CC_CALLBACK_1(HeroBulletTwo::removeBullet, this)); //子弹开始跑动 Sequence* sequence1 = Sequence::create(actionMove1, actionDone, NULL); spritebullet1->runAction(sequence1); Sequence* sequence2 = Sequence::create(actionMove2, actionDone, NULL); spritebullet2->runAction(sequence2); }
2.4 发射三个子弹的类
HeroBulletThree.h,注意,直接继承
/** *功能 每次发射三个子弹 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #pragma once #ifndef __HeroBulletThree_H__ #define __HeroBulletThree_H__ #include "cocos2d.h" #include "BulletStyle.h" USING_NS_CC; class HeroBulletThree : public BulletStyle { public: virtual void shootBullet(float dt); }; #endif
实现文件:
/** *功能 每次发射三个子弹 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.14 */ #include "HeroBulletThree.h" void HeroBulletThree::shootBullet(float dt) { Size winSize = Director::getInstance()->getWinSize(); auto PlanePos = plane->getPosition(); double angle = M_PI * 80 / 180;//旋轉的角度 //从缓存中创建子弹 auto spritebullet = Sprite::createWithTexture(bulletBatchNode->getTexture()); auto spritebullet1 = Sprite::createWithTexture(bulletBatchNode->getTexture()); spritebullet1->setRotation(-angle); auto spritebullet2 = Sprite::createWithTexture(bulletBatchNode->getTexture()); spritebullet2->setRotation(angle); //将创建好的子弹添加到BatchNode中进行批次渲染 bulletBatchNode->addChild(spritebullet); bulletBatchNode->addChild(spritebullet1); bulletBatchNode->addChild(spritebullet2); //将创建好的子弹添加到容器 vecBullet.pushBack(spritebullet); vecBullet.pushBack(spritebullet1); vecBullet.pushBack(spritebullet2); Point bulletPos = (Point(PlanePos.x, PlanePos.y + plane->getContentSize().height / 2 + 20)); Point bulletPos1 = (Point(PlanePos.x - plane->getContentSize().width / 4-10, PlanePos.y + plane->getContentSize().height / 2+10 )); Point bulletPos2 = (Point(PlanePos.x + plane->getContentSize().width / 4+10, PlanePos.y + plane->getContentSize().height / 2+10)); spritebullet->setPosition(bulletPos); spritebullet->setScale(0.8f); spritebullet1->setPosition(bulletPos1); spritebullet1->setScale(0.8f); spritebullet2->setPosition(bulletPos2); spritebullet2->setScale(0.8f); float flyVelocity = 500;//运行速度,可以自己控制,每秒所走的像素 float flyLen = winSize.height - PlanePos.y; float flyLen1 = PlanePos.x / cos(angle);//按照度來算 float flyLen2 = (winSize.width - PlanePos.x) / cos(angle); float realFlyDuration = flyLen / flyVelocity;//实际飞行的时间 float realFlyDuration1 = flyLen1 / flyVelocity;//实际飞行的时间 float realFlyDuration2 = flyLen2 / flyVelocity;//实际飞行的时间 //子弹运行的距离和时间,从飞机处开始运行到屏幕顶端 auto actionMove = MoveTo::create(realFlyDuration, Point(bulletPos.x, winSize.height)); auto actionMove1 = MoveTo::create(realFlyDuration1, Point(0, PlanePos.x*tan(angle) + PlanePos.y)); auto actionMove2 = MoveTo::create(realFlyDuration2, Point(winSize.width, (winSize.width - PlanePos.x)*tan(angle) + PlanePos.y)); //子弹执行完动作后进行函数回调,调用移除子弹函数 auto actionDone = CallFuncN::create( CC_CALLBACK_1(HeroBulletThree::removeBullet, this)); //子弹开始跑动 Sequence* sequence = Sequence::create(actionMove, actionDone, NULL); spritebullet->runAction(sequence); Sequence* sequence1 = Sequence::create(actionMove1, actionDone, NULL); spritebullet1->runAction(sequence1); Sequence* sequence2 = Sequence::create(actionMove2, actionDone, NULL); spritebullet2->runAction(sequence2); }
2.5、子弹管理器编写
/** *功能 管理子弹、切换不同的子弹 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #pragma once #ifndef __HeroBulletLayer_H__ #define __HeroBulletLayer_H__ #include "cocos2d.h" #include "BulletStyle.h" #include "HeroBulletOne.h" #include "HeroBulletTwo.h" #include "HeroBulletThree.h" class HeroBulletLayer : public cocos2d::Layer { public: HeroBulletLayer(Node* heroPlane); virtual bool init(); //根据英雄飞机创建子弹 static HeroBulletLayer* create(Node* heroPlane); //改变子弹 void changeBullet(int bulletNumber); public: Node* heroPlane;//传入的英雄飞机 BulletStyle *mBulletStyle;//子弹类型 int bulletNumber;//当前子弹编号 }; #endif
实现文件:
/** *功能 管理子弹、切换不同的子弹 *作者 林炳文([email protected] 博客:http://blog.csdn.net/evankaka) *时间 2015.3.31 */ #include "HeroBulletLayer.h" HeroBulletLayer::HeroBulletLayer(Node* heroPlane) { this->heroPlane = heroPlane; mBulletStyle = NULL; bulletNumber = 1; } /** *创建子弹的静态方法 *@param heroPlane为英雄飞机 */ HeroBulletLayer* HeroBulletLayer::create(Node* heroPlane){ HeroBulletLayer* pRet = new HeroBulletLayer(heroPlane); if (pRet&&pRet->init()){ pRet->autorelease(); return pRet; } else{ delete pRet; pRet = NULL; return NULL; } } bool HeroBulletLayer::init() { bool bRet = false; do { CC_BREAK_IF(!Layer::init()); mBulletStyle = new HeroBulletOne(); mBulletStyle->autorelease(); mBulletStyle->createBullet(heroPlane); this->addChild(mBulletStyle); bRet = true; } while (0); return bRet; } /** *切换不同的子弹 *@param number 表示子弹的数目 */ void HeroBulletLayer::changeBullet(int number){ switch (number) { case 1: if (bulletNumber != 1){ this->removeChild(mBulletStyle, true); mBulletStyle = new HeroBulletOne(); bulletNumber = 1; mBulletStyle->createBullet(heroPlane); mBulletStyle->autorelease(); this->addChild(mBulletStyle); } break; case 2: if (bulletNumber != 2){ this->removeChild(mBulletStyle, true); mBulletStyle = new HeroBulletTwo(); bulletNumber = 2; mBulletStyle->createBullet(heroPlane); mBulletStyle->autorelease(); this->addChild(mBulletStyle); } break; case 3: if (bulletNumber != 3){ this->removeChild(mBulletStyle, true); mBulletStyle = new HeroBulletThree(); bulletNumber = 3; mBulletStyle->createBullet(heroPlane); mBulletStyle->autorelease(); this->addChild(mBulletStyle); } break; default: break; } }
2.6、调用方法
游戏入口主文件 GameMain.h添加头文件
#include "HeroBulletLayer.h"//这是子弹管理的层
增加变量:
HeroBulletLayer *mHeroBulletLayer;
然后是实现方法中GameMain.cpp的init()函数中增加
//加子弹 mHeroBulletLayer = HeroBulletLayer::create(mHeroPlane); this->addChild(mHeroBulletLayer,1);
注意mHeroPlane是你的英雄飞机类,是上文中可以跟随手指运动的手机,不懂看这里,Cocos2d-x《雷电大战》(2)-精灵随手指移动,你点哪我走哪!这里还没写成单例模式的,后头会再来改!
然后就是切换子弹啦:
你只需要在要切换子弹的地方:
发射一个子弹
mHeroBulletLayer->changeBullet(1);
发射二个子弹
mHeroBulletLayer->changeBullet(2);
发射三个子弹
mHeroBulletLayer->changeBullet(3);
这里我为了测试,设置成每个5秒自动切换子弹类型:
GameMain.h加个变量
int number;//表示当前子弹的类型
GameMain.cpp中init()函数中增加:
//每隔5S改變一次子子彈類型 number = 1; schedule(schedule_selector(GameMain::changeBullet),5.0f);
下面是定时器的方法
void GameMain::changeBullet(float dt){ if (number == 1){ mHeroBulletLayer->changeBullet(2); number = 2; } else if (number == 2){ mHeroBulletLayer->changeBullet(3); number = 3; } else if (number == 3) { mHeroBulletLayer->changeBullet(1); number = 1; } CCLOG("CHANGE"); }
效果:
这里它会自动每隔5s切换不同的子弹,由于上传图片的限制。只能这样了。
三、总结
是不是很方便呢?当我需要增加一个子弹类型时,我只需要继承BulletStyle.然后重写函数shootBullet(float dt) 即可。然后在需要更改子弹的位置bool HeroBulletLayer::changeBullet(int number)增加每4种子弹。第5种子弹......这样就增加了代码的复用性,而且很容易懂。也省去了一大堆的if-else判断。
林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka