三关于细节
3.1当前主题与当前地图是如何确定的?
数据管理器维护了全局的当前主题编号和当前地图编号。在选择主题界面中,每一次滑动界面,主题会跟着左右滑动,选择主题界面内也有一个记录当前滑动界面编号,当点击主题图片后会跳转到该主题下的选择关卡界面,此时在数据管理器中记录当前主题编号。在选择关卡界面中根据当前的主题编号选择对应的小地图展示,同样的道理来记录当前地图编号。当点击地图后,判断当前地图的解锁与否,如果是解锁状态则进入游戏层,如果地图处于被锁状态则点击无效。在进入游戏层加载地图之前,数据管理器中记录了唯一确定的主题编号和当前地图编号,所以可以无误的加载地图。
//选择主题界面确定全局主题编号的函数
voidCCSubject::menuBegin()
{
if(CCGlobal::sharedGlobal().isPassSubject(m_nCurSubject))
{
if(m_nCurSubject!=CCGlobal::sharedGlobal().getCurrentSubject())
{
//不同主题之间切换,不再保留地图序号,而是重置从1开始,或是从将要转到的主题的最后一个待过关地图开始
CCGlobal::sharedGlobal().reSetCurrentMap();
}
CCGlobal::sharedGlobal().setCurrentSubject(m_nCurSubject);
CCScene*pScene=CCScene::create();
pScene->addChild(CCSelectMap::create());
CCDirector::sharedDirector()->replaceScene(pScene);
}
}
//选择关卡界面确定全局地图编号的函数
voidCCSelectMap::menuBegin()
{
if(CCGlobal::sharedGlobal().isPassMap(m_nCurMap))
{
CCGlobal::sharedGlobal().setCurrentMap(m_nCurMap);
CCScene*pScene=CCScene::create();
pScene->addChild(CCGameLayer::create());
CCDirector::sharedDirector()->replaceScene(pScene);
}
}
//游戏层加载地图
stringmapPath=CCString::createWithFormat("subject/subject%d/tmx/map%d.tmx",
CCGlobal::sharedGlobal().getCurrentSubject(),
CCGlobal::sharedGlobal().getCurrentMap())->m_sString;
m_map=CCGameMap::create(CCGlobal::crossPlatformString(mapPath).c_str());
m_map->initGameLayer(this);
m_map->setPosition(origin);
this->addChild(m_map,1);
3.2 Tmx格式地图上的都携带了哪些信息?
Tmx格式地图有三层,一个背景层,一个道路层,一个对象层。携带数据信息的是对象层,所有的对象被大范围的名称,小范围的类型。名称有points,jam。Points下面有道路的起始点和终止点信息,有道路的拐弯处的导航点信息。Jam下有各种各样的障碍物信息。每个对象上携带的信息被组织成字典。在程序中根据key来解析出对应的value。怪物携带的导航点信息来自于tmx地图,地图层创建的障碍物也是来自于tmx地图上的对象。对于Tmx地图的对象层,引擎中有对应的解析类和解析函数。
//地图层的对象解析
CCString*name=(CCString*)pDic->objectForKey("name");
CCString*type=(CCString*)pDic->objectForKey("type");
CCJam*pJam=CCJam::create();
pJam->initPos(tileXY);
if(type->m_sString=="damo")
{
pJam->initType(eJam_damo);
}elseif(type->m_sString=="fang")
{
pJam->initType(eJam_fang);
}elseif(type->m_sString=="honglan")
{
pJam->initType(eJam_honglan);
}
3.3一个点击事件是怎么分发给地图层的?
游戏层开启触摸响应协议,触摸事件携带的点击位置信息是基于物理屏幕的坐标,原点在左上角,需要将其转换为GL坐标,然后再传递给地图层。
if(!pSelectMenu->isVisible())
{
CCPointpos=pTouch->getLocation();
m_map->handleTouch(pos);
}
在地图层接受到点击位置后,要先判断当前地图上的一些控制变量的状态然后再做出对应的处理逻辑,当炮塔选择列表出于显示状态时,那应该判断该点击是否可以产生一个炮塔,如果炮塔的升级与撤销界面出于显示状态时,则应该判断该点击是否能升级或是撤销炮塔,而且在这个过程中涉及到坐标的从全局到局部的转换。只有当炮塔选择列表界面和炮塔升级撤销界面都处于不显示状态时,才能进行判断该点击位置对应的地图砖块格子的状态是什么,然后再做出对应的逻辑处理。
voidCCGameMap::handleTouch(CCPointpos)
{
//炮塔列表可见情况下,凡是没有点击到炮塔的触摸均视为无效,然后将炮塔列表隐藏
if(m_bTurretVisible)
{
if(this->isInTurretList(pos))
{
//判断是否有足够金币开启炮塔,有的话就放置,没有的话不作为
this->attemptPutTurret(pos);
}
else
{
this->hideTurretList();
}
}elseif(m_bUpgradeVisiable)
{
if(m_turretUpgrade->isUnderBtnU(pos))
{
//进入升级逻辑,升级的话需要看全局的金币数量和升级所需要的数量,所以隐藏应写在升级函数内部
this->upgradeTurret();
}elseif(m_turretUpgrade->isUnderBtnR(pos))
{
//进入撤销逻辑,一定是可以撤销的
this->repealTurret();
this->hideTurretUpgradeForRepeal();
}else
{
this->hideTurretUpgrade();
}
}else
{
//判断触摸点是否在糖果矩形内
if(m_sugarRect.containsPoint(pos))
{
pSugar->showTouched();
return;
}
//因为地图和层大小一致,所以不需要做局部坐标转换
//直接将pos转换为GL形式下的格子坐标
//然后再根据格子的位置查找状态数组,判断处理
TileStatetileState=this->tileStateByPos(pos);
switch(tileState)
{
caseeTile_Jam:
this->handleTouchForJam(pos);
break;
caseeTile_Road:
this->handleTouchForRoad(pos);
break;
caseeTile_Touch:
this->handleTouchForTouch(pos);
break;
caseeTile_Turret:
this->handleTouchForTurret(pos);
break;
caseeTile_Dec:
break;
default:
break;
}
}
}
《糖果保卫传奇》2