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

       如果我们多体验一下目前的游戏,就会发现勇士走起路来怪怪的。没错,感觉像是悬浮在地图上,碰到墙没有停止,行走范围也没有在格子规定的范围内。下面的工作就是把勇士规整地贴合在地图内,让它不要乱跑。先从最简单的入手,假设初始时,我们要把勇士放在地图的左下角(虽然我们已经知道并把勇士初始位置设在了左下角,即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();”。

     好了,现在编译程序,点击方向按钮,勇士可以随意地在地图内穿梭了,并且当移动到宽或高的一半后场景就会发生滚动。

                

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值