zoukankan      html  css  js  c++  java
  • cocos2d-x中的Tiled地图

    cocos2d-x中的瓦片地图是通过tiledMap软件制作的,存档格式是.tmx格式。此软件的使用步骤简单总结如下:

    (1)制作瓦片地图

    1 打开软件,软件界面如下图。

    2. 新建地图(文件->新文件)

    3.增加新图块(地图->新图块)

    4. 制作地图。在图层区域,单击右键可以新建图层和对象,上面的工具栏中的工具,制作地图。

    5 导出,记住格式一定是tmx格式

    说明

    • 有时候,在使用地图的时候,可能会报找不到资源的错误,可以使用xcode打开地图文件,将资源路径修改如图所示形式。

    在地图完成后,我们接下来要做的是读取tmx文件并操作文件实现我们想要的效果

    (2)读取地图文件的内容

    1. CCTMXTiledMap类

    这个类是cocos2d-x专门设计用来读取和解析TMX文件的。CCTMCTiledMap类继承自CCNode类,我们通过调用static CCTMXTiledMap * create(const char *tmxFile)和static CCTMXTiledMap * create(const char *tmxFile,const char *resourcePath)方法就可以创建CCTMXTiledMap的对象,然后我们调用addChild();就可以将地图加入到层中

    CCTMXTiledMap有以下特点:

    • 每个砖块元素都被当作精灵对象来处理。
    • 每个地图中的砖块,只有在需要的时候才会被创建,只有在开发者调用类CCLayer的tileAt之后,对象才会创建。
    • 每个砖块都支持旋转、移动、伸缩、变色以及透明度。
    • 开发者可以删除或者添加砖块元素。
    • 砖块的z轴,也是可以修改地。
    • 每个地图对象存在锚点,默认为(0,0)。
    • 每个TMX中的图层都会成为地图地子节点。
    • 每个砖块元素都有一个唯一的标志数值。
    • 仅支持XML格式,不支持Json格式。

    CCTMXTiledMap比较常用的成员函数:

     获得和设置地图的尺寸

    l const CCSize & getMapSize(void);

    l void setMapSize(const CCSize  &var)

    获得和设置砖块的尺寸

    l const CCSize &  getileSize(void);

    l void setTileSize(cosnt CCSize &var);

    获得和设置地图的方向

    l const int getMapOrientation();

    l void setMapOrientation(int var);

    设置和获得对象数组

    l CCArray * getObjectGroups();

    l Void setObjectGroups(CCArray *var);

    获得和设置地图地属性

    l void setProperties(CCDictionary *var);

    l CCDictionary * getProperties(void);

    /* 初始化方法(常用)*/

      static CCTMXTiledMap* create(const char *tmxFile);

    /** 通过名字获得地图中的层*/

        CCTMXLayer* layerNamed(const char *layerName);

    /** TMXObjectGroup 对象组,获得一个名字下的对象组*/

        CCTMXObjectGroup* objectGroupNamed(const char *groupName);

    /** 获得一个属性的值 */

        CCString *propertyNamed(const char *propertyName);

     /**获得地图中某个图块的所有对象(注意一个图块可能有多个对象)   */

       CCDictionary* propertiesForGID(int GID);

    2.  CCTMXLayer类(地图图层类)

    //获得和设置图层尺寸的大小

        virtual const CCSize & getLayerSize(void);

        virtual void setLayerSize(const CCSize &var);

     //获得和设置砖块尺寸

        virtual const CCSize & getMapTileSize(void);

        virtual void setMapTileSize(const CCSize &var);

    //获得和设置砖块的属性

        virtual CCTMXTilesetInfo * getTileSet(void);

        virtual void setTileSet(CCTMXTilesetInfo *var);

    //获得和设置图层属性字典

        virtual CCDictionary *getProperties(void);

        virtual void setProperties(CCDictionary *var);

    //返回指定位置的砖块对象

        CCSprite *tileAt(const CCPoint &tileCoordinate);

    //返回指定位置的砖块的对象的ID

        unsigned int tileGIDAt(const CCPoint &tileCoordinate);

    //移除指定位置的砖块对象

        void removeTileAt(const CCPoint& tileCoordinate);

    //获得和设置图层名字

        inline const char* getLayerName();

        inline void setLayerName(const char *layerName);

    3. CCTMXObjectGroup类(地图物体层(对象层))

    //获得物体层的属性字典

        virtual CCDictionary * getProperties(void);

    //设置物体层的属性字典

        virtual void setProperties(CCDictionary *var);

    //获得物体层中的物体对象

        virtual CCArray * getObjects(void);

    //设置物体层中的物体对象

        virtual void setObjects(CCArray *var);

    //返回物体层的名字

        inline const char* getGroupName();

    //设置物体层的名字

        inline void setGroupName(const char *groupName);

    //获得指定属性的数值

        CCString *propertyNamed(const char* propertyName);

    //根据属性名字,返回属性字典

        CCDictionary* objectNamed(const char *objectName);

    (3)下面是对瓦片地图练习的Demo

    1. 制作的地图如下:

    2. 代码关联地图

    1>加载地图并读取地图上得层

    bool HelloWorld::init()
    {
        if ( !CCLayer::init() )
        {
            return false;
        }
        
        // 将按钮添加到场景中,避免按钮随地图运动
        btnMenu = ButtonMenu::create();
        sc->addChild(btnMenu, 5);
        
        //加载地图
        _tileMap = CCTMXTiledMap::create("myMap.tmx");
        addChild(_tileMap);
        
        //添加英雄
        addHero();
        
        //添加层的触摸
        CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 1, false);
        
        //获取地图中的不能行走的层(墙等障碍物)
        _meta = _tileMap->layerNamed("meta");
        
        //使不能行走区隐藏起来
        _meta->setVisible(false);
        
        //获取地图上的西瓜层
        _fruit = _tileMap->layerNamed("Fruit");
        
        //记得数组要retain一下。
        _enemyArray = CCArray::create();
        _enemyArray->retain();
        
        //初始化存放子弹的数组
        _bulletArray = CCArray::create();
        _bulletArray->retain();
        
        //添加敌人
        addEnemy();
        
        //碰撞检测
        this->schedule(schedule_selector(HelloWorld::updataGame), 0.1);
    
        return true;
    }
    

    <2> 添加玩家精灵

    void HelloWorld::addHero()
    {
        //获取对象图层hero中的对象组
        CCTMXObjectGroup *objectsValue =  _tileMap->objectGroupNamed("hero");
        
        //获取地图上的设置好的英雄对象的信息
        CCDictionary *spawnPoint = objectsValue->objectNamed("pa");
        
        _player = CCSprite::create("www.png");
        _player->setAnchorPoint(ccp(0, 0));
        
        //根据地图上设置好的英雄位置来设置当前精灵的位置
        _player->setPosition(ccp(spawnPoint->valueForKey("x")->floatValue(), spawnPoint->valueForKey("y")->floatValue()));
        addChild(_player);
    }

    <3>添加敌人精灵和人物行走动作

    void HelloWorld::addEnemy()
    {
        //获取对象图层hero中的对象组
        CCTMXObjectGroup *objectsValue =  _tileMap->objectGroupNamed("hero");
        
        //添加敌人, 非快速遍历
        for(int i = 0; i < int(objectsValue->getObjects()->count()); ++i)
        {
            //获取地图中对应的元素
            CCDictionary *element = (CCDictionary *)objectsValue->getObjects()->objectAtIndex(i);
            
            //如果找到敌人对象,把精灵放到地图上显示出来
            if (element->valueForKey("n")->intValue() == 1) {
                
                //初始化敌人精灵,把地图中已经初始化好的敌人对象初始化为要用的敌人精灵。
                CCSprite *enemySprite = CCSprite::create("enemy1.png");
                float x = element->valueForKey("x")->floatValue();
                float y = element->valueForKey("y")->floatValue();
                enemySprite->setPosition(ccp(x, y));
                enemySprite->setAnchorPoint(ccp(0, 0));
                _tileMap->addChild(enemySprite, 4);
                _enemyArray->addObject(enemySprite);
             
                //让敌人向英雄运动。
                CCCallFuncN *func = CCCallFuncN::create(this, callfuncN_selector(HelloWorld::goon));
                enemySprite->runAction(func);
            }
        }
    }
    
    
    void HelloWorld::goon(CCNode *pSender)
    {
        //获取敌人精灵
        CCSprite *s = (CCSprite *)pSender;
        
        //x轴方向移动的距离,画图理解
        float x = _player->getPosition().x - s->getPosition().x > 0 ? 10 : -10;
        
        //y轴方向上移动的距离
        float y = _player->getPosition().y - s->getPosition().y > 0 ? 10 : -10;
        
        CCMoveBy *move = CCMoveBy::create(0.5, ccp(x, y));
        
        //递归调用,让敌人精灵向英移动
        CCCallFuncN *func = CCCallFuncN::create(this, callfuncN_selector(HelloWorld::goon));
        
        s->runAction(CCSequence::create(move, func, NULL));
        
    }
    

    <4>检测碰撞

    void HelloWorld::updataGame()
    {
        //数组的快速遍历
        CCObject* obj = NULL;
        CCARRAY_FOREACH(_enemyArray, obj)
        {
            //强制类型转换
            CCSprite *s = static_cast<CCSprite *>(obj);
            
            if(_player->boundingBox().intersectsRect(s->boundingBox()))
            {
                //如果英雄与敌人发生碰撞就结束游戏回到主场景。
                CCDirector::sharedDirector()->replaceScene(MainLayer::scene());
            }
        }
        
        CCARRAY_FOREACH(_bulletArray, obj)
        {
            CCSprite *bullet = (CCSprite *)obj;
            
            CCARRAY_FOREACH(_enemyArray, obj)
            {
                CCSprite *enemy = (CCSprite *)obj;
                
                //如果子弹与敌人发生碰撞
                if(bullet->boundingBox().intersectsRect(enemy->boundingBox()))
                {
                    //把子弹和敌人从地图中移除
                    enemy->removeFromParent();
                    bullet->removeFromParent();
                    
                    _enemyArray->removeObject(enemy);
                    _bulletArray->removeObject(bullet);
                    
                    //子弹移除之后就跳出当前的循环
                    break;
                }
            }
        }
    }
    

    <5>获得触摸点并操作人物

    bool HelloWorld::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
    {
        //获取当前触摸到的点的位置
        CCPoint point = pTouch->getLocation();
        
        //将openGL坐标系转化为结点做标系,可以定位到节点真实(距离地图最左边的位置而不是距离屏幕左下角的位置)的位置
        _beginPoint = convertToNodeSpace(point);
        
        return true;
    }
    
    void HelloWorld::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
    {
        //获取当前用户的点击结束点
        CCPoint point = pTouch->getLocation();
        
        CCPoint endPoint = convertToNodeSpace(point);
        
        if(!(btnMenu->isbullet)) {
    
            //如果开始与结束点是同一个点,保证是点击而不是手指滑动
            if(_beginPoint.equals(endPoint))
            {
                //获取精灵原来的位置
                CCPoint playerPos = _player->getPosition();
                
                //得到用户触摸点与原来精灵点的位置的差距
                CCPoint disPos =  endPoint - playerPos;
                
                //判断偏移的距离是偏上下,还是偏左右。画图理解很容易
                //偏左右
                if(abs(disPos.x) >= abs(disPos.y))
                {
                    if(disPos.x > 0)
                    {
                        //每次偏移一个图块的宽度。
                        playerPos.x += _tileMap->getTileSize().width;
                    }
                    else
                    {
                        playerPos.x -= _tileMap->getTileSize().width;
                    }
                }
                else //偏上下
                {
                    if(disPos.y > 0)
                    {
                        //每次偏移一个图块的高度
                        playerPos.y += _tileMap->getTileSize().height;
                    }
                    else
                    {
                        playerPos.y -= _tileMap->getTileSize().height;
                    }
                }
                movePlayer(playerPos);
                
                //地图随精灵移动
                setViewpointCenter(_player->getPosition());
            }
        } else {
            //发射飞镖
            CCSprite *bullet = CCSprite::create("Projectile.png");
    		bullet->setPosition(_player->getPosition());
            _tileMap->addChild(bullet, 4);
            _bulletArray->addObject(bullet);
            
            //当前手触摸点与英雄精灵x轴上的差值
            float dx = endPoint.x - _player->getPosition().x;
            //当前手触摸点与英雄精灵y轴上的差值
            float dy = endPoint.y - _player->getPosition().y;
            
            //子弹向x, y方向移动的坐标点
            float lx, ly;
            
            //向前发子弹
            if(dx > 0)
            {
                //画图看一下就明白为什么是这样了。winSize是指右边屏幕的边界。
                lx = _tileMap->getTileSize().width * _tileMap->getMapSize().width - _player->getPosition().x;
                //ly / lx = dy / dx;
                ly = dy / dx * lx;
            } else {
                lx = 0 - _player->getPosition().x;
                ly = dy / dx * lx;
            }
            
            CCMoveBy *move = CCMoveBy::create(2, ccp(lx, ly));
            //子弹发射完毕之后执行回收子弹的函数。
            CCCallFuncN *ff = CCCallFuncN::create(this, callfuncN_selector(HelloWorld::targetFinish));
            
            bullet->runAction(CCSequence::create(move, ff, NULL));
        }
    
    }
    

    <6>地图随人物的移动而发生移动

    void HelloWorld::setViewpointCenter(CCPoint position)
    {
        //获取当前屏幕的尺寸
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        
        //如果精灵在屏幕中央的左边或者右边,x的值就是精灵当前的x值,否则x的值就是屏幕宽度的一半。
        int x = MAX(position.x, winSize.width / 2);
        
        //获取整个地图的尺寸
        CCSize mapSize =  _tileMap->getMapSize();
        
        //获取每一个图块的宽高
        CCSize tileSize = _tileMap->getTileSize();
        
        CCLOG("%lf, %lf, %lf, %lf", mapSize.width, mapSize.height, tileSize.width, tileSize.height);
        
        //mapSize.width是地图的x轴方向上有多少个瓦片,当前是30
        //mapSize.height是地图的y轴方向上有多少个瓦片,当前是10
        
        //重新规划x的值。
        x = MIN(x, mapSize.width * tileSize.width - winSize.width / 2);
        
        //获取屏幕中心点的位置
        CCPoint centerPoint = CCPoint(winSize.width / 2, winSize.height / 2);
        
        //重新规划的位置
        CCPoint actualPoint = CCPoint(x, winSize.height / 2);
        
        //地图最终要移动到的位置
        CCPoint viewPoint = centerPoint - actualPoint;
        
        //重新规划当前视图的位置。
        this->setPosition(viewPoint);
    }
    

     <7> 玩家与障碍物间的移动控制

    void HelloWorld::movePlayer(CCPoint p)
    {
        //map->getMapSize():地图的图块数
        
        //map->getTileSize():每一个图块的宽高
        
        //获取目标的位置,就是当前在哪个图块上
        int x = p.x / _tileMap->getTileSize().width;//定位在x轴的第几块上
        int y = _tileMap->getMapSize().height  - (p.y / _tileMap->getTileSize().height);//定位在y轴的第几块上
        
        //得到当前的定位点
        CCPoint currentPoint = ccp(x, y);
        
        //判断是否在地图范围内
        if(x != _tileMap->getMapSize().width && y != _tileMap->getMapSize().height) {
            
            //是否获取到目标,根据指定的点获取到图块中小图块的id.比如遇到障碍物id是49,遇到西瓜id是50。(map图块中有48块),(meta图块中有2块),红色的是第一块48+1= 49,绿色的是第二块48+2=50;
            int tilegId = _meta->tileGIDAt(currentPoint);
            
            //如果瓦片存在
            if(tilegId){
                
                //Dictionary;根据图块id获取当前图块上的各个属性
                CCDictionary *properties = _tileMap->propertiesForGID(tilegId);
                CCString *metaStr = (CCString *)properties->objectForKey("barrier");
                CCString *fruitStr = (CCString *)properties->objectForKey("watermelon");
                //如果是遇到了障碍物,且不是西瓜,就不让精灵移动
                if (metaStr && metaStr->compare("red") == 0) {
                    CCLog("+++++++++++++++
    ");
                    return;
                } else if (fruitStr && fruitStr->compare("green") == 0) {
                    _meta->removeTileAt(currentPoint);
                    
                    //获得当前的水果,并将其从视图中移除。
                    CCSprite *fruitSprite = _fruit->tileAt(currentPoint);
                    fruitSprite->removeFromParent();
                }
            }
            
            //让英雄移到到点击的瓦片上
            _player->setPosition(p);
        }
    }
    

     <8> 移除精灵

    //当飞镖到达边界时移除飞镖
    void HelloWorld::targetFinish(CCNode *pSender)
    {
        pSender->removeFromParent();
        _bulletArray->removeObject(pSender);
    }
    
  • 相关阅读:
    循环图片 yi
    给大家一个经典的.net情感故事 yi
    [东邪西毒][程序员版][原版][剧情] yi
    Sqlite 使用笔记 中文显示为乱码 yi
    sql2005安装过程,(不装C盘) yi
    Visual Studio 2010 美女与程序员的爱情网剧全集 yi
    IT行业几大职业病 yi
    标准化操作
    【ActiveMQ Tuning】Serializing to Disk
    我的山寨敏捷四季之春
  • 原文地址:https://www.cnblogs.com/alenoscar/p/4025896.html
Copyright © 2011-2022 走看看