Cocos2d-x 实现地图滚动,解释缝隙产生的原因以及解决方案
前端之家收集整理的这篇文章主要介绍了
Cocos2d-x 实现地图滚动,解释缝隙产生的原因以及解决方案,
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
温馨提示:如果只是找缝隙解决方案的请直接跳到最后的第4点。
在跑酷游戏(天天酷跑)或者射击游戏(打飞机)中都有背景循环移动的机制。网上也有很多的实现方法,我这里先直接用代码反映出来:
首先申明的是我这个是做的横版向左移动:
-
-
- */
-
- #ifndef__HELLOWORLD_SCENE_H__
- #define__HELLOWORLD_SCENE_H__
-
- #include"cocos2d.h"
- #defineMAP_1_Tag1//宏定义两个Map的Tag
- #defineMAP_2_Tag2
@H_301_89@ classHelloWorld:publiccocos2d::CCLayer
- {
- private:
- voidupdate(floattime);
- virtualvoidonExit();
- public:
- virtualboolinit();
- staticcocos2d::CCScene*scene();
- CREATE_FUNC(HelloWorld);
- };
- #endif
HelloWorldScene.cpp
- #include"HelloWorldScene.h"
@H_301_89@ USING_NS_CC;
@H_301_89@ CCScene*HelloWorld::scene()
@H_301_89@ CCScene*scene=CCScene::create();
- HelloWorld*layer=HelloWorld::create();
- scene->addChild(layer);
- returnscene;
- }
@H_301_89@ boolHelloWorld::init()
- if(!CCLayer::init())
- returnfalse;
- }
- CCSizevisibleSize=CCDirector::sharedDirector()->getVisibleSize();
- CCPointorigin=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,MAP_1_Tag);
- this->addChild(map2,MAP_2_Tag);
- this->scheduleUpdate();
- true;
- voidHelloWorld::update(floattime)
- {
- CCSizevisibleSize=CCDirector::sharedDirector()->getVisibleSize();
- CCPointorigin=CCDirector::sharedDirector()->getVisibleOrigin();
@H_301_89@ 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)
- floatoffset=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)
- floatoffset=temMap2->getPositionX()+temMap2->getContentSize().width/2-origin.x;
- temMap2->setPosition(ccp(temMap2->getContentSize().width/2+origin.x+visibleSize.width+offset,153); background-color:inherit; font-weight:bold">voidHelloWorld::onExit()
- this->unscheduleUpdate();
- CCLayer::onExit();
- }
@H_504_
403@
这里就提几点问题吧:
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倍,所以我才在前面说了为什么要以
图片的大小来调位置而不是屏幕的大小。
如果大家有什么问题的话欢迎
评论,也欢迎大神来拍砖!
用Tiled新建 好地图后放到cocos2d上使用,
// 加载Tiled地图
CCTMXTiledMap*map=CCTMXTiledMap::create("birdMap.tmx");
this->addChild(map);
然后报如下错误
Assert Failed: TMX: Only 1 tileset per layer is supported
原来是我把多个图块上的元素,都画到一个块层上,一个块层只能包含一个图块的元素,
新建不同块层安放其它图块元素,就解决了。
还有自己处理的图块文件,如在导入时,
块宽度与块高度是最重要的,
如果自已处理的
文件,不计算好这个像素的话,
显示就会出问题,像这样
显示不全,那是因为我这张PNG图,尺寸是180 X 250,但我导入块时的设置是 块宽度与高度都是32像素。
而Tiled都是按整数计算,而且无视四舍五入,直接去掉小数位,就是说 180÷32=5.625,它只会
显示该
图的
5×32 =160的宽度像素。因此会显示不全。 解决方法是,把我原来的PNG图尺寸改为192×250那就
能显示全了(6*32)。
所以在自作地图素材时,要想好自己要以怎样的块宽度和高度导入,然后到PS里,先用辅助线,拉好相
应的位置然后把元素放到相应位置上,那样,就能准确地使用了。
ocos2d-x中的CCObject类及其派生类,使用autorelease()方法,将自身交托于CCPoolManager管理器进行管理,都可以使用retain()方法来使自身的引用计数加一,使用release()方法来使自身的引用计数减一,当引用计数为0的时候,CCPoolManager管理器就会将其删除释放。
类
所有实例化Cocos2d-x里面的以CCObject为基类的类时,都要使用其create()方法来创建对象,对于自己添加的派生类,需要通过
CREATE_FUNC
宏来实现create()方法,下面以《
如何制作一个横版格斗过关游戏 Cocos2d-x 2.0.4
》来举例介绍:
Hero.h
1
2
3
4
5
6
7
8
9
|
|
classHero:
publicActionSprite
{
public:
Hero(
void);
~Hero(
void);
CREATE_FUNC(Hero);
//…… };
|
若是需要create()方法带有参数的话,仿造CREATE_FUNC的定义来实现,
CREATE_FUNC
宏定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#defineCREATE_FUNC(__TYPE__)\
static__TYPE__*create()\
{\
__TYPE__*pRet=
new__TYPE__();\
if(pRet&&pRet->init())\
{\
pRet->autorelease();\
returnpRet;\
}\
else\
{\
deletepRet;\
pRet=
NULL;\
return
NULL;\
}\
}
|
具体可以类似如下:
SimpleDPad.h
1
2
3
4
5
6
7
8
9
10
classSimpleDPad:
publiccocos2d::CCSprite,publiccocos2d::CCTargetedTouchDelegate
{
public:
SimpleDPad(
void);
~SimpleDPad(
void);
staticSimpleDPad*dPadWithFile(cocos2d::CCString*fileName,floatradius);
boolinitWithFile(cocos2d::CCString*filename,255)">floatradius);
其中dPadWithFile静态方法就是仿造的create()方法,具体实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
SimpleDPad*SimpleDPad::dPadWithFile(CCString*fileName,255)">floatradius)
{
SimpleDPad*pRet=
newSimpleDPad();
if(pRet&&pRet->initWithFile(fileName,radius))
{
pRet->autorelease();
returnpRet;
}
else
{
deletepRet;
pRet=
NULL;
NULL;
}
}
|
当然这里的方法名可以改为以create开头方便统一。
变量
当create出来的变量,被
addChild
到以CCNode为基类的类时,或者被
addObject
到CCArray、CCSet等时,都会自动将这个变量对象retain()一次,以防止被自动释放导致的野指针问题,所以一般情况都不需要再手动调用retain()方法了。对于类定义中用
CC_SYNTHESIZE_RETAIN
宏声明的变量,或者对临时变量手动调用了retain()方法,一般都需要在析构函数或者特定的函数进行手动调用release()方法,类似如下:
GameLayer.h
classGameLayer:
publiccocos2d::CCLayer,255)">publicSimpleDPadDelegate
{
public:
GameLayer(
void);
~GameLayer(
void);
CREATE_FUNC(GameLayer);
//……
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*,_robots,Robots);
};