Cocos2d-x3.0游戏实例之《别救我》第三篇——循环滚动背景

好,这篇我们来讲解无限循环滚动背景,这个知识已经被讲到烂了,我以前的文章也介绍过,所以就不那么详细地说明了。

笨木头花心贡献,啥?花心?不呢,是用心~

转载请注明,原文地址:http://www.benmutou.com/blog/archives/823

文章来源:笨木头与游戏开发

为什么是循环滚动背景?

用循环滚动背景,其实是因为我想偷懒,因为这样我只需要准备一张图片就可以了。

我们最终要创建这样的背景,如图:

背景是在滚动的,大家有没有看到?(小若:看你妹,这是jpg,不是gif)

大家是不是很在意下面的那多出来的一条锯齿?它可不是坐标错位了,这是一个伏笔(还伏笔,你以为写小说啊!)。

本篇教程会用到的图片资源到这里下载:http://yunpan.cn/QNXxe5fekPy4y  访问密码
8c82

创建2张连续的背景图片

要实现循环滚动的背景,需要2张相同的图片实现,图片首尾相接。

我们要创建一个新的类,叫做BackgroundLayer,用来实现滚动背景。

创建2张相同的背景图片,很简单,代码如下:

  1. Size visibleSize = Director::getInstance()->getVisibleSize();
  2. /* 背景图片 */
  3. m_bg1 = Sprite::create("background.jpg");
  4. m_bg1->setPosition(Point(visibleSize.width * 0.5f, visibleSize.height * 0.5f));
  5. this->addChild(m_bg1);
  6. m_bg2 = Sprite::create("background.jpg");
  7. m_bg2->setPosition(Point(visibleSize.width * 0.5f, -visibleSize.height * 0.5f));
  8. this->addChild(m_bg2);
m_bg1和m_bg2都是Sprite对象,因为后面要用到,所以直接作为类的成员属性,方便调用。

m_bg1是屏幕居中,m_bg2要紧接着m_bg1的下面,大家感受一下。

创建边缘锯齿

先跑一下题,我们把边缘锯齿也给添加好:

  1. /* 创建边缘锯齿 */
  2. auto border = Sprite::create("border.png");
  3. Size borderSize = border->getContentSize();
  4. auto border1 = createBorder(Point(borderSize.width * 0.5f, borderSize.height * 0.5f));
  5. this->addChild(border1);
  6. auto border2 = createBorder(Point(visibleSize.width - borderSize.width * 0.5f, borderSize.height * 0.5f));
  7. border2->setFlippedX(true);
  8. this->addChild(border2);
  9. auto border3 = createBorder(Point(visibleSize.width * 0.5f, visibleSize.height * 0.15f));
  10. borderSize = border3->getContentSize();
  11. border3->setRotation(90.0f);
  12. this->addChild(border3);
一共三个锯齿,左右各一个,下方一个。

createBorder是自定义函数,代码如下:

  1. Sprite* BackgroundLayer::createBorder(Point pos)
  2. {
  3. auto border = Sprite::create("border.png");
  4. Size borderSize = border->getContentSize();
  5. auto body = PhysicsBody::createBox(borderSize);
  6. body->setDynamic(false);
  7. body->setCategoryBitmask(1);    // 0001
  8. body->setCollisionBitmask(1);   // 0001
  9. body->setContactTestBitmask(1); // 0001
  10. border->setPhysicsBody(body);
  11. border->setPosition(pos);
  12. return border;
  13. }
好,这个函数要稍微解释一下,这里使用PhysicsBody的createBox函数创建实体盒子刚体,因为边缘不是空心的。

然后调用了setDynamic函数,让刚体成为静态物体,也就是说,物理世界不会对它起产生影响了,它不会被撞飞,随你怎么撞,它都纹丝不动~

但是,它会对其他物理对象产生影响,比如有人撞了它,那个人就可能会反弹~

接着,有三个很特别的函数:setCategoryBitmask、setCollisionBitmask、setContactTestBitmask。

这三个函数是用于物体间的碰撞检测的,用来作为判断条件,要解释它们需要不小的篇幅,所以我就不解释了(小若:有没有墙?我想撞一下)。

本游戏在碰撞检测方面极其简单,所以不理解这三个函数都完全没有影响,因为游戏里的所有对象都能产生碰撞,没有什么特别的地方。

如果以后有机会,我再单独写一篇文章来介绍吧(或者大家百度一下)

目前的代码

好,来看看目前为止BackgroundLayer的代码,头文件如下:

  1. #ifndef BackgroundLayer_H
  2. #define BackgroundLayer_H
  3. #include "cocos2d.h"
  4. USING_NS_CC;
  5. class BackgroundLayer : public Layer
  6. {
  7. public:
  8. BackgroundLayer();
  9. ~BackgroundLayer();
  10. CREATE_FUNC(BackgroundLayer);
  11. virtual bool init();
  12. private:
  13. Sprite* m_bg1;
  14. Sprite* m_bg2;
  15. Sprite* createBorder(Point pos);
  16. };
  17. #endif
Cpp文件如下:
  1. #include "BackgroundLayer.h"
  2. BackgroundLayer::BackgroundLayer(){}
  3. BackgroundLayer::~BackgroundLayer(){}
  4. bool BackgroundLayer::init()
  5. {
  6. if (!Layer::init())
  7. {
  8. return false;
  9. }
  10. Size visibleSize = Director::getInstance()->getVisibleSize();
  11. /* 背景图片 */
  12. m_bg1 = Sprite::create("background.jpg");
  13. m_bg1->setPosition(Point(visibleSize.width * 0.5f, visibleSize.height * 0.5f));
  14. this->addChild(m_bg1);
  15. m_bg2 = Sprite::create("background.jpg");
  16. m_bg2->setPosition(Point(visibleSize.width * 0.5f, -visibleSize.height * 0.5f));
  17. this->addChild(m_bg2);
  18. /* 创建边缘锯齿 */
  19. auto border = Sprite::create("border.png");
  20. Size borderSize = border->getContentSize();
  21. auto border1 = createBorder(Point(borderSize.width * 0.5f, borderSize.height * 0.5f));
  22. this->addChild(border1);
  23. auto border2 = createBorder(Point(visibleSize.width - borderSize.width * 0.5f, borderSize.height * 0.5f));
  24. border2->setFlippedX(true);
  25. this->addChild(border2);
  26. auto border3 = createBorder(Point(visibleSize.width * 0.5f, visibleSize.height * 0.15f));
  27. borderSize = border3->getContentSize();
  28. border3->setRotation(90.0f);
  29. this->addChild(border3);
  30. return true;
  31. }
  32. Sprite* BackgroundLayer::createBorder(Point pos)
  33. {
  34. auto border = Sprite::create("border.png");
  35. Size borderSize = border->getContentSize();
  36. auto body = PhysicsBody::createBox(borderSize);
  37. body->setDynamic(false);
  38. body->setCategoryBitmask(1);    // 0001
  39. body->setCollisionBitmask(1);   // 0001
  40. body->setContactTestBitmask(1); // 0001
  41. border->setPhysicsBody(body);
  42. border->setPosition(pos);
  43. return border;
  44. }
先测试一下

我们来先测试一下代码的运行情况吧,我们给TollgateScene添加BackgroundLayer层,修改一下TollgateScene的scene函数:

  1. Scene* TollgateScene::scene()
  2. {
  3. auto scene = Scene::createWithPhysics();
  4. /* 这里省略了很多代码 */
  5. /* 背景层 */
  6. auto backgroundLayer = BackgroundLayer::create();
  7. scene->addChild(backgroundLayer, 0);
  8. auto layer = TollgateScene::create();
  9. scene->addChild(layer, 10);
  10. return scene;
  11. }
 

OK,这样就可以了,再次运行代码,正常情况下,如图所示:

(小若:这就是一开始的那张图吧?连图片地址都一样好吧)

现在地图是不会滚动的,没意思,我们来开始滚床单…不,不好意思,习惯了(邪恶),是滚动背景才对。

统一控制游戏逻辑

地图滚动,其实就是不断改变2张地图的坐标,要不断改变坐标,就要用schedule来实现,schedule可以在游戏每一帧或者每隔一段时间的时候执行一次逻辑,这个如果不了解的,可以看看我的另外几篇关于schedule的文章:

【木头Cocos2d-x 032】我是定时器(第01章)—我爱单线程之schedule介绍

【木头Cocos2d-x 033】我是定时器第02章—HelloWorld之scheduleUpdate

【木头Cocos2d-x 034】我是定时器(第03章)—真正的定时器之schedule

【木头Cocos2d-x 035】我是定时器(第04章)—停止update和触发器

木头我有一个坏习惯,那就是把游戏中所有的逻辑都用一个schedule来完成,这么说有点模糊,直接看代码吧。

首先给TollgateScene添加一个logic函数:

  1. class TollgateScene : public Layer
  2. {
  3. public:
  4. ~TollgateScene();
  5. static Scene* scene();
  6. CREATE_FUNC(TollgateScene);
  7. virtual bool init();
  8. virtual void onExit() override;
  9. private:
  10. void logic(float dt);
  11. BackgroundLayer* m_backgroundLayer;
  12. };
  13. #endif
 

以及我偷偷添加了一个BackgroundLayer成员变量,大有用处,不要着急~

然后修改一下TollgateScene的scene函数:

  1. Scene* TollgateScene::scene()
  2. {
  3. auto scene = Scene::createWithPhysics();
  4. /* 这里省略了很多代码 */
  5. /* 背景层 */
  6. auto backgroundLayer = BackgroundLayer::create();
  7. scene->addChild(backgroundLayer, 1);
  8. auto layer = TollgateScene::create();
  9. scene->addChild(layer, 10);
  10. layer->m_backgroundLayer = backgroundLayer;
  11. return scene;
  12. }
 

留意最后一句代码(小若:是倒数第二句!),好吧,倒数第二句,我们保留了BackgroundLayer的引用。也许这样保持引用是比较糟糕的做法,或许用getChildByTag的方式来获取BackgroundLayer会好一些,但因为这对象要使用很多次,我选择了直接保存引用。大家根据个人喜好来决定吧~

OK,最重要的,看看TollgateScene的logic函数实现,有点复杂,大家要有心理准备:

  1. void TollgateScene::logic(float dt)
  2. {
  3. m_backgroundLayer->logic(dt);
  4. }
 

(小若:= =哇,好复杂啊,完全看不懂….(蛇精病))

我想,大家已经理解我之前说的,统一控制逻辑的意思了吧?由TollgateScene场景来调用各个层的logic函数,这样很方便,要停止逻辑的时候,只要由TollgateScene控制一下就可以了,不需要对各个层单独地进行控制。

OK,别忘了,在TollgateScene的init函数加上schedule的调用:

  1. bool TollgateScene::init()
  2. {
  3. if (!Layer::init())
  4. {
  5. return false;
  6. }
  7. this->schedule(schedule_selector(TollgateScene::logic));
  8. return true;
  9. }
 

BackgroundLayer背景层的逻辑

好了,我们还得为BackgroundLayer添加一个logic逻辑处理函数,头文件添加函数声明:

  1. class BackgroundLayer : public Layer
  2. {
  3. public:
  4. BackgroundLayer();
  5. ~BackgroundLayer();
  6. CREATE_FUNC(BackgroundLayer);
  7. virtual bool init();
  8. void logic(float dt);
  9. private:
  10. Sprite* m_bg1;
  11. Sprite* m_bg2;
  12. Sprite* createBorder(Point pos);
  13. };
 

BackgroundLayer的logic函数实现如下,这个就真的有点小复杂了:

  1. void BackgroundLayer::logic(float dt) {
  2. int posY1 = m_bg1->getPositionY(); // 背景地图1的Y坐标
  3. int posY2 = m_bg2->getPositionY(); // 背景地图2的Y坐标
  4. int iSpeed = 3;    // 地图滚动速度
  5. /* 两张地图向上滚动(两张地图是相邻的,所以要一起滚动,否则会出现空隙) */
  6. posY1 += iSpeed;
  7. posY2 += iSpeed;
  8. /* 屏幕宽 */
  9. int iVisibleHeight = Director::getInstance()->getVisibleSize().height;
  10. /* 当第1个地图完全离开屏幕时,让第2个地图完全出现在屏幕上,同时让第1个地图紧贴在第2个地图后面 */
  11. if (posY1 > iVisibleHeight * 1.5f) {
  12. posY2 = iVisibleHeight * 0.5f;
  13. posY1 = -iVisibleHeight * 0.5f;
  14. }
  15. /* 同理,当第2个地图完全离开屏幕时,让第1个地图完全出现在屏幕上,同时让第2个地图紧贴在第1个地图后面 */
  16. if (posY2 > iVisibleHeight * 1.5f) {
  17. posY1 = iVisibleHeight * 0.5f;
  18. posY2 = -iVisibleHeight * 0.5f;
  19. }
  20. m_bg1->setPositionY(posY1);
  21. m_bg2->setPositionY(posY2);
  22. }
 

原理我就不解释了,大家看看代码注释,然后自己在纸上画一画,很好理解的。反正目的就是让两张背景不断往上移动,一旦图片完全离开屏幕,就让它回到屏幕下方,然后又继续往上移动。

好了,现在运行游戏,就能看到背景在滚动了~

OK,下一篇我们就正式加入主角了。

Cocos2d-x3.0游戏实例之《别救我》第三篇——循环滚动背景

时间: 2024-12-31 03:26:17

Cocos2d-x3.0游戏实例之《别救我》第三篇——循环滚动背景的相关文章

Cocos2d-x3.0游戏实例之《别救我》第二篇——创建物理世界

这篇我要给大家介绍两个知识点: 1. 创建游戏物理世界 2. 没了(小若:我噗) 害怕了?不用担心,这太简单了~! 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址:http://www.benmutou.com/blog/archives/804 文章来源:笨木头与游戏开发 3.0新亮点,史上最简单的物理引擎 在Cocos2d-x3.0里使用物理引擎,会很有快感,因为很多繁琐的东西它都帮我们封装好了. 那么,我要开始创建游戏的关卡场景了,大家跟紧了. 我们给关卡场景命名为Toll

Cocos2d-x3.0游戏实例之《别救我》第六篇——从代码中获取UI控件

这篇的内容很简单,获取UI控件,然后使用它. 还记得我们在UI编辑器中给三个按钮分别命名了吧? 现在要用上了. 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址: http://www.benmutou.com/blog/archives/918 文章来源:笨木头与游戏开发 根据名字查找控件 首先给TollgateScene再include一些头文件,不然等会编译又报错了: #include "editor-support/cocostudio/CCSGUIReader.h&quo

Cocos2d-x3.0游戏实例之《别救我》第九篇——从tmx文件中加载关卡怪物

上一篇我们已经制作好tg1.tmx文件了,现在就要使用它了. 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址: http://www.benmutou.com/blog/archives/944 文章来源:笨木头与游戏开发 很抱歉,我们又要新建2个类了,我已经尽力少新建类了,毕竟是教程,类越多越容易混乱. 我们要新建一个Monster类,以及一个MonsterLayer类,专门添加Monster对象. Monster类 来看看Monster.h文件: #ifndef Monste

Cocos2d-x3.0游戏实例之《别救我》第一篇——前言

我们能学到什么? 这是一个很简单的游戏,但是用到的东西比较多(对新手而言),起码,对于一个实例来说,它涉及的确实有点多. 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址:http://www.benmutou.com/blog/archives/797 文章来源:笨木头与游戏开发 通过这个游戏实例,可爱的你(小若:那不可爱的人呢?),可以学到以下知识点: 1. Cocos2d-x3.0物理引擎的简单使用 2. 读取Json文件作为怪物和物品配置 3. 利用Tiled地图实现可视化

Cocos2d-x3.0游戏实例之《别救我》第八篇——TiledMap实现关卡编辑器

好吧,我真心完全搞不懂,我现在只不过是写了3个类而已,怎么就已经到第八篇了?我感觉我说话已经够简单明了毫不唠叨了,真是奇怪.(小若:我噗,噗噗!) 好吧,既然现在主角已经能出现并且进行操作了,那,也是时候让怪物出场了. 让怪物出现太简单了,不就是写一个Monster类,然后创建刚体,让它可以和主角碰撞,然后让主角扣血或者加血么? 没错,确实就是这样的,但是,这不是重点,我想介绍的是,利用关卡编辑器来创建所有要出场的怪物,这才有意思~ 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址

Cocos2d-x3.0游戏实例之《别救我》第十篇(完结)——用Json配置各类型怪物数据

现在我们有2种类型的怪物,而且创建的时候是写死在代码里的,这是要作死的节奏~ 所以,必须可配置,不然会累死人的. 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址: http://www.benmutou.com/blog/archives/949  文章来源:笨木头与游戏开发 Json文件 什么是Json文件?说白了,它就是一个文本文档,只不过它的内容是按照一定的规则填写的. 于是,我们就可以按照那个规则去读取这份文档. 这,就是配置文件产生的缘由(才怪). (小若:才怪是什么意

Cocos2d-x3.0游戏实例之《别救我》第五篇——使用Cocostudio UI编辑器创建操作界面

这一篇内容其实很简单,已经对Cocostudio比较熟悉的朋友就可以随便扫一下了~(小若:熟悉Cocostudio的人谁还有空看你的基础教程呢) 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址:http://www.benmutou.com/blog/archives/892 文章来源:笨木头与游戏开发 下载Cocostudio 首先,去官网下载最新版的Cocostudio:http://www.cocos2d-x.org/download 我这篇教程使用的版本是V1.3.0.1

cocos2d-x 3.0游戏实例学习笔记 《跑酷》 第三步---主角开跑&同时带着刚体

在这一步,我们主要是把主角加入到游戏场景中来,并且让它跑动,这里的跑动,实际上也就是执行一组动画,让其看起来像是在跑动,而且相对屏幕的位置也不会改变 我们会定义一个主角类:Runner,而这一步就要用到 帧动画 的创建和使用等知识点. 对于Runner,我的设计思路如下: 1.主角有一个动作集合,包括跑动,跳起来,以及蹲下.那么就有不同的帧动画,那么这里借鉴到 晓风残月前辈的方法,将帧动画打包命名,之后根据动作名来执行动作 2.主角需要绑定刚体,并且在不同的动作下的刚体大小不同 3.我们给主角设

编写小游戏《贪头蛇》第三篇

源码下载地址:http://download.csdn.net/detail/oyangyufu/7492917 点击NEW GAME按钮,进入游戏主场景 代码: 游戏背景 layer = (CCLayer*)this->getChildren()->objectAtIndex(SnakeConstants::LAYER_BACKGROUND); layer->setTouchEnabled(false); //游戏背景 CCSize size = CCDirector::sharedD