第一部分 引擎基础
第一章 Hello Cocos2d-x
(1) 精灵(sprite):一切可见元素
(2) 只有把一个游戏元素放置到其他已经呈现出来的游戏元素中,它才会呈现出来。
(3) CCNode::addChild方法:组合游戏元素
第二章 基本概念
(1) 游戏流程控制——》场景——》层——》精灵(FPS:每秒帧速率)
场景(CCScene):内容相对不变的游戏元素集合称作场景
流程控制:游戏在场景之间切换的过程;游戏流程控制器(CCDirectory——>单例模式)
层(CCLayer):隶属场景之下的游戏元素(菜单、触摸、动作、背景层)
精灵(CCSprite):隶属于层,是场景中出现的可见图形
(2) 关系图==渲染树——》子节点相对于父节点的属性。
(3) 动作(CCAction)== 持续性动作类(CCActionInterval)+ 瞬时性动作类(CCActionInstant),动画(animation)只能应用于精灵。
(4) 使用宏USING_NS_CC来引用cocos2d命名空间,类名称采用驼峰法。
(5) Cocos2d-x创建对象:所有对象都创建在堆上,然后通过指针引用。
A.new操作符 + init初始化
B.静态工厂方法(Create()方法——autorelease())
两者不同:使用构造函数创建的对象,它的所有权已经属于调用者了,使用工厂方法创建的对象的所有权却并不属于调用者,因此,使用构造函数创建的对象需要调用者负责释放,而使用工厂方法创建的对象则不需要。
(6) 为了保证初始化方法可以被子类重载,需要确保初始化方法声明为虚函数。
(7) 选择器(Selector):类似于C++中的类的函数指针的机制。
(8) Cocos2d-x提供了一系列宏来帮助我们方便的创建属性,他们定义在引擎目录中的“platform/CCPlatformMacros.h”中。每个宏都有3个参数:
Cocos2d-x内存管理:
(9) 两种实现智能内存管理的技术:一是引用计数——根类CCObjec;二是(自动且不可预知的进行)垃圾回收——autorelease()+自动回收池。
(10)Cocos2d-x在每一帧结束后释放一次回收池,并在下一帧开始前创建一个新的回收池。代价——维护一个将要执行释放操作的对象列表。
(11)手动释放和创建回收池——CCPoolManager的pop()或push()方法。自动回收池可嵌套,多个自动回收池排列成栈结构,autorelease对象仅添加到顶端的池中。
(12)避免滥用autorelease(),原因1 占用内存和cpu;2对象释放操作的错误很难定位,autorelease只在每一帧结束时才执行。结论:只在工厂方法等不得不用的情况下使用。
(13)Cocos2d-x引擎为我们提供了CCArray、CCDictionary等Objective-C风格的容器——Cocos2d-x容器保证了容器对对象的存取过程总是符合引用计数的内存管理原则。
(14)Cocos2d-x中与内存管理有关的宏——头文件“CCPlatformMacro.h”
Cocos2d-x内存管理原则:
- 程序段必须成对执行retain()和release()或者执行autorelease()来开始和结束对象的引用;
- 工厂方法返回前,应通过autorelease()结束对该对象的引用;
- 对象传值时,应考虑到新旧对象相同的特殊情况;
- 尽量使用release()而不是autorelease()来释放对象引用,以确保性能最优;
- 保存CCObject的子类对象是,应严格使用Cocos2d-x提供的容器,避免使用STL容器,对象必须以指针形式存入。
(15)AppDelegate是整个程序的入口,处理生命周期中的4个重要事件:程序完成初始化,程序进入后台,程序重回前台和程序结束退出。
第三章 游戏的基本元素
(1)CCDirector(单例设计模式):游戏在CCDirector的管理下完成了呈现设定与流程控制。《Cocos2d-x高级开发教程》P33
(2)CCScene:层的容器+流程控制。CCTransitionScene:场景切换特效。
(3)CCLayer:精灵的容器+接受用户输入,addChild向场景中添加层。
(4)CCSprite:最小可见单位,装载一个平面纹理。
(5)纹理:被精灵显示出来的图片,更深入:纹理是3D绘图库OpenGL绘制到物体表面上的图案。纹理的坐标系中原点(0,0)位于左上角,原点向右是x轴的正方向,原点向下是y轴的正方向。
2.纹理相关属性。《》P39
CCNode
(6)渲染树包含许多的CCNode。一切游戏元素都继承自CCNode,CCNode提供的基本功能如下:(CCNode属性参见《》P42)
1. 包含其他CCNode对象;
2. 接受各种事件与回调函数,如定时器事件;
3. 运行动作。
(7)Cocos2d-x坐标系:
绘图坐标系(以左下角为原点):与OpenGL采用的坐标系相同;
纹理坐标系(以左上角为原点):只有从纹理中截取部分矩阵时才使用这个坐标系。
(8)锚点(AnchorPoint):锚点两个参量x和y取值通常都是0-1之间的实数,表示锚点相对于本节点长宽的位置。《》P42
位置(Position):Position指的是锚点(相对于本节点长宽的位置坐标点)在本节点的父节点中的坐标值。
(9)定时器:以一定时间间隔连续引发游戏事件的工具。两种实现定时机制——update方法和schedule方法。《》P44
update定时器:在每帧绘制之前都会被触发一次。启用(scheduleUpdate方法)+重载update
schedule定时器:以一定的时间间隔(必须大于两帧的间隔)连续调用某个函数。(schedule方法启用)
(10)定时器机制是Cocos2d-x调度机制的基础,Cocos2d-x的调度是纯粹的串行机制,所有函数都运行在同一个线程。
(11)Cocos2d-x内置的常用层:
CCLayerColor:一个单纯实心色块
CCLayerGradient:一个色块,但可以设置两种颜色的渐变效果
CCMenu:十分常用的游戏菜单(普通状态、按下状态和响应事件)
Cocos2d-x调度原理
(12)事件驱动的游戏框架,如定时器事件、触摸事件和传感器事件。
(13)游戏主循环:处理用户输入,处理定时事件,绘图
(14)定时调度器:一个管理所有节点定时器的对象,它负责记录定时器,并在合适的时间发出定时事件。CCSchedule类(定时调度器类)管理定时器,并且分别处理update定时器(Updates容器)和普通定时器(Selectors散列表)
(15)CCTimer也提供了update方法,不同的是其累计接收到的dt时间,周期性的反复(或一次)触发定时事件。《》P57
(16)事件驱动的普通定时器调用顺序:系统的时间事件驱动游戏主循环,游戏主循环调用CCScheduler的update方法,CCScheduler调用普通定时器对应的CCTimer对象的update方法,CCTimer类的update方法调用定时器对应的回调函数。对于update定时器,调用顺序更为简单。
(17)每个定时器互不干扰,串行执行。
第四章 动作
(1)CCAction是一个基类,一个 CCAction 只能使用一次,CCAction的绝大多数实现类都派生自CCFiniteTimeAction,这个类定义了在有限时间内可以完成的动作。由CCFiniteTimeAction派生出的两个主要类分别是瞬时动作(CCActionInstant)和持续性动作(CCActionInterval)。
瞬时性动作:CCPlace、CCFlipX和CCFlipY、CCShow和CCHide、CCCallFunc
持续性动作:
1.位置变化动作:CCMoveTo、CCJumpTo、CCBezierTo;
2.属性变化动作:CCScaleTo、CCRotateTo、CCFadeIn/Out、CCFadeTo、CCTintTo;
3.视觉特效动作: CCBlink(闪烁);
4.控制动作:CCDelayTime、CCRepeat、CCRepeatForever;
复合动作:重复(CCRepeat)、并列(CCSpawn)、序列(CCSequence)、延时(CCDelayTime);
变速动作:CCSpeed、CCActionEase
(2)当我们创建更多动作的组合时,引擎会把动作分解为两部分来看待,其中后一部分只包含最后一个动作,而前一部分包含它之前的所有动作,引擎把 m_pTwo 设置为后一部分的动作,把 m_pOne 设置为其余所有动作的组合。
(3)创建自定义动作:CCAction 包含两个重要的方法:step 与 update。step 方法会在每一帧动作更新时触发,该方法接受一个表示调用时间间隔的参数 dt,dt 的积累即为动作运行的总时间。引擎利用积累时间来计算动作运行的进度(一个从 0 到 1 的实数),并调用 update 方法更新动作。update 方法是 CCAction 的核心,它由 step 方法调用,接受一个表示动作进度的参数,每一个动作都需要利用进度值改变目标节点的属性或执行其他指令。自定义动作只需要从这两个方法入手即可,我们通常只需要修改 update 方法就可以实现简单的动作。
(4)Cocos2d-x动作原理:《》P80
(5)CCActionManager的工作原理:动作的调度与定时器的调度都统一受到CCScheduler的控制,具体地说,我们可以方便地同时暂停或恢复定时器与动作的运行,而不必考虑它们不同步的问题。
第五章 动画与场景特效
(1)动画:由帧组成(帧动画),利用定时器不停的改变精灵的显示内容。
(2)框帧类(CCSpriteFrame):纹理+区域——纹理是要显示的纹理,区域是此纹将要被显示的部分。
(3)动画帧类(CCAnimationFrame):两个属性:对一个框帧的引用+帧的延时。
(4)动画类(CCAnimation):对一个动画的描述,它包含显示动画所需要的动画帧。
(5)动画动作(CCAnimate):我们使用 CCAnimation 描述一个动画,而精灵显示动画的动作则是一个 CCAnimate 对象。动画动作 CCAnimate 是精灵显示动画的动作,它由一个动画对象创建,并由精灵执行。动画与动画动作的关系就如同 CD 光盘与 CD 播放机的关系一样--前者记录了动画的内容,而后者是播放动画的工具。
(6)场景切换特效类(CCTransitionScene):场景特效的实现方式与复合动作类似,可以使用原场景作为参数来创建一个特效场景。文件源码:layers_scenes_transitions_nodes/CCTransition.h(cpp)
第六章 音乐与音效
(1)Cocos2d-x的音效引擎库CocosDenshion:
1.CDSoundEngine(封装OpenAL,只能被OS X平台支持)
2.CDAudioManager(封装OpenAL,只能被OS X平台支持)
3.SimpleAudioEngine(单例):预加载、播放与停止、暂停与恢复播放《》P89
第七章 用户输入
(1)CCStandardTouchDelegate接口:四个触摸事件回调函数(开始、移动、结束和取消),TouchEnable属性:开启或关闭接收触摸输入。
(2)响应触摸事件:触摸层——》通知游戏中的其他部件来响应触摸事件。
(3)两种Cocos2d-x触摸事件:《》P94。我们已经见到了 CCLayer 处理触摸事件的方法。当我们开启 CCLayer 的 TouchEnable 属性后,层中的 4 个回调函数就会在接收到事件时被触发,我们把这类事件称作1.标准触摸(standard touch)事件,它的特点是任何层都会平等地接收全部触摸事件。
除此以外,Cocos2d-x 还提供了另一种被称作2.带目标的触摸事件(targeted touch)机制。在带目标的触摸机制中,接收者并不平等,较早处理事件的接收者有权停止事件的分发,使它不再继续传递给其他接收者。换句话说,带目标的触摸事件并不一定会被广播给所有的接收者。通常,游戏的菜单按钮、摇杆按钮等元件常使用目标触摸事件,以保证触摸事件不对其他层产生不良影响。
(4)标准触摸事件步骤:
1. 需要此对象实现 CCStandardTouchDelegate 接口。
2.使用 addStandardDelegate 方法把自己注册给触摸事件分发器。
3.重载事件回调函数,处理触摸事件(Began/Moved/Ended/Cancelled);
4.当不再需要接收触摸事件时,使用 removeDelegate 方法来注销触摸事件的接收。
(5)带目标的触摸事件步骤:
1.实现 CCTargetedTouchDelegate 接口。
2.使用 addTargetedDelegate 方法注册到触摸事件分发器。
3.重载事件回调函数。注意,我们必须在触摸开始事件中针对需要接受的事件返回 true 以捕捉事件。
- 当不再需要接受触摸事件时,使用removeDelegate 方法来注销触摸事件的接收。与标准触摸事件相比,不同之处主要在于开始触摸事件需要返回一个代表是否捕捉事件的值。
(6)触摸分发器(CCTouchDispatcher类)原理:《》P97
(7)使用触摸事件:
触摸层(接收系统触摸事件)——代理(协议类)——精灵层(实际处理触摸事件,继承并实现代理类中的触摸事件处理)
第二部分 引擎进阶
第八章 粒子效果
(1)Cocos2d-x中的粒子系统(CCParticleSystem):粒子效果的代码放在 Cocos2d-x 引擎目录下"particle_nodes"目录中的"CCParticleSystem.cpp"文件中:
1.产生粒子;
2.更新粒子状态;
3.回收无效粒子;
创建一个全新的粒子系统通常较为烦琐,大多数情况下,我们更乐意把粒子系统的参数保存在文件中,而 Cocos2d-x 就是使用 Plist 文件来保存这些参数的。
第九章 大型地图
(1)大型地图——瓦片地图——许多较小的纹理创建瓦片
(2)TileMap地图(TMX 文件)支持3种不同的视图:正交视图(orthogonal view,瓦片水平垂直排列)、六边形视图(hexagonal view,六边形瓦片紧密连接)和等轴视图(isometric view,45 度斜视排列)
(3)Tiled Map Editor:地图编辑器,与 Cocos2d-x 不同的是,TileMap 的坐标系的原点位于左上角;TileMap 中的每一个瓦片拥有一个唯一的编号 GID。
(4)Cocos2d-x 为我们提供了 CCTMXTileMap 和 CCTMXLayer 两个类来处理瓦片地图。
(5)Tiled Map Editor 地图编辑器支持图层的概念,地图中的每一个图层恰好对应着 Cocos2d-x 里的 CCLayer,每一层在 Cocos2d世界中的 Z 顺序值都不尽相同。我们将水纹的 Z 坐标设定为 10,随之确定的是鱼的 Z 坐标应该在 10 以下,而炮台和子弹的 Z 坐标则应该在 10 以上。(Z越大,越上层)
第十章 Cocos2d-x绘图原理及优化
OpenGL基础:
(1)OpenGL 是一个基于 C 语言的三维图形 API; OpenGL 是一个基于状态的绘图模型,我们把这种模型称为状态机,优势1. OpenGL 把所有的参数作为状态来保存。2. 我们可以把绘图设备人为地分为两个部分:"服务器端",负责具体的绘制渲染;"客户端",负责向服务器端发送绘图指令。在设备中 cpu 负责运行游戏的逻辑,并向 GPU(硬件显卡或是软件模拟的显卡)发送绘图指令。
(3)GLenum 类型用来表示 OpenGL 的状态量,全部状态的列表定义在"gl2.h"头文件中。
(4)OpenGL 是一个三维图形接口,在程序中使用右手三维坐标系。具体地说,在初始化时,屏幕向右的方向为 X 方向,屏幕向上的方向为 Y 方向,由屏幕指向我们的方向为 Z 方向。OpenGL 负责把三维空间中的对象通过投影、光栅化转换为二维图像,然后呈现到屏幕上。
(5)在不对 OpenGL 做任何设置的时候,初始的坐标系称作世界坐标系,OpenGL 还维护了一个绘图坐标系。
(6)OpenGL 从 2.0 版本开始引入了可编程着色器(shader)。可编程着色器主要包含顶点着色器和片段着色器,其中前者负责对顶点进行几何变换以及光照计算,后者负责处理光栅化得到的像素以及纹理。
(7)OpenGL 对顶点进行的处理实际上可以归纳为接受顶点数据、进行投影、得到变换后的顶点数据这 3 个步骤。
(8)变换利用矩阵表示。常见的变换包含平移变换、旋转变换和缩放变换等,它们分别对应了平移矩阵、旋转矩阵和缩放矩阵等。OpenGL 为我们提供了一系列创建变换矩阵的函数,因此,在实际开发中,我们并不需要手动构造变换矩阵。(OpenGL ES 2.0)
OpenGL绘图原理:
(9)绘制精灵的代码位于引擎源码的"sprite_nodes\CCSprite.cpp"中。打开 CCSprite 的代码文件,其中 draw 方法负责绘制精灵。针对不同的情况,可以采取不同的策略来降低 OpenGL 调用次数,从而大幅提高游戏性能。
(10)渲染树的绘制:无论如何复杂的游戏场景也都是精灵通过不同的层次、位置组合构成的,因此只要可以把精灵按照前后层次,在不同的位置绘制出来就完成了游戏场景的绘制。渲染树是由各种游戏元素按照层次关系构成的树结构,它展示了 Cocos2d-x 游戏的绘制层次,因此游戏的渲染顺序就是由渲染树决定的。游戏元素都在渲染树中表示为节点(CCNode),游戏元素的归属关系就转换为了节点间的归属关系。CCNode 的 visit 方法实现了对一棵渲染树的绘制。为了绘制树中的一个节点,就需要绘制自己的子节点,直到没有子节点可以绘制时再结束这个过程。绘制父节点时会引起子节点的绘制,同时,子节点的绘制方式与父节点的属性也有关。
(11)visit 方法调用了 transform 方法进行一系列变换,以便把自己以及子节点绘制到正确的位置上。为了理解transform 方法,我们首先从 draw 方法的含义开始解释。draw 方法负责把图形绘制出来,但是从上一节的学习可知,draw方法并不关心纹理绘制的位置,实际上它仅把纹理绘制到当前坐标系中的原点。为了把纹理绘制到正确的位置,我们需要在绘制之前调整当前坐标系,这个操作就由 transform 方法完成,经过变换后的坐标系恰好可以使纹理绘制到正确的位置。
(12)transform 方法:在绘制渲染树中,最关键的步骤之一就是进行坐标系的变换。没有坐标系的变换,则无法在正确的位置绘制出纹理。同时,坐标系的变换在其他的场合(例如碰撞检测中)也起着十分重要的作用。形象地讲,transform 方法的任务就是根据当前节点的属性计算出如何把绘图坐标系变换为新坐标系的矩阵。
(13)若我们需要判断一个点在另一坐标系下是否在同一个矩形之内,则可以把此点转换为世界坐标系,再从世界坐标系转换到目标坐标系中,此后只需要通过 contentSize 属性进行判断即可。
(14)Cocos2d-x 绘图流程:在游戏主循环中调用场景的 visit 方法,在此之中通过 transform 方法进行坐标系变换,然后递归调用子节点的 visit 方法,完成对整棵渲染树的绘制。Cocos2d-x 使用的一个开源几何计算库 Kazmath。
绘图优化
(1)游戏的帧率降低到一定的值,明显的延迟现象会导致极差的游戏体验。
绘图瓶颈:
- 纹理过小。
- 纹理切换次数过多。
- 纹理过大。
优化:
- 碎图压缩(图的大小恰好符合 OpenGL 的纹理规范,碎图合并工具:TexturePacker)与精灵框帧。
- 批量渲染:如果不需要切换绑定纹理,那么几个 OpenGL 的渲染请求是可以批量提交的(CCSpriteBatchNode),也就是说,在同一纹理下的绘制都可以一次提交完成。CCSpriteBatchNode 的使用方法也很简单,它是一个特殊的节点,我们只要把需要绘制的精灵添加为它的子节点,然后再把CCSpriteBatchNode 添加到层或场景之中即可。
- 色彩深度优化:适应低端设备cpu和显存不足的问题。
小结:
在 Cocos2d-x 2.0 中,引入了众多革命性的新特性,尤其在绘图机制上进行了大量的改进,使用了全新的 OpenGL ES 2.0绘图,支持可编程管线 shader 等。底层的绘图矩阵优化对开发者是透明的、显式的,我们可以使用更多特效实现更丰富的绘图效果。
OpenGL ES 是 OpenGL 三维图形 API 的子集,专门针对移动设备设计,其 1.0 版本是针对固定管线硬件的,而 2.0 版本已经扩展至支持可编程管线硬件。Cocos2d-x 2.0 正是将底层绘图从 OpenGL ES 1.0 升级到了 OpenGL ES 2.0。
第十一章 OpenGL绘图技巧
(1) 引擎在CCNode中为我们预留了自定义绘图接口:draw。可以重载此方法,最好仅在这个方法中绘制自定义的内容。
(2) draw 函数是一个每帧都会调用的函数,注释中明确指出,通常情况下我们只应该在这个函数内编写自定义绘制效果,不要在这个函数以外的任何地方绘图。这是因为引擎在这个函数调用的上下文间进行了绘图环境的准备和必要的状态设置。在其他地方添加绘制代码可能引起不可预料的错误。
(3) 快捷绘图接口:这些接口由"CCDrawingPrimitives.h"和对应的 cpp 文件提供。
(4) 实际上,"CCDrawingPrimitives.h"中封装的形如 ccDrawLine 的绘图函数,每调用一次都会引发 OpenGL 的渲染,但是只完成很少量的绘制。大量地调用绘图函数对于 GPU 来说是个不小的负担,因此这类绘图函数只适合在特定的情况下少量使用。
(5) 遮罩效果又称为剪刀效果,允许一切的渲染结果只在屏幕的一个指定区域显示:开启遮罩效果后,一切的绘制提交都是正常渲染的,但最终只有屏幕上的指定区域会被绘制。
数据交流
(1) 底层的数据交流必须介绍两个类:CCImage 和 CCTexture2D,这是引擎提供的描述纹理图片的类,也是我们和显卡进行数据交换时主要涉及的数据结构。
(2) CCImage 在"CCImage.h"中定义,表示一张加载到内存的纹理图片。在其内部的实现中,纹理以每个像素的颜色值保存在内存之中。CCImage 通常作为文件和显卡间数据交换的一个工具,因此主要提供了两个方面的功能:一方面是文件的加载与保存,另一方面是内存缓冲区的读写。我们可以使用 CCImage 轻松地读写图片文件。CCImage 也提供了读写内存的接口。注意,目前仅支持从内存中加载 RGBA8888 格式的图片。
(3) CCTexture2D:该类所包含的纹理大小必须是 2 的幂次,因此纹理的大小不一定就等于图片的大小;另外,有别于 CCImage,这是一张存在于显存中的纹理,实际上并不一定存在于内存中。我们使用 OpenGL 的一个底层函数 glReadPixels 实现截图。OpenGL的绘制是从上到下绘制像素点。
(4) 渲染纹理类CCRenderTexture:其作用是将绘图设备从屏幕转移到一张纹理上,从而使得一段连续的绘图被保存到纹理中。我们可以使用这个渲染纹理类配合主动调用的绘图实现截图效果。之所以说是"一小段",是因为图形渲染的执行周期非常短,不允许过于臃肿的程序,因此通常都比较简短。
可编程着色器:
(1)顶点着色器(vertex shader)。对每个顶点调用一次,完成顶点变换(投影变换和视图模型变换)、法线变换与规格化、纹理坐标生成、纹理坐标变换、光照、颜色材质应用等操作,并最终确定渲染区域。在 Cocos2d-x 的世界中,精灵和层等都是矩形,它们的一次渲染会调用 4 次顶点着色器。
(2)段着色器(fragment shader,又称片段着色器)。这个着色器会在每个像素被渲染的时候调用,也就是说,如果我们在屏幕上显示一张 320×480 的图片,那么像素着色器就会被调用 153 600 次。所幸,在显卡中通常存在不止一个图形处理单元,渲染的过程是并行化的,其渲染效率会比用串行的 cpu 执行高得多。
(3)这两个着色器不能单独使用,必须成对出现,这是因为顶点着色器会首先确定每一个显示到屏幕上的顶点的属性,然后这些顶点组成的区域被化分成一系列像素,这些像素的每一个都会调用一次段着色器,最后这些经过处理的像素显示在屏幕上,二者是协同工作的。
(4)引擎提供了 CCGLProgram 类来处理着色器相关操作。在 Cocos2d-x 游戏中导入自定义的着色器效果。
(5)OpenGL 接口的一个风格:对象(纹理、着色器程序或其他非标准类型)都是使用整型标识符来表示的。
(6)变量传递:《》P158.仅仅加载肯定是不够的,我们还需要给着色器传递运行时必要的输入数据。在着色器中存在两种输入数据,分别被标识为attribute 和 uniform。attribute 变量是应用程序直接传递给顶点着色器的变量,在段着色器中不能访问。uniform 变量是全局性的,可以同时在顶点着色器和段着色器中访问。这些变量都是只读的,不允许在渲染过程中改变。
(7)void glUniform1i(GLint location,GLint x);
上面的这个函数设置目标 uniform 变量的值为一个整型值。我们可以看到,OpenGL 的函数末尾总是紧接着类似"1i"和"3f"这样的后缀,这也是 OpenGL 函数的另一个特色。传值类函数可能会接受多种不同的参数,后缀中的数字从 1 到 4 分别对应标量和 2、3、4 维的向量。后缀中的另一个字符表示的是参数类型,常见的类型有"f"(float)、"i"(integer)、"b"(byte)等。这个做法可以避免类型转换,提高程序效率。
(8)对于 attribute 变量,直接使用会相对复杂一些,需要我们来处理变量的绑定与编号的管理等细节。
(9)两个调用时机的问题:
首先是绑定 attribute 变量的时机。CCGLProgram 在装载完毕后需要调用 link 函数连接着色器程序到显卡,绑定必须在连接之前,保证绑定能正确传递到显卡。
其次是设置开启数组绑定的时机。由于这是一个在不同渲染程序间共享的状态,会被不同的绘制反复开启关闭,所以必须在每次绘制时主动设置。
(10)在 Cocos2d-x 中,由于可编程管线的使用,每个 CCNode 都会附有一个着色器程序。我们完成加载自定义的着色器后,应该调用 setShaderProgram 设置到 CCNode 中。最后再次强调,应该在链接着色器程序之前绑定attribute 变量。
(11)attribute vec4 a_color; //水纹颜色
attribute vec4 a_position; //顶点坐标
uniform mat4 u_MVPMatrix; //坐标变换矩阵
uniform float time; //时间
uniform vec2 resolution; //分辨率
uniform sampler2D tex0; //背景纹理
(12)初始化着色器程序后,就应该准备需要传递的各个变量的值了。在两个 attribute 变量中,色彩直接通过设置函数设置,而顶点位置将直接在 draw 函数中传递。因此,我们先介绍如何准备 3 个 uniform 变量:时间、分辨率和背景纹理。
1. 时间只需要在更新函数内刷新
2. 分辨率是指当前效果覆盖区域的分辨率。
3. 背景纹理则可以认为是效果区域的截图,作为纹理被使用。
注:特别需要指出的是,这里的目标纹理并不是某一张纹理,而是已经激活的某一个特殊的纹理槽位,截取的绘制数据将存入绑定到该槽位的纹理中。在 Cocos2d-x 中,这个纹理槽位一般为 GL_TEXTURE_2D,即 2D 纹理槽位。《》163.
(13)ccGLBindTexture2D 这个 Cocos2d-x 的接口:这是 Cocos2d-x的统一绑定接口,其内部做了一些缓存优化,可以提高纹理绑定的效率。
(14)滥用 OpenGL 会使得代码变得混乱而难以维护,因此除非迫不得已,不应首先考虑 OpenGL。引擎封装了一个特殊的动作类 CCActionGrid3D,可以模拟一些简单的 3D 特效,在一些情况下可以代替 OpenGL。
(15)效率问题:引擎提供的封装效果在效率上是一定会有损失的。OpenGL ES 2.0 提供的可编程管线在效率上必然是弱于 OpenGL ES 1.0 的硬件渲染的,即便是实现同样的效果。
小结:
1.draw 方法:它是每个 CCNode 都提供的一个方法,用于在绘制节点的时候绘制自身内容。通常,所有的自定义绘图都应该在这个方法中完成。在绘制自定义内容的时候,需要重载 draw 方法,然后在 draw 方法中加入绘图代码。
2.glScissor:用于实现裁剪绘图。使用 glScissor 函数前,需要调用 glEnable (GL_SCISSOR_TEST)来启用裁剪,设置裁剪区域后所绘制的图形只有区域内的部分会被真正绘制,其余部分保持透明。
3.纹理图片:CCImage 与 CCTexture 分别代表一张图片和一个可载入到显存的纹理。CCTexture 可由 CCImage 创建,而 CCImage可以载入或保存 PNG、TIFF、JPG 等格式的文件。
4.着色器:着色器是用于代替渲染流水线的一段程序,可以用来实现对顶点和像素的操作。在这一章中,我们使用着色器实现了水纹效果。
5.CCGrid3D:Cocos2d-x 提供一套网格变换的功能,通过 CCActionGrid3D 动作类可以实现一些简单画面的 3D 变换,例如水纹效果。
第十二章 物理引擎
(1)物理引擎正是为这样的场景而生的。物理引擎充分考虑了游戏对象的物理性质,并尽可能精确地计算出物体之间的相互作用效果。通常,物理引擎会把游戏中出现的对象与一个物理模型绑定起来。模型包含了物体的形状、密度、材质和速度等属性,引擎会根据模型间的相互作用实时地改变模型属性,并借用游戏引擎的渲染环节把物体表现出来。
(2)Box2D 是一套基于刚体模拟的物理引擎,它的核心概念为世界、物体、形状、约束和关节,这些概念具体实现为 Box2D 的各个组件。
(3) 在游戏中,我们想要做的通常是赋予精灵物理性质,我们会为精灵创建 Box2D 物理模型,然而物理引擎运作起来后,为了把物理模型的运动体现在屏幕上,我们必须手动把物理模型的数据同步到精灵中。接入 Box2D 的每个精灵在物理世界中都有相关联的物理模型"物体",即 b2Body。
(4)Box2D 相关的头文件为"Box2D/Box2D.h"。和"cocos2d.h"一样,该头文件囊括了 Box2D 需要用到的所有头文件。Box2D 没有自己的命名空间,所以不需要相关的命名空间引用。
(5)Box2D 世界里的最佳物体大小:根据 Box2D参考手册,Box2D 针对大小为 0.1~10 个单位长度的物体作了优化,处理效率相对高一些。