基于cocos2dx的横版动作游戏制作(二)

如果你看过第一部分介绍,你应该大体知道一个横版游戏该怎么样去做,需要什么东西了....本部分介绍一些细节设计...

第一个:单例对象我们应该怎么设计才比较方便用呢?里面需要放置哪些对象的引用和指针呢?

在一个战斗场景里,就当前这个游戏来说,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版本的应该是都可以运行的

原文地址:https://www.cnblogs.com/zouly/p/3844531.html