温馨提示:如果只是找缝隙解决方案的请直接跳到最后的第4点。

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

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

[cpp] view plaincopy
  1. /*
  2. HelloWorldScene.h
  3. */
  4. #ifndef __HELLOWORLD_SCENE_H__
  5. #define __HELLOWORLD_SCENE_H__
  6. #include "cocos2d.h"
  7. #define MAP_1_Tag   1       // 宏定义两个Map的Tag
  8. #define MAP_2_Tag   2
  9. class HelloWorld : public cocos2d::CCLayer
  10. {
  11. private:
  12. void update(float time);
  13. virtual void onExit();
  14. public:
  15. virtual bool init();
  16. static cocos2d::CCScene* scene();
  17. CREATE_FUNC(HelloWorld);
  18. };
  19. #endif
[cpp] view plaincopy
  1. /*
  2. HelloWorldScene.cpp
  3. */
  4. #include "HelloWorldScene.h"
  5. USING_NS_CC;
  6. CCScene* HelloWorld::scene()
  7. {
  8. CCScene *scene = CCScene::create();
  9. HelloWorld *layer = HelloWorld::create();
  10. scene->addChild(layer);
  11. return scene;
  12. }
  13. bool HelloWorld::init()
  14. {
  15. if ( !CCLayer::init() )
  16. {
  17. return false;
  18. }
  19. CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
  20. CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
  21. CCSprite* map1 = CCSprite::create("HelloWorld.png");
  22. CCSprite* map2 = CCSprite::create("HelloWorld.png");
  23. map1->setPosition(ccp(map1->getContentSize().width/2 + origin.x, map1->getContentSize().height/2 + origin.y));
  24. map2->setPosition(ccp(map2->getContentSize().width/2 + origin.x + map2->getContentSize().width, map2->getContentSize().height/2 + origin.y));
  25. this->addChild(map1, 0, MAP_1_Tag);
  26. this->addChild(map2, 0, MAP_2_Tag);
  27. this->scheduleUpdate();
  28. return true;
  29. }
  30. void HelloWorld::update(float time)
  31. {
  32. CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
  33. CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
  34. CCSprite* temMap1 = (CCSprite*)this->getChildByTag(MAP_1_Tag);
  35. CCSprite* temMap2 = (CCSprite*)this->getChildByTag(MAP_2_Tag);
  36. temMap1->setPositionX(temMap1->getPositionX() - 5);
  37. temMap2->setPositionX(temMap2->getPositionX() - 5);
  38. if(temMap1->getPositionX() + temMap1->getContentSize().width/2 <= origin.x)
  39. {
  40. float offset = temMap1->getPositionX() + temMap1->getContentSize().width/2 - origin.x;
  41. temMap1->setPosition(ccp(temMap1->getContentSize().width/2 + origin.x + visibleSize.width + offset, visibleSize.height/2 + origin.y));
  42. }
  43. if(temMap2->getPositionX() + temMap2->getContentSize().width/2 <= origin.x)
  44. {
  45. float offset = temMap2->getPositionX() + temMap2->getContentSize().width/2 - origin.x;
  46. temMap2->setPosition(ccp(temMap2->getContentSize().width/2 + origin.x + visibleSize.width + offset, visibleSize.height/2 + origin.y));
  47. }
  48. }
  49. void HelloWorld::onExit()
  50. {
  51. this->unscheduleUpdate();
  52. CCLayer::onExit();
  53. }

这里就提几点问题吧:

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倍,所以我才在前面说了为什么要以图片的大小来调位置而不是屏幕的大小。
如果大家有什么问题的话欢迎评论,也欢迎大神来拍砖!

cocos2d-x学习记录-Tiled使用注意点

用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 2.0.4 小心隐藏的retain
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
class Hero : public ActionSprite
{
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
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
    __TYPE__ *pRet = new __TYPE__(); \
    if (pRet && pRet->init()) \
    { \
        pRet->autorelease(); \
        return pRet; \
    } \
    else \
    { \
        delete pRet; \
        pRet = NULL; \
        return NULL; \
    } \
}

具体可以类似如下:
SimpleDPad.h

1
2
3
4
5
6
7
8
9
10
class SimpleDPad : public cocos2d::CCSprite, public cocos2d::CCTargetedTouchDelegate
{
public:
    SimpleDPad(void);
    ~SimpleDPad(void);

static SimpleDPad* dPadWithFile(cocos2d::CCString *fileName, float radius);
    bool initWithFile(cocos2d::CCString *filename, float radius);
    //……
};

其中dPadWithFile静态方法就是仿造的create()方法,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SimpleDPad* SimpleDPad::dPadWithFile(CCString *fileName, float radius)
{
    SimpleDPad *pRet = new SimpleDPad();
    if (pRet && pRet->initWithFile(fileName, radius))
    {
        pRet->autorelease();
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = NULL;
        return NULL;
    }
}

当然这里的方法名可以改为以create开头方便统一。
变量
当create出来的变量,被addChild到以CCNode为基类的类时,或者被addObject到CCArray、CCSet等时,都会自动将这个变量对象retain()一次,以防止被自动释放导致的野指针问题,所以一般情况都不需要再手动调用retain()方法了。对于类定义中用CC_SYNTHESIZE_RETAIN宏声明的变量,或者对临时变量手动调用了retain()方法,一般都需要在析构函数或者特定的函数进行手动调用release()方法,类似如下:
GameLayer.h

1
2
3
4
5
6
7
8
9
10
class GameLayer : public cocos2d::CCLayer, public SimpleDPadDelegate
{
public:
    GameLayer(void);
    ~GameLayer(void);

CREATE_FUNC(GameLayer);
    //……
    CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _robots, Robots);
};

GameLayer.cpp

1
2
3
4
5
6
7
8
9
10
11
GameLayer::GameLayer(void)
{
    //……
    _robots = NULL;
}

GameLayer::~GameLayer(void)
{
    //……
    CC_SAFE_RELEASE_NULL(_robots);
}

但是有一种特殊情况,类与变量的互相retain(),导致无法释放,内存泄露。
ActionSprite.h

1
2
3
4
5
6
7
8
9
class ActionSprite : public cocos2d::CCSprite
{
public:
    ActionSprite(void);
    ~ActionSprite(void);

//……
    CC_SYNTHESIZE_RETAIN(cocos2d::CCAction*, _attackAction, AttackAction);
};

Hero.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
bool Hero::init()
{
    bool bRet = false;
    do 
    {
        //……
        this->setAttackAction(CCSequence::create(CCAnimate::create(attackAnimation), CCCallFunc::create(this, callfunc_selector(Hero::idle)), NULL));

//……
    } while (0);

return bRet;
}

_attackAction变量以CCSequence类创建,CCSequence创建包含CCCallFunc的创建,CCCallFunc创建的时候将this指针传递下去,跟踪源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
CCCallFunc * CCCallFunc::create(CCObject* pSelectorTarget, SEL_CallFunc selector) 
{
    CCCallFunc *pRet = new CCCallFunc();

if (pRet && pRet->initWithTarget(pSelectorTarget)) {
        pRet->m_pCallFunc = selector;
        pRet->autorelease();
        return pRet;
    }

CC_SAFE_DELETE(pRet);
    return NULL;
}

bool CCCallFunc::initWithTarget(CCObject* pSelectorTarget) {
    if (pSelectorTarget) 
    {
        pSelectorTarget->retain();
    }

if (m_pSelectorTarget) 
    {
        m_pSelectorTarget->release();
    }

m_pSelectorTarget = pSelectorTarget;
    return true;
}

可以看到它对this指针的对象进行retain()调用,导致Hero对象无法被自动释放,需要先手动对_attackAction变量进行release()调用,CCCallFunc将进行析构,进而将会对this指针的对象进行release()调用

1
2
3
4
virtual ~CCCallFunc()
{
    CC_SAFE_RELEASE(m_pSelectorTarget);
}

解决方法举例如下:
ActionSprite.h

1
2
3
4
5
6
class ActionSprite : public cocos2d::CCSprite
{
public:
    //……
   virtual void cleanup();
};  

ActionSprite.cpp

1
2
3
4
5
6
7
8
void ActionSprite::cleanup()
{
    CC_SAFE_RELEASE_NULL(_idleAction);
    CC_SAFE_RELEASE_NULL(_attackAction);
    CC_SAFE_RELEASE_NULL(_walkAction);
    CC_SAFE_RELEASE_NULL(_hurtAction);
    CC_SAFE_RELEASE_NULL(_knockedOutAction);

CCSprite::cleanup();
}

最后用Visual Leak Detector和DevPartner检测,均未检测到内存泄露。

如文章存在错误之处,欢迎指出,以便改正。By 无

疑问:  为啥这也是临时变量:就不用retain哪?
----------------------

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

  1. cocos植物大战僵尸(三)游戏场景:地图滚动

    2.3游戏场景 2.3.1 地图滚动 根据json表读取出,当前关卡要载入哪张地图,并读取当前地图的相关.Json表为数组结构,配置如下: [{"Level":0,"Ma ...

  2. 【Cocos2D-X 游戏引擎】初窥门径(7)无限地图滚动

    地图的滚动原理很简单,就是持续设置地图的位置即可.具体实现用schedule()函数. pMap = CCSprite::create("MapHenn.png");pMap-&g ...

  3. 游戏编程基础(五)背景地图滚动显示

    在游戏过程中,背景地图需要跟着人物的移动而动态的滚动变换.C++游戏编程基础中介绍了2D游戏中常用的3种动态背景表现手法.其原理和实现技巧分析如下: 方法一:单一背景滚动         原理是:利用 ...

  4. Cocos2d:使用 CCCamera 做滚动效果 (Four Ways of Scrolling with Cocos2D)

    原版的:http://www.koboldtouch.com/display/IDCAR/Four+Ways+of+Scrolling+with+Cocos2D There are two class ...

  5. UNITY2D 地图滚动DEMO

    UNITY 2D开发,对地图划分为21x21=441块,改变每一块的图片加载,达到显示地图对象的目的. 不好意思,这是一种非常笨的办法... 核心代码: using System.Collection ...

  6. 百度地图——判断用户是否在配送范围内解决方案

    需求: 在pc端设置商家的配送范围,用户在下单时,根据用户设置的配送地点判断是否在可配送范围内,并给用户相应的提示. 实现: 1.用百度地图在PC端设置配送范围,可拖拽选择 2.根据用户设置的配送地址 ...

  7. Vue 移动端项目 百度地图 点击事件无效、不触发 解决方案

    解决百度地图 手机端 点击事件不触发 的一种方案 版权声明:本文为博主原创文章,转载请标明原文出处.  问题背景 我是半路接手的这个项目,该项目使用 Vue 开发的一款 手机端的页面.到手里时,使用百 ...

  8. 百度卫星地图无法使用鼠标绘制工具的问题简单解决方案

    问题 最近手上的一个有关百度地图的bug 具体是这样的: 在街道地图和自定义地图上是没问题的,可以框选提交 在地球模式下,没有√×选项,没法提交 解决过程 首先,我觉得应该不是代码的问题,于是提了相关 ...

  9. 地图大量数据查询与渲染——bug及解决方案

    本文记录大数据可视化项目中信息查询过程遇到的实际问题及解决方案,用到了Vue自定义组件.Promise.all.DocumentFragment.event loop等. 项目需求 项目使用的arcg ...

最新文章

  1. oracle双机切换 无法挂载,Linux下Oracle RAC一个节点宕机导致共享存储无法挂载的故障排除...
  2. 5、Makefile基础知识汇总(转自陈皓总述)
  3. heartbeat+drbd+mysql:实现最廉价的高可用组合
  4. python时间模块哪个好arrow模块_python库: arrow (时间)
  5. java list 转 map_Java8 快速实现List转map 、分组、过滤等操作
  6. Linux i2c子系统驱动probe
  7. python 安装中的错误解决
  8. Android Gradle实用技巧——APK文件名中加上SVN版本号,日期等
  9. SQLyog客户端常用快捷键
  10. Xcode 7.3 解决自定义类无法自动联想
  11. WPS EXCEL 处理字符串转换为日期格式
  12. Android--BRVAH官方使用指南
  13. 人工智能7大关键技术,终于有人讲明白了
  14. marlin固件烧录教程_marlin固件中文(marlin固件下载)
  15. 第二十四题——[ZJCTF 2019]NiZhuanSiWei
  16. 概率论复习笔记二——离散型分布和连续型分布
  17. 数据库子查询-select后面语句
  18. word段落居中的快捷键_Word段落设置中的小技巧和快捷键推荐
  19. 学生成绩计算的c语言,学生成绩标准管理系统C语言.doc
  20. 归因分析计算因子贡献度常见的方法

热门文章

  1. Tyrion 中文文档(含示例源码)
  2. 中国31省 5G网速 排名!
  3. 为什么转置512x512矩阵,会比513x513矩阵慢很多?
  4. CSS - 使表格td中的文字垂直居中
  5. Android---简易的底部导航栏
  6. 单例模式java概括_单例模式中总有几个歪门邪道的问题要知道!
  7. 基于SpringBoot调用百度ocr以及企查查接口实现对营业执照信息的提取并识别真伪
  8. 优美的数列(斐波那契数列)
  9. 万国觉醒服务器维护,万国觉醒好像没看到1服怎么回事 官方关闭部分服务器公告[多图]...
  10. 学mei私聊问我【DISTINCT】关键字有什么作用?查一个字段和多个字段的区别?