Cocos2d基础及基本函数
1.基本概念:
Scene对象其实是所有层的容器,与Layer一起组织各节点的图像显示。除了Scene外所有节点只有一个父节点,可以有无数个子节点。但注意这里的父子关系并不是面象对象中的继承关系,仅仅是addChild的方式建立起来的父子。
CCDirector类存储了cocos2d中大量的全局配置信息,而且管理所有的cocos2d场景,它可以访问正在运行的场景,运行替换场景等等。共有四种导演类型,其中默认的是kCCDirectorTypeDisplayLink
Tips: 在为对象建分组和层时一般通用的CCNode类而不是CCLayer类,因为CCLayer会接收键盘等的输入,增加不必要的开销。
2.节点处理:
CCNode* childNode = [CCNode node];
[myNode addChild:childNode z:0 tag:123];
CCNode* retrievedNode = [myNode getChildByTag:123];
[myNode removeChildByTag:123 cleanUp:YES];
(cleanUp的意思是将节点从父节点中删除)
[myNode removeChild: retrievedNode];
[myNode removeAllChildrenWithCleanUp:YES];
[myNode removeFromParentAndCleanUp:YES];
3.动作的处理方式
1)动作声明
通过以下声明可以让节点闪烁
CCAction* action = [CCBlink actionWithDuartion:10blinks:20];
action.tag = 234;
[myNode runAction:action];
如果之后想取得动作,可以通过标记来获得:
CCAction* retrievedAction = [myNode getActionByTag: 234];
[myNode stopActionByTag:234];
[myNode stopAction: action];
[myNode stopAllActions];
2)消息调度
节点可以对消息进行调度,其实就是每段时间调用一次方法,如碰撞检测。要使特定的方法每帧调用一次,可以如下:
-(void) scheduleUpdateMethod
{
[self scheduleUpdate];
}
-(void) update: (ccTime)delta
{
//This method iscalled every frame;
}
参数delta表示方法自上次被调用到现在所经历过的时间。如果想以0.1秒的频率调用该方法可以如下:
-(void) scheduleUpdateMethod
{
[selfschedule:@selector (updateTenTimesPerSecond: ) interval:0.1f];
}
-(void) updateTenTimesPerSecond: (ccTime)delta
{
//This method iscalled according to its interval, ten times per second
}
Tips: 如果想用每帧更新,还是用scheduleUpdate好,反过来,如果在后面有可能要取消这个办法,还是用上面的方法,因为scheduleUpdate做不到。
3) @selector方法
在这里selector方法相当于是告诉编译器去找一个名为updateTenTimesPerSecond的方法,并用“:”来告诉这个函数只有一个参数,如果这个函数有多个参数时,就要用多个,千万别忘了:号,如:
-(void) example:(ccTime)delta sender:(id)senderflag:(bool)aBool
那么对应的selector语句应该是:
@selector(example:sender:flag:)
如果要取消用选择器调度的方法,可以用
[self unscheduleAllSelectors];
[self unscheduled:@selector(updateTenTimesPerSecond)];
但要注意,用这些方法不会使scheduleUpdate的调度更新方法停止。
这里注意,如果想以不同的时间来调度一个方法,可以用_cmd来简化工作,_cmd在所有的Ob-C中都可用,是当前方法的选择器。
-(void) scheduleUpdates
{
[selfschedule:@selector(irregularUpdate:) interval:1];
}
-(void) irregularUpdate: (ccTime)delta
{
[self unscheduled:_cmd];
float nextUpdate= CCRANDOM_0_1() * 10;
[selfschedule:_cmd interval:nextUpdate];
}
在更新中还有更新方法的优先级,可以用[selfscheduleUpdateWithPriority:1]来进行更新,数字越小更新等级越高。
3. CCScene类
CCDirector要求有派生自CCScene的类来进行runWithScene,replaceScene, pushScene等操作来修发当前场景,可以把CCScene封到一个派生自CCSceneTransition的类中,以便能够进行切换。
因为大多数时候场景对象本身不包含具体的游戏代码,而且基本不会从它派生类,所以一般都在CCLayer对象的“+(id)scene”方法中创建它们,如下:
+(id) scene
{
CCScene *scene =[CCScene node];
CCLayer *layer =[HelloWorld node];
[sceneaddChild:layer];
return scene;
}
然后可以在applicationDidFinishLaunching中结尾创建场景:
[[CCDirector sharedDirector] runWithScene: [HelloWorldscene]];
而在替换场景时,还可以使用过渡效果:
CCScene* scene = [HelloWorld scene];
CCSceneTransition* tran = [CCTransitionShrinkGrowtransitionWithDuration:2 scene: scene];
[[CCDirector sharedDirector] replaceScene: tran];
Tips:不能用self作为参数传给replaceScene以达到刷新场景的目的,会卡死的。
4. CCTransitionScene类
场景过渡就是任何从CCTransitionScene继承的类,如下例:
CCTransitionFade *tran = [CCTransitionFadetransitionWithDuration:1 scene:[HelloWorld scene] withColor:ccWHITE];
[[CCDirector sharedDirector] replaceScene: tran];
Tips: pushScene有过渡效果,但是popScene没有
4. CCLayer 类
有时候也许场景中的层不止一个,可以用如下方式来创建:
+(id) scene
{
CCScene* scene =[CCScene node];
CCLayer*backgroundLayer = [HelloWorldBackground node];
[scene addChild:backgroundLayer];
CCLayer* layer =[HelloWorld node];
[sceneaddChild:layer];
CCLayer*userInterfaceLayer = [HelloWorldUserInterface node];
[scene addChild:userInterfaceLayer];
return scene;
}
Tips: 可以在一个场景中用很多个层,但是注意不要让他们都能接收输入,这样会大大降低效率,一般只要一个层接收以后,再把消息传给别的层就好了。
5. 接收触摸事件
CCLayer启动可输入要用以下方式:
self.isTouchEnabled = YES;
这个部分是在init中完成的,但可以在任何地方进行修改,以下是调用的情况:
当单指接触到屏幕时被调用:
-(void) ccTouchesBegan: (NSSet *)touches withEvent: (UIEvent*)event
当手指在屏幕上移动时被调用:
-(void) ccTouchesMoved: (NSSet *)touches withEvent: (UIEvent*)event
当手指在离开时被调用:
-(void) ccTouchesEnded:(NSSet *)touches withEvent: (UIEvent*)event
当触摸取消时被调用(很少被用到):
-(void) ccTouchesCancelled: (NSSet *)touches withEvent:(UIEvent *)event
因为触摸事件都是由CocoaTouch API接收的,所以一定要把触摸位置转换成OpenGL坐标,用以下方法:
-(CGPoint) locationFromTouches: (NSSet *)touches
{
UITouch *touch =[touches anyObject];
CGPointtouchLocation = [touch locationInView: [touch view]];
Return[[CCDirector sharedDirector] convertToGL: touchLocation];
}
不过此方法仅对单指触摸有效,因为它使用了[touchesanyObject]。要追踪多指触摸的位置,你必须分别追踪每个触摸的位置。而cocos2d支持的是有针对性的触摸处理,它把多指触模简单的分割成了独立事件,这样可以更方便的根据游戏需求去处理。更重要的是,它可以把特定的触摸事件从队列中移除,可以指定一个触摸事件为已处理事件,并阻止它传给别的层。这样就可以把触摸集中在一个特写区域内,你就可以很快识别出来,处理完后标记为已处理,传给其它层时就不用测了。
通过向层中添加如下方法来启用有针对性的触摸处理:
-(void) registerWithTouchDispatcher
{
[[CCTouchDispatchersharedDispatcher] addTargetDelegate:self priority:INT_MIN+1swallowsTouches:YES];
}
默认与多点的不同之处在于,默认用的方法是以(NSSet*)touches作为第一个参数,而你要用的方法是以“(UITouch *)touches”作为第一个参数。
6.接收加速计事件
以下方法启用加速计事件,与触摸类似:
self.isAccelerometerEnabled =YES;
同样也得用如下方法来接收加速计事件:
-(void) accelerometer: (UIAccelerometer*) accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
CCLOG(@”acceleration: x:%f / y:%f / z:%f, acceleration.x,acceleration.y, acceleration.z);
}
7. CCSprite类
Cocos2d是通过图片来创建Sprite的
CCSprite* sprite = [CCSpritespriteWithFile:@”Default.png”];
[self addChild:sprite];
(iOS设备上文件是区分大小写的,但模拟器不区分,所以要注意因此引起的崩溃)
8. anchorPoint属性
每个Node都有anchorPoint属性,但是只有在节点要用纹理显示时才有作用,如CCSprite和CCLabelTTF,任何节点的anchorPoint属性都默认是(0.5,0.5),也就是纹理中心。(区间是0~1)
举例,下图因为其anchorPoint被设为0,0所以精灵正好可以完整显示在屏幕左下角:
CCSprite* sprite = [CCSpritespriteWithFile:@”Default.png”];
Sprite.anchorPoint =CGPointMake(0,0);
[self addChild:sprite];
(非常注意,现在iOS设备只支持尺寸为2的次幂的纹理,如果不是2次幂会被转换从而加大消耗)
8. CCLabelTTF类
用以下方式来显示文本:
CCLabelTTF* label =[CCLabelTTF labelWithString:@”text” fontName:@”AppleGothic” fontSize:32];
[self addChild:label];
在要重新发变文本时,用如下方法:
[label setString:@”new text”];
因为此文本是居中对齐的,如果要改就需要改锚点,如:
label.anchorPoint =CGPointMake(1, 0.5f);
9. 菜单
CCMenu是CCLayer的子类,只能接受CCMenuItem对象作为其子节点,其子类可以有CCMenuItemLabel(CCMenuItemAtlasFont, CCMenuItemFont), CCMenuItemSprite(CCMenuItemImage), CCMenuItemToggle
创建方法有:
CCMenuItemFont* item1 =[CCMenuItemFont itemFromString:@”Go Back!” target:selfselector:@selector(menuItem1Touched:)];
用Sprite创建方法有:
CCMenuItemSprite* item2 = [CCMenuItemSpriteitemFromNormalSprite:normal selectedSprite:selected target:selfselector:@selector(menuItem2Touched:)];
10. 动作
CCAction是一个轻量级的类,可以做移动,旋转等,有3种动作是从CCAction直接派生的:
CCFollow A节点跟随B节点
CCRepeatForever 无限的重复一个动作
CCSpeed 在一个动作运行时修发其更新频率
可以让一个节点跟随另一个节点:
[label runAction: [CCFollow actionWithTarget:playerSprite]];
像轮子不停转:
CCRotateBy* rotateBy = [CCRotateBy actionWithDuration:2angle:360];
CCRepeatForever* repeat = [CCRepeatForeveractionWithAction:rotateBy];
CCSpeed* speedAction = [CCSpeed actionWithAction:repeatspeed:0.5f];
speedAction.tag = 1;
[myNode runAction:repeat];
因为CCSpeed动作的speed属性被设为0.5f,现在节点转一圈所需时间为原来的两倍。要修改其速度,发CCSpeed的speed属性就行。
1)间隔动作
由于动作会执行一段时间,如转3秒钟,因此通常做法是写一个更新方法,并加一些变量来存中间结果。如CCActionInterval动作封装了这部分逻辑,并将之转化为了带参的方法:
CCMoveTo* move = [CCMoveTo actionWithDuration:3position:CGPointMake(100,200)];
[myNode runAction:move];
(在3秒内移动100,200)
但此移动不匀速,用如下方式实现匀速:
CGPoint targetPos = CGPointMake(100,200);
float speed = 10;
float duration = ccpDistance(myNode.position, targetPos)/speed;
CCMoveTo* move = [CCMoveTo actionWithDuration: durationposition:targetPos];
2)动作序列
通常情况下,一个节点加几个动作时,它们会在同一时间运行,如果想让他们一个接一个的运行就要用到CCSequence.如以下变色:
CCTintTo* tint1 = [CCTintTo actionWithDuration:4 red:255green:0 blue:0];
CCTintTo* tint2 = [CCTintTo actionWithDuration:4 red:0green:255 blue:0];
CCTintTo* tint3 = [CCTintTo actionWithDuration:4 red:0green:0 blue:255];
CCSequence* sequence = [CCSequence actions:tint1, tint2,tint3, nil];
(CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequence];)
([label runAction:repeat];)
[label runAction:sequence];
但一定要注意sequence最后一个一定是nil。
3)流畅动作
节点在3秒内移动至100,200
CCMoveTo* move = [CCMoveTo actionWithDuration:3position:CGPointMake(100, 200)];
CCEaseInOut* ease = [CCEaseInOut actionWithAction:moverate:4];
[myNode runAction:ease];