改写《魔塔》后篇06:传送点及地图切换(附:后期阶段项目下载)
2013-04-06 15:13
411 查看
魔塔中会有地图切换的需求。我们首先在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。请大家自行下载项目源码参考,如有不同的地方以项目源码为准。
下面我们来创建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。请大家自行下载项目源码参考,如有不同的地方以项目源码为准。
相关文章推荐
- 改写《魔塔》前篇07:碰撞检测(附:前期阶段项目下载)
- 改写《魔塔》中篇06:善后工作和注意事项(附:中期阶段项目下载)
- 资源 | 26份机器学习视频资源,涵盖入门->中级->项目的各个阶段!(可直接下载......)
- 软件工程项目冲刺阶段二:第四天(06-06)
- 改写《魔塔》中篇02:重构代码之分离游戏地图类
- 知识地图项目中当前暂时搁置,后期需要优化的地方--给自己记录一下
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十八)地图间的传送与切换
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十八)地图间的传送与切换
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十八)地图间的传送与切换
- 团队项目第二阶段冲刺站立会议06
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十八)地图间的传送与切换
- 改写《魔塔》前篇01:绘制最简单的游戏地图
- DateChooser控件发布ASP.NET 2.0新版(我的ASP.NET 2.0控件开发书的第二个阶段项目)[请大家一定注意版本的更新,下载最新版]
- 团队项目冲刺第一阶段06
- 改写《魔塔》后篇01:在地图上绘制怪物
- 团队项目第一阶段冲刺站立会议06
- 把项目分阶段给后期潜在客户
- 团队项目第一阶段冲刺站立会议06
- 项目后期Lua接入笔记06--按钮事件监听及消息分发
- 我的Java阶段项目1 - 骑士飞行棋[两人对战][固定地图版]