改写《魔塔》后篇06:传送点及地图切换(附:后期阶段项目下载)

        魔塔中会有地图切换的需求。我们首先在TileMap中绘制传送点。打开Tiled编辑器,选中object层,选择工具栏中的“插入对象”按钮,在地图(6,1)处创建一个对象,编辑其属性如下所示。其中,“类型”项用来区分对象是否为传送点,heroTileCoordX和heroTileCoordY定义了勇士在目标地图中出现的坐标,targetMap定义了目标地图的层数。down_floor.png是用来显示的图片纹理,我们首先把down_floor.png和up_floor.png复制到我们的项目中。

                           

下面我们来创建Teleport对象,用于记录上述配置信息,具体头文件如下:

#ifndef __TELEPORT_H__
#define __TELEPORT_H__

#include "MTGame.h"

using namespace cocos2d;

class Teleport:public CCObject
{
public:
	Teleport(CCDictionary *dict,int x,int y);
	~Teleport();
	//传送点所在位置
	CCPoint tileCoord;
	//传送到目标后,勇士所在坐标
	CCPoint heroTileCoord;
	//目标地图的层数
	int targetMap;
	//唯一的ID
	int index;
	//图片纹理的文件路径
	CCString *imagePath;
	CCSprite *teleportSprite;
};

#endif

Teleport类的具体实现如下:

#include "Teleport.h"

Teleport::Teleport(CCDictionary *dict,int x,int y)
{
	CCPoint position=ccp(x,y);
	//传送点所在的TileMap位置
	tileCoord=sGlobal->gameMap->tileCoordForPosition(position);
	//得出勇士目标层的起始位置
	std::string key="heroTileCoordX";
	int x1=((CCString*)dict->objectForKey(key))->intValue();
	key="heroTileCoordY";
	int y1=((CCString*)dict->objectForKey(key))->intValue();
	heroTileCoord=ccp(x1,y1);
	//取得目标地图的层数
	key="targetMap";
	targetMap=((CCString*)dict->objectForKey(key))->intValue();
	//取得image项
	key="image";
	imagePath=(CCString*)dict->objectForKey(key);
	//创建用于显示Teleport的精灵
	teleportSprite=CCSprite::create(imagePath->m_sString.c_str());
	teleportSprite->setAnchorPoint(CCPointZero);
	teleportSprite->setPosition(position);
	sGlobal->gameLayer->addChild(teleportSprite,kZTeleport);
}

//析构函数
Teleport::~Teleport()
{
	CC_SAFE_DELETE(imagePath);
	CC_SAFE_DELETE(teleportSprite);
}
     

      在GameMap的initObject方法中,遍历object层的所有对象时,判断对象类型如果是teleport,则新创建一个Teleport对象,并存放到teleport中。

   //如果是传送门
  else if(type->m_sString=="teleport")	
  {
     Teleport *teleport=new Teleport(dict,x,y);
     teleportDict->setObject(teleport,index);
   }

      修改Hero类的checkCollision方法,添加传送点的碰撞检测逻辑:

//在Teleport字典中查询
   Teleport* teleport=(Teleport*)sGlobal->gameMap->teleportDict->objectForKey(index);
   if(NULL!=teleport)
   {
     doTeleport(teleport);
     return kTeleport;
    }

       在doTeleport方法中,需要实现切换地图的逻辑。我们也许会犹豫不决:切换地图的操作到底是切换GameScene呢,还是切换GameLayer?事实上两种方式都是可以的,只不过在效率上有一些差别:切换GameLayer操作实际上仅更新了GameMap和Hero对象,对ControlLayer实际上并无影响;而切换GameScene会重新创建GameLayer以及ControlLayer。因此切换GameLayer效率高一些。由于GameLayer是依附于GameScene对象的,我们需要在GameScene中添加一些方法,用于销毁当前的GameLayer对象,再重新生成目标地图的GameLayer对象。这里新建了一个方法,即GameScene::switchMap,它的实现代码如下:

//切换地图
void GameScene::switchMap()
{
	//创建一个遮罩层,用于地图切换时显示淡入淡出的效果
	CCLayerColor *fadelayer=CCLayerColor::create(ccc4(0,0,0,0));
	fadelayer->setAnchorPoint(CCPointZero);
	fadelayer->setPosition(CCPointZero);
	this->addChild(fadelayer,kFadeLayer,kFadeLayer);

	//执行淡入动画,结束时调用resetGameLayer方法
	CCAction *action=CCSequence::create(
		CCFadeIn::create(0.5f),
		CCCallFunc::create(this,
		callfunc_selector(GameScene::resetGameLayer)),
		NULL);
	fadelayer->runAction(action);
}

      为了实现地图切换时的淡入淡出效果,新创建了一个CCLayerColor层,颜色为黑色,先让其执行CCFadeIn动作,视觉效果就是当前场景逐渐被黑色遮罩直至完全覆盖,实现了淡出效果。CCFadeIn执行结束后,会调用resetGameLayer方法,执行真正的切换逻辑。

      resetGameLayer方法如下。开始时先删除当前的gameLayer对象,再用create方法创建新的gameLayer,最后让遮罩层执行CCFadeOut操作,并且注册了一个回调函数,用于移除遮罩层:

//切换游戏地图
void GameScene::resetGameLayer()
{
	//删除老的GameLayer
	this->removeChildByTag(kGameLayer,true);

	//创建新的GameLayer
	GameLayer *gameLayer=GameLayer::create();
	this->addChild(gameLayer,kGameLayer,kGameLayer);

	//执行淡出动画,结束后,调用removeFadeLayer方法
	CCAction *action=CCSequence::create(
		CCFadeOut::create(0.5f),
		CCCallFunc::create(this,
		callfunc_selector(GameScene::removeFadeLayer)),
		NULL);
	this->getChildByTag(kFadeLayer)->runAction(action);
}

在removeFromFadeLayer中,仅需要删除遮罩层即可:

//删除遮罩层
void GameScene::removeFadeLayer()
{
	this->removeChildByTag(kFadeLayer,true);
}

     我们发现switchMap方法并没有向GameLayer传递任何数据,那么是怎么通知GameLayer关于新地图的信息呢?为了避免来回传参的复杂性,我们简单地在Global类中新增了两个变量用以标识目标地图的层数以及勇士的起始位置。

   //目标地图的层数
   int currentLevel;

   //勇士出现的位置
   CCPoint heroSpawnTileCoord;

随后修改GameLayer的init方法,让其根据这两个变量来动态读取地图和设置勇士位置。

       //解析TMX地图
	char temp[20];
	sprintf(temp,"%d.tmx",sGlobal->currentLevel);
	map=GameMap::gameMapWithTMXFile(temp);
	addChild(map);	

	//调用Hero类的静态方法创建实例
	hero=Hero::heroWithinLayer();
	//设置Hero的起始位置
	hero->setPosition(map->positionForTileCoord(sGlobal->heroSpawnTileCoord));
	//将Hero加入GameLayer
	addChild(hero);		

      由于修改了GameLayer的初始化方法,在GameScene的init方法中需要对上面两个变量进行赋值:

        //新游戏,当前地图层数为0
        sGlobal->currentLevel=0;

	//勇士出生的位置
	sGlobal->heroSpawnTileCoord=ccp(1,11);

     同样,在Hero类的doTeleport中先给这两个变量赋值,再调用GameScene的switchMap方法就可以轻松实现地图的切换了:

//处理传送门
void Hero::doTeleport(Teleport *teleport)
{
	//从传送点的属性中设置目标地图的层数
	sGlobal->currentLevel=teleport->targetMap;
	//获取勇士在新地图中的起始位置
	sGlobal->heroSpawnTileCoord=teleport->heroTileCoord;
	//开始切换地图
	sGlobal->gameScene->switchMap();
}

      将0.tmx复制一份命名为1.tmx,大家可以自由发挥修改里面的元素,将第0层的传送点的targetMap设置为1,第一层的传送点设置为0,就可以控制勇士在第0层和第1层之间来回切换了。注意一点,在Teleport.h中,"#include "GameMap.h""要放在类的定义后面,否则会出现编译错误。

      让我们看看进入第二个地图后的样子:

                     

最后,附上后期阶段项目源码下载地址:点此下载。本篇结束,我们的魔塔改写就完成了,再次说明的是我用的cocos2d-x版本是2.1.1。请大家自行下载项目源码参考,如有不同的地方以项目源码为准。




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值