本人最近几个月在工做之余,都有断断续续地去学习cocos2dx的一些东西,在一些论坛上参考有关资料,源码,好比www.9miao.com,泰然网等等,毕竟开源,并且较为有趣。html
7月份离职后,但愿换个方向作手游(我以前一直作的是JAVA,web,数据库),发现没有工做经验真的是很差找....,笔试过了,面试总会问:“你有没有什么做品”,一想一想,平时都是作一些demo,或者是参阅别人的项目源码,好像真的没有完整的弄同样东西出来。web
为此,我准备作一个横版的动做游戏(由于是第一次作,先后总共花了6天时间才基本完成)面试
在看官您阅读如下内容时,我认为您是对cocos2x是有一些基础了解的。数据库
在作这个游戏以前,咱们先来设想一下,咱们游戏须要哪些对象,内容等等到这些东西呢?设计模式
1.在一个战斗场景中,咱们须要一副地图做为基础,因此你可能须要用到Tiled 这个工具,来制做咱们的地图(在此我会认为您对瓦片地图是有了解的);缓存
2.咱们须要控制操做英雄(hero),怪物(AI)要来攻击英雄,hero和AI的闲置,行走,战斗都是须要不停地播放动画的,因此你可能须要本身去找一些人物素材(这玩意还真是很差找),而后为了方便加载使用,你可能还须要将帧动画序列打包,因此你极可能须要用到TexturePacker工具编辑器
3.英雄须要释放各类技能,还须要能使用血瓶加血,魔瓶加蓝,同时咱们还须要左上角可以显示当前英雄的血量,蓝量和等级等等信息,咱们还须要一个摇杆来操控英雄,等等......麻雀虽小,东西也不能少啊.....,这些素材都须要本身去找,准备好了布局又是一个问题了,这里你可能须要用到CocosStudio的UI编辑器了工具
4.素材的处理:你找的素材不必定都能直接用,你极可能还得本身去用PotoShop进行加工....布局
好了,以上这些介绍东西,已经大概能支撑咱们作一款横版游戏最基础的“砖石”了,固然还有cocos2dx了,无论你在XCode下仍是在VS下,这是你各人的喜爱了,这里就不介绍cocos2d的配置了,网上有不少这方面的帖子。学习
在此咱们来整理一下,咱们上面说到要用到的一些工具:
1.Tiled :用来作瓦片地图的
2.TexturePacker :帧动画打包的
3.CocosStudio:UI制做的
4.PotoShop:素材加工
在此,我先放张图出来,看看开发完成以后的效果
这张图就是最基本的效果了。
接下来,咱们开动以前还须要作些预备工做,那就是设计啦.....,咱们不能直接拿上素材就开搞。
//------------------------设计部分----------------------------
整个战斗场景咱们能够将他当作两层:
一个是游戏层(GameLayer):这里包括英雄,怪物,地图,已经地图上的掉落,道具等等.....
一个是操做层(OptionLayer):这里包括操做摇杆,技能UI,物品UI,人物头像属性UI
而后咱们须要考虑一下一些面向对象的东西,在这里,英雄跟怪物是有不少共性的.咱们能够将他们当作是一类角色(Role).....(包括你可能须要加一些非敌对类的NPC等均可归为这一类),怪物和英雄均可继承自它,这样咱们能省不少事.....
咱们最后还得考虑如下,各个对象直接的交互访问问题....由于一个场景里的各个对象有时候须要相互访问,那这个对象的实例怎么获取比较方便呢,这里咱们用一个单例来保存这些对象的指针或者引用(若是您对单例设计模式不了解的话,能够先去稍微了解一十几分钟)
好了,设计部分好像说的差很少了(再往下说就没个模块的细节设计了,这点咱们后面结合代码将),看起来是否是很简单呢?(是的,一目了然,可是咱们作的最多的工做永远都是细节上的处理)
//---------------------地图的处理-------------
咱们将地图图片的可达区域和不可区域理解为地面(floor)和墙壁(wall)两个Layer
这在后续控制英雄和怪物移动位置是能较好把握好范围,毕竟,地图的每一个区域不该该都是可达的(具体的编辑细节可本身摸索和参考网上的资料,这里再也不赘述);
总之你作好以后,在后续开发时确定会用到它,如下是地图的加载......
//初始化地图 void GameDisplayLayer::initMapWithFile(const char * path){ CCTMXTiledMap *tileMap = CCTMXTiledMap::create(path); CCObject *pObject = NULL; //遍历tmx地图中的Layer CCARRAY_FOREACH(tileMap->getChildren(), pObject) { CCTMXLayer *child = (CCTMXLayer*)pObject; child->getTexture()->setAliasTexParameters(); } tileMap->setAnchorPoint(ccp(0,0)); tileMap->setPosition(ccp(0,0)); this->addChild(tileMap, 0); global->tmxTileMap = tileMap; }
加载完后,若咱们想要知道对象要移动的点到底在不在floor这一区域内呢?如下是转换和判断位置代码(这里我提早贴出代码.....省的后面忘记)
//判断位置是否在地图可移动范围内 bool Global::tileAllowMove(CCPoint MovePoint){ CCTMXLayer *floor = global->tmxTileMap->layerNamed("floor"); //计算当前touchpoint在地图中的Gid CCPoint tileGid = Global::tilePosFromLocation(MovePoint); if(0 == floor->tileGIDAt(tileGid)){ //CCLog("current touchPoint tileGIDAt(?) is Empty "); return false; } return true; } //将CCPoint转换成Gid位置 CCPoint Global::tilePosFromLocation(CCPoint MovePoint, CCTMXTiledMap *map) { if(NULL == map){ map = global->tmxTileMap; } // 移动点的屏幕坐标必须减去瓷砖地图的坐标 - 由于地图可能比屏幕大,不少时候地图会随着操做移动,地图位置不必定在(0,0)点上了 CCPoint point = ccpSub(MovePoint, map->getPosition()); // 将获得坐标值转换成整数 CCPoint pointGID = ccp(0,0); pointGID.x = (int) (point.x / map->getTileSize().width); pointGID.y = (int) ((map->getMapSize().height * map->getTileSize().height - point.y) / map->getTileSize().height); //CCLog("pointGID.x,%f,%f",pointGID.x,pointGID.y); return pointGID; }
//----------------------Role类设计--------------------------------------
Role类是怪物和英雄的基类,它定义了二者所共有的属性,方法等.......如下是代码示例,Role.h的头文件
注意:每一个序列帧动画Animation 须要在怪物和英雄的init()中给其初始化
//基础角色类,主角和NPC都须要继承它 class Role :public CCSprite { public: Role(void); ~Role(void); CC_SYNTHESIZE(std::string,Name,Name); //角色名称 CC_SYNTHESIZE(CCAnimation*,idleAnimation,IdleAnimation); //角色空闲时序列 CC_SYNTHESIZE(CCAnimation*,movingAnimation,MovingAnimation); //角色移动时动画帧序列 CC_SYNTHESIZE(CCAnimation*,attackAnimation,AttackAnimation); //角色普通攻击时动画帧序列 CC_SYNTHESIZE(CCAnimation*,attackAnimation_1,AttackAnimation_1); //角色特殊攻击1动画帧序列 CC_SYNTHESIZE(CCAnimation*,attackAnimation_2,AttackAnimation_2); //角色特殊攻击2动画帧序列 CC_SYNTHESIZE(CCAnimation*,attackAnimation_3,AttackAnimation_3); //角色特殊攻击3动画帧序列 CC_SYNTHESIZE(CCAnimation*,attackAnimation_4,AttackAnimation_4); //角色特殊攻击4动画帧序列 CC_SYNTHESIZE(CCAnimation*,hurtAnimation,HurtAnimation); //角色受伤时动画帧序列 CC_SYNTHESIZE(CCAnimation*,deadAnimation,DeadAnimation); //角色死亡时动画帧序列 CC_SYNTHESIZE(float,curtLifeValue,CurtLifeValue); //角色当前生命值 CC_SYNTHESIZE(float,sumLifeValue,SumLifeValue); //角色整体生命值 CC_SYNTHESIZE(float,attackStrenth,AttackStrenth); //角色当前攻击力 CC_SYNTHESIZE(ActionState,actionState,ActionState); //当前Action状态(据此状态处理各个动画之间的衔接问题) CC_SYNTHESIZE(Direction, roleDirection, RoleDirection); //角色朝向(分向左仍是向右) CC_SYNTHESIZE(bool, allowMove, AllowMove); //角色是否容许移动,例如:攻击,受伤等动画执行期间不可移动 CC_SYNTHESIZE(CCPoint, _vector, Vector); //偏移量,AI自动移动时下一帧的偏移向量 void Role::callBackAction(CCNode* pSender); //动画执行完毕的通用回调处理 //action methods virtual void RunIdleAction(); //执行闲置动画 virtual void RunMovingAction(); //执行移动行走动画 virtual void RunAttackAction(); //执行普通攻击动画 virtual void RunAttackAction_1(); //执行特殊攻击1动画 virtual void RunHurtAction(); //执行被攻击后受伤动画 //......死亡动画等 };
//-------------下面我再给出动画执行部分的代码和回调----------------------- void Role::RunAttackAction() { if(this->getActionState() == ActionStateNone || this->getActionState() == ActionStateIdle || this->getActionState() == ActionStateMove){ this->setAllowMove(false); this->stopAllActions(); this->setActionState(ActionStateAttack); CCFiniteTimeAction *sequence = NULL; CCAnimate * AttackAnimate = CCAnimate::create(this->getAttackAnimation()); CCFiniteTimeAction * callFuncN = CCCallFuncN::create(this, callfuncN_selector(Role::callBackAction)); sequence = CCSequence::create(AttackAnimate, callFuncN,NULL); //最后加上NULL,不然报错 this->runAction(sequence); } } //动画执行结束后的通用回调,可在子类本身定义回调 void Role::callBackAction(CCNode* pSender){ if(pSender != 0) { this->setActionState(ActionStateNone); this->setAllowMove(true); //非行走类动画结束角色能够移动 this->RunIdleAction(); //继续执行空闲动画 } }
//这里我再给出英雄(hero)类的初始化init()方法中帧动画的初始动做,怪物类可依葫芦画瓢.......
bool Hero::init(){ //预加载精灵图片 CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("MyImages/role_ac_monkey.plist"); //获取纹理缓存 CCSpriteFrameCache *spriteFrameCache = CCSpriteFrameCache::sharedSpriteFrameCache(); Hero::initWithSpriteFrameName("39597-1.png"); int frameNum = 0; //取几帧 int frameStart = 0;//起始number CCArray *actionArry = CCArray::createWithCapacity(20); CCSpriteFrame *spriteFrame = NULL; //------------------------------idle action 空闲---------------------- frameNum = 4; //取几帧 frameStart = 1;//起始number for(int i=frameStart;i<frameNum;i++){ spriteFrame = spriteFrameCache->spriteFrameByName(CCString::createWithFormat("39597-%d.png",i)->getCString()); actionArry->addObject(spriteFrame); } CCAnimation* idleAnimation = CCAnimation::createWithSpriteFrames(actionArry,0.2f); actionArry->removeAllObjects(); idleAnimation->retain(); this->setIdleAnimation(idleAnimation);
//......还有行走,攻击动画等等初始化 //---------------------释放array-------------------------------------- actionArry->release(); return true; }
//-----------------------摇杆控制英雄行走的设计---------------------
怪物和英雄都有一样的属性,血量,攻击,状态,是否容许移动等等,貌似作的事情还很多,为了能看到某种效果,咱们先来看看如何经过触摸摇杆来控制hero行走
为此咱们设计一个专门的腰杆类来处理英雄的移动控制,摇杆的控制为此我作单章说明,请参考:http://www.cnblogs.com/zouly/p/3841830.html
//---------------------技能UI,及技能遮罩冷却效果-------------------------
为了使本篇幅看起来不是太长,我还但愿在介绍本功能点时能开单章说明技能UI的制做过程,冷却遮罩处理,请参考:http://www.cnblogs.com/zouly/p/3842333.html
好了,为了使本文篇幅看起来不至于太长,咱们第一部分就介绍到这里
另外,实现这个游戏参考了不少文章,记不住了,也不一一列举了
http://pan.baidu.com/s/1jGh0wJk 这是源代码,在vs2012 cocos2dx 2.2.2上运行,3.0~2.0版本的应该是均可以运行的