Cocos2d-x3.0游戏实例之《别救我》第三篇——循环滚动背景
2014-05-04 17:33
465 查看
好,这篇我们来讲解无限循环滚动背景,这个知识已经被讲到烂了,我以前的文章也介绍过,所以就不那么详细地说明了。
笨木头花心贡献,啥?花心?不呢,是用心~转载请注明,原文地址:http://www.benmutou.com/blog/archives/823 文章来源:笨木头与游戏开发
我们最终要创建这样的背景,如图:
背景是在滚动的,大家有没有看到?(小若:看你妹,这是jpg,不是gif)
大家是不是很在意下面的那多出来的一条锯齿?它可不是坐标错位了,这是一个伏笔(还伏笔,你以为写小说啊!)。
本篇教程会用到的图片资源到这里下载:http://download.csdn.net/detail/musicvs/7392931
我们要创建一个新的类,叫做BackgroundLayer,用来实现滚动背景。
创建2张相同的背景图片,很简单,代码如下:
但是,它会对其他物理对象产生影响,比如有人撞了它,那个人就可能会反弹~
接着,有三个很特别的函数:setCategoryBitmask、setCollisionBitmask、setContactTestBitmask。
这三个函数是用于物体间的碰撞检测的,用来作为判断条件,要解释它们需要不小的篇幅,所以我就不解释了(小若:有没有墙?我想撞一下)。
本游戏在碰撞检测方面极其简单,所以不理解这三个函数都完全没有影响,因为游戏里的所有对象都能产生碰撞,没有什么特别的地方。
如果以后有机会,我再单独写一篇文章来介绍吧(或者大家百度一下)
(小若:这就是一开始的那张图吧?连图片地址都一样好吧)
现在地图是不会滚动的,没意思,我们来开始滚床单…不,不好意思,习惯了(邪恶),是滚动背景才对。
【木头Cocos2d-x032】我是定时器(第01章)—我爱单线程之schedule介绍
【木头Cocos2d-x033】我是定时器第02章—HelloWorld之scheduleUpdate
【木头Cocos2d-x034】我是定时器(第03章)—真正的定时器之schedule
【木头Cocos2d-x035】我是定时器(第04章)—停止update和触发器
木头我有一个坏习惯,那就是把游戏中所有的逻辑都用一个schedule来完成,这么说有点模糊,直接看代码吧。
首先给TollgateScene添加一个logic函数:
然后修改一下TollgateScene的scene函数:
OK,最重要的,看看TollgateScene的logic函数实现,有点复杂,大家要有心理准备:
我想,大家已经理解我之前说的,统一控制逻辑的意思了吧?由TollgateScene场景来调用各个层的logic函数,这样很方便,要停止逻辑的时候,只要由TollgateScene控制一下就可以了,不需要对各个层单独地进行控制。
OK,别忘了,在TollgateScene的init函数加上schedule的调用:
好了,现在运行游戏,就能看到背景在滚动了~
OK,下一篇我们就正式加入主角了。
笨木头花心贡献,啥?花心?不呢,是用心~转载请注明,原文地址:
为什么是循环滚动背景?
用循环滚动背景,其实是因为我想偷懒,因为这样我只需要准备一张图片就可以了。我们最终要创建这样的背景,如图:
背景是在滚动的,大家有没有看到?(小若:看你妹,这是jpg,不是gif)
大家是不是很在意下面的那多出来的一条锯齿?它可不是坐标错位了,这是一个伏笔(还伏笔,你以为写小说啊!)。
本篇教程会用到的图片资源到这里下载:
创建2张连续的背景图片
要实现循环滚动的背景,需要2张相同的图片实现,图片首尾相接。我们要创建一个新的类,叫做BackgroundLayer,用来实现滚动背景。
创建2张相同的背景图片,很简单,代码如下:
SizevisibleSize=Director::getInstance()->getVisibleSize(); /*背景图片*/ m_bg1=Sprite::create("background.jpg"); m_bg1->setPosition(Point(visibleSize.width*0.5f,visibleSize.height*0.5f)); this->addChild(m_bg1); m_bg2=Sprite::create("background.jpg"); m_bg2->setPosition(Point(visibleSize.width*0.5f,-visibleSize.height*0.5f)); this->addChild(m_bg2);
m_bg1和m_bg2都是Sprite对象,因为后面要用到,所以直接作为类的成员属性,方便调用。m_bg1是屏幕居中,m_bg2要紧接着m_bg1的下面,大家感受一下。
创建边缘锯齿
先跑一下题,我们把边缘锯齿也给添加好:/*创建边缘锯齿*/ autoborder=Sprite::create("border.png"); SizeborderSize=border->getContentSize(); autoborder1=createBorder(Point(borderSize.width*0.5f,borderSize.height*0.5f)); this->addChild(border1); autoborder2=createBorder(Point(visibleSize.width-borderSize.width*0.5f,borderSize.height*0.5f)); border2->setFlippedX(true); this->addChild(border2); autoborder3=createBorder(Point(visibleSize.width*0.5f,visibleSize.height*0.15f)); borderSize=border3->getContentSize(); border3->setRotation(90.0f); this->addChild(border3);
一共三个锯齿,左右各一个,下方一个。createBorder是自定义函数,代码如下:
Sprite*BackgroundLayer::createBorder(Pointpos) { autoborder=Sprite::create("border.png"); SizeborderSize=border->getContentSize(); autobody=PhysicsBody::createBox(borderSize); body->setDynamic(false); body->setCategoryBitmask(1);//0001 body->setCollisionBitmask(1);//0001 body->setContactTestBitmask(1);//0001 border->setPhysicsBody(body); border->setPosition(pos); returnborder; }
好,这个函数要稍微解释一下,这里使用PhysicsBody的createBox函数创建实体盒子刚体,因为边缘不是空心的。然后调用了setDynamic函数,让刚体成为静态物体,也就是说,物理世界不会对它起产生影响了,它不会被撞飞,随你怎么撞,它都纹丝不动~
但是,它会对其他物理对象产生影响,比如有人撞了它,那个人就可能会反弹~
接着,有三个很特别的函数:setCategoryBitmask、setCollisionBitmask、setContactTestBitmask。
这三个函数是用于物体间的碰撞检测的,用来作为判断条件,要解释它们需要不小的篇幅,所以我就不解释了(小若:有没有墙?我想撞一下)。
本游戏在碰撞检测方面极其简单,所以不理解这三个函数都完全没有影响,因为游戏里的所有对象都能产生碰撞,没有什么特别的地方。
如果以后有机会,我再单独写一篇文章来介绍吧(或者大家百度一下)
目前的代码
好,来看看目前为止BackgroundLayer的代码,头文件如下:#ifndefBackgroundLayer_H #defineBackgroundLayer_H #include"cocos2d.h" USING_NS_CC; classBackgroundLayer:publicLayer { public: BackgroundLayer(); ~BackgroundLayer(); CREATE_FUNC(BackgroundLayer); virtualboolinit(); private: Sprite*m_bg1; Sprite*m_bg2; Sprite*createBorder(Pointpos); }; #endif
Cpp文件如下:
#include"BackgroundLayer.h"
BackgroundLayer::BackgroundLayer(){}
BackgroundLayer::~BackgroundLayer(){}
boolBackgroundLayer::init()
{
if(!Layer::init())
{
returnfalse;
}
SizevisibleSize=Director::getInstance()->getVisibleSize();
/*背景图片*/
m_bg1=Sprite::create("background.jpg");
m_bg1->setPosition(Point(visibleSize.width*0.5f,visibleSize.height*0.5f));
this->addChild(m_bg1);
m_bg2=Sprite::create("background.jpg");
m_bg2->setPosition(Point(visibleSize.width*0.5f,-visibleSize.height*0.5f));
this->addChild(m_bg2);
/*创建边缘锯齿*/
autoborder=Sprite::create("border.png");
SizeborderSize=border->getContentSize();
autoborder1=createBorder(Point(borderSize.width*0.5f,borderSize.height*0.5f));
this->addChild(border1);
autoborder2=createBorder(Point(visibleSize.width-borderSize.width*0.5f,borderSize.height*0.5f));
border2->setFlippedX(true);
this->addChild(border2);
autoborder3=createBorder(Point(visibleSize.width*0.5f,visibleSize.height*0.15f));
borderSize=border3->getContentSize();
border3->setRotation(90.0f);
this->addChild(border3);
returntrue;
}
Sprite*BackgroundLayer::createBorder(Pointpos) { autoborder=Sprite::create("border.png"); SizeborderSize=border->getContentSize(); autobody=PhysicsBody::createBox(borderSize); body->setDynamic(false); body->setCategoryBitmask(1);//0001 body->setCollisionBitmask(1);//0001 body->setContactTestBitmask(1);//0001 border->setPhysicsBody(body); border->setPosition(pos); returnborder; }
先测试一下我们来先测试一下代码的运行情况吧,我们给TollgateScene添加BackgroundLayer层,修改一下TollgateScene的scene函数:
Scene*TollgateScene::scene() { autoscene=Scene::createWithPhysics(); /*这里省略了很多代码*/ /*背景层*/ autobackgroundLayer=BackgroundLayer::create(); scene->addChild(backgroundLayer,0); autolayer=TollgateScene::create(); scene->addChild(layer,10); returnscene; }OK,这样就可以了,再次运行代码,正常情况下,如图所示:
(小若:这就是一开始的那张图吧?连图片地址都一样好吧)
现在地图是不会滚动的,没意思,我们来开始滚床单…不,不好意思,习惯了(邪恶),是滚动背景才对。
统一控制游戏逻辑
地图滚动,其实就是不断改变2张地图的坐标,要不断改变坐标,就要用schedule来实现,schedule可以在游戏每一帧或者每隔一段时间的时候执行一次逻辑,这个如果不了解的,可以看看我的另外几篇关于schedule的文章:木头我有一个坏习惯,那就是把游戏中所有的逻辑都用一个schedule来完成,这么说有点模糊,直接看代码吧。
首先给TollgateScene添加一个logic函数:
classTollgateScene:publicLayer以及我偷偷添加了一个BackgroundLayer成员变量,大有用处,不要着急~
{
public:
~TollgateScene();
staticScene*scene();
CREATE_FUNC(TollgateScene);
virtualboolinit();
virtualvoidonExit()override;
private:
voidlogic(floatdt);
BackgroundLayer*m_backgroundLayer;
};
#endif
然后修改一下TollgateScene的scene函数:
Scene*TollgateScene::scene()留意最后一句代码(小若:是倒数第二句!),好吧,倒数第二句,我们保留了BackgroundLayer的引用。也许这样保持引用是比较糟糕的做法,或许用getChildByTag的方式来获取BackgroundLayer会好一些,但因为这对象要使用很多次,我选择了直接保存引用。大家根据个人喜好来决定吧~
{
autoscene=Scene::createWithPhysics();
/*这里省略了很多代码*/
/*背景层*/
autobackgroundLayer=BackgroundLayer::create();
scene->addChild(backgroundLayer,0);
autolayer=TollgateScene::create();
scene->addChild(layer,10);
layer->m_backgroundLayer=backgroundLayer;
returnscene;
}
OK,最重要的,看看TollgateScene的logic函数实现,有点复杂,大家要有心理准备:
voidTollgateScene::logic(floatdt)(小若:==哇,好复杂啊,完全看不懂….(蛇精病))
{
m_backgroundLayer->logic(dt);
}
我想,大家已经理解我之前说的,统一控制逻辑的意思了吧?由TollgateScene场景来调用各个层的logic函数,这样很方便,要停止逻辑的时候,只要由TollgateScene控制一下就可以了,不需要对各个层单独地进行控制。
OK,别忘了,在TollgateScene的init函数加上schedule的调用:
boolTollgateScene::init()
{
if(!Layer::init())
{
returnfalse;
}
this->schedule(schedule_selector(TollgateScene::logic));
returntrue;
}
BackgroundLayer背景层的逻辑
好了,我们还得为BackgroundLayer添加一个logic逻辑处理函数,头文件添加函数声明:classBackgroundLayer:publicLayerBackgroundLayer的logic函数实现如下,这个就真的有点小复杂了:
{
public:
BackgroundLayer();
~BackgroundLayer();
CREATE_FUNC(BackgroundLayer);
virtualboolinit();
voidlogic(floatdt);
private:
Sprite*m_bg1;
Sprite*m_bg2;
Sprite*createBorder(Pointpos);
};
voidBackgroundLayer::logic(floatdt){原理我就不解释了,大家看看代码注释,然后自己在纸上画一画,很好理解的。反正目的就是让两张背景不断往上移动,一旦图片完全离开屏幕,就让它回到屏幕下方,然后又继续往上移动。
intposY1=m_bg1->getPositionY();//背景地图1的Y坐标
intposY2=m_bg2->getPositionY();//背景地图2的Y坐标
intiSpeed=3;//地图滚动速度
/*两张地图向上滚动(两张地图是相邻的,所以要一起滚动,否则会出现空隙)*/
posY1+=iSpeed;
posY2+=iSpeed;
/*屏幕宽*/
intiVisibleHeight=Director::getInstance()->getVisibleSize().height;
/*当第1个地图完全离开屏幕时,让第2个地图完全出现在屏幕上,同时让第1个地图紧贴在第2个地图后面*/
if(posY1>iVisibleHeight*1.5f){
posY2=iVisibleHeight*0.5f;
posY1=-iVisibleHeight*0.5f;
}
/*同理,当第2个地图完全离开屏幕时,让第1个地图完全出现在屏幕上,同时让第2个地图紧贴在第1个地图后面*/
if(posY2>iVisibleHeight*1.5f){
posY1=iVisibleHeight*0.5f;
posY2=-iVisibleHeight*0.5f;
}
m_bg1->setPositionY(posY1);
m_bg2->setPositionY(posY2);
}
好了,现在运行游戏,就能看到背景在滚动了~
OK,下一篇我们就正式加入主角了。
相关文章推荐
- Cocos2d-x3.0游戏实例之《别救我》第三篇——循环滚动背景(上)
- Cocos2d-x3.0游戏实例之《别救我》第三篇——循环滚动背景(下)
- Cocos2d-x3.0游戏实例《不要救我》三——背景滚动周期
- Cocos2d-x3.0游戏实例之《别救我》第七篇——物理世界的碰撞检测
- Cocos2d-x3.0游戏实例之《别救我》第五篇——使用Cocostudio UI编辑器创建操作界面
- Cocos2d-x3.0游戏实例《别救我》目录导航(共10篇,已完结)
- Cocos2d-x3.0游戏实例《别救我》目录
- Cocos2d-x3.0游戏实例之《别救我》第二篇——创建物理世界
- Cocos2d-x3.0游戏实例之《别救我》第一篇——前言
- Cocos2d-x3.0游戏实例之《别救我》第八篇——TiledMap实现关卡编辑器
- Cocos2d-x3.0游戏实例之《别救我》第九篇——从tmx文件中加载关卡怪物
- Cocos2d-x3.0游戏实例之《别救我》第四篇——乱入的主角
- Cocos2d-x3.0游戏实例之《别救我》第四篇——乱入的主角
- Cocos2d-x3.0游戏实例之《别救我》第十篇(完结)——用Json配置各类型怪物数据
- Cocos2d-x3.0游戏实例之《别救我》第一篇——前言
- Cocos2d-x3.0游戏实例之《别救我》第八篇——TiledMap实现关卡编辑器
- Cocos2d-x3.0游戏实例之《别救我》第六篇——从代码中获取UI控件
- Cocos2d-x3.0游戏实例之《别救我》第六篇——从代码中获取UI控件
- Cocos2d-x3.0游戏实例《别救我》目录
- Cocos2d-x3.0游戏实例之《别救我》第五篇——使用Cocostudio UI编辑器创建操作界面