如果你看过第一部分介绍,你应该大体知道一个横版游戏该怎么样去做,需要什么东西了....本部分介绍一些细节设计...
第一个:单例对象我们应该怎么设计才比较方便用呢?里面需要放置哪些对象的引用和指针呢?
在一个战斗场景里,就当前这个游戏来说,GameLayer(游戏层),OptionLayer(操作层) 是他最基础的构成部分,自然在每个层中的child你都是可以访问的,对于英雄和NPC也是一样,如果将这些对象的引用当成一个全局的指针放置在一个单例对象里,那在接下来的开发访问中将会有很大的便利,因为这些对象基本上是不可缺的....
这里我先介绍一个很好用的单例模版类,所有你需要创建的单例类只要继承它就可以了,非常方便
#ifndef _SINGLETON_H #define _SINGLETON_H using namespace std; template <class T> class Singleton { public: //获取类的唯一实例 static inline T* instance(); //释放类的唯一实例 void release(); protected: Singleton(void){} ~Singleton(void){} static T* _instance; }; template <class T> inline T* Singleton<T>::instance() { if(NULL == _instance){ _instance = new T; } return _instance; } template <class T> void Singleton<T>::release() { if (!_instance) return; delete _instance; _instance = 0; } //cpp文件中需要先声明静态变量 #define DECLARE_SINGLETON_MEMBER(_Ty) template <> _Ty* Singleton<_Ty>::_instance = NULL; #endif//_SINGLETON_H
那么单例中你可以引用这以下对象,方便访问,但是要注意cpp文件中需要声明静态变量,可以通过模板中的宏 DECLARE_SINGLETON_MEMBER(XXX);
//需引入以下类,否则在这些类中访问单例对象会报错 class GameDisplayLayer; class OptionLayer; class Hero; class Role; USING_NS_CC; //全局单例 class Global:public Singleton<Global> { public: Global(void); ~Global(void); //GameScene *gameScene; GameDisplayLayer *gameLayer; //游戏层 OptionLayer *optionLayer; //操作层 Hero *hero; //英雄 CCArray *npcs; //怪物 CCTMXTiledMap *tmxTileMap; //地图
//还有其他一些对象的引用和公共方法也可以放这里......自己定义
}
第二,我们得介绍一下人物和地图的移动的细节问题,比如:地图一般情况下是比当前屏幕大的,横版游戏人物在各个方向移动时,必须考虑什么时候该由地图本身移动位置,而不是英雄移动,不过这个问题应该是比较好得到答案的,只要人物移动的下一帧地址快要超出屏幕中间,我们就让地图超反方向移动相等距离,这样可以造成英雄向前移动的效果,但是如果地图最边缘方向快要移动完毕时,为了保证不把黑色的越界区域显示出来,你还得在每次移动前判断是否地图边缘位置快要超过屏幕了,这时候,就算任务下一帧到达屏幕中间部分,你也得让英雄移动;除此之外,若让地图移动,那么你也必须遍历Npc,道具什么的,让他们跟着地图一起移动....以下贴出部分代码。
if (heroCtrl->getAllowMove()&&(Direction.x != 0||Direction.y != 0)) { CCSize size=CCDirector::sharedDirector()->getWinSize(); //制造一个矩形框,人物只会在当前框里活动,通过地图移动来造成更大面积的活动范围 CCRect rect=CCRectMake(heroCtrl->getContentSize().width/2,heroCtrl->getContentSize().height/2,size.width-(heroCtrl->getContentSize().width/2),size.height-(heroCtrl->getContentSize().height/2)); if(rect.containsPoint(position)){ float mapMaxX = 0; //计算是否是人移动还是地图移动,注意:地图在初始化时锚点已设为cpp(0,0) if(heroCtrl->getPosition().x <= SCREEN.width/2 && Direction.x>0 && position.x > SCREEN.width/2){ //地图需要左移动,position表示人物下一帧的位置 //计算地图最右端x轴下一次位置 mapMaxX = tmxTileMap->getPositionX()+tmxTileMap->getContentSize().width-Direction.x; //保证地图不会把越界部分移出来 if(mapMaxX > SCREEN.width){ global->tmxTileMap->setPositionX(global->tmxTileMap->getPositionX()-Direction.x); OptionLayer::npcMoveByMap(ccp(-Direction.x,0)); if(Direction.y != 0){ heroCtrl->setPositionY(heroCtrl->getPositionY() + Direction.y);//防止地图移动时,人物Y轴不能移动 } }else{ heroCtrl->setPosition(position); //地图快要越界,人物移动即可 } }else if(heroCtrl->getPosition().x >= SCREEN.width/2 && Direction.x<0 && position.x < SCREEN.width/2){//地图需要像右移动 //计算地图最左端x轴下一次位置 mapMaxX = tmxTileMap->getPositionX()+Direction.x; //保证地图不会把越界部分移出来 if(mapMaxX < 0){ global->tmxTileMap->setPositionX(global->tmxTileMap->getPositionX()-Direction.x); OptionLayer::npcMoveByMap(ccp(-Direction.x,0)); if(Direction.y != 0){ heroCtrl->setPositionY(heroCtrl->getPositionY() + Direction.y);//防止地图移动时,人物Y轴不能移动 } }else{ heroCtrl->setPosition(position); //地图快要越界,人物移动 } }else{ heroCtrl->setPosition(position); //人物移动即可 } } }
第三,英雄跟怪物的相互遮挡问题,一个比较偷懒但很有效的解决办法是,根据Y轴的位置来设置当前对象的zOrder值......
第四,AI 自动追杀英雄的问题,方法有很多种,其中之一的方法,可以参考摇杆控制的办法来一步步靠近英雄......(这个功能点可以花点心思考虑考虑,AI智能....说起来很广了)
第五,英雄跟怪物之间释放攻击技能的碰撞问题,这个貌似比较简单,但是有一点需要注意的是,png图片的大小往往要比精灵实际的大小要大,如果计算有效的攻击范围,可被攻击的范围等到 rect = CCRectMake(originX,originY,width,height);这个需要根据实际情况来定,通过attackReck.intersectsRect(hurtReck)方法判断碰撞是否有效
代码里之前写的地方有错,CCRect理解出错,应该是左下角原点+宽和高(汗.....)
//计算有效攻击区域 CCRect PublicFun::getAttackRect(Role* role){ CCRect rect ; float originX = 0; float originY = 0; float width = 0; float height = 0; //计算正前方攻击区域 if(role->getRoleDirection() == RolelTurnRight){//朝右 originX = role->getPositionX(); originY = role->getPositionY() - role->getContentSize().height/2; width = role->getContentSize().width/2; height = role->getContentSize().height/2; rect = CCRectMake(originX,originY,width,height); }else{ originX = role->getPositionX() -role->getContentSize().width/2; originY = role->getPositionY() - role->getContentSize().height/2; width = role->getContentSize().width/2; height = role->getContentSize().height/2; rect = CCRectMake(originX,originY,width,height); } return rect; } //计算可被攻击有效区域(当前精灵大小) CCRect PublicFun::getHurtRect(Role* role){ CCRect rect ; float originX = 0; float originY = 0; float width = 0; float height = 0; //受攻击范围 originX = role->getPositionX() - role->getContentSize().width/2; originY = role->getPositionY() - role->getContentSize().height/2; width = role->getContentSize().width; height = role->getContentSize().height; rect = CCRectMake(originX,originY,width,height); return rect; }
之后计算是否被攻击
CCRect hurtReck = PublicFun::getHurtRect(npc);//怪物受伤区域 if(attackReck.intersectsRect(hurtReck)){ npc->setAllowMove(false); //此处可处理怪物被攻击后的后续 Hero::damageDisplay("-999", npc); npc->RunHurtAction(); }
第六,技能伤害数字渐隐效果,这个问题也比较好解决,只要在当前角色头顶上方生成CCLabelBMFont 或者其他字体都行,通过CCFadeOut产生渐隐效果,之后移除掉该字体即可
void Hero::damageDisplay(const char* str,NPC* npc){//产生数字动画 CCFadeOut *labelFadeOut = CCFadeOut::create(1.5f); CCLabelBMFont* atLabel = CCLabelBMFont::create(str, "fonts/helvetica-32.fnt"); atLabel->setColor(ccRED); atLabel->setPosition(ccp(npc->getPositionX()-5,npc->getPositionY()+npc->getContentSize().height/2+5)); global->gameLayer->addChild(atLabel, 1); CCFiniteTimeAction * callFuncN = CCCallFuncN::create(atLabel, callfuncN_selector(Hero::FontsCallBackAction)); CCFiniteTimeAction *sequence = CCSequence::create(labelFadeOut, callFuncN,NULL); atLabel->runAction(sequence); }
说到这里,貌似整个游戏的介绍都算比较完全了,麻雀虽小,涵盖的东西细节上还是有不少需要处理的,后续考虑用quick-cocos2dx用全lua实现移植它
代码和资源附件后续会上传上来....
http://pan.baidu.com/s/1ntFQwrB 这是源代码,在vs2012 cocos2dx 2.2.2上运行,3.0以下~2.0版本的应该是都可以运行的