COCOS2D学习(第三章 基础知识).docx
《COCOS2D学习(第三章 基础知识).docx》由会员分享,可在线阅读,更多相关《COCOS2D学习(第三章 基础知识).docx(27页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第三章 基础知识本章将会为你介绍cocos2d游戏引擎的构成要素。你将会在每一个编写的游戏里 用到这些类,所以了解它们是些什么样的类和这些类是如何在一起工作的,将 会帮助你写出更好的游戏。有了这些知识,你会发现使用cocos2d很容易。配套本章的Xcode项目叫作 “Essentials”。它包含了所有在此讨论的内容, 外加一些额外的例子。源代码附带详尽的注释,所以读起来就像本书的附录。我们将以讨论cocos2d游戏引擎结构作为开始。每个游戏引擎在管理和呈现屏幕 上的游戏对象的方式都是不一样的。所以一开始我们就要了解有哪些元素存在 以及它们之间的关系。cocos2d的单例 cocos2d很好的
2、利用了单例设计模式。因为此模式经常引起争议,所以我想有必 要在此解释一下单例。原则上,单例是在程序生命周期里只被实例化过一次的 类。为了确保这一点,我们利用类的一个静态方法来生成和访问对象。因此, 你是通过以“shared”开头的方法来访问cocos2d的单例对象的,而不是用 alloc/init或者静态autorelease初始化方法。以下是一些最常用到的cocos2d 单例类和访问它们的方法:CCActionManager* sharedManager = CCActionManager sharedManager;CCDirector* sharedDirector = CCDirect
3、or sharedDirector;CCSpriteFrameCache* sharedCache = CCSpriteFrameCache sharedSpriteFrameCache; CCTextureCache* sharedTexCache = CCTextureCache sharedTextureCache; CCTouchDispatcher* sharedDispatcher = CCTouchDispatcher sharedDispatcher; CDAudioManager* sharedManager = CDAudioManager sharedManager; S
4、impleAudioEngine* sharedEngine = SimpleAudioEngine sharedEngine;单例的好处是它可以在任何时间任何地点被任何类所调用。它接近于全局类的 作用,更像一个全局变量。如果你需要在任何地方都能用到某些数据或者方法, 单例是很好的选择。音频就是个很好的例子:因为任何一个类,不管是玩家, 敌人,菜单按钮,或是过场动画,都可能需要播放声效或者改变背景音乐。因 此,使用单例来播放音频是很好的选择。同样,如果存在全局的游戏状态,比 如说玩家军队的大小和每支部队排的数目,你可以把这些信息存到一个单例中, 把这些信息从一个关卡传到另一个关卡。列表3-1演
5、示了如何实现单例。这些代 码使用了最少的代码实现了MyManager类的单例。SharedManager提供了访问 MyManager单一实例的静态方法。如果实例不存在,一个MyManager的实例将 会被分配和初始化;否则已经存在的实例会被返回。2列表3-2:MyManager类实现单例static MyManager *sharedManager = nil; +(MyManager*) sharedManagerif (sharedManager = nil) sharedManager = MyManager alloc init; return sharedManager; 不过,单
6、例也有不好的方面。因为单例很容易实现,而且可以在任何地方访问到,它们可能会被用在不该用的地方。例如,你可能觉得你的游戏只有一个玩家对象,所以为什么就不能把玩家这个 类变成单例呢?一切看起来都没有什么问题 直到你认识到不管什么时候这 个玩家进入下一个关卡,这个玩家不仅带着上一关卡的得分,而且还有上一关 卡的最后一帧动画信息,健康值,和所有已经捡到的物品,并且由于他在离开 上一关卡的时候还在“狂暴”状态下,在新关卡开始的时候,他还处于之前的 状态中。为了解决这个问题,你可能会在类里加入一个重置某些变量的方法。看起来问 题解决了。但是当你在游戏代码中添加越来越多的功能以后,在转换关卡时, 你需要维护
7、的变量也会越来越多。最糟糕的是,某天你的朋友建议你为iPad版 本增加个双人模式。但是你发现你的玩家类是个单例,在任何时候你只能有一 个玩家对象存在!这可麻烦了:要么你要重写很多代码,或者只能放弃很酷的 双人模式了。你越依赖于单例,类似的问题就会越多。在创建任何一个单例类之前,你都要考虑是否真的需要单例,是否需求会在不久的将来改变。The Director(导演) CCDirector类,简称Director(导演),是cocos2d游戏引擎的核心。如果你回想一下第二章的HelloWorld应用,你会记得有很多cocos2d的初始化过程包含了 CCDirector sharedDirector
8、的调用。Director是一个单例:它保存着 cocos2d的全局配置设定,同时管理着cocos2d的场景。Director的主要用处如下:1. 访问和改变场景2. 访问cocos2d的配置细节3. 访问视图(OpenGL,UIView,UIWindow) 4. 暂停,恢复和结束游戏5. 在UIKit和OpenGL之间转换坐标3实际上存在四种类型的Director。它们在细节上有所不同。最常用的Director 是CCDisplayLinkDirector,它的内部使用了苹果的CADisplayLink类。它是最 好的选择,但是只有在iOS 3.1以上的版本中才能使用。其次,你可以使用 CCF
9、astDirector。如果你想让Cocoa Touch视图和cocos2d一同工作,你必须转 到CCThreadedFastDirector,因为只有这个Director才能完全支持。 CCThreadedFastDirector不好的一面是:使用它会很耗电。最后的选择是 CCTimerDirector,但这是没有办法的选择,因为它是四种Director里面最慢的。场景图(The Scene Graph) 有时候又被称为“场景层级”。场景图是由所有目前活跃的cocos2d节点所组成的一个层级图。除了场景本身,每一个节点只有一个父节点,但是可以有任意数量的子节点。当你将节点添加到其它节点中时,
10、你就在构建一个节点场景图。图3-1描绘了一 个虚构的游戏场景图。在最上面,你总是放置场景节点(MyScene),通常跟着的 是一个层节点(MyLayer)。在cocos2d里,层节点的作用是接收触摸和加速计 的输入。图3-1. 一个简化的由多个不同节点组成的cocos2d场景图。场景图中有一个玩家节点和他的武 器节点,游戏的得分,和游戏中用于暂停和改变游戏选项的菜单。在CCLayer下一层的是你游戏的组成要素,它们大多数是精灵(sprite)节点。 它们包括用于显示游戏得分的标签节点,用于显示游戏内菜单的菜单和菜单项 目节点,玩家用这些菜单来暂停游戏或者回去主菜单。在图3-1中你会注意到Pla
11、yerSprite节点中有个子节点PlayerWeaponSprite。换 句话说,PlayerWeaponSprite是附加在PlayerSprite上的。如果PlayerSprite 移动,旋转或放大缩小,PlayerWeaponSprite将会跟着做同样的事情而不需要 额外的代码。这就是场景图的强大之处:你对一个节点施加的影响将会影响到 它的所有子节点。但是有时候这也会产生混淆,因为像位置和旋转都是相对于 父节点来说的。我写了一个叫作“NodeHierarchy”的Xcode样例,你可以在本书提供的源代码4里找到。它演示了在一个层级关系里的节点是如何相互影响的。我想实际的例子比用文字和图
12、片说明要来的更直观和容易理解。CCNode类的层级. 所有节点都有一个共同的父类:CCNode。它定义了许多除显示节点外的通用的属性和方法。图3-2展示了继承自CCNode的一些最重要的类。这些类是你最常用 到的。其实即使你只用这些类,你也可以创造出很有意思的游戏。图3-2. CCNode是cocos2d中最重要的类。所有类都继承自CCNode。CCNode定义了通用的属性和 方法。CCNode CCNode是所有节点的基类。它是一个抽象类,没有视觉表现。它定义了所有节 点都通用的属性和方法。使用节点 CCNode类实现了所有添加,获取和删除子节点的方法。以下是一些处理子节点 的方法:1. 生
13、成一个新的节点 : CCNode* childNode = CCNode node;2.3. 将新节点添加为子节点:myNode addChild:childNode z:0 tag:123; 4. 获取子节点:CCNode* retrievedNode = myNode getChildByTag:123; 5. 通过tag删除子节点;cleanup会停止任何运行中的动作: myNode removeChildByTag:123 cleanup:YES; 6. 通过节点指针删除节点:myNode removeChild:retrievedNode; 566. 删除一个节点的所有子节点:myN
14、ode removeAllChildrenWithCleanup:YES; 7. 从myNode的父节点删除myNode:myNode removeFromParentAndCleanup:YES; addChild中的z参数决定了节点的绘制顺序。拥有最小z值的节点会首先被绘制; 拥有最大z值的节点最后一个被绘制。如果多个节点拥有相同的z值,他们的绘 制顺序将由他们的添加顺序来决定。当然,这个规则只适用于像sprites那样有 视觉表现的节点。tag参数允许你通过getChildByTag方法来获取指定的节点。注:如果有多个节点拥有相同的tag数值,getChildByTag将把找到的第一个节
15、点返回。其它节点将不能够再被访问。所以你要确保为你的节点指定独有的tag数值。动作(Actions)也有tag。不过,节点和动作的tag不会冲突,所以拥有相同tag数值的动作和节点可以和平共处。使用动作(Actions) 节点可以运行动作。我会在以后多讲一些动作相关的知识。现在你只要知道动 作可以让节点移动,旋转和缩放,还可以让节点做一些其它的事情。1. 以下是一个动作的声明:CCAction* action = CCBlink actionWithDuration:10 blinks:20; action.tag = 234; 2. 运行这个动作会让节点闪烁: myNode runActio
16、n:action; 3. 如果你想在以后使用此动作,你可以用tag获取:CCAction* retrievedAction = myNode getActionByTag:234; 4. 你可以用tag停止相关联的动作: myNode stopActionByTag:234; 5. 或者你也可以用动作指针停止动作: myNode stopAction:action; 6. 你可以停止所有在此节点上运行的动作: myNode stopAllActions; 7预定信息 节点可以预定信息,其实就是Objective-C里面的每隔一段时间调用一次方法。 在很多情况下,你需要节点调用指定的更新方法以处
17、理某些情况,比如说碰撞 测试。以下是一个最简单的,可以在每一帧都被调用的更新方法:-(void) scheduleUpdates self scheduleUpdate;-(void) update:(ccTime)delta/ 此方法每一帧都会被调用很简单不是吗?你会注意到我们现在的更新方法是固定的,每一帧都会调用上 述方法。delta这个参数表示的是此方法的最后一次调用到现在所经过的时间。 如果你想每一帧都调用相同的更新方法,上述做法很适用。不过有时候你需要 用到更灵活的更新方法。如果你想运行不同的方法,或者是每秒调用10次更新方法的话,你应该使用以 下代码:-(void) schedul
18、eUpdates self schedule:selector(updateTenTimesPerSecond:) interval:0.1f;-(void) updateTenTimesPerSecond:(ccTime)delta/ 此方法将根据时间间隔来调用,每秒10次如果时间间隔(interval)为0的话,你应该使用scheduleUpdate方法。不过, 如果你想之后停止对某个指定更新方法的预定信息的话,上述代码更加合适。 因为scheduleUpdate方法没有停止预定信息的功能。更新方法的签名和之前是一样的:delta时间是它唯一的参数。但是这次你可以 使用任何名称,而且它会每
19、十分之一秒被调用一次。如果你不想每一帧都判断 是否达到了胜利的条件(有可能判断的过程很复杂),每秒调用10次更新方法 会比每帧都调用要好。或者,你想让代码在10分钟以后调用运行一个动作,你 可以将时间间隔(interval)设置为600。selector(.)里的冒号数量必须和方法的参数数量和名称相匹配。看一下以注:selector(.)这个语法看起来有点怪。这是Objective-C用来参照指定方法的方式。这里很重要的一点是最后的那个冒号。它告诉Objective-C去找在此指定的方法名,并且此方法只有一个参数。如果你忘记在最后加上冒号,程序 还是会继续编译,但是之后会崩溃。在调试控制台(D
20、ebugger Console)里,你会看到这样的错误日志:“unrecognized selector sent to instance .”。8下方法:-(void) example:(ccTime)delta sender:(id)sender flag:(bool)aBool相对应的selector应该是:selector(example:sender:flag:)通过你自己的选择器(selector)或者用selector(.)关键词的方式来预定更新方法会有一个很大的问题。默认情况下,如果方法名不存在的话,编译器 并不会报错,而是在方法被调用时直接导致程序崩溃。因为调用是在cocos
21、2d内 部进行的,所以很难发现问题的根源。幸运的是,有一个相关的编译器报警设 置可以使用。图3-3显示“Undeclared Selector”设置已被勾选,示例项目 “Essentials”里的这项设置也已被启用。图2-3:启用构建设置(Build Setting)中的“Undeclared Selector”设置接下去我们讨论如何停止对预定方法的调用。以下代码会停止节点的所有选择器,包括那些已经在scheduleUpdate里面设置 了预定的选择器:self unscheduleAllSelectors;以下代码会停止某个指定的选择器(假设选择器名称是updateTenTimesPerSe
22、cond): self unschedule:selector(updateTenTimesPerSecond:);注:此方法不会停止scheduleUpdate中设置的预定更新方法。还有一个挺有用的设置和停止选择器预定的方法。很多时候你需要在设置好的 预定方法里面停止调用某个指定的方法,同时因为参数和方法名可能发生变化, 你又不想重复相同的方法名和参数,这时你可以用以下的方法设置(预定的控 制器只会运行一次):-(void) scheduleUpdates self schedule:selector(tenMinutesElapsed:) interval:600;-(void) tenM
23、inutesElapsed:(ccTime)delta/ 用_cmd关键词停止当前方法的预定 self unschedule:_cmd;_cmd关键词是当前方法的缩写。上述代码只会让tenMinutesElapsed方法运行一 次。实际上你也可以用_cmd来设置方法调用的预定。假设你需要调用一个方法, 这个方法会使用不同的时间间隔来调用,每次方法被调用以后,时间间隔都会 发生变化。你的代码看起来会是像下面这样:-(void) scheduleUpdates / 像之前一样预定第一次更新self schedule:selector(irregularUpdate:) interval:1; -(
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- COCOS2D学习第三章 基础知识 COCOS2D 学习 第三 基础知识
限制150内