首页 > 代码库 > cocos2d-x 使用tmx地图总结

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)		//转成oepngl坐标
{
	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);
	}
}


4.碰撞检测



碰撞检测比较简单,现有地图如下:(因为正在看A*算法,所以就拿这地图举例了,A*寻路算法原文地址如下 http://blog.csdn.net/akof1314/article/details/19333255)



现在我们来判断鼠标点击的是不是障碍物:


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

cocos2d-x 使用tmx地图总结