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

2023-11-11 15:50

本文主要是介绍Cocos2d-x3.0游戏实例之《别救我》第三篇——循环滚动背景,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

 

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

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

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

 

 

为什么是循环滚动背景?

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

 

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

 

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

 

 

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

 

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

 

本篇教程会用到的图片资源到这里下载:http://download.csdn.net/detail/musicvs/7392931

 

创建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,这样就可以了,再次运行代码,正常情况下,如图所示:

 

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

 

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

 

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

 

统一控制游戏逻辑

地图滚动,其实就是不断改变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, 0);
  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游戏实例之《别救我》第三篇——循环滚动背景的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/391174

相关文章

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结

JAVA中while循环的使用与注意事项

《JAVA中while循环的使用与注意事项》:本文主要介绍while循环在编程中的应用,包括其基本结构、语句示例、适用场景以及注意事项,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录while循环1. 什么是while循环2. while循环的语句3.while循环的适用场景以及优势4. 注意

MySQL的索引失效的原因实例及解决方案

《MySQL的索引失效的原因实例及解决方案》这篇文章主要讨论了MySQL索引失效的常见原因及其解决方案,它涵盖了数据类型不匹配、隐式转换、函数或表达式、范围查询、LIKE查询、OR条件、全表扫描、索引... 目录1. 数据类型不匹配2. 隐式转换3. 函数或表达式4. 范围查询之后的列5. like 查询6

Python中的异步:async 和 await以及操作中的事件循环、回调和异常

《Python中的异步:async和await以及操作中的事件循环、回调和异常》在现代编程中,异步操作在处理I/O密集型任务时,可以显著提高程序的性能和响应速度,Python提供了asyn... 目录引言什么是异步操作?python 中的异步编程基础async 和 await 关键字asyncio 模块理论

Python开发围棋游戏的实例代码(实现全部功能)

《Python开发围棋游戏的实例代码(实现全部功能)》围棋是一种古老而复杂的策略棋类游戏,起源于中国,已有超过2500年的历史,本文介绍了如何用Python开发一个简单的围棋游戏,实例代码涵盖了游戏的... 目录1. 围棋游戏概述1.1 游戏规则1.2 游戏设计思路2. 环境准备3. 创建棋盘3.1 棋盘类

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

国产游戏崛起:技术革新与文化自信的双重推动

近年来,国产游戏行业发展迅猛,技术水平和作品质量均得到了显著提升。特别是以《黑神话:悟空》为代表的一系列优秀作品,成功打破了过去中国游戏市场以手游和网游为主的局限,向全球玩家展示了中国在单机游戏领域的实力与潜力。随着中国开发者在画面渲染、物理引擎、AI 技术和服务器架构等方面取得了显著进展,国产游戏正逐步赢得国际市场的认可。然而,面对全球游戏行业的激烈竞争,国产游戏技术依然面临诸多挑战,未来的

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数