您的位置:首页 > 移动开发 > Cocos引擎

改写《魔塔》前篇05:场景滚动

2013-03-30 10:36 260 查看
       如果我们多体验一下目前的游戏,就会发现勇士走起路来怪怪的。没错,感觉像是悬浮在地图上,碰到墙没有停止,行走范围也没有在格子规定的范围内。下面的工作就是把勇士规整地贴合在地图内,让它不要乱跑。先从最简单的入手,假设初始时,我们要把勇士放在地图的左下角(虽然我们已经知道并把勇士初始位置设在了左下角,即ccp(48,48),但我们还是要根据书上描述的方法进行初始位置设定)。

       为了实现这个功能,首先要了解Tilemap的坐标系是以左上角为原点,而cocos2d-x坐标系是以左下角为原点。

       我们可以看到勇士若在左下角的话,按照Tilemap的坐标系计算应是(1,11),那么对应的cocos2d-x的坐标是多少呢?只需要将y坐标反转,用Tilemap的最大高度减去当前y的高度,再乘以图块宽高即可(书上有具体的图形示意和数学描述,在此就不再赘述)。

增加一个方法positionForTileCoord(),首先在HelloWorldScene.h文件中声明此方法,即在“bool isHeroWalking;”后面增加“CCPoint positionForTileCoord(CCPoint tileCoord);”代码,然后打开HelloWorldScene.cpp文件,在最后添加如下代码:

CCPoint HelloWorld::positionForTileCoord(CCPoint tileCoord)
{
CCPoint pos=ccp((tileCoord.x*map->getTileSize().width),
((map->getMapSize().height-tileCoord.y-1)*map->getTileSize().height));
return pos;
}

接着还是在HelloWorldScene.cpp文件里,我们找到init()函数中的“heroSprite->setPosition(ccp(48,48));”,删除,然后换成如下代码:
heroSprite->setAnchorPoint(CCPointZero);
heroSprite->setPosition(positionForTileCoord(ccp(1,11)));

这样就成功地将勇士放置在地图的左下角了,运行看看效果。
               


       接下来,我们为游戏加入场景滚动的效果。设想一下,随着勇士的移动,原本不在视野内的地图需要逐渐显示出来。为了便于理解,先只讨论y轴上的场景滚动。假设勇士已经移动到Tilemap的(1,4)位置,对应cocos2d-x坐标为(32,224),如何计算出场景应该滚动多少距离?首先,将屏幕高度的1/2作为滚动的临界位置,y值小于1/2高度的不需要滚动,大于1/2的才开始滚动。为什么要把屏幕的1/2作为临界位置呢?因为这样可以保证场景在滚动时,勇士始终处于屏幕高度的1/2处,这样的视觉效果最佳。当然也可以使用其他高度。现在计算出了屏幕的一半高度是320/2=160像素,而勇士的y值为224,那么场景需要滚动的距离就是224-(320/2)=64像素。此外,还需要注意几点:

(1)如果地图总宽/高小于屏幕的宽/高,那么直接可以断定不需要滚动。

(2)场景滚动的最大距离不能超过地图总宽高减去屏幕宽高的1/2,否则在勇士走到地图边缘时,场景继续滚动,会造成屏幕周围显示黑边。

(3)这里使用的“移动”是场景移动,而不是单纯的地图移动。实际上,我们需要连人带地图一起移动!勇士相对屏幕的位置没有发生变化,仍然在屏幕1/2处。

       好了,我们已经知道了场景滚动的原理,下面用代码来实现它。我们添加一个方法:setSceneScrollPosition。它有一个参数,是勇士当前在cocos2d-x坐标系内的位置。此方法可以将场景移动到相应位置。首先在HelloWorldScene.h里面声明它,即添加“void setSceneScrollPosition(CCPoint position);”,然后在HelloWorldScene.cpp里实现此方法,在最后增加如下代码:

void HelloWorld::setSceneScrollPosition(CCPoint position)
{
//获取屏幕尺寸
CCSize screenSize=CCDirector::sharedDirector()->getWinSize();
//计算Tilemap的宽高,单位是像素
CCSize mapSizeInPixel=CCSizeMake(map->getMapSize().width*map->getTileSize().width,
map->getMapSize().height*map->getTileSize().height);
//取勇士当前x坐标和屏幕中点x的最大值,如果勇士的x值较大,则会滚动
float x=MAX(position.x,screenSize.width/2.0f);
float y=MAX(position.y,screenSize.height/2.0f);
//地图总宽度大于屏幕宽度的时候才有可能滚动
if(mapSizeInPixel.width>screenSize.width)
{
x=MIN(x,mapSizeInPixel.width-screenSize.width/2.0f);
}
if(mapSizeInPixel.height>screenSize.height)
{

y=MIN(y,mapSizeInPixel.height-screenSize.height/2.0f);
}
//勇士的实际位置
CCPoint heroPosition=ccp(x,y);
//屏幕中点位置
CCPoint screenCenter=ccp(screenSize.width/2.0f,screenSize.height/2.0f);
//计算勇士实际位置和中点位置的距离
CCPoint scrollPosition=ccpSub(screenCenter,heroPosition);
//将场景移动到相应位置
this->setPosition(scrollPosition);
CCLog("%f,%f",scrollPosition.x,scrollPosition.y);
}

      那么,什么时候使用setSceneScrollPosition方法呢?我们只能在游戏的每帧里做这件事情。我们新建一个schedule_selector:HelloWorld::update,设置其调用间隔为每帧,在里面实现对场景位置的更新。然后在场景初始化的时候启动定时器,并在析构函数里销毁定时器。
      首先,我们在HelloWorldScene.h文件里声明update方法,添加代码“void update(float dt);”,然后在.cpp文件里实现它,即将下面代码添加到文件的最后。

void HelloWorld::update(float dt)
{
//如果勇士不在行走状态,不需要更新场景位置
if (isHeroWalking)
{
setSceneScrollPosition(heroSprite->getPosition());
}
}

     接着在init()函数中初始化一个定时器,在“bRet = true;”前面添加“schedule(schedule_selector(HelloWorld::update));”。最后,别忘了在析构函数里销毁这个定时器,我们在析构函数的最后添加“this->unscheduleAllSelectors();”。
     好了,现在编译程序,点击方向按钮,勇士可以随意地在地图内穿梭了,并且当移动到宽或高的一半后场景就会发生滚动。

                
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Cocos2d-x 魔塔