通过对CCNode和CCAction的介绍以及上一章的例子,相信看过前几章的读者已经对cocos2d有了大体的了解(没有的话我只能去面壁了= =。。。),但是之前的代码除了这两个类,还涉及到之前我们没有接触过的一些知识,那么这一次我就针对这些做个补充,完整地讲一下cocos2d的工作体系。
CCScene
之前也提到过,CCScene是场景类,它相当于一个大容器,将包含在内的层和精灵输出到屏幕上,是整个树的根节点。其实CCScene的内部构成非常简单,虽然继承自CCNode,但没有在它的基础上增加任何成员变量和方法,只是重构了init。由此可以看出,其实CCScene并没有屏显的作用,其实它的作用只是承上启下,之前说过,节点只有被加到树中才会更新逻辑以及绘制,绘制的方法visit是节点实现的,场景只是把节点添加到树中使其可以执行该函数,然后导演类激活场景实例,使它构成的树生效(树可以有多个,但只有导演类激活的树才有效,在cocos2d中导演类最多只能激活一个树)。
-(id) init
导演类只重构了CCNode的一个方法,就是init,它用setContentSize方法将屏幕的尺寸传递给场景,使其默认和屏幕一样大,将锚点设置为(0.5, 0.5)并将其锁定。
CCLayer
层的作用主要有两个:一是容纳精灵等节点,使它们被包含进场景(层的父节点肯定是场景,因为导演类只能和场景实例相关联);二是接收用户的输入操作,这里cocos2d打了一个标签,分别实现iphone和mac的处理逻辑,因为二者的输入方式有差异,iphone支持重力感应,而mac支持鼠标和键盘。(由于我们研究的主要是移动开发,所以暂时只讲iphone的逻辑)
CCLayer声明了两个参数:
BOOL isTouchEnabled_
很好理解,是否支持触摸响应。
BOOL isAcceleromterEnabled_
是否支持重力感应。
-(id) init
重构的初始化方法,和CCScene一样,设置了锚点和宽高(和屏幕一样),此外还将isTouchEnabled_和isAcceleromterEnabled_置为NO。
-(void) registerWithTouchDispatcher
将自己注册进CCTouchDispatch,这是管理触摸信息的类,在该类注册的对象才有被分发到触摸信息的可能,具体原理在后面的章节中会单独介绍,现在我们只要会使用层就可以了。当层可以被触摸时该方法会自动执行,所以开发者也不用对它太过纠结。
-(BOOL) isAccelerometerEnabled
返回对象的isAcceleromterEnabled_,即检测该层是否支持重力感应。
-(void) setIsAccelerometerEnabled: (BOOL)enabled
设置对象的的isAcceleromterEnabled_,使其允许或拒绝对重力的感应。
-(BOOL) isTouchEnabled
返回对象的isTouchEnabled_,即检测该层是否支持触摸响应。
-(void) set isTouchEnabled: (BOOL)enabled
设置对象的的isTouchEnabled_,使其允许或拒绝对触摸的响应。如果允许,会向
CCTouchDispatch注册,如果不允许则注销。
-(void) onEnter
在CCNode的基础上添加了一个判断,如果支持触摸则执行registerWithTouchDispatcher。
-(void) onEnterTransitionDidFinish
只在父类基础上增加了一个功能,就是当对象支持重力感应的时候,设置自己为UIAccelerometer单例的代理,此时当终端发生晃动时,该对象会被回调,但回调函数- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration并未声明,需要开发者自行扩展。
-(void) onExit
就是在父类的基础上,增加了对CCTouchDispatcher的注销和取消对UIAccelerometer的代理(如果有触摸和重力感应的话)
CCLayerColor
CCLayer的子类,在CCLayer的基础上增加了绘制颜色的功能。
CCLayerColor新增的属性:
GLubyte opacity
GLubyte就是一个无符号的byte,它的取值范围在0到255之间。opacity表示层的透明度,0为全透明,255为不透明。
ccColor3B color_
ccColor3B是个结构体,三个成员r、g、b都是GLubyte类型,分别表示层的红、绿、蓝色值。
ccVerter2F squareVertices_[4]
ccVerter2F其实和CGPoint一样,都是描述坐标点的结构体,squareVertices_是一个长度为4的数组,分别存储对象四个点的坐标(position_和contentSize可以组成一个矩形),作为openGL绘制颜色的参数。
ccColor4F squareColors_[4]
ccColor4F是个结构体,由r、g、b、a四个参数组成(a是透明度,即alpha),但和ccColor3B不同的是,它们都是浮点数,取值范围为0~1(1就相当于GLubyte的255)。squareColors_的作用是充当openGL绘制颜色的参数,因为GL的API需要浮点数,所以ccColor3B不能直接用于绘制,当层的颜色发生变化时,squareColors_会根据color_和opacity自动换算。
ccBlendFunc blendFunc_
ccBlendFunc是一个封装了两个无符号int的结构体,保存的是gl渲染图片的方式,CCLayerColor的渲染方式是固定的,因此对渲染参数的种类不用太过在意,CCLayerColor中用的是GL_SRC_ALPHA和GL_ONE_MINUS_SRC_ALPHA,这种方式融合出的图像亮度不会改变。至于一共有多少种效果,由于参数类型太多,作者也没有一一研究过,总之这丝毫不会影响我们对cocos2d的使用,所以读者也不必太过纠结。
+(id) layerWithColor: (ccColor4B)color width: (GLfloat)w height: (GLfloat)h
创建一个CCLayerColor实例并返回,可以设置颜色和宽高,其实就是封装了alloc、initWithColor和aurorelease方法。
+(id) layerWithColor: (ccColor4B)color
缺省了宽高的创建实例方法,宽高默认和屏幕尺寸相同。
-(id) init
缺省了颜色和宽高的初始化,所有参数默认均为0。
-(id) initWithColor: (ccColor4B)color width: (GLfloat)w height: (GLfloat)h
CCLayerColor标准的初始化,做的工作有:将color的rgb存到color_中,a存到opacity中;将blendFunc_的值置为{GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};将color换算成0~1之间的浮点数并赋给squareColors_;将w和h赋给contentSize_,并换算出squareVertices_的值。
-(id) initWithColor: (ccColor4B)color
缺省了宽高的初始化,宽高默认和屏幕尺寸相同。
-(void) setContentSize: (CGSize)size
除了父类的逻辑外,还将squareVertices_的坐标换算出来。
-(void) changeWidth: (GLfloat)w height: (GLfloat)h
作用和setContentSize一样,设置层的宽高,只是参数的类型换了一下。
-(void) changeWidth: (GLfloat)w
改变层的宽度。
-(void) changeHeight: (GLfloat)h
改变层的高度。
-(void) updateColor
将color_和opacity的值换算成浮点数并赋给squareColors_。
-(void) setColor: (ccColor3B)color
给color_赋值,同时调用updateColor进行换算。
-(void) setOpacity: (GLubyte)o
给opacity赋值,即设置透明度,同时调用updateColor方法。
CCLayerGradient
继承自CCLayerColor,增加了颜色渐变功能,CCLayerColor只能显示一种单一的颜色,而CCLayerGradient可以实现在两个颜色间逐渐过度的效果。因为有两种颜色,因此参数也会相应增加,但功能和CCLayerColor大同小异,就不赘述了,下面说一下方法:
-(void) initWithColor: (ccColor4B)start fadingTo: (ccColor4B)end
初始化方法,比CCLayerColor多了一个参数,因为CCLayerGradient是在两个颜色之间渐变。默认为start在正上,end在正下。
-(void) initWithColor: (ccColor4B)start fadingTo: (ccColor4B)end alongVector: (CGPoint)v
可以自由设置颜色渐变方向的初始化,当v的值为(0, -1)时(上一个方法的缺省值),表示颜色自上至下过度(start -> end),值(0, 1)表示自下至上过度,(1, 0)表示自左至右过度,(-1, 0)表示自右至左过度。1表示颜色自屏幕一侧过度到另一侧才会完全由start变为end,如果是0.5的话,那么到了屏幕中间就会变为end的色值。
-(ccColor3B) startColor
返回对象的color_,即获取起始点的色值。
-(void) setStartColor: (ccColor3B)colors
设置起始点色值。
-(void) setEndColor: (ccColor3B)colors
设置终点色值。
-(void) setStartOpacity: (GLubyte)o
设置起始点透明度。
-(void) setEndOpacity: (GLubyte)o
设置起始点透明度。
-(void) setVector: (CGPoint)v
设置渐变方向。
CCLayerMultiplex
就是一个层的集合,它可以涵盖N个层,但一次只能激活一个。
+(id) layerWithLayers: (CCLayer*)layer, …
创建一个CCLayerMultiplex对象并返回,即封装了alloc、initWithLayers和autorelease方法,参数为组成该对象的层集合。
-(id) initWithLayers: (CCLayer*)layer vaList: (va_list)params
初始化方法,将包含的曾添加到对象的成员队列中,但只有被激活的层(默认为第0个)会用addChild方法加为对象的子节点。
-(void) switchTo: (unsigned int)n
切换当前激活的层,n是索引值,用来在队列中检索待激活的层。
-(void) switchToAndReleaseMe: (unsigned int)n
切换当前激活的层,并将取消激活的层从队列中删除。
CCDirector
导演类也是一个单例,有两大作用,一是设置主窗口的显示属性(比如垂直或水平、是否显示FPS),二是管理、显示场景。
之前提到过,场景是树的根节点,一个游戏可以同时存在多个树,但被激活的只能有一个,导演类的实现方法是:将所有树的根节点(即场景对象)保存在一个堆栈中(array),最后进栈的就是当前激活的场景,当需要切换场景时,导演通过进栈、出栈或替换的方式实现。
常用方法:
+( CCDirector) shared CCDirector
获取单例。
-(void) runWithScene: (CCScene*)scene
将场景对象压入栈中,并激活(此时不能有已经激活的场景,因此该方法常用于刚进游戏时)。
-(void) replaceScene: (CCScene*)scene
替换当前激活的场景,同时将旧场景从堆栈中删除,新场景压入堆栈。
-(void) pushScene: (CCScene*) scene
添加新的场景进栈,并将其激活。
-(void) popScene
当前激活的场景出栈,激活上一个场景。
-(void) pause
暂停。
-(void) resume
恢复。
有了这些知识,我们已经可以写一些小功能了,那么就让我再写个例子,抛砖引玉吧~(下面的项目会用到前几章的知识)
这次我们做两个场景,让导演在它们之间切换,下面是第一个场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
-(id) init { if (self = [super init]) { //创建一个层,颜色为天蓝色 CCLayerColor* layer = [CCLayerColor layerWithColor:ccc4(100, 120, 255, 255)]; //以yz.png为资源创建一个精灵 CCSprite* sprite = [CCSprite spriteWithFile:@"yz.PNG"]; //将精灵和层加到场景中 [layer addChild:sprite z:0 tag:1]; [self addChild:layer z:0 tag:1]; //设置精灵的行为 [self setAction:sprite]; } return self; } -(void) setAction:(CCSprite*)sprite { //瞬间移动 id place = [CCPlace actionWithPosition:CGPointMake(0, 240)]; //缓慢右移 id move = [CCMoveTo actionWithDuration:10 position:CGPointMake(320, 240)]; //等待1秒 id delay0 = [CCDelayTime actionWithDuration:1]; //左右翻转 id flipX = [CCFlipX actionWithFlipX:YES]; id delay1 = [CCDelayTime actionWithDuration:1]; //上下翻转 id flipY = [CCFlipY actionWithFlipY:YES]; id delay2 = [CCDelayTime actionWithDuration:1]; //旋转180度 id rotate = [CCRotateBy actionWithDuration:3 angle:180]; //改变色值 id tint = [CCTintTo actionWithDuration:2 red:200 green:80 blue:120]; id delay3 = [CCDelayTime actionWithDuration:1]; //调用setSpriteOpacity函数 id alpha = [CCCallFunc actionWithTarget:self selector:@selector(setSpriteOpacity)]; //调用changeScene函数 id end = [CCCallFunc actionWithTarget:self selector:@selector(changeScene)]; id spawn0 = [CCSpawn actions:rotate, tint, nil]; id queue0 = [CCSequence actions:delay0, flipX, delay1, flipY, delay2, spawn0, delay3, alpha, nil]; id spawn1 = [CCSpawn actions:move, queue0, nil]; id queue1 = [CCSequence actions:place, spawn1, end, nil]; [sprite runAction:queue1]; } -(void) setSpriteOpacity { //将精灵的透明度设为150 CCSprite* sprite = (CCSprite*)[[self getChildByTag:1] getChildByTag:1]; [sprite setOpacity:150]; } -(void) changeScene { //进入MOMO场景,本场景并没有消失,而是暂停行为 [[CCDirector sharedDirector] pushScene:[MOMO scene]]; } |
这是第二个场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
-(id) init { if (self = [super init]) { ccColor4B red = ccc4(255, 0, 0, 255); // ccColor4B green = ccc4(0, 255, 0, 255); ccColor4B blue = ccc4(0, 0, 255, 255); //创建颜色渐变的层 CCLayerGradient* layer = [CCLayerGradient layerWithColor:red fadingTo:blue alongVector:CGPointMake(0, 1)]; m_spriteKyo = [CCSprite spriteWithFile:@"kyo.png"]; //设置精灵的坐标 [m_spriteKyo setPosition:CGPointMake(0, 160)]; //设置精灵的锚点 [m_spriteKyo setAnchorPoint:CGPointMake(0.5f, 0)]; [layer addChild:m_spriteKyo]; [self addChild:layer z:0 tag:1]; //设置精灵的行为 [self setAction]; //启动定时任务,调用display函数 [self schedule:@selector(display) interval:0.1f]; } return self; } -(void) setAction { ccBezierConfig bezier; //设置贝赛尔曲线的轨迹 bezier.controlPoint_1 = CGPointMake(80, 100); bezier.controlPoint_2 = CGPointMake(240, 100); bezier.endPosition = CGPointMake(320, 0); //贝塞尔曲线 id move = [CCBezierBy actionWithDuration:8 bezier:bezier]; //改变透明度 id fade0 = [CCFadeTo actionWithDuration:2 opacity:50]; id fade1 = [CCFadeTo actionWithDuration:2 opacity:255]; //缩放 id zoom0 = [CCScaleTo actionWithDuration:1 scale:1.5f]; id zoom1 = [CCScaleTo actionWithDuration:1 scale:0.75f]; id zoom2 = [CCScaleTo actionWithDuration:1 scale:1]; //调用changeScene函数 id end = [CCCallFunc actionWithTarget:self selector:@selector(changeScene)]; id queue = [CCSequence actions:fade0, fade1, zoom0, zoom1, zoom2, nil]; id spawn = [CCSpawn actions:move, queue, nil]; [m_spriteKyo runAction:[CCSequence actions:spawn, end, nil]]; } -(void) display { //设置是否可见 [m_spriteKyo setVisible:![m_spriteKyo visible]]; } -(void) changeScene { //返回上一场景 [self unschedule:@selector(diplay)]; [[CCDirector sharedDirector] popScene]; } |
效果图
- 本文固定链接: https://www.xuanyusong.com/archives/1240
- 转载请注明: 失落的宇宙 于 雨松MOMO程序研究院 发表
Thinking of consistently…
Thanks, I?ve recently been looking for info approximately this subject for a long time and yours is the best I have found out so far. But, what about the conclusion? Are you certain in regards to the source?…
写得真好,为啥不接着写呢
谢谢支持哈,我肯定会继续写的,只是最近一段确实太忙了,等忙过这段一定继续写,希望你能继续关注~
宇宙的分享 给力!!!
谢谢宇宙的分享 不过我能提个建议吗? 例子能不能使用Eclipse来写?或者VS也好吧
来顶…… 拳皇! 宇宙宇宙,爆发的小宇宙!奋斗