Cocos2d-x学习笔记九—— 地图系统地图编辑器,事件响应

游戏地图是游戏过程重要的环节,这里我们使用地图编辑器编辑我们的地图,其中CCTMXTiledMap是我们创建地图时使用的类对象,接下来是地图编辑器的使用,免费下载地址:http://www.mapeditor.org/

地图编辑器

打开下载好的地图编辑器,打开“文件--->新文件”:


打开后弹出窗口,只设置地图高度和宽度(即每一行地图的块数),块的大小(根据自己所选地图),其他为默认。


接着选择“地图--->新图块”,弹出窗口,选择编辑地图块,点击“浏览”选择。



点击确定后,即可在右下角看到地图块,拖动编辑地图。


这里我们可以建立两个图层,如下图所示,一个图层存放背景,另一个存放障碍物,方便我们在做碰撞检测时使用。


完成点击“文件---->另存为”,即可生成.tmx地图文件。


地图文件的使用

当然上面的地图能生成垂直俯视和45度俯视两种类型的地图,可以根据需要编辑生成相应的地图。

这里需要注意的是,我们编辑的地图坐标和屏幕坐标一致,都是左上角为原点,但是和cocos2d坐标系不一致,所以需要做一定的转换。另外,由于新的Cocos2d引擎引入了屏幕适配,所以在设置坐标的时候,需要注意坐标转换,这个在例子中会表明。

地图文件.tmx的简单使用:

// 读取地图文件
auto map = TMXTiledMap::create("TileMap.tmx");
addChild(map); 

下面是一个简单的具体事例:

创建一个新的项目“HelloMap”,在HelloMap.h:

<span style="font-size:14px;">#ifndef __HELLOMAP_H__
#define __HELLOMAP_H__

#include "cocos2d.h"
#include "Box2D\Box2D.h"
#include "SimpleAudioEngine.h"

using namespace cocos2d;

class HelloMap : public cocos2d::Layer
{
private:
	CCTMXTiledMap* _mapTile;
	CCSprite* _spriteNPC;

public:
	static cocos2d::Scene* createMapScene();

	virtual bool init();

	void mapMenuCallBack(cocos2d::Ref* pSender);

	CREATE_FUNC(HelloMap);
};

#endif // !__HELLOMAP_H__</span>

HelloMap.cpp:

#include "HelloMap.h"

USING_NS_CC;

// 定义地图块的实际大小
#define MAPTILESIZE 33*480.00/1024.00

// 按键标示(索引)
typedef enum _CONTROLTAG
{
	TAG_UP=100,TAG_DAOW,TAG_LEFT,TAG_RIGHT,TAG_FIRE
}CONTROLTAG;

Scene* HelloMap::createMapScene()
{
	auto mapScene = Scene::create();
	auto mapLayer = HelloMap::create();

	mapScene->addChild(mapLayer);
	return mapScene;
}

bool HelloMap::init()
{
	if (!Layer::init())
	{
		return false;
	}

	CCSize s = Director::getInstance()->getWinSize();

	// 创建地图块
	_mapTile = CCTMXTiledMap::create("ok.tmx");
	_mapTile->setPosition(Vec2(0,0));
	this->addChild(_mapTile);

	// 添加游戏NPC
	_spriteNPC = CCSprite::create("NPC.png");
	_spriteNPC->setPosition(MAPTILESIZE/2,MAPTILESIZE/2);
	// 将创建的精灵添加到地图中
	_mapTile->addChild(_spriteNPC);

	// 创建菜单按钮,设置回调函数
	CCMenuItemFont *upMenuItem = CCMenuItemFont::create("UP",this,menu_selector(HelloMap::mapMenuCallBack));
	CCMenuItemFont *downMenuItem = CCMenuItemFont::create("DOWN",menu_selector(HelloMap::mapMenuCallBack));
	CCMenuItemFont *leftMenuItem = CCMenuItemFont::create("LEFT",menu_selector(HelloMap::mapMenuCallBack));
	CCMenuItemFont *rightMenuItem = CCMenuItemFont::create("RIGHT",menu_selector(HelloMap::mapMenuCallBack));
	CCMenuItemFont *fireMenuItem = CCMenuItemFont::create("FIRE",menu_selector(HelloMap::mapMenuCallBack));

	// 将菜单项添加到菜单
	CCMenu *menu = CCMenu::create(upMenuItem,downMenuItem,leftMenuItem,rightMenuItem,fireMenuItem,NULL);
	menu->setPosition(0,0);
	this->addChild(menu);

	upMenuItem->setPosition(s.width / 2,s.height - 20);
	downMenuItem->setPosition(s.width / 2,20);
	leftMenuItem->setPosition(35,s.height / 2);
	rightMenuItem->setPosition(s.width - 40,s.height / 2);
	fireMenuItem->setPosition(35,s.height - 30);

	// 设置按键标示(索引)
	upMenuItem->setTag(TAG_UP);
	downMenuItem->setTag(TAG_DAOW);
	leftMenuItem->setTag(TAG_LEFT);
	rightMenuItem->setTag(TAG_RIGHT);
	fireMenuItem->setTag(TAG_FIRE);

	return true;
}

// 回调函数
void HelloMap::mapMenuCallBack(Ref* pSender)
{
	// 获取菜单项,并获取索引
	CCMenuItem *item = (CCMenuItem*)pSender;
	int tag = item->getTag();

	// 获得精灵的当前位置和下一个节点的位置,这里(33.00 * 480.00 / 1024.00)为每块小地图快的实际像素大小
	// 由于Y轴方向上地图坐标与Cocos2d坐标相反,需要转换为地图坐标,及左上角为原点
	CCPoint spriteCurPos = ccp((int)(_spriteNPC->getPositionX() / (33.00 * 480.00 / 1024.00)),(int)(_mapTile->getMapSize().height - (_spriteNPC->getPositionY() / (33.00 * 480.00 / 1024.00))));
	CCPoint spriteNextPos = spriteCurPos;

	// 根据图层名字获得图层对象,我们编辑地图时设置了两个图层,layer1存放背景,layer2存放障碍物
	CCTMXLayer* ly = _mapTile->layerNamed("layer2");
	// 地图块ID
	int GID = 0;

	switch (tag)
	{
	case TAG_UP:
		// 获取地图下一个位置节点,如果节点小于0,说明已经出边界,直接返回
		spriteNextPos.y -= 1;
		//log("%f----!!!!!!!!------------",spriteNextPos.y);
		if (spriteNextPos.y < 0)	return;
		// 获取下一个节点的ID,如果节点存在则返回
		GID = ly->tileGIDAt(spriteNextPos);
		if (GID)	return;
		// 碰撞检测,当NPC没走到地图中间时,即在边界的时候,只移动精灵
		// 判断精灵是否到达边界或者到达Y轴的中心,这里我们屏幕的分辨率为480*320
		// 这里由于Cocos2d 3.x对屏幕适配的影响,不同电脑显示的地图块的屏幕分辨率与实际分辨率会有所不同,
		// 因此我们需要根据电脑的显示情况进行适当的坐标调整
		if (_spriteNPC->getPositionY() < 160 || 
			(_mapTile->getContentSize().height - _spriteNPC->getPositionY() < 160 + MAPTILESIZE
			&& _mapTile->getContentSize().height - _spriteNPC->getPositionY() > MAPTILESIZE))
		{
			// 精灵向上移动,以cocos2d坐标系作为判断,Y轴需要加上一个地图块的距离
			_spriteNPC->setPosition(_spriteNPC->getPositionX(),_spriteNPC->getPositionY() + MAPTILESIZE);
		}
		// 当NPC走到地图中间时,移动地图和精灵来代替精灵走动
		else if (_mapTile->getContentSize().height - _spriteNPC->getPositionY() > 160 + MAPTILESIZE)
		{
			_mapTile->setPosition(_mapTile->getPositionX(),_mapTile->getPositionY() - MAPTILESIZE);
			_spriteNPC->setPosition(_spriteNPC->getPositionX(),_spriteNPC->getPositionY() + MAPTILESIZE);
		}
		break;
	case TAG_DAOW:
		// 获取下一个节点,如果节点大于59,说明已经出边界,直接返回
		spriteNextPos.y += 1;
		if (spriteNextPos.y > _mapTile->getMapSize().height - 1)		return;
		// 获取下一个节点的ID,如果节点存在则返回
		GID = ly->tileGIDAt(spriteNextPos);
		if (GID)	return;
		if ((_spriteNPC->getPositionY() > MAPTILESIZE && _spriteNPC->getPositionY() < 160 + MAPTILESIZE)
			|| (_mapTile->getContentSize().height - _spriteNPC->getPositionY() < 160
			&& _mapTile->getContentSize().height - _spriteNPC->getPositionY() > 0))
		{
			_spriteNPC->setPosition(_spriteNPC->getPositionX(),_spriteNPC->getPositionY() - MAPTILESIZE);
		}
		else if (_spriteNPC->getPositionY() < _mapTile->getContentSize().height - 160 
			&& _spriteNPC->getPositionY() > 160 + MAPTILESIZE)
		{
			_mapTile->setPosition(_mapTile->getPositionX(),_mapTile->getPositionY() + MAPTILESIZE);
			_spriteNPC->setPosition(_spriteNPC->getPositionX(),_spriteNPC->getPositionY() - MAPTILESIZE);
		}
		break;
	case TAG_LEFT:
		// 获取下一个节点,如果节点小于0,说明已经出边界,直接返回
		spriteNextPos.x -= 1;
		if (spriteNextPos.x < 0)	return;
		// 获取下一个节点的ID,如果节点存在则返回
		GID = ly->tileGIDAt(spriteNextPos);
		if (GID)	return;
		if ((_spriteNPC->getPositionX() > MAPTILESIZE && _spriteNPC->getPositionX() < 240 + MAPTILESIZE)
			|| (_mapTile->getContentSize().width - _spriteNPC->getPositionX() < 240
			&& _mapTile->getContentSize().width - _spriteNPC->getPositionX() > 0))
		{
			_spriteNPC->setPosition(_spriteNPC->getPositionX() - MAPTILESIZE,_spriteNPC->getPositionY());
		}
		else if (_spriteNPC->getPositionX() < _mapTile->getContentSize().width - 240
			&& _spriteNPC->getPositionX() > 240 + MAPTILESIZE)
		{
			_mapTile->setPosition(_mapTile->getPositionX() + MAPTILESIZE,_mapTile->getPositionY() );
			_spriteNPC->setPosition(_spriteNPC->getPositionX() - MAPTILESIZE,_spriteNPC->getPositionY());
		}
		break;
	case TAG_RIGHT:
		// 获取下一个节点,如果节点大于59,说明已经出边界,直接返回
		spriteNextPos.x += 1;
		if (spriteNextPos.x > _mapTile->getMapSize().width - 1)		return;
		// 获取下一个节点的ID,如果节点存在则返回
		GID = ly->tileGIDAt(spriteNextPos);
		if (GID)	return;
		if (_spriteNPC->getPositionX() < 240 ||
			(_mapTile->getContentSize().width - _spriteNPC->getPositionX() < 240 - MAPTILESIZE
			&& _mapTile->getContentSize().width - _spriteNPC->getPositionX() > MAPTILESIZE))
		{
			_spriteNPC->setPosition(_spriteNPC->getPositionX() + MAPTILESIZE,_spriteNPC->getPositionY());
		}
		else if (_mapTile->getContentSize().width - _spriteNPC->getPositionX() > 240 - MAPTILESIZE)
		{
			_mapTile->setPosition(_mapTile->getPositionX() - MAPTILESIZE,_mapTile->getPositionY());
			_spriteNPC->setPosition(_spriteNPC->getPositionX() + MAPTILESIZE,_spriteNPC->getPositionY());
		}
		break;
	case TAG_FIRE:
		// 替换地图块
		ly->setTileGID(46,spriteCurPos);
		break;
	}
}

由于文件过大,这里只上传类文件,源码下载:http://download.csdn.net/detail/u013707014/9003805

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...