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

Cocos2d-x 实现地图滚动,解释缝隙产生的原因以及解决方案

2013-12-27 13:50 489 查看
温馨提示:如果只是找缝隙解决方案的请直接跳到最后的第4点。

在跑酷游戏(天天酷跑)或者射击游戏(打飞机)中都有背景循环移动的机制。网上也有很多的实现方法,我这里先直接用代码反映出来:

首先申明的是我这个是做的横版向左移动:

/*
HelloWorldScene.h
*/

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

#define MAP_1_Tag	1		// 宏定义两个Map的Tag
#define MAP_2_Tag	2

class HelloWorld : public cocos2d::CCLayer
{
private:
void update(float time);
virtual void onExit();
public:
virtual bool init();
static cocos2d::CCScene* scene();
CREATE_FUNC(HelloWorld);
};

#endif


/*
HelloWorldScene.cpp
*/

#include "HelloWorldScene.h"

USING_NS_CC;

CCScene* HelloWorld::scene()
{
CCScene *scene = CCScene::create();
HelloWorld *layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}

bool HelloWorld::init()
{
if ( !CCLayer::init() )
{
return false;
}
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();

CCSprite* map1 = CCSprite::create("HelloWorld.png");
CCSprite* map2 = CCSprite::create("HelloWorld.png");
map1->setPosition(ccp(map1->getContentSize().width/2 + origin.x, map1->getContentSize().height/2 + origin.y));
map2->setPosition(ccp(map2->getContentSize().width/2 + origin.x + map2->getContentSize().width, map2->getContentSize().height/2 + origin.y));
this->addChild(map1, 0, MAP_1_Tag);
this->addChild(map2, 0, MAP_2_Tag);
this->scheduleUpdate();
return true;
}

void HelloWorld::update(float time)
{
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();

CCSprite* temMap1 = (CCSprite*)this->getChildByTag(MAP_1_Tag);
CCSprite* temMap2 = (CCSprite*)this->getChildByTag(MAP_2_Tag);

temMap1->setPositionX(temMap1->getPositionX() - 5);
temMap2->setPositionX(temMap2->getPositionX() - 5);

if(temMap1->getPositionX() + temMap1->getContentSize().width/2 <= origin.x)
{
float offset = temMap1->getPositionX() + temMap1->getContentSize().width/2 - origin.x;
temMap1->setPosition(ccp(temMap1->getContentSize().width/2 + origin.x + visibleSize.width + offset, visibleSize.height/2 + origin.y));
}

if(temMap2->getPositionX() + temMap2->getContentSize().width/2 <= origin.x)
{
float offset = temMap2->getPositionX() + temMap2->getContentSize().width/2 - origin.x;
temMap2->setPosition(ccp(temMap2->getContentSize().width/2 + origin.x + visibleSize.width + offset, visibleSize.height/2 + origin.y));
}
}

void HelloWorld::onExit()
{
this->unscheduleUpdate();
CCLayer::onExit();
}


这里就提几点问题吧:

1. 首先是需要两张地图精灵来实现地图滚动,这个很好理解,有一张是负责切换的。

2. 第二点是设置地图精灵的位置,最好不要以屏幕的大小,而是以地图的大小为基准。比如我这个例子,屏幕是480×320,图片也是480×320的,这样就两种都可以,但是如果你的美工那天突然希望来个长景地图,他又不希望拼接,就给你弄了个720×320的图片,这个时候如果你还将图片的位置设为(visibleSize.width/2, visibleSize.height/2)的话,很显然就不对,所以我设置图片的初始位置以及图片移动和切换都是以(map1->getContentSize().width/2,
map1->getContentSize().height/2)作为基准的,对了,这里的锚点是图片中点。

3. 第三点是地图滚动,我是把这个逻辑写在Update()函数里面的,当然还有其他的方法,不过我觉得这样要好一点。目前我们小组的游戏里,实际上有三层背景在以不同的速度移动,以显示层次感,所以可以分别封装这三层背景,然后在Update()函数里一起执行。

4. 第四点就是地图移动时产生的黑缝问题,以前学习的时候,无论用Cocos2d-x,还是Unity3D,都会出现这个问题,并且在各种电脑上都会出现,起初网上有人说是因为移动速度的原因,估计是因为他把速度调得越高,缝隙越大而得出的结论。在这里我想告诉大家:完全不是这么一回事。重点只在于这句判断的问题

if(temMap1->getPositionX() + temMap1->getContentSize().width/2 <= origin.x)

这一句看似很正确,其实只是逻辑上的正确,这句代码的意思是当第一张图片刚好移出屏幕时,马上将其位置变到到第二张后面后面。然后第二张图片移出屏幕时,又将第二张图片的位置变到第一张后面——其实这就是滚动图片实现的原理。
但问题就是出在刚好这两个字上,下面是图解:



所以说我们需要加一个偏移量Offset,这个Offset的值就是temMap1->getPositionX() + temMap1->getContentSize().width/2 - origin.x,这里解释一下,由于我们是向左移动,x一直是做减法运算,所以确切的说应该是减一个偏移量。实际上这个Offset的值就是负数,所以...大家意会吧。也可以在float offset = temMap1->getPositionX() + temMap1->getContentSize().width/2
- origin.x;这段代码前面加断点调试一下,可以发现大部分情况下他是不等于0的。

5. 其他的好像没什么了,最后提一些自己的想法吧,抛砖引玉吧,在实际项目中,为了避免各种变化而带来频繁修改代码的麻烦,程序员应该编写适用性很强大代码,比如这个地图实现,由于当时进度的原因美工的图片一直交不出来,所以代码一直各种改,于是心一横直接把地图类全部改了。也就是前面所说的,一个函数控制一层地图,并且可以控制传入地图的张数,比如我们希望地面可以花哨点,于是用8张图片拼成一个屏幕长的地面,然后随机出现这些地面,这样造成不重复的幻觉。还有就是前面说的,远景是跑得很慢的一层地图,于是我们只有两张,一张的大小就是屏幕的1.5倍,所以我才在前面说了为什么要以图片的大小来调位置而不是屏幕的大小。

如果大家有什么问题的话欢迎评论,也欢迎大神来拍砖!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: