Cocos2d-x3.x塔防游戏(保卫萝卜)从零开始(三)

转自:http://www.cnblogs.com/hll2008/p/4266776.html

一、前提:

完成前一篇的内容。

具体参考:Cocos2d-x3.x塔防游戏(保卫萝卜)从零开始(二)篇

二、本篇目标:

l  说说游戏中各种角色的动作、属性以及重构思路

l  进行代码重构让色狼大叔和女主角生动鲜活起来

三、内容:

l  说说游戏中各种角色的动作、属性以及重构思路

通过前两篇我们建立的一个简陋的游戏原型,但是游戏中的人物比如色狼大叔、女主角都看去来很呆板不够鲜活,比如色狼会沿着道路移动,那这个只能说是移动根本不像是在走路,没有走的动作感觉就是沿着道路在漂移,女主角也是一动不动的站那里。这样的游戏很没有乐趣,所以需要给这些游戏角色加入动作和表情,让人看去来他们是鲜活的,对这些角色进行一下简单的动画以及属性分析如下:

色狼大叔的动画:

1、          静止动画:游戏刚开始处于静止状态

2、          走路动画:沿着道路走

3、          死亡动画:当子弹击中血量消耗完时死亡消失

色狼大叔的属性:

1、          是否运动:色狼是否处于激活沿着道路行走

2、          是否死亡:是否被炮塔打死

3、          行走速度:不同色狼的行走速度不同

4、          色狼血量:不同色狼血量不同

5、          行走:沿着道路行走

女主角的动画:

1、          静止动画:游戏刚开始处于静止状态

2、          卖萌动画:不能像木头似的,就加点表情动作

3、          死亡动画:当纯洁值被色狼玷污光了就死亡了

女主角的属性:

1、          女主角贞洁值:相当于生命值

 

根据上面的分析我们把每个角色拆分成动画显示和业务属性逻辑两个部分,对色狼和女主角进行代码重构。

重构后大概结构如上图:

ActionSprite:属于CCSprite类,负责游戏角色精灵的动画显示,Luoli(萝莉)、DaShu(大叔)、JiaoShou(叫兽)等角色精灵均继承自ActionSprite,都属于动画显示类。

Luoli(萝莉):属ActionSprite的子类,负责女主角的动画效果展示。

DaShu(大叔):属ActionSprite的子类,负责大叔色狼的动画效果展示。

JiaoShou (叫兽):属ActionSprite的子类,负责叫兽色狼的动画效果展示。

Selang(色狼):属于CCNode类,负责色狼的行走、血量、速度、攻击等具体的业务,每个Selang都包含一个DaShu(大叔)或JiaoShou(叫兽)类的游戏精灵。并且具备最重要的行为实现沿着道路行走。

Girl(女孩):属于CCNode类,负责女主角的血量等具体的业务,每个Girl都包含一个Luoli类的游戏精灵。

l  进行代码重构让色狼大叔和女主角生动鲜活起来

打开项目工程按照上面的思路重点对色狼和女主角的代码实现进行重构。

色狼大叔代码重构

第一步:

新建ActionSprite.h、ActionSprite.cpp类(角色动画类),这个类继承自CCSprite负责游戏角色的动画效果显示,色狼和女孩都会是这个类的子类。

ActionSprite.h代码:

复制代码 //声明一个动作状态的枚举类型 typedef enum _ActionState{kActionStateNone = 0, //无状态kActionStateIdle, //静止状态kActionStateWalk, //行走状态kActionStateDeath //死亡状态 }ActionState;class ActionSprite: public cocos2d::CCSprite { public:ActionSprite(void);~ActionSprite(void);//静止void idle();//死亡void death();//行走void walk();//价格CC_SYNTHESIZE(int,_price,Price);//生命值CC_SYNTHESIZE(float,_hp,HP);//静止状态动作CC_SYNTHESIZE_RETAIN(cocos2d::Action*,_idleAction,IdleAction);//死亡状态动作CC_SYNTHESIZE_RETAIN(cocos2d::Action*,_deathAction,DeathAction);//行走状态动作CC_SYNTHESIZE_RETAIN(cocos2d::Action*,_walkAction,WalkAction);//当前动作状态 CC_SYNTHESIZE(ActionState,_actionState,ActionState);//行走速度CC_SYNTHESIZE(float,_walkSpeed,WalkSpeed);//伤害值CC_SYNTHESIZE(float,_damage,Damage);//金钱CC_SYNTHESIZE(float,_money,Money);//是否有光环CC_SYNTHESIZE(bool,_halo,Halo);}; 复制代码

ActionSprite.cpp代码:

复制代码 ActionSprite::ActionSprite(void) {_price=0;_idleAction=NULL;_walkAction=NULL;_deathAction=NULL; }ActionSprite::~ActionSprite(void) {//释放内存 CC_SAFE_RELEASE_NULL(_idleAction);CC_SAFE_RELEASE_NULL(_deathAction);CC_SAFE_RELEASE_NULL(_walkAction); }//设置精灵为静止状态 void ActionSprite::idle() {if (_actionState != kActionStateIdle){//先停止所有动作this->stopAllActions();//运行静止动作this->runAction(_idleAction);_actionState=kActionStateIdle;} }//设置精灵为行走状态 void ActionSprite::walk() {if (_actionState != kActionStateWalk){//先停止所有动作this->stopAllActions();//运行行走动作this->runAction(_walkAction);_actionState=kActionStateWalk;}}//设置精灵为死亡状态 void ActionSprite::death() {//先停止所有动作this->stopAllActions();this->runAction(_deathAction);_actionState=kActionStateDeath; } 复制代码

第二步:

素材准备,设计2张大叔不同动作的图片,交替显示模拟色狼行走动画,完成后把图片拷贝到Resources的iphonehd文件夹中,为了适应小分辨率的手机把这个2张图片按比例缩小一半并且拷贝到Resources的iphone文件夹中。

第三步:

新建DaShu.h、DaShu.cpp类(色狼大叔动画类),这个类继承自上面的ActionSprite负责游戏色狼大叔的动画效果显示。

DaShu.h:

复制代码 class DaShu : public ActionSprite { public:DaShu(void);~DaShu(void);CREATE_FUNC(DaShu);//初始化方法bool init();//设置光环,拥有光环的色狼生命值加倍void setHalo(bool halo); }; 复制代码

DaShu.cpp:

复制代码 bool DaShu::init() {bool bRet=false;do {CC_BREAK_IF(!ActionSprite::initWithFile("dashu_2.png"));//设置静止状态动作Vector idleFrames(1);SpriteFrame *frame1=SpriteFrame::create("dashu_2.png", Rect(0, 0, 60, 83));idleFrames.pushBack(frame1);Animation *idleAnimation=Animation::createWithSpriteFrames(idleFrames,float(6.0 / 12.0));this->setIdleAction(CCRepeatForever::create(CCAnimate::create(idleAnimation)));int i=0;//设置行走状态动作Vector walkFrames(2);for (i=0;i<2;i++){SpriteFrame *frame2=SpriteFrame::create(CCString::createWithFormat("dashu_%d.png", i+1)->getCString(), Rect(0, 0, 60, 83));walkFrames.pushBack(frame2);}Animation *walkAnimation=Animation::createWithSpriteFrames(walkFrames,float(6.0 / 12.0));this->setWalkAction(CCRepeatForever::create(CCAnimate::create(walkAnimation)));//设置死亡状态动作Vector deathFrames(1);SpriteFrame *frame3=SpriteFrame::create("dashu_2.png", Rect(0, 0, 60, 83));deathFrames.pushBack(frame3);Animation *deathAnimation=Animation::createWithSpriteFrames(deathFrames,float(6.0 / 12.0));this->setDeathAction(Animate::create(deathAnimation));//设置攻击值this->setDamage(1);//设置行走速度this->setWalkSpeed(0.4f);//设置生命值this->setHP(18);//设置金钱数this->setMoney(1.0f/10.0f);bRet=true;} while (0);return bRet; }//设置光环 void DaShu::setHalo(bool halo) {if (halo){//拥有光环后生命值加4倍float h=this->getHP()*4.0f;this->setHP(h);}} 复制代码

第四步:

新建Selang.h、Selang.cpp类(色狼类),这个类继承自CCNode游戏场景中的每一个色狼都有这个类产生,它肯定包含一个ActionSprite的色狼动画类,并且之前在MainScene.cpp的update方法中实现的色狼沿路行走代码也将转移到这个类的update方法中。

Selang.h:

复制代码 #include "cocos2d.h" #include "Waypoint.h" #include "GameMediator.h" #include "ActionSprite.h" class Selang : public cocos2d::CCNode { public:Selang(void);~Selang(void);//根据提供的spriteIndex实例化成不同的色狼static Selang* nodeWithType(int spriteIndex);//初始化方法bool initWithType(int spriteIndex,bool halo);//激活色狼void doActivate(float dt);//获取精灵Rectvirtual cocos2d::Rect getRect();//设置精灵是否激活void setActive(bool active);//是否死亡bool isDie;void update(float delta);//色狼精灵CC_SYNTHESIZE_RETAIN(ActionSprite*,_mySprite,MySprite);private://精灵序号,为每种精灵编一个序号int _spriteIndex;GameMediator* m;//当前精灵的位置 cocos2d::Point myPosition;//走路速度float walkingSpeed;//开始路点Waypoint *beginningWaypoint;//结束路点Waypoint *destinationWaypoint;//是否激活bool active; //色狼高度float myHeight;//两个点的碰撞检测bool collisionWithCircle(cocos2d::Point circlePoint,float radius,cocos2d::Point circlePointTwo, float radiusTwo); }; 复制代码

Selang.cpp:

复制代码 //根据提供的spriteIndex实例化成不同的色狼 Selang* Selang::nodeWithType(int spriteIndex) {Selang* pRet=new Selang();bool b=false;if (pRet && pRet->initWithType(spriteIndex,b)){pRet->autorelease();return pRet;} else{delete pRet;pRet=NULL;return NULL;} } //初始化方法 bool Selang::initWithType(int spriteIndex,bool halo) {bool bRet=false;do {//色狼类型index_spriteIndex=spriteIndex;// m = GameMediator::sharedMediator();//不激活active=false;//行走速度walkingSpeed=0.2f;ActionSprite* sprite=NULL;if (spriteIndex==1)//如果类型是1初始化成大叔色狼 {sprite=DaShu::create();sprite->setHalo(halo);//设置速度walkingSpeed=sprite->getWalkSpeed();}this->setMySprite(sprite);//添加精灵到当前Selang中this->addChild(_mySprite);//计算当前色狼精灵1/2高myHeight=sprite->getTextureRect().size.height/2.0f;//获得路点集合中的最后一个点Waypoint *waypoint=(Waypoint*)m->getWayPoints().back();//设置为色狼出发点beginningWaypoint=waypoint;//获取出发点的下个点为色狼目标点destinationWaypoint=waypoint->getNextWaypoint();//获得出发点坐标Point pos=waypoint->getMyPosition();//对坐标进行校正提供半个身位高度pos.add(Vec2(0,myHeight));//记录位置坐标myPosition=pos;//设置精灵的初始坐标_mySprite->setPosition(pos);//设置初始不可见this->setVisible(false);//把当前色狼添加到游戏的MainScene场景中显示m->getNowScene()->addChild(this);//启动定时器this->scheduleUpdate();bRet=true;} while (0);return bRet; }void Selang::doActivate(float dt) {//激活色狼active=true;//设置色狼可见this->setVisible(true); }//获取精灵Rect Rect Selang::getRect() {Rect rect =Rect(_mySprite->getPosition().x - _mySprite->getContentSize().width * 0.5f,_mySprite->getPosition().y - _mySprite->getContentSize().height* 0.5f,_mySprite->getContentSize().width,_mySprite->getContentSize().height);return rect; }//设置精灵是否激活 void Selang::setActive(bool aactive) {active=aactive;this->setVisible(true); }void Selang::update(float delta) {if (!active){return;}Point destinationPos=destinationWaypoint->getMyPosition();//提升色狼半个身位destinationPos.add(Vec2(0,myHeight));//是否拐弯if (this->collisionWithCircle(myPosition,1,destinationPos,1)){if (destinationWaypoint->getNextWaypoint()){//设置新的出发点和目标点beginningWaypoint=destinationWaypoint;destinationWaypoint=destinationWaypoint->getNextWaypoint();}}Point targetPoint=destinationWaypoint->getMyPosition();//提升色狼半个身位targetPoint.add(Vec2(0,myHeight));float movementSpeed=walkingSpeed;//计算目标点的向量Point normalized=Point(targetPoint.x-myPosition.x,targetPoint.y-myPosition.y).getNormalized();//根据速度和向量分别计算x,y方式上的偏移值float ox=normalized.x * walkingSpeed;float oy=normalized.y *walkingSpeed;//更新色狼移动后的位置myPosition = Point(myPosition.x + ox, myPosition.y +oy);_mySprite->setPosition(myPosition); } //两个点的碰撞检测 bool Selang::collisionWithCircle(cocos2d::Point circlePoint,float radius,cocos2d::Point circlePointTwo, float radiusTwo) {float xdif = circlePoint.x - circlePointTwo.x;float ydif = circlePoint.y - circlePointTwo.y;//计算两点间的距离float distance = sqrt(xdif * xdif + ydif * ydif);if(distance <= radius + radiusTwo) {return true;}return false; } 复制代码

第五步:

如果运行一下那么上面的代码中Waypoint *waypoint=(Waypoint*)m->getWayPoints().back();这行应该会报错,因为GameMediator中没有提供getWayPoints()这个方法,所以我们要对GameMediator类进行修改加上这个方法,代码如下:

复制代码 void GameMediator::setWayPoints(cocos2d::Vector wayPoints) {_wayPoints=wayPoints; }Vector GameMediator::getWayPoints() {return _wayPoints; } 复制代码

第六步:

在MainScene的init方法中把路点集合通过setWayPoints方法赋值给GameMediator,这样在Selang.cpp中就可以取到路点集合了:

…… this->wayPositions.pushBack(waypoint12); GameMediator::sharedMediator()->setWayPoints(wayPositions); ……

第七步:

测试这个Selang类具体效果,先给MainScene添加一个void startGame(float delta)的方法,用这个方法开始游戏。

复制代码 //开始游戏 void MainScene::startGame(float delta) {//实例化一个大叔类型的色狼Selang* selang=Selang::nodeWithType(1);//激活这个色狼selang->setActive(true);//设置色狼动画为行走动画selang->getMySprite()->walk();//取消定时器方法,保证startGame只执行一次this->unschedule(schedule_selector(MainScene::startGame)); } 复制代码

第八步:

我们在MainScene的init方法末尾处调用这个startGame的方法:

//0.5秒后调用startGame方法 this->schedule(schedule_selector(MainScene::startGame),0.5f);

到这里,把第一篇中临时添加色狼的代码删除,就可以运行测试游戏了,会看到色狼大叔一扭一扭的沿着道路靠近女主角。效果非常好,我们成功的对色狼的代码进行了重构。

女主角代码重构

第一步:

素材准备,设计4张萝莉不同动作的图片,交替显示模拟萝莉卖萌动画,完成后把图片拷贝到Resources的iphonehd文件夹中,为了适应小分辨率的手机把这个4张图片按比例缩小一半并且拷贝到Resources的iphone文件夹中。

第二步:

新建Luoli.h、Luoli.cpp类(女主角动画类),这个类继承自上面的ActionSprite负责游戏女主角的动画效果显示。

Luoli.h:

复制代码 class Luoli : public ActionSprite { public:Luoli(void);~Luoli(void);CREATE_FUNC(Luoli);bool init(); }; 复制代码

Luoli.cpp:

复制代码 bool Luoli::init() {bool bRet=false;do {CC_BREAK_IF(!ActionSprite::initWithFile("girl1_1.png"));//设置静止状态动作Vector idleFrames(1);SpriteFrame *frame1=SpriteFrame::create("girl1_1.png", Rect(0, 0, 100, 126));idleFrames.pushBack(frame1);Animation *idleAnimation=Animation::createWithSpriteFrames(idleFrames,float(6.0 / 12.0));this->setIdleAction(CCRepeatForever::create(CCAnimate::create(idleAnimation)));//设置行走状态动作int i;Vector walkFrames(4);for (i=0;i<4;i++){SpriteFrame *frame1=SpriteFrame::create(CCString::createWithFormat("girl1_%d.png", i+1)->getCString(), Rect(0, 0, 100, 126));walkFrames.pushBack(frame1);}Animation *walkAnimation=Animation::createWithSpriteFrames(walkFrames,float(6.0 / 12.0));this->setWalkAction(CCRepeatForever::create(CCAnimate::create(walkAnimation)));bRet=true;} while (0);return bRet; } 复制代码

第三步:

新建Girl.h、Girl.cpp类(女孩类),这个类继承自CCNode游戏场景中的女主角由这个类产生,它肯定包含一个ActionSprite的萝莉动画类。

Girl.h:

复制代码 class Girl : public cocos2d::CCNode { public:Girl(void);~Girl(void);//根据提供的type实例化成不同的女主角static Girl* nodeWithType(int type);//初始化方法bool initWithLocation(cocos2d::Point location);//获取精灵Rect cocos2d::Rect getRect();private://萝莉精灵CC_SYNTHESIZE_RETAIN(ActionSprite*,_mySprite,MySprite); }; 复制代码

Girl.cpp:

复制代码 //根据提供的type实例化成不同的女主角 Girl* Girl::nodeWithType(int type) {Girl* pRet=new Girl();GameMediator* m = GameMediator::sharedMediator();Waypoint *waypoint=(Waypoint*)m->getWayPoints().front();Point pos=waypoint->getMyPosition();if (pRet && pRet->initWithLocation(pos)){pRet->autorelease();return pRet;} else{delete pRet;pRet=NULL;return false;} }//初始化方法 bool Girl::initWithLocation(cocos2d::Point location) {bool bRet=false;do {//实例化一个萝莉ActionSprite *sprite= Luoli::create();this->setMySprite(sprite);//添加精灵到当前Gril中this->addChild(sprite);//设置为静止sprite->idle();//计算当前萝莉精灵1/2高int myHeight=sprite->getTextureRect().size.height/2.0f;//对坐标进行校正提供半个身位高度location.add(Vec2(0,myHeight));sprite->setPosition(location);//把当前女主角添加到游戏的MainScene场景中显示GameMediator* m = GameMediator::sharedMediator();m->getNowScene()->addChild(this,10000);bRet=true;}while (0);return bRet; }Rect Girl::getRect() {Rect rect = Rect(_mySprite->getPosition().x - _mySprite->getContentSize().width * 0.5f,_mySprite->getPosition().y - _mySprite->getContentSize().height* 0.5f+20,_mySprite->getContentSize().width,_mySprite->getContentSize().height-40);return rect; } 复制代码

第四步:

在MainScene的 startGame(float delta)的方法中加上初始化女主角的代码。

复制代码 …… //初始一个女主角Girl* girl=Girl::nodeWithType(1);//设置女主角动画为卖萌动画girl->getMySprite()->walk();//取消定时器方法,保证startGame只执行一次this->unschedule(schedule_selector(MainScene::startGame)); 复制代码

到这里,把第一篇中临时添加女主角的代码删除,就可以运行测试游戏了,本篇的任务到此为止,本篇完成后android真机的运行效果如下:

 结束语:

这个塔防游戏系列已经写了3篇了,到现在为止还没有出现炮塔,说好的炮塔呢?请期待下一篇炮塔姑娘的保护神~

 

作者交流QQ:2303452599

           邮箱:mymoney1001@126.com



本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部