zoukankan      html  css  js  c++  java
  • 基于cocos2dx的横版动作游戏制作(一)

    本人最近几个月在工作之余,都有断断续续地去学习cocos2dx的一些东西,在一些论坛上参考有关资料,源码,比如www.9miao.com,泰然网等等,毕竟开源,而且较为有趣。

    7月份离职后,希望换个方向做手游(我之前一直做的是JAVA,web,数据库),发现没有工作经验真的是不好找....,笔试过了,面试总会问:“你有没有什么作品”,一想想,平时都是做一些demo,或者是参阅别人的项目源码,好像真的没有完整的弄一样东西出来。

    为此,我准备做一个横版的动作游戏(因为是第一次做,前后总共花了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版本的应该是都可以运行的

  • 相关阅读:
    win10 uwp 弹起键盘不隐藏界面元素
    win10 uwp 存放网络图片到本地
    win10 uwp 存放网络图片到本地
    sublime Text 正则替换
    sublime Text 正则替换
    win10 uwp 绘图 Line 控件使用
    win10 uwp 绘图 Line 控件使用
    AJAX 是什么?
    什么是 PHP SimpleXML?
    PHP XML DOM:DOM 是什么?
  • 原文地址:https://www.cnblogs.com/zouly/p/3841794.html
Copyright © 2011-2022 走看看