转自: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<SpriteFrame *> 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<SpriteFrame *> 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<SpriteFrame *> 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<Waypoint*> wayPoints)
{_wayPoints=wayPoints;
}Vector<Waypoint*> 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<SpriteFrame *> 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<SpriteFrame *> 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

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

  1. (译)如何使用cocos2d制作一个塔防游戏:第三部分

    原文链接地址:http://www.iphonegametutorials.com/2011/04/19/cocos2d-game-tutorial-%E2%80%93-how-to-build-a- ...

  2. (译)如何使用cocos2d制作一个塔防游戏:引子

    原文链接地址:http://www.iphonegametutorials.com/2011/04/11/cocos2d-game-tutorial-how-to-build-a-tower-defe ...

  3. Pygame实战:这年头塔除了拆还能干什么?这款好玩上瘾的塔防游戏,了解一下嘛

    导语 >>> 这年头塔除了拆还能干什么?这款好玩上瘾的塔防游戏,了解一下!! 自从塔诞生的时候起,我们就开始让毁灭的雨点从敌人的头顶上方倾泻而下. 现在很多游戏都将塔作为一个标志性的 ...

  4. 如何制作一个塔防游戏 Cocos2d x 2 0 4

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本文实践 ...

  5. (译)如何做一个塔防游戏(cocos2d 2012-8-17)

    PS:一直关注http://www.raywenderlich.com/这个网站,前几天看了他们8月17发的一个塔防游戏教程,试了一下感觉不错,搜了一下没发现没有译成中文的(不知道现在有没有),就自己 ...

  6. 超萌塔防游戏:《保卫萝卜CarrotFantasy》

           推荐理由:萌到底的游戏风格,经典的塔防通关玩法        塔防类游戏一直都比较受玩家喜欢,因为这类游戏通常都会有N多关卡,每一关卡会充分考验玩家对于排兵布阵的能力,十分锻炼思维能力; ...

  7. 6.pixi.js编写的塔防游戏(类似保卫萝卜)-游戏资源打包逻辑

    游戏说明 一个用pixi.js编写的h5塔防游戏,可以用electron打包为exe,支持移动端,也可以用webview控件打包为app在移动端使用 环境说明 cnpm@6.2.0 npm@6.14. ...

  8. [Cocos2d塔防游戏开发]Cocos2dx-3.X完成塔防游戏《王国保卫战》--简介+代码+资源

    学校实训项目需要用cocos2dx做一款塔防游戏,因为时间与制图技术.创意原因无法进行原创,碰巧找到了皇家守卫军这款游戏的图片资源,决定用cocos2dx来重现这款经典塔防游戏. 本项目已完成3分之1 ...

  9. IOS塔防游戏《坦克对大炮》的开发设计记录

    IOS塔防游戏<坦克对大炮>的开发设计记录 引子 游戏已经在App Store上线几个月了,一直很想写点什么记录一下.真要写的时候,却又发现无从下笔没啥好写的.在2012年进入IOS,对于 ...

最新文章

  1. java mina多线程_Java多线程基础总结九:Mina窥探(1)
  2. Logstash 参考指南(使用Filebeat Modules配置示例)
  3. “约见”面试官系列之常见面试题之第九十七篇之怎么定义vue-router的动态路由(建议收藏)
  4. mysql 分页优化策略(一)
  5. PAT乙级1025反转链表 25(分)
  6. python应聘要求_python爬取招聘要求等信息实例
  7. Linux:CPU使用率100%排查方法
  8. c语言入门经典+第5版+习题答案,《C语言入门经典(第5版)》—甲虎网一站式图书批发平台...
  9. android 备忘录 知乎,实用的语音转文字软件,知乎大佬力荐!会议纪要好帮手
  10. python设置excel套打_你不一定知道这个用 Python 快速设置 Excel 表格边框的技巧
  11. 【离散数学】最大元素、最小元素、极大元素、极小元素、上界、下界、最小上界(上确界)、最大下界(下确界)
  12. 【python】pythonPTA编程练习2
  13. AI可能真的要代替插画师了……
  14. java非必传参数怎么处理_SpringBoot 设置传入参数非必要的操作
  15. stm32固件库(STM32F10x标准外设库)V3.5简介
  16. python web微信应用(二) webwx 模块源码
  17. 源码安装mysql,详细
  18. PPT分享第01季-226套多种风格模板
  19. 小米网关接入Homekit完整教程
  20. BroadcastReceiver应用详解

热门文章

  1. 机器学习之回归(Regression)再理解
  2. linux C 二维数组 作为函数参数
  3. iOS小技能:重签名、逆向分析方法、多开原理
  4. 东方财富代码选股_注册制下的选股技巧:论疫苗概念股中的牛股基因
  5. matplotlib绘制散点图之基本配置——万能模板案例
  6. linux整行剪切_linux整行剪切_linux 的复制/剪切命令用法
  7. 管中窥豹--机器学习之我见
  8. 安卓声音管理器AudioManager的使用
  9. 最新版lumion8.0下载地址
  10. Excel表格如何进行美化,Leo老师来教你!