首先我们需要知道,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星算法地图为例):
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