主要功能是对障碍物进行封装,其中有两个自定义的子类,分别是TriangleBarrier类和RectBarrier类。
整个游戏源码解析:
(注:因为知识面有限,有些地方笔者可能也不清楚代码的具体作用和实现原理,可能会避开不讲,怕误导。)
AppDelegate::applicationDidFinishLaunching(),该方法是程序准备完成,游戏启动的入口函数。
1.先获取导演类的单例,然后通过导演类的单例获取GLViewImpl对象,如果不存在(glview为空),就重新(create())创建一个。
2.setOpenGLView(pEGLView)之后,调用setDesignResolutionSize()函数,进行自动适配屏幕分辨率设置。
屏幕设置的相关函数:
3.0中有以下相关接口(参考自:http://blog.sina.com.cn/s/blog_923fdd9b0101g4i3.html):
Director::getInstance()->getOpenGLView()->setDesignResolutionSize() //设计分辨率大小及模式
setDesignResolutionSize(DW, DH, resolutionPolicy) 有三个参数,设计分辨率宽,设计分辨率高,分辨率策略。
适配策略ResolutionPolicy,作为一个枚举类型,包括了如下方案:
enum class ResolutionPolicy
{
EXACT_FIT,
NO_BORDER,
SHOW_ALL,
FIXED_HEIGHT,
FIXED_WIDTH,
UNKNOWN,
};
其中的各个配置参数的配置含义如下:
EXACT_FIT:图片在选定的区域全部可见,图片显示可能会被压缩或者拉伸,不再保持原来的纵横比。
NO_BORDER:图片在特定的区域全部可见,不会扭曲,但可能会被剪裁,维持原来的纵横比。
SHOW_ALL:图片在特定的区域全部可见,不会扭曲,维持原来的纵横比,但可能显示边界。
FIXED_HEIGHT:固定高度,修改内部画布的宽度已适应设备的纵横比。不会扭曲。
FIXED_WIDTH:固定宽度,修改内部画布的高度已适应设备的纵横比。不会扭曲。
Director::getInstance()->setContentScaleFactor() //内容缩放因子
决定了图片显示到屏幕的缩放因子,但是这个接口的参数不是通过资源图片的宽、高比屏幕宽、高得来。Cocos2d-x引擎设计试图屏蔽游戏开发者直接去关注屏幕,
所以这个因子是资源宽、高比设计分辨率宽、高。setContentScaleFactor()通常有两个方式来设置参数。RH/DH或RW/DW,不同的因子选择有不同的缩放负作用。
3.接下来看这两行代码:
GameSceneManager* gsm = new GameSceneManager();
gsm->createScene(); //跌换到欢迎场景显示
new一个GameSceneManager类的对象,返回一个指针,将该指针赋值给gsm,然后调用createScene()方法创建第一个场景,从这里开始,其实整个游戏就已经被带动起来了。
3.1 void GameSceneManager::createScene()
1)调用MusicManager::loadMusic()(静态方法)加载背景音乐。
2)Scene* gameScene = Scene::create();//创建一个场景对象(返回一个 autorelease 的场景对象。)
3)MenuLayer* layer = MenuLayer::create();//创建一个欢迎布景对象
这里说下MenuLayer::create方法以及init方法的调用和关系。
定位MenuLayer::create() 方法,指向 CREATE_FUNC(MenuLayer),再跟踪进去看,此宏定义了一个静态函数create():
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
__TYPE__ *pRet = new(std::nothrow) __TYPE__(); \
if (pRet && pRet->init()) \
{ \
pRet->autorelease(); \
return pRet; \
} \
else \
{ \
delete pRet; \
pRet = nullptr; \
return nullptr; \
} \
}
将__TYPE__替换成MenuLayer,该方法先new一个MenuLayer对象,如果对象创建成功,调用init()(在这里对该方法对父类的init进行了重写)方法,调用成功后调用autorelease()对对象进行托管。
4)前面说过在MenuLayer::create()中会调用init()方法,看下重写的init方法做了哪些事。
首先调用了父类的初始化方法——设置节点标签,方便布景之间的查找——new一个SpriteManager对象,我们来看看SpriteManager类的构造函数:
1)SpriteManager(Layer* layerIn),有参构造函数,MenuLayer对象在init中创建SpriteManager对象时将自己(this)作为形参传给了SpriteManager的构造函数
2)调用SpriteBatchNode::create加载一张大图,接下来重要的一步是设置触摸事件。下面介绍下触摸事件的知识:
先看下事件处理机制:事件处理机制一般都有三个重要的角色:事件、事件源和事件处理者。事件源是事件发生的场所,通常就是各个试图或控件;事件处理者是接收事件并进行处理的一段程序。
触摸事件有两个事件监听器:EventListenerTouchOneByOne 表示的是单点触摸;而EventListenerTouchAllAtOnce 表示的就是多点触摸。
EventListenerTouchOneByOne中触摸事件响应属性:
(1)std::function
当一个手指触摸屏幕时回调该属性所指定的函数。如果函数返回值为true,则可以回调后面的两个属性(onTouchMoved和onTouchEnded)所指定的函数,否则不回调。
(2)std::function
当一个手指在屏幕移动时回调该属性所指定的函数。
(3)std::function
当一个手指离开屏幕时回调该属性所指定的函数。
(4)std::function
当单点触摸事件被取消时回调该属性所指定的函数。
EventListenerTouchAllAtOnce中触摸事件响应属性:
(1)std::function
(2)std::function
(3)std::function
(4)std::function
通过该游戏的例子源码来分析单点触摸事件:
首先使用EventListenerTouchOneByOne::create()创建单独触摸事件监听对象listenerTouch,设置是否下传触摸(setSwallowTouches(true) true不向下触摸,简单点来说,比如有两个sprite ,A和 B,A在上B在下(位置重叠),
触摸A的时候,B不会受到影响,反之false,向下传递触摸,触摸A也等于触摸了B),然后分别设置它的listenerTouch->onTouchBegan、listenerTouch->onTouchMoved、listenerTouch->onTouchEnded
、listenerTouch->onTouchCancelled属性,其中CC_CALLBACK_2宏定义绑定了回调函数。看看回调函数里做了哪些事:
1)myOnTouchBegan(Touch *touch, Event *event):获取事件所绑定的触摸对象并将其转化为精灵类型——获取当前坐标(getLocationInView()是获得UI坐标,getLocation()获得的是OpenGL坐标)
接下来就应该是创建触摸对象并将触摸对象添加到监听器中去。这里游戏的作者并没有先创建触摸对象,而是把触摸对象添加到监听器这一动作封装成
独立的方法SpriteManager::AddEventListenerSprite(Sprite* sp)。接下来,我看看都有哪些触摸对象。
SpriteManager类的构造函数执行结束之后init()方法接着new一个MenuLayerHelp对象,继续执行MenuLayerHelp类的构造函数:此构造函数只初始化了Layer和SpriteManager的属性,初始值分别是MenuLayer本身和MenuLayer的SpriteManager属性。
SpriteManager类的构造函数执行完之后,调用mlh->initCreateSprite()方法,进入游戏看到的所有触摸对象均在这里被创建,创建方法是利用MenuLayerHelp类中自定义的initCreateSprite()方法创建的,创建后分别设置成触摸监听对象。
5)MenuLayer::create()中调用init()方法结束后,游戏会根据相应的触摸事件调用相应的代码,下面来详细看看每个触摸事件所调用的代码:
先复习下指针数组和数组指针的概念:
指针数组:array of pointers,即用于存储指针的数组,也就是数组元素都是指针
数组指针:a pointer to an array,即指向数组的指针
还要注意的是他们用法的区别,下面举例说明。
int* a[4] 指针数组
表示:数组a中的元素都为int型指针
元素表示:*a[i] *(a[i])是一样的,因为[]优先级高于*
int (*a)[4] 数组指针
表示:指向数组a的指针
元素表示:(*a)[i]
(1)刚进入欢迎界面看到的布景的标签是INDEX_MENULAYER(this->setTag(INDEX_MENULAYER)),所以首次触摸时会执行:(MenuLayer*)(layer))->mlh->JudgeSp(sp)
。。。接下来就是根据触摸对象跳转到相应的场景了
顺序动作(Sequnce)可以顺序得一个接着一个运行内部动作序列。
RemoveSelf动作用来删除执行动作的节点。
(2)当布景的标签是INDEX_LEVELLAYER,执行:(LevelLayer*)(layer))->llh->JudgeSp(sp)
。。。往后都是根据对应的标签转到对应的游戏场景,对于后面的游戏场景及场景中实现的逻辑在这就不做解释了。