Cocos2d-x 使用TMX地图总结

首先我们需要知道,TMX地图的坐标为格子坐标,左上角为原点(0,0),而Cocos里面一般使用OpenGL坐标系,即左下角为原点(0,0)。

我们可以这样子来转换TMX地图和OpenGL的坐标:

Point HelloLayer::tiledCoorForPosition(const Point& position)   //转成格子坐标
{
    Size mapSize = _tiledMap->getMapSize(); 
    Size tileSize = _tiledMap->getTileSize();
    int x = (position.x ) / tileSize.width;
    int y = (mapSize.height * tileSize.height - position.y ) / tileSize.height;
    return Point(x,y);
}
Point HelloLayer::positionForTiledCoor(const Point& tiledCoor)   //转成OpenGL坐标
{
    Size mapSize = _tiledMap->getMapSize(); 
    Size tileSize = _tiledMap->getTileSize();
    int x = tiledCoor.x * tileSize.width + tileSize.width / 2;
    int y = (mapSize.height * tileSize.height) - (tiledCoor.y * tileSize.height + tileSize.height / 2);
    return Point(x,y);
}

layer的锚点为(0,0),TMX地图的锚点默认是(0,0),如果我们在layer中添加TMX地图,比如这样子:

bool HelloLayer::init()
{
    if(!Layer::init())
    {
        return false; 
    }
    //地图
    _tileMap = TMXTiledMap::create("map.tmx");
    this->addChild(_tileMap);
    //精灵
    _sprite = Sprite::create("sp.png");
    this.addChild(_sprite);
}

那么我们现在来依次实现如下功能:

1.地图随主角移动

以sprite为焦点来调整地图的位置:

void HelloLayer::setViewPoint(const Point& point)
{
    Size winSize = Director::getInstance()->getWinSize();
 
    int x = MAX(point.x, winSize.width / 2);
    int y = MAX(point.y, winSize.height / 2);
    x = MIN(x, _tiledMap->getMapSize().width * _tiledMap->getTileSize().width - winSize.width / 2);
    y = MIN(y, _tiledMap->getMapSize().height * _tiledMap->getTileSize().height - winSize.height / 2);
     
    Point actualPoint(x,y);
    Point centerOfView(winSize.width / 2, winSize.height / 2);
    Point viewPoint = centerOfView - actualPoint;
    _tiledMap->setPosition(viewPoint);
}

然后不断的在update函数中不断的检测sprite的位置并设置地图位置就可以了:

void HelloLayer::update(float dt)
{
    this->setViewPoint(_sprite->getPosition());
}

2.地图拖动

地图移动主要是在onTouchMoved里面判断两个点的偏移向量vec,然后判断边界值避免出现黑边,具体如下:

listener->onTouchMoved = [&] (Touch* touch, Event* event)
{
    Point prePos = touch->getPreviousLocation();
    Point curPos = touch->getLocation(); 
    Point vec = curPos - prePos;  
    Point mapPos = _tiledMap->getPosition();
    Point viewPos = mapPos + vec;
     
    Size winSize = Director::getInstance()->getWinSize();
    Size mapSize = _tiledMap->getMapSize(); 
    Size tileSize = _tiledMap->getTileSize();
     
    //若x坐标值超过边界值,则去掉x的偏移
    if(viewPos.x <  winSize.width - mapSize.width * tileSize.width || viewPos.x > 0 )
    {
        viewPos.x -= vec.x;
    }
     
    //同理,若y坐标值超过边界值,则去掉y的偏移
    if(viewPos.y < winSize.height - mapSize.height * tileSize.height || viewPos.y >0 )
    {
        viewPos.y -= vec.y;
    }
    _tiledMap->setPosition(viewPos);
};

3.地图缩放

下面来实现地图的放大功能,具体为点击一个点p1,然后以p1为屏幕的焦点不断去放大地图:

listener->onTouchBegan = [this] (Touch* touch, Event* event)
{
    //点击位置p1
    Point touchLocation = _tiledMap->convertTouchToNodeSpace(touch);
    _touchPos = touchLocation;
    //缩放
    _tiledMap->runAction(ScaleBy::create(1.0f,1.2f,1.2f,1.0f));
    this->scheduleUpdate();
    return true;
}
 
void HelloLayer::update(float dt)
{
    //缩放的同时不断更新焦点p1的位置(因为地图放大,地图中的点的坐标也要相应放大)
    float scale = _tiledMap->getScale();
    Point viewPos(scale * _touchPos.x,scale * _touchPos.y); 
    //根据p1不断设置地图的位置
    this->setViewPointByScale(viewPos,scale);
}
 
void HelloLayer::setViewPointByScale(const Point& point,float scale)
{
    Size winSize = Director::getInstance()->getWinSize();
 
    int x = MAX(point.x, winSize.width / 2);
    int y = MAX(point.y, winSize.height / 2);
 
    //需要注意的是,地图放大后,mapSize和tileSize并没有变大,所以判断边界的时候我们需要手动 * scale 
    x = MIN(x, _tiledMap->getMapSize().width * _tiledMap->getTileSize().width * scale - winSize.width / 2);
    y = MIN(y, _tiledMap->getMapSize().height * _tiledMap->getTileSize().height * scale - winSize.height / 2);
 
    Point actualPoint(x,y);
    Point centerOfView(winSize.width / 2, winSize.height / 2);
    Point viewPoint = centerOfView - actualPoint;
 
    _tiledMap->setPosition(viewPoint);
}

以上是一个比较简单的思路,比如我们要实现双指缩放,可以参照上面的思路这样子做:
listener->onTouchesBegan = [&] (const std::vector<Touch*>& touches, Event* event)
{
    if(touches.size() >= 2)
    {
        Point p1 = _tiledMap->convertTouchToNodeSpace(touches[0]);
        Point p2 = _tiledMap->convertTouchToNodeSpace(touches[1]);
    //算出亮点距离,保存到类变量中
    _distance = sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
    }
    return true;
}
 
listener->onTouchesMoved = [&] (const std::vector<Touch*> &touches, Event* event)
{
    if(touches.size() >= 2)
    {
        Point p1 = _tiledMap->convertTouchToNodeSpace(touches[0]);
        Point p2 = _tiledMap->convertTouchToNodeSpace(touches[1]);
         
        //算出缩放倍数,并保存新的距离
    float new_distance = sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
    float scale = new_distance / _distance;
    _distance = new_distance;
         
    //取p1和p2中点,缩放地图并设置屏幕焦点
    Point pCenter ((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
    _tiledMap->setScale(scale);
    this->setViewPointByScale(pCenter,scale);
    }
}

碰撞检测

碰撞检测比较简单,现有地图如下(以A星算法地图为例):

20140922191537875.jpg

现在我们来判断鼠标点击的是不是障碍物:
listener->onTouchBegan = [this] (Touch* touch, Event* event)
{
    Point touchLocation = _tiledMap->convertTouchToNodeSpace(touch);
    Point tileCoord = this->tiledCoorForPosition(touchLocation);
 
     if(this->isWallAtTileCoord(tileCoord))      //检测是否为墙壁
     {
       CCLOG("isWall");
     }
}
 
 
bool HelloLayer::isWallAtTileCoord(const Point &tileCoord) const
{
    //先获取格子的gid值,再获取属性值
    int gid = _bgLayer->getTileGIDAt(tileCoord);
    Value properties = _tiledMap->getPropertiesForGID(gid);
    if (properties.isNull())
    {
        return false;
    }
    return properties.asValueMap().find("Wall") != properties.asValueMap().end();
}
来源网址: http://blog.csdn.net/shun_fzll/article/details/39480393


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值