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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章