cocos2d-x 游戏 之 Tiled Map Editor(地图编辑)

转载于:http://blog.youkuaiyun.com/zhy_cheng/article/details/8308609

Tiled Map Editor是Cocos2d-x支持的地图编辑器,使用Tiled编辑出的地图可以很方便的被Cocos2d-x使用Tiled的官网是Tiled Map Editor。我使用的地图编辑器是QT版本。

 

好了,下面就试一试吧。

1.编辑地图

选择文件----->新文件

然后选择地图----->新图块,选择Tiled安装目录下的examples里的图片

在这里图片中间和最左边最上边都有黑线,所以绘制偏移1个像素,边距和间距都为一。

下面将图层的名字改为floor,作为地板。

把地板铺上砖。

新建一个图层,改名为wall,在上面摆上自己喜欢的东西吧。我设计成如下:

 

下一步是设置主角,这也比较难的一步。

选择图层------>添加对象层,改对象层为hero。

现在在对象层中添加对象。点击工具栏上的添加对象,在地图上话按住鼠标拖出一块。右击该块,改成如下

我们看到,下面可以添加名称和值,其实这就是键值对。今后会用对,现在还没有必要用到。

好了,地图编辑好了,先设置参数 ,点击编辑----->参数,设置为下

保存地图。

2.使用地图

使用文本编辑器打开刚才编辑好的地图,将

<image source="D:/application/Tiled/examples/tmw_desert_spacing.png" width="265" height="199"/>

改为

<image source="tmw_desert_spacing.png" width="265" height="199"/>

新建一个Cocos2d-x的项目,将地图文件和打开的图块文件复制到resource文件夹下。

在头文件中加入

  1. cocos2d::CCTMXTiledMap *_tileMap;  
cocos2d::CCTMXTiledMap *_tileMap;

将init函数中的菜单,精灵,文字的代码删除,加入下面的代码:

  1. _tileMap=CCTMXTiledMap::create("theMap.tmx");  
  2. addChild(_tileMap);  
		_tileMap=CCTMXTiledMap::create("theMap.tmx");
		addChild(_tileMap);


编译运行,效果如下

 

下面从地图中获得精灵的位置,在头文件中加入精灵的声明

  1. cocos2d::CCSprite *_player;  
cocos2d::CCSprite *_player;


在源文件中添加如下代码

  1.     CCTMXObjectGroup *objects=_tileMap->objectGroupNamed("hero");//获取对象层  
  2.     CCDictionary  *spawnPoint=objects->objectNamed("pa");//获取对象  
  3.     const CCString  *x=spawnPoint->valueForKey("x");//获取对象的坐标  
  4.     const CCString  *y=spawnPoint->valueForKey("y");  
  5. /  
  6.     char *tempx=new char[30];//这里的代码将CCString转换为int  
  7.     char *tempy=new char[30];  
  8.     memset(tempx,0,30);  
  9.     memset(tempy,0,30);  
  10.     sprintf(tempx,x->getCString());  
  11.     sprintf(tempy,y->getCString());  
  12.     int px=atoi(tempx);  
  13.     int py=atoi(tempy);  
  14.     delete tempx;  
  15.     delete tempy;  
  16. //  
  17.     _player=CCSprite::create("www.png");  
  18.     _player->setPosition(ccp(px,py));  
  19.     addChild(_player);  
		CCTMXObjectGroup *objects=_tileMap->objectGroupNamed("hero");//获取对象层
		CCDictionary  *spawnPoint=objects->objectNamed("pa");//获取对象
		const CCString  *x=spawnPoint->valueForKey("x");//获取对象的坐标
		const CCString  *y=spawnPoint->valueForKey("y");
	/
		char *tempx=new char[30];//这里的代码将CCString转换为int
		char *tempy=new char[30];
		memset(tempx,0,30);
		memset(tempy,0,30);
		sprintf(tempx,x->getCString());
		sprintf(tempy,y->getCString());
		int px=atoi(tempx);
		int py=atoi(tempy);
		delete tempx;
		delete tempy;
///
		_player=CCSprite::create("www.png");
		_player->setPosition(ccp(px,py));
		addChild(_player);


这里创建精灵,并且从地图中获得精灵的位置,从而设置精灵的位置。下面是效果图

 

下面接着让这个hero可以移动,先添加鼠标响应,在init中添加如下代码

  1. CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);  
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);


接着覆盖父类的鼠标响应消息

  1. bool HelloWorld::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)  
  2. {  
  3.     return 1;  
  4. }  
  5. void HelloWorld::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)  
  6. {  
  7.     cocos2d::CCPoint pp=pTouch->getLocation();  
  8.     if(fabs(pp.x-_player->getPosition().x)>=fabs(pp.y-_player->getPosition().y))  
  9.     {  
  10.   
  11.         if(pp.x>=_player->getPosition().x)  
  12.         {  
  13.             _player->setPosition(ccp(_player->getPosition().x+32,_player->getPosition().y));  
  14.         }  
  15.         else  
  16.         {  
  17.             _player->setPosition(ccp(_player->getPosition().x-32,_player->getPosition().y));  
  18.         }  
  19.   
  20.     }  
  21.     else  
  22.     {  
  23.   
  24.         if(pp.y>=_player->getPosition().y)  
  25.         {  
  26.             _player->setPosition(ccp(_player->getPosition().x,_player->getPosition().y+32));  
  27.         }  
  28.         else  
  29.         {  
  30.             _player->setPosition(ccp(_player->getPosition().x,_player->getPosition().y-32));  
  31.         }  
  32.     }  
  33.       
  34.   
  35. }  
bool HelloWorld::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
{
	return 1;
}
void HelloWorld::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
{
	cocos2d::CCPoint pp=pTouch->getLocation();
	if(fabs(pp.x-_player->getPosition().x)>=fabs(pp.y-_player->getPosition().y))
	{

		if(pp.x>=_player->getPosition().x)
		{
			_player->setPosition(ccp(_player->getPosition().x+32,_player->getPosition().y));
		}
		else
		{
			_player->setPosition(ccp(_player->getPosition().x-32,_player->getPosition().y));
		}

	}
	else
	{

		if(pp.y>=_player->getPosition().y)
		{
			_player->setPosition(ccp(_player->getPosition().x,_player->getPosition().y+32));
		}
		else
		{
			_player->setPosition(ccp(_player->getPosition().x,_player->getPosition().y-32));
		}
	}
	

}


在这里,先在ccTouchBegan中返回true,以便在ccTouchEnded中处理事件。

原理是先判断鼠标点击的点与hero现在的点是在x方向还是在y方向上的距离大,若在x方向上距离大,则改变x左边,若在y方向上距离大,则改变y的坐标。然后使hero向着点击点移动。

下面是效果图

 

对上面的一些代码进行优化,一个是从对象获取对象的坐标。上次使用的代码有点麻烦,现在改为下面的代码:

  1. CCTMXObjectGroup *objects=_tileMap->objectGroupNamed("hero");//获取对象层  
  2. CCDictionary  *spawnPoint=objects->objectNamed("pa");//获取对象  
  3. heroPoint.x=spawnPoint->valueForKey("x")->floatValue();//获取对象的坐标  
  4. heroPoint.y=spawnPoint->valueForKey("y")->floatValue();  
  5. CCLog("x=%f,y=%f",heroPoint.x,heroPoint.y);  
		CCTMXObjectGroup *objects=_tileMap->objectGroupNamed("hero");//获取对象层
		CCDictionary  *spawnPoint=objects->objectNamed("pa");//获取对象
		heroPoint.x=spawnPoint->valueForKey("x")->floatValue();//获取对象的坐标
		heroPoint.y=spawnPoint->valueForKey("y")->floatValue();
		CCLog("x=%f,y=%f",heroPoint.x,heroPoint.y);


这是因为我找到了一个函数,直接获得float值。

 

将hero放到地图中,随地图一起移动。

  1. _player=CCSprite::create("www.png");  
  2. _player->setAnchorPoint(CCPoint(0,0));  
  3. _player->setPosition(heroPoint);  
  4. _tileMap->addChild(_player,0);  
		_player=CCSprite::create("www.png");
		_player->setAnchorPoint(CCPoint(0,0));
		_player->setPosition(heroPoint);
		_tileMap->addChild(_player,0);


 

 

还有一个地方就是,要严格判断用户是点击还是拖动,在头文件中声明beginPoint,在ccTouchBegan函数中赋值,在ccTouchEnded中判断,只要这个点与beginPoint相同,则为点击。

 

对于判断地图的拖动,则放到了ccTouchMoved中,这样用户体验更好。

  1. void HelloWorld::ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)  
  2. {  
  3.     CCPoint pp=pTouch->getPreviousLocation();//获取之前的点  
  4.     CCPoint np=pTouch->getLocation();//获取现在的点  
  5.     CCPoint dp=ccpSub(np,pp);//获取差  
  6.       
  7.       
  8.     if(_tileMap->getPosition().x+dp.x>-480&&_tileMap->getPosition().x+dp.x<0)  
  9.     {  
  10.         mapPoint=ccp(_tileMap->getPosition().x+dp.x,0);  
  11.         _tileMap->setPosition(mapPoint);//移动地图  
  12.     }  
  13.       
  14.       
  15.   
  16.   
  17. }  
void HelloWorld::ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
{
	CCPoint pp=pTouch->getPreviousLocation();//获取之前的点
	CCPoint np=pTouch->getLocation();//获取现在的点
	CCPoint dp=ccpSub(np,pp);//获取差
	
	
	if(_tileMap->getPosition().x+dp.x>-480&&_tileMap->getPosition().x+dp.x<0)
	{
		mapPoint=ccp(_tileMap->getPosition().x+dp.x,0);
		_tileMap->setPosition(mapPoint);//移动地图
	}
	
	


}


好了,优化都做完了,现在开始新的知识。

3.碰撞检测

获取建筑层,在hero移动的时候,检测该层是否有建筑,有则不让hero移动。wall->tileGIDAt()这个函数获得在某一个Tile的块是那一块,图块都被编号了,从1开始,如果wall没有图块的话则为0.

  1. if(9-_player->getPosition().y/32<10&&9-_player->getPosition().y/32>=0&&_player->getPosition().x/32-1<50&&_player->getPosition().x/32-1>=0&&!(wall->tileGIDAt(ccp(_player->getPosition().x/32-1,9-_player->getPosition().y/32))))  
  2.                 {  
  3.                     _player->setPosition(ccp(_player->getPosition().x-32,_player->getPosition().y));}  
if(9-_player->getPosition().y/32<10&&9-_player->getPosition().y/32>=0&&_player->getPosition().x/32-1<50&&_player->getPosition().x/32-1>=0&&!(wall->tileGIDAt(ccp(_player->getPosition().x/32-1,9-_player->getPosition().y/32))))
				{
					_player->setPosition(ccp(_player->getPosition().x-32,_player->getPosition().y));}


这行代码就是先判断将要去的位置是否在地图中,然后判断是否有建筑,没有建筑则让hero移动。

4.动态修改地图

这原来的地图上再建一个图层,改为foreground,在这个图层中添加一些西瓜,用于沙漠中的hero解渴。

做好之后地图为

然后判断hero所在的位置是否有西瓜,若有,则在foreground中移除西瓜,表示hero吃了西瓜。

  1. if(9-_player->getPosition().y/32<10&&9-_player->getPosition().y/32>=0&&  
  2.     _player->getPosition().x/32-1<50&&_player->getPosition().x/32-1>=0&&  
  3.     !(wall->tileGIDAt(ccp(_player->getPosition().x/32-1,9-_player->getPosition().y/32))))  
  4.     {  
  5.         _player->setPosition(ccp(_player->getPosition().x-32,_player->getPosition().y));  
  6.         if(foreground->tileGIDAt(ccp(_player->getPosition().x/32,9-_player->getPosition().y/32)))  
  7.         {  
  8.         foreground->removeTileAt(ccp(_player->getPosition().x/32,9-_player->getPosition().y/32));  
  9.         }  
  10.     }  
if(9-_player->getPosition().y/32<10&&9-_player->getPosition().y/32>=0&&
	_player->getPosition().x/32-1<50&&_player->getPosition().x/32-1>=0&&
	!(wall->tileGIDAt(ccp(_player->getPosition().x/32-1,9-_player->getPosition().y/32))))
	{
		_player->setPosition(ccp(_player->getPosition().x-32,_player->getPosition().y));
		if(foreground->tileGIDAt(ccp(_player->getPosition().x/32,9-_player->getPosition().y/32)))
		{
		foreground->removeTileAt(ccp(_player->getPosition().x/32,9-_player->getPosition().y/32));
		}
	}

使用foreground->removeTileAt(CCPoint &pt)这个函数移除图块。

5.创建一个积分器

创建一个积分器,显示hero吃了多少西瓜,在头文件中声明一个int的count,每当hero吃了一个西瓜,就加一。然后创建一个CCLabelTTF显示出来。

  1. if(foreground->tileGIDAt(ccp(_player->getPosition().x/32,9-_player->getPosition().y/32)))  
  2. {  
  3. foreground->removeTileAt(ccp(_player->getPosition().x/32,9-_player->getPosition().y/32));  
  4. count++;  
  5. CCString *temp=CCString::stringWithFormat("%d",count);  
  6. label->setString(temp->getCString());  
  7. }  
if(foreground->tileGIDAt(ccp(_player->getPosition().x/32,9-_player->getPosition().y/32)))
{
foreground->removeTileAt(ccp(_player->getPosition().x/32,9-_player->getPosition().y/32));
count++;
CCString *temp=CCString::stringWithFormat("%d",count);
label->setString(temp->getCString());
}


吃了西瓜后,计数加一,改变CCLabelTTF的值。

最后还应该加入一些音效,前面讲过,就不多说了。

下面来几张截图:

这张图吃了3个西瓜

6.添加敌人

在对象层中加入敌人,

注意,加入了一个键值对,键为n,值为1。这个键值对主要就是为了区分敌人和英雄。将敌人画在地图上。
  1. for(int i=0;i<int(objects->getObjects()->count());i++)  
  2.         {  
  3.             CCDictionary *enemy=(CCDictionary *)objects->getObjects()->objectAtIndex(i);  
  4.             if(enemy->valueForKey("n")->intValue()==1)  
  5.             {  
  6.                 CCSprite *s=CCSprite::create("enemy1.png");  
  7.                 float x=enemy->valueForKey("x")->floatValue();  
  8.                 float y=enemy->valueForKey("y")->floatValue();  
  9.                 s->setPosition(ccp(x,y));  
  10.                 s->setAnchorPoint(CCPoint(0,0));  
  11.                 _tileMap->addChild(s,4);  
  12.                 CCActionInterval *move=CCMoveBy::create(2,CCPoint(_player->getPosition().x-s->getPosition().x>0?10:-10,_player->getPosition().y-s->getPosition().y>0?10:-10));//  
  13.                 CCFiniteTimeAction *func=CCCallFuncN::create(this,callfuncN_selector(HelloWorld::goon));  
  14.                 s->runAction(CCSequence::create(move,func,NULL));  
  15.             }  
  16.         }  
for(int i=0;i<int(objects->getObjects()->count());i++)
		{
			CCDictionary *enemy=(CCDictionary *)objects->getObjects()->objectAtIndex(i);
			if(enemy->valueForKey("n")->intValue()==1)
			{
				CCSprite *s=CCSprite::create("enemy1.png");
				float x=enemy->valueForKey("x")->floatValue();
				float y=enemy->valueForKey("y")->floatValue();
				s->setPosition(ccp(x,y));
				s->setAnchorPoint(CCPoint(0,0));
				_tileMap->addChild(s,4);
				CCActionInterval *move=CCMoveBy::create(2,CCPoint(_player->getPosition().x-s->getPosition().x>0?10:-10,_player->getPosition().y-s->getPosition().y>0?10:-10));//
				CCFiniteTimeAction *func=CCCallFuncN::create(this,callfuncN_selector(HelloWorld::goon));
				s->runAction(CCSequence::create(move,func,NULL));
			}
		}
这段代码从对象层中获得所有对象,判断键为n的是否为1,有则为敌人,加入到地图中,并且让敌人运动。
在运动完后调用 goon函数:
  1. void HelloWorld::goon(CCNode *pSender)  
  2. {  
  3.     CCSprite *s=(CCSprite *)pSender;  
  4.     CCActionInterval *move=CCMoveBy::actionWithDuration(2,CCPoint(_player->getPosition().x-s->getPosition().x>0?10:-10,_player->getPosition().y-s->getPosition().y>0?10:-10));//  
  5.                 CCFiniteTimeAction *func=CCCallFuncN::actionWithTarget(this,callfuncN_selector(HelloWorld::goon));  
  6.                 s->runAction(CCSequence::actions(move,func,NULL));  
  7. }  
void HelloWorld::goon(CCNode *pSender)
{
	CCSprite *s=(CCSprite *)pSender;
	CCActionInterval *move=CCMoveBy::actionWithDuration(2,CCPoint(_player->getPosition().x-s->getPosition().x>0?10:-10,_player->getPosition().y-s->getPosition().y>0?10:-10));//
				CCFiniteTimeAction *func=CCCallFuncN::actionWithTarget(this,callfuncN_selector(HelloWorld::goon));
				s->runAction(CCSequence::actions(move,func,NULL));
}
在goon函数中继续调用自己,这样敌人就一直向英雄运动。

7.使英雄可以攻击

现在游戏的点击是使得英雄移动向指定的位置,所以,为了使得英雄有攻击,可以添加一个状态,状态为true的话,控制英雄移动,状态为false的话,控制发射子弹。在头文件中加入
  1. bool mode;  
bool mode;

在屏幕上添加菜单
  1. CCMenuItem *oon,*ooff;  
  2. oon=CCMenuItemImage::create("projectile-button-on.png","projectile-button-on.png");  
  3. ooff=CCMenuItemImage::create("projectile-button-off.png","projectile-button-off.png");  
  4. CCMenuItemToggle *toggle=CCMenuItemToggle::createWithTarget(this,//回调函数所在的类     
  5. menu_selector(HelloWorld::toggleGame),//回调函数     
  6. ooff,oon,NULL    //添加的菜单      
  7.   );    
  8. CCMenu *menu=CCMenu::create(toggle,NULL);  
  9. menu->setPosition(ccp(oon->getContentSize().width/2,oon->getContentSize().height/2));  
  10. addChild(menu);  
		CCMenuItem *oon,*ooff;
		oon=CCMenuItemImage::create("projectile-button-on.png","projectile-button-on.png");
		ooff=CCMenuItemImage::create("projectile-button-off.png","projectile-button-off.png");
		CCMenuItemToggle *toggle=CCMenuItemToggle::createWithTarget(this,//回调函数所在的类   
		menu_selector(HelloWorld::toggleGame),//回调函数   
		ooff,oon,NULL    //添加的菜单   
    );  
		CCMenu *menu=CCMenu::create(toggle,NULL);
		menu->setPosition(ccp(oon->getContentSize().width/2,oon->getContentSize().height/2));
		addChild(menu);

在toggleGame函数中添加控制状态改变的代码:
  1. void HelloWorld::toggleGame(CCObject *pSender)  
  2. {  
  3.     mode=mode?false:true;  
  4. }  
void HelloWorld::toggleGame(CCObject *pSender)
{
	mode=mode?false:true;
}

当状态为false的时候,hero发射子弹:
  1. else  
  2. {  
  3.     CCSprite *s=CCSprite::create("Projectile.png");  
  4.     s->setPosition(_player->getPosition());  
  5.     _tileMap->addChild(s,4);  
  6.     float dx=pp.x-_player->getPosition().x;  
  7.     float dy=pp.y-_player->getPosition().y;  
  8.     if(dx>0)  
  9.     {  
  10.         float lx=32*30-_player->getPosition().x;  
  11.         float ly=dy/dx*lx;  
  12.         CCActionInterval *move=CCMoveBy::create(3,ccp(lx+s->getContentSize().width,ly));  
  13.         CCFiniteTimeAction *ff=CCCallFuncN::create(this,callfuncN_selector(HelloWorld::targetFinish));  
  14.         s->runAction(CCSequence::actions(move,ff,NULL));  
  15.     }  
  16.     else  
  17.     {  
  18.         float lx=0-_player->getPosition().x;  
  19.         float ly=dy/dx*lx;  
  20.         CCActionInterval *move=CCMoveBy::actionWithDuration(3,ccp(lx-s->getContentSize().width,ly);  
  21.         CCFiniteTimeAction *ff=CCCallFuncN::create(this,callfuncN_selector(HelloWorld::targetFinish));  
  22.         s->runAction(CCSequence::actions(move,ff,NULL));  
  23.     }  
	else
	{
		CCSprite *s=CCSprite::create("Projectile.png");
		s->setPosition(_player->getPosition());
		_tileMap->addChild(s,4);
		float dx=pp.x-_player->getPosition().x;
		float dy=pp.y-_player->getPosition().y;
		if(dx>0)
		{
			float lx=32*30-_player->getPosition().x;
			float ly=dy/dx*lx;
			CCActionInterval *move=CCMoveBy::create(3,ccp(lx+s->getContentSize().width,ly));
			CCFiniteTimeAction *ff=CCCallFuncN::create(this,callfuncN_selector(HelloWorld::targetFinish));
			s->runAction(CCSequence::actions(move,ff,NULL));
		}
		else
		{
			float lx=0-_player->getPosition().x;
			float ly=dy/dx*lx;
			CCActionInterval *move=CCMoveBy::actionWithDuration(3,ccp(lx-s->getContentSize().width,ly);
			CCFiniteTimeAction *ff=CCCallFuncN::create(this,callfuncN_selector(HelloWorld::targetFinish));
			s->runAction(CCSequence::actions(move,ff,NULL));
		}
发射完子弹后,让子弹移动到地图外,通过hero的位置和点击的位置,可以知道发射子弹的角度,然后通过相似三角形对应边成比例的原理,可以知道x超出地图的位置。这样让子弹移出地图。

8.后期工作

现在地图上该有什么的都有了,就只剩下判断胜负的问题了。具体而言,英雄吃完所有西瓜就赢了,被敌人碰到了就输了。这就是要求做碰撞检测。在cocos2d-x中判断碰撞很好做,我只说一说原理,具体实现没有什么难度。
将所有的敌人加入到一个CCArray中,将所有的子弹加入到另外一个CCArray中,在这个CCLayer中加入一个schedule,每隔一定的时间对敌人的CCArray与子弹的CCArray做碰撞检测,如果碰撞了,移除碰撞的敌人和子弹,还要检测敌人和hero的碰撞检测,如果碰撞了,就结束游戏。

具体怎么检测碰撞,我们可以得到子弹的CCRect,敌人的CCRect,英雄的CCRect,使用CCRect的成员函数
  1. bool intersectsRect (const CCRect & rect) const  
bool intersectsRect (const CCRect & rect) const
来判断碰撞。

 
好了,使用Tiled制作游戏就结束了.
 
最后给出工程代码下载:Cocos2d-x  游戏  Tiled Map Editor 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值