zoukankan      html  css  js  c++  java
  • 【cocos2d-x 手游研发----地图活起来了】

    谈到地图不少人都说要做地图编辑器了,但是我暂时绕过这一步,如果不用寻路地图就不能移动?寻路就是会绕过障碍物的算法。

    我做了一个简单的地图的思想,就是地图分层3层:背景层、可行区域层、遮罩层,但是地图就不寻路了,通过设置可行区域层来

    实现地图障碍物的方法。下面看一个视图,我把地图详细的分层了:

    OK,有了这个思路,大家应该也知道我要怎么做了?代码实现上怎么处理呢?

    重点:可行区域层原理是根据点击屏幕上的坐标点来取得这个点是否透明!如果不透明那就不让他进行移动,透明则为不可行区域;

    首先感谢一下为我提供取色源码的哥们(firedragonpzy),帮助我实现了这个另类的地图设计;下面我贴一下他的源码,

    新建了FDPixelSprite.cpp,FDPixelSprite.h代码如下:

    FDPixelSprite.h

    //
    //  FDPixelSprite.h
    //  PixelDemo
    //
    //  Created by firedragonpzy on 13-2-19.
    //
    //
    
    #ifndef __PixelDemo__FDPixelSprite__
    #define __PixelDemo__FDPixelSprite__
    #include "cocos2d.h"
    USING_NS_CC;
    
    class FDPixelSprite : public CCSprite, public CCTargetedTouchDelegate {
    public:
        FDPixelSprite();
        virtual ~FDPixelSprite();
        
        void onEnter();
        void onExit();
        void setimg(CCString Url);
        
        FDPixelSprite* create(CCString Url);
        CCImage* img ;
        
        CCRect atlasRect();
        bool isContainTouchLocation(CCTouch *pTouch);
        
        bool ccTouchBegan(CCString thismapurl,CCTouch *pTouch, CCEvent *pEvent);
        void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
        void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
    
        CC_SYNTHESIZE(const char*, m_pName,Name);
    };
    
    #endif /* defined(__PixelDemo__FDPixelSprite__) */
    View Code

    FDPixelSprite.cpp

    //
    //  FDPixelSprite.cpp
    //  PixelDemo
    //
    //  Created by firedragonpzy on 13-2-19.
    //
    //
    
    #include "FDPixelSprite.h"
    #include "FontChina.h"
    
    FDPixelSprite::FDPixelSprite()
    {}
    FDPixelSprite::~FDPixelSprite()
    {}
    
    FDPixelSprite* FDPixelSprite::create(CCString Url)
    {
        FDPixelSprite *sprite = new FDPixelSprite();
        if (sprite && sprite->initWithFile(Url.getCString())) {
            sprite->setName(Url.getCString());
            sprite->autorelease();
            return sprite;
        }
        CC_SAFE_DELETE(sprite);
        sprite = NULL;
        
        
        return NULL;
    }
    
    void FDPixelSprite::setimg(CCString Url){
        img= new CCImage();
        img->initWithImageFileThreadSafe(CCFileUtils::sharedFileUtils()->fullPathForFilename(Url.getCString()).c_str());
    
    }
    
    void FDPixelSprite::onEnter()
    {
        CCSprite::onEnter();
        CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
    }
    
    
    void FDPixelSprite::onExit()
    {
        CCSprite::onExit();
        CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
    }
    
    
    bool FDPixelSprite::ccTouchBegan(CCString thismapurl,cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
    {
        if (this->isContainTouchLocation(pTouch) ) {
            ccColor4B c = {0, 0, 0, 0};
            
            CCSize winSize = CCDirector::sharedDirector()->getWinSize();
            
            CCPoint touchPoint = pTouch->getLocationInView();
            
            CCSize cSize = this->getContentSize();
            CCPoint point =this->getAnchorPointInPoints();
            point = ccp(cSize.width - point.x,cSize.height- point.y);
            CCPoint pos(this->getPositionX() - point.x,winSize.height-this->getPositionY()- point.y);
            
            CCPoint localPoint = ccp(touchPoint.x - pos.x,
                                     touchPoint.y -pos.y);
            
            float scaleFactor = CCDirector::sharedDirector()->getContentScaleFactor();
            unsigned int x = localPoint.x  * scaleFactor, y = localPoint.y * scaleFactor;
            
            float _width = this->getContentSize().width*scaleFactor;
    
            //This method is currently only supports symmetric image
            //unsigned char *data_ = this->getTexture()->getFDImageData();
            
            //Efficiency of this method is relatively low
            //CCImage * img = new CCImage();
            //img->initWithImageFileThreadSafe(CCFileUtils::sharedFileUtils()->fullPathForFilename(thismapurl.getCString()).c_str());
            unsigned char *data_ = img->getData();
            
            
            unsigned int *pixel = (unsigned int *)data_;
            pixel = pixel + (y * (int)_width)* 1 + x * 1;
    
            c.r = *pixel & 0xff;
            c.g = (*pixel >> 8) & 0xff;
            c.b = (*pixel >> 16) & 0xff;
            c.a = (*pixel >> 24) & 0xff;
            if (c.a == 0) {
                CCLog(FontChina::G2U("不可点击!"));
                return false;
            }else
            {
                CCLog(FontChina::G2U("可点击!"));
                return true;
            }
        }
        return false;
    }
    
    
    void FDPixelSprite::ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
    {
        //CCPoint pos = this->getPosition();
        //CCPoint sub = pTouch->getDelta();
        //this->setPosition(ccpAdd(pos, sub));
    }
    
    
    void FDPixelSprite::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
    {
        //CCLog("firedragonpzy:ccTouchEnded");
    }
    
    
    CCRect FDPixelSprite::atlasRect()
    {
        CCSize cSize = this->getContentSize();
        CCPoint point = this->getAnchorPointInPoints();
        return CCRectMake( -point.x, -point.y, cSize.width,cSize.height);
    }
    
    
    bool FDPixelSprite::isContainTouchLocation(cocos2d::CCTouch *pTouch)
    {
       return this->atlasRect().containsPoint(convertTouchToNodeSpaceAR(pTouch));
    }
    View Code

    有了他们我们就能判断地图上是否可行了。OK废话不多,继续走向精彩,详细解决一下背景层我们应该做些什么东西,有什么内容?

    背景层肯定要装载精灵,把我们之前第二章说的【cocos2d-x 大型ARPG手游研发----精灵的八面玲珑】精灵加载出来,就可以当主角了。

    这里有人说,那其他不带主角功能的怎么办?继承你写的精灵类拓展成怪物类(智能AI攻击操作),NPC(任务功能模块),可拓展行是

    杠杠滴,继承下来NPC都能移动,和你打起来,我的思路是把地图做成一个大容器,每一块新地图继承一个MapsBase的同时他自己也有

    有自己的特殊逻辑和特殊业务;

    继续贴代码,地图是这么实现的:

    Maps_Diyu.h

    #include "cocos2d.h"
    #include "../Commen/FDPixelSprite.h"
    #include "../Spirits/SpiritsPlayer.h"
    
    USING_NS_CC;
    
    class Maps_Diyu :public cocos2d::CCSprite
    {
    public:
        Maps_Diyu(CCLayer* layer,CCString mapsurl,CCString mapsurl_1,int zOrder,CCPoint cp);
        ~Maps_Diyu(void);
       CCSprite
    * nowmap; CCSprite* nowmap_zhezhao; //基本数据 float lastmove_x,lastmove_y; bool moveMapto(CCPoint cp,FDPixelSprite* mainmap_Touch); CCActionInterval* act_moveto_maps; CCActionInterval* act_moveto_maps_touch; CCActionInterval* act_moveto_maps_zhezhao; private: SpiritsPlayer* role_main; CCAnimate* playdonghua; };

    Maps_Diyu.cpp

    #include "Maps_Diyu.h"
    #include "../GameData/GetNPCData.h"
    #include "../Commen/FontChina.h"
    #include "../Spirits/SpiritsMonster.h"
    #include "../Effects/SkillEffects.h"
    
    Maps_Diyu::Maps_Diyu(CCLayer* layer,CCString mapsurl,CCString mapsurl_1,int zOrder,CCPoint cp)
    {
        act_moveto_maps=NULL;
        act_moveto_maps_zhezhao=NULL;
        lastmove_x=0;
        lastmove_y=0;
        float map_x , map_y;
    
        float center_x,center_y;
    
        CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
        CCSize size = CCDirector::sharedDirector()->getWinSize();
    
        nowmap = Maps_Diyu::create(mapsurl.getCString());
        nowmap_zhezhao = Maps_Diyu::create(mapsurl_1.getCString());
        center_x = size.width/2;
        center_y = size.height/2;
    
        map_y = nowmap->getAnchorPointInPoints().y+origin.y;
        map_x = nowmap->getAnchorPointInPoints().x;
    
        if(cp.getLength()>0)
        {
            nowmap->setPosition(cp);
            nowmap_zhezhao->setPosition(cp);
        }
        else
        {
            nowmap->setPosition(ccp(map_x,map_y));
            nowmap_zhezhao->setPosition(ccp(map_x,map_y));
        }
    
        //计算地图上绝对位置的原点坐标
        float map_fornpc_x,map_fornpc_y;
        map_fornpc_x= nowmap->getContentSize().width/2;
        map_fornpc_y=nowmap->getContentSize().height/2;
    
        //主角加载
        GetNPCData* basedatas = new GetNPCData();
        basedatas->GetNPCchapter1();
        basedatas->role_player.nowpoint= CCPointMake(map_fornpc_x+428,map_fornpc_y+480);
        role_main = new SpiritsPlayer(basedatas->role_player,1,false);
        role_main->npc->retain();
    
        //加载NPC
        basedatas->role_mengpo.nowpoint= CCPointMake(map_fornpc_x+158,map_fornpc_y+510);
        SpiritsPlayer* role_mengpo= new SpiritsPlayer(basedatas->role_mengpo,1,true);
        nowmap->addChild(role_mengpo->npc, 2);
    //-------------------------------------------------------
        nowmap->addChild(role_main->npc, 2,999);
        layer->addChild(nowmap_zhezhao, zOrder+1);
        layer->addChild(nowmap, zOrder);
    }

    OK,地图初始化就加载了这些基础的数据,这些应该大家都能看懂,下面贴最核心的代码,如何把他们都关联起来

    并且做移动操作呢??????

    /*************************
    参数说明:
    CCPoint cp  可行区域的坐标
    mainmap_Touch 可行区域,需要去随时改变他移动
    **************************/
    bool Maps_Diyu::moveMapto(CCPoint cp,FDPixelSprite* mainmap_Touch)
    {
    
        float center_x,center_y,move_x,move_y, map_x , map_y ,to_c_x,to_c_y;
    
        CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
        CCSize size = CCDirector::sharedDirector()->getWinSize();
        center_x = size.width/2;
        center_y = size.height/2;
        move_x = center_x-cp.x;
        move_y = center_y-cp.y;
        map_x = nowmap->getPositionX();
        map_y = nowmap->getPositionY();
        to_c_x = nowmap->getContentSize().width/2;
        to_c_y = nowmap->getContentSize().height/2+origin.y;
        map_x = map_x + move_x;
        map_y  = map_y + move_y-origin.y;
    
        //计算移动时间,这边大家可以帮我优化一下
        //现在就这块移动时间有一些问题
        float a1 , b1 ;
        a1 = fabs(move_x)/size.width;
        b1 = fabs(move_y)/size.height;
        float movetime = ((a1+b1)*8);
        if(movetime<=1)
        {
            movetime=1;
        }
    
        //这里是精华,主要是处理任意地图放进来之后,
        //防止显示区域超出地图的长宽,移动到边界就不能移动了!
        if(map_x>=to_c_x)
        {
            map_x = to_c_x;
        }
        else if(map_x<=-((nowmap->getContentSize().width/2)-size.width))
        {
            map_x =-((nowmap->getContentSize().width/2)-size.width);
        }
        if(map_y>=to_c_y)
        {
            map_y = to_c_y;
        }
        else if(map_y <= -((nowmap->getContentSize().height/2)-size.height))
        {
            map_y = -((nowmap->getContentSize().height/2)-size.height);
        }
    
        //经典中的经典//
        //主角移动
        CCPoint role_move_pc = nowmap->convertToNodeSpace(ccp(cp.x,cp.y));//此处需要通过地图的视角把人物移动的坐标转化一下。
        role_main->moveTomap_dir(role_move_pc);    
        role_main->moveTomap_move(movetime,role_move_pc,false);
        //地图移动
        if(map_x!=lastmove_x&&map_y!=lastmove_y)
        {
            nowmap->stopAction(act_moveto_maps);
            nowmap_zhezhao->stopAction(act_moveto_maps_zhezhao);
            mainmap_Touch->stopAllActions();
            act_moveto_maps = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y));
            act_moveto_maps_touch = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y));
            act_moveto_maps_zhezhao = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y));
            nowmap->runAction(act_moveto_maps);
            nowmap_zhezhao->runAction(act_moveto_maps_zhezhao);
            mainmap_Touch->runAction(act_moveto_maps_touch);
            return true;
        }
        else                  
        {
            return false;
        }
    
    }

    核心的地方有三处,帮大家分析一下:

    第一,就是计算移动时间,我是根据屏幕长宽来计算,这个地方一直是我心结,这个方法效果现在很不好,跑起来

    长距离用时长,短距离就很快,所以请大家也帮我优化一下,可以往下贴代码,

    第二,就是计算出地图移动的区域,你不可能随便丢一张图进去,地图超过边界会显示黑色,不能让黑色显示出来(除非丢进来的图小过屏幕地图);

    第三,就是通过地图移动的标识来进行人物和地图的移动,在人物移动的时候需要转化一下成地图的坐标!

    map_x!=lastmove_x&&map_y!=lastmove_y
    可移动的标识
    act_moveto_maps = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y));
    act_moveto_maps_touch = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y));
    act_moveto_maps_zhezhao = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y));
    
    大家也看到,我移动的时候,移动的是3个层,这下就保证了可行区域也是不停在变动的

    然后就是如何传数据了,几句话就可以搞定Scene加载的地图上所有层;

    Scene_Diyu.h

    #include "cocos2d.h"
    #include "ToScene.h"
    #include "../MapSpirits/Maps_Diyu.h"
    USING_NS_CC;
    class Scene_Diyu : public CCLayer
    {
    public:
        Scene_Diyu(void);
        ~Scene_Diyu(void);
        Maps_Diyu* mainmap;
        FDPixelSprite* mainmap_Touch;
        void nextCallback(CCObject* pSender);
        virtual void registerWithTouchDispatcher(void);
        virtual bool ccTouchBegan(CCTouch *pTouch,CCEvent *pEvent);
        virtual void ccTouchMoved(CCTouch *pTouch,CCEvent *pEvent);
        virtual void ccTouchEnded(CCTouch *pTouch,CCEvent *pEvent);
        virtual void ccTouchCancelled(CCTouch *pTouch,CCEvent *pEvent);
    };
    View Code

    Scene_Diyu.cpp

    #include "Scene_Diyu.h"
    #include "../ImagePaths.h"
    #include "../PublicUI/BaseUI.h"
    
    Scene_Diyu::Scene_Diyu(void)
    {
        float x,y;
        CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
        CCSize size = CCDirector::sharedDirector()->getWinSize();
        x = size.width;
        y = size.height;
        //地图
        mainmap = new Maps_Diyu(this,"map_diyu_naihe.jpg","map_diyu_naihe1.png",0,ccp(x/2-308,y/2-486));
        mainmap_Touch =  mainmap_Touch->create("map_diyu_naihe0.png");
        mainmap_Touch->setimg("map_diyu_naihe0.png");
        mainmap_Touch->setPosition(ccp(x/2-308,y/2-486));
        mainmap_Touch->setVisible(false);//是否显示点击层
    
        BaseUI* baseui = new BaseUI(this);
    
        this->addChild(mainmap_Touch, 0);
        setTouchEnabled(true);
    }
    
    
    void Scene_Diyu::registerWithTouchDispatcher()  
    {  
        CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,true);  
    }  
    
    
    bool Scene_Diyu::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)  
    {  
        if(mainmap_Touch->ccTouchBegan("map_diyu_naihe0.png",pTouch,pEvent)==true)
        {
            mainmap->moveMapto(pTouch->getLocation(),mainmap_Touch);
        }
        return true;  
    }  
      
    void Scene_Diyu::ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)  
    {  
    }  
      
    void Scene_Diyu::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)  
    {  
    }  
      
    void Scene_Diyu::ccTouchCancelled(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)  
    {  
    }  
    
    Scene_Diyu::~Scene_Diyu(void)
    {
    }

     大家也看到了再Scene里面控制的点击事件主要就是处理可行区域的:

     if(mainmap_Touch->ccTouchBegan("map_diyu_naihe0.png",pTouch,pEvent)==true)
        {
            mainmap->moveMapto(pTouch->getLocation(),mainmap_Touch);
        }

    好了,大家如果理解,可以自己研究一下自己的方式去实现一下这样一套,人物移动,地图移动的原理,当然,我在这里声明一下

    这套实现思路其实是很歪门,另类的,应为他并没有采用寻路,但是你也不能完全说不采用寻路算法的地图系统就不行。

    跑起来看一下效果截图:

    跑起来后的效果图!!!

    人物被遮罩层遮挡的效果图!!

    这篇就讲这么多了,下一篇直接讲一下【怪物智能AI的制作】怪物实现追踪主角,怪物随机生成怪圈,怪物随机移动巡逻。

    游戏demo及素材下载地址(demo里面包含了所有的素材资料);

    http://pan.baidu.com/share/link?shareid=4012433582&uk=4097703620&third=15

    我建了一个QQ群:和大家一起分享cocos2dx开发经验【41131516】

  • 相关阅读:
    Kafka0.10.2.0分布式集群安装
    Windows下本机简易监控系统搭建(Telegraf+Influxdb+Grafana)
    Influxdb1.2.2安装
    Grafana4.2安装
    Zookeeper3.4.9分布式集群安装
    Grafana中多租户设置
    Grafana关键表结构分析
    Spark2.1.0分布式集群安装
    Hadoop2.7.3分布式集群安装
    JVM学习--(七)性能监控工具
  • 原文地址:https://www.cnblogs.com/zisou/p/cocos2d-xARPG3.html
Copyright © 2011-2022 走看看