zoukankan      html  css  js  c++  java
  • cocos2d 学习 第三章

    1、cocos2d很好的利用了单例设计模式 

    以下是一些最常用到的cocos2d 单例类和访问它们的方法:

    CCActionManager* sharedManager = [CCActionManager sharedManager];

    CCDirector* sharedDirector = [CCDirector sharedDirector];

    CCSpriteFrameCache* sharedCache = [CCSpriteFrameCache sharedSpriteFrameCache];

    CCTextureCache* sharedTexCache = [CCTextureCache sharedTextureCache];

    CCTouchDispatcher* sharedDispatcher = [CCTouchDispatcher sharedDispatcher];

    CDAudioManager* sharedManager = [CDAudioManager sharedManager];

    SimpleAudioEngine* sharedEngine = [SimpleAudioEngine sharedEngine]; 

    单例的好处是它可以在任何时间任何地点被任何类所调用。它接近于全局类的 作用,更像一个全局变量。 如果你需要在任何地方都能用到某些数据或者方法, 单例是很好的选择。 

    static MyManager *sharedManager = nil;

    +(MyManager*) sharedManager
    {

      if (sharedManager == nil)

      {

        sharedManager = [[MyManager alloc] init];

      }

      return sharedManager;

    2、CCDirector类,简称Director(导演),是cocos2d游戏引擎的核心。 Director是一个单例:它保存着 cocos2d的全局配置设定,同时管理着cocos2d的场景。 

    Director的主要用处如下:
    1. 访问和改变场景
    2. 访问cocos2d的配置细节
    3. 访问视图(OpenGL,UIView,UIWindow)

    4. 暂停,恢复和结束游戏

    5. 在UIKit和OpenGL之间转换坐标 

    存在四种类型的Director

    最常用的Director是CCDisplayLinkDirector,它的内部使用了苹果的CADisplayLink类。它是最好的选择,但是只有在iOS 3.1以上的版本中才能使用。

    其次,你可以使用 CCFastDirector

    如果你想让Cocoa Touch视图和cocos2d一同工作,你必须转 到CCThreadedFastDirector,因为只有这个Director才能完全支持。 CCThreadedFastDirector不好的一面是:使用它会很耗电。

    最后的选择是 CCTimerDirector,但这是没有办法的选择,因为它是四种Director里面最慢的。

    3、场景图 

    场景图是由所有目前活跃的cocos2d节点所组成的一个层级图。除了场景本身,每一个节点只有一个父节点,但是可以有任意数量的子节点。当你将节点添加到其它节点中时,你就在构建一个节点场景图。 

    图3-1. 一个简化的由多个不同节点组成的cocos2d场景图。场景图中有一个玩家节点和他的武 器节点,游戏的得分,和游戏中用于暂停和改变游戏选项的菜单。 

    当你将节点添加到其它节点中时,你就在构建一个节点场景图。图3-1描绘了一 个虚构的游戏场景图。在最上面,你总是放置场景节点(MyScene),通常跟着的是一个层节点(MyLayer)。在cocos2d里,层节点的作用是接收触摸和加速计的输入。 

    在图3-1中你会注意到PlayerSprite节点中有个子节点PlayerWeaponSprite。换 句话说,PlayerWeaponSprite是附加在PlayerSprite上的。如果PlayerSprite 移动,旋转或放大缩小,PlayerWeaponSprite将会跟着做同样的事情而不需要 额外的代码。这就是场景图的强大之处:你对一个节点施加的影响将会影响到 它的所有子节点。但是有时候这也会产生混淆,因为像位置和旋转都是相对于 父节点来说的。 

     

    4、CCNode类的层级

    所有节点都有一个共同的父类:CCNode。

    CCNode是所有节点的基类。它是一个抽象类,没有视觉表现。它定义了所有节 点都通用的属性和方法。 

     

    图3-2. CCNode是cocos2d中最重要的类。所有类都继承自CCNode。CCNode定义了通用的属性和 方法。 

    CCNode类实现了所有添加,获取和删除子节点的方法。 

    1. 生成一个新的节点 :

         CCNode* childNode = [CCNode node];
      
    2. 将新节点添加为子节点:
      [myNode addChild:childNode z:0 tag:123];

    3. 获取子节点:
      CCNode* retrievedNode = [myNode getChildByTag:123];

    4. 通过tag删除子节点;cleanup会停止任何运行中的动作: [myNode removeChildByTag:123 cleanup:YES];

    5. 通过节点指针删除节点:
      [myNode removeChild:retrievedNode];

    1. 删除一个节点的所有子节点:
      [myNode removeAllChildrenWithCleanup:YES];

    2. 从myNode的父节点删除myNode:
      [myNode removeFromParentAndCleanup:YES];

    addChild中的z参数决定了节点的绘制顺序。拥有最小z值的节点会首先被绘制; 拥有最大z值的节点最后一个被绘制。如果多个节点拥有相同的z值,他们的绘 制顺序将由他们的添加顺序来决定。当然,这个规则只适用于像sprites那样有 视觉表现的节点。 

    5、使用动作(Actions) 

    节点可以运行动作。现在你只要知道动作可以让节点移动,旋转和缩放,还可以让节点做一些其它的事情。 

    1. 以下是一个动作的声明:
      CCAction* action = [CCBlink actionWithDuration:10 blinks:20];
      action.tag = 234;

    2. 运行这个动作会让节点闪烁:
      [myNode runAction:action];

    3. 如果你想在以后使用此动作,你可以用tag获取:
      CCAction* retrievedAction = [myNode getActionByTag:234];

    4. 你可以用tag停止相关联的动作: [myNode stopActionByTag:234];

    5. 或者你也可以用动作指针停止动作: [myNode stopAction:action];

    6. 你可以停止所有在此节点上运行的动作: [myNode stopAllActions]; 

    6、预定信息

    节点可以预定信息,其实就是Objective-C里面的每隔一段时间调用一次方法。 在很多情况下,你需要节点调用指定的更新方法以处理某些情况,比如说碰撞测试。

    以下是一个最简单的,可以在每一帧都被调用的更新方法:

    -(void) scheduleUpdates
    {
    
           [self scheduleUpdate];
    
    }
    -(void) update:(ccTime)delta
    {
    

        // 此方法每一帧都会被调用

      } 

    delta这个参数表示的是此方法的最后一次调用到现在所经过的时间。 如果你想每一帧都调用相同的更新方法,上述做法很适用。 

    如果你想运行不同的方法,或者是每秒调用10次更新方法的话,你应该使用以下代码:
    -(void) scheduleUpdates
    {

           [self schedule:@selector(updateTenTimesPerSecond:) interval:0.1f];
    }
    -(void) updateTenTimesPerSecond:(ccTime)delta
    {
    

        // 此方法将根据时间间隔来调用,每秒10次

     

    以下代码会停止节点的所有选择器,包括那些已经在scheduleUpdate里面设置 了预定的选择器:
    [self unscheduleAllSelectors];

    以下代码会停止某个指定的选择器

    [self unschedule:@selector(updateTenTimesPerSecond:)]; 

    很多时候你需要在设置好的预定方法里面停止调用某个指定的方法,同时因为参数和方法名可能发生变化, 你又不想重复相同的方法名和参数,这时你可以用以下的方法设置(预定的控制器只会运行一次):

    -(void) scheduleUpdates
    {
    
           [self schedule:@selector(tenMinutesElapsed:) interval:600];
    
    }
    -(void) tenMinutesElapsed:(ccTime)delta
    {
    

      // 用_cmd关键词停止当前方法的预定

      [self unschedule:_cmd];

    上述代码只会让tenMinutesElapsed方法运行一 次。 

     

    假设你需要调用一个方法, 这个方法会使用不同的时间间隔来调用,每次方法被调用以后,时间间隔都会 发生变化。

    你的代码看起来会是像下面这样:

    -(void) scheduleUpdates

    {

      // 像之前一样预定第一次更新

      [self schedule:@selector(irregularUpdate:) interval:1];

    }

    -(void) irregularUpdate:(ccTime)delta

    {

      // 首先,停止方法调用的预定

      [self unschedule:_cmd];

      // 这里我们用随机数来决定下次调用此方法需要经过的时间

      float nextUpdate = CCRANDOM_0_1() * 10;

      // 然后用_cmd来代替选择器,用新的时间间隔来重新预定方法调用

      [self schedule:_cmd interval:nextUpdate];

    最后一个预定方法调用的问题是安排更新方法的优先次序。请先看一下以下代 码:
    // 在A节点里
    -(void) scheduleUpdates

    {
      [self scheduleUpdate];

    }

    // 在B节点里
    -(void) scheduleUpdates

    {

      [self scheduleUpdateWithPriority:1];

    }

    // 在C节点里
    -(void) scheduleUpdates

    {

      [self scheduleUpdateWithPriority:-1];

    所有的节点还是在调用同样的-(void)update: (ccTime) delta方法。但是因为使用了优先级设置,C节点将会被首先运行。然 后是调用A节点,因为默认情况下优先级设定为0。B节点最后一个被调用,因为 它的优先级的数值最大。更新方法的调用次序是从最小的优先级数值到最大的 优先级数值。 

     7、场景和层 

    CCNode,CCScene和CCLayer这些类是没有视觉表现的。它们是在内部作为场景图的抽象概念来使用的。CCLayer最典型的应用是把各个节点组织起来,还有接收触摸输入和加速计输入的信息。

    CCScene对象总是场景图里面的第一个节点。通常CCScene的子节点都是继承自 CCLayer。CCLayer包含了各个游戏对象。因为大多数情况下场景对象本身不包 含任何游戏相关的代码,而且很少被子类化,所以它一般都是在CCLayer对象里 通过+(id)scene这个静态方法来创建的。 

    第一个创建场景的地方是在AppDelegate中aplicationDidFinishLaunching方法结束处。你在那里用Director的runWithScene方法开始运行第一个场景:

    // 用以下代码运行第一个场景

    [[CCDirector sharedDirector] runWithScene:[HelloWorld scene]];

    在其它情况下,用replaceScene方法来替换已有的场景:
    // 用replaceScene来替换所有以后需要变化的场景
    [[CCDirector sharedDirector] replaceScene:[HelloWorld scene]]; 

     

    8、场景和内存 

    当你替换一个场景时,新场景被加载进内存,但是旧的场景还没有从内存中释 放。这会让内存使用量在短时间内忽然增大。替换场景的过程很关键,因为很 多时候你会因为系统内存不够而收到内存警告或者导致程序崩溃。如果你在开 发过程中,发现游戏在场景转换过程中占用很多内存的话,你应该尽早和尽量 多的进行测试。 

    在你替换场景的时候,cocos2d会把自己占用的内存清理干净。它会移除所有的节点,停止所有的动作,并且停止所有选择器的预定。我之所以提到这一 点,是因为我有时候看到开发者会直接调用cocos2d的removeAll方法,那是没有必要的。你应该相信cocos2d的内存管理能力。 

    有件事情你永远都不应该尝试,那就是首先把一个节点添加到场景中作为它的 子节点,然后又自己把此节点retain下来。相反,你应该用cocos2d的方式来访 问创建的节点,或者至少是弱引用节点指针,而不是直接retain节点。只要你 让cocos2d来管理节点的内存使用,你就不会遇到麻烦。 

    9、推进(Pushing)和弹出(Popping)场景 

    用以下代码在任意一个地方显示“设置场景”:

    [[CCDirector sharedDirector] pushScene:[Settings scene]];

    如果你身处“设置场景”,但又想关闭“设置场景”时,你可以调用popScene。 这样你会回到之前还保留在内存里的场景:
    [[CCDirector sharedDirector] popScene]; 

     

    10、CCTransitionScene 

    所有过渡效果的类都继承自CCTransitionScene。 

    以下是很流行的淡入淡出过渡效果:它在一秒内过渡到了白色:

    // 用我们想要在下一步显示的场景初始化一个过渡场景
    CCFadeTransition* tran = [CCFadeTransition transitionWithDuration:1

                                                    scene:[HelloWorld scene]
                                                    withColor:ccWHITE];
    

    // 使用过渡场景对象而不是HelloWorld
    [[CCDirector sharedDirector] replaceScene:tran]; 

    你可以把CCTransitionScene与replaceScene和pushScene结合使用,但是你不能将过渡效果和popScene一起使用。 

    有很多种过渡效果可以使用,大多是和方向有关的,比如从哪个地方开始过渡到哪个地方过渡结束。以下是目前可以使用的过渡效果和描述:
    
    1. CCFadeTransition: 淡入淡出到一个指定的颜色,然后回来。

    2. CCFadeTRTransition (还有另外三个变化): 瓦片(tiles)反转过来揭示场景。

    3. CCJumpZoomTransition: 场景跳动着变小,新场景则跳动着变大。

    4. CCMoveInLTransition (还有另外三个变化): 场景移出,同时新的场景从左边,右边,上

      方或者下方移入。

    5. CCOrientedTransitionScene (还有另外六个变化): 这种过渡效果会将整个场景翻转过来。

    6. CCPageTurnTransition: 翻动书页的过渡效果。

    7. CCRotoZoomTransition: 当前场景旋转变小,新的场景旋转变大。

    8. CCShrinkGrowTransition: 当前场景缩小,新的场景在其之上变大。

      9. CCSlideInLTransition (还有另外三个变化): 新的场景从左边,右边,上方或者下方滑 入。

      10. CCSplitColsTransition (还有另外一个变化): 将当前场景切成竖条,上下移动揭示新场 景。

      11. CCTurnOffTilesTransition:将当前场景分成方块,用分成方块的新场景随机的替换当前 场景分出的方块。 

    11、CCLayer 

    有时候在同一个场景里你需要多个CCLayer。你可以参照以下代码生成这样的场景:

    +(id) scene {

    CCScene* scene = [CCScene node];
    
    CCLayer* backgroundLayer = [HelloWorldBackground node];
    [scene addChild: backgroundLayer];
    
    CCLayer* layer = [HelloWorld node];
    [scene addChild:layer];
    
    CCLayer* userInterfaceLayer = [HelloWorldUserInterface node];
    [scene addChild: userInterfaceLayer];
    
    return scene;

    和场景一样,层没有大小的概念。层是一个组织的概念。比如,如果你对一个层使用动作,那么所有在这个层上的物体都会受到影响。这意味着你可以让同一层上的所有物体一起移动,旋转和缩放。通常,如果你想让一组物体执行相同的动作和行为,层是很好的选择。比如说让所有的物体一起滚动;有时候你可能想让他们一起旋转,或者将他们重新排列然后覆盖在其它物体上面。如果所有这些物体是同一个层的子节点,你就可以通过改变层的属性或者在层上执行动作,来达到影响层上所有子节点的目的。

    有人建议不要在同一个场景里使用过多的CCLayer对象。这是一个误解。用层和使用其它的节点一样,并不会因为使用多个层而降低运行效率。不过,如果你的层接收触摸或者加速计事件的话就不一样了。因为接收处理外来事件很耗费资源。所以,你不应该使用很多接收外来事件的层。比较好的处理方式是:只使用一个层来接收和处理事件。如果需要的话,这个层应该通过转发事件的方式来通知其它节点或类。

     

    12、接收触摸事件 

     CCLayer类是用来接收触摸输入的。不过你要首先启用这个功能才可以使用它。 你通过设置isTouchEnabled为YES来让层接收触摸事件:

    self.isTouchEnabled = YES; 

    1. 当手指首次触摸到屏幕时调用的方法:
      -(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent*)event

    2. 手指在屏幕上移动时调用的方法:
      -(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent*)event

    3. 当手指从屏幕上提起时调用的方法:
      -(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent*)event

    4. 当触摸事件被取消时调用的方法:
      -(void) ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent*)event 

    你可能想知道触摸是在哪里开始的。因为触摸事件由Cocoa Touch API接收,所以触摸的位置必须被转换为OpenGL的坐标。以下是一个用来转换坐 标的方法:
    -(CGPoint) locationFromTouches:(NSSet *)touches

    {

    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInView: [touch view]];
    return [[CCDirector sharedDirector] convertToGL:touchLocation];
    }

    现在,你将使用一套有点不一样的方法来代替默认的触摸输入处理方法。它们 几乎完全一样,除了一点:用 (UITouch *)touch 代替 (NSSet *)touches 作 为方法的第一个参数:
    -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {}

    -(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {}
    -(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {}
    -(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event {}
    

    这里很重要的一点是:ccTouchBegan返回的是一个布尔值(BOOL)。如果你返 回了YES,那就意味着你不想让当前的触摸事件传导到其它触摸事件处理器。你 实际上是“吞下了”这个触摸事件。 

     

    13、接收加速计事件 

    和触摸输入一样,加速计必须在启用以后才能接收加速计事件: self.isAccelerometerEnabled = YES;

    同样的,层里面要加入一个特定的方法来接收加速计事件: -(void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
    {

           CCLOG(@"acceleration: x:%f / y:%f / z:%f", acceleration.x, acceleration.y,acceleration.z);
    

    你可以通过加速参数来决定任意三个方向的加速度值。 

    14、CCSprite 

    CCSprite* sprite = [CCSprite spriteWithFile:@”Default.png”];

    [self addChild:sprite]; 

    精灵贴图的中心点和精灵的左下角位置是一致的。生成的精灵被放 置在(0,0)点,也就是屏幕的左下角。因为精灵贴图的中心点和精灵的左下 角位置一致,导致贴图只能显示一部份(也就是贴图的右边上半部份)。比如, 假设图片大小是80x30px,你必须将精灵移动到坐标(40,15)才能将精灵贴图 与屏幕的左下角完美对齐,从而看到完整的贴图。 

    乍看上去这样安排位置很不寻常,不过将贴图的中心点和精灵的左下角位置设为一致有很大的好处。一旦你开始使用精灵的旋转或缩放属性,精灵的中心点将会保持在它的位置上。

    每个节点都有一个定位点,但是只有当此节点拥有贴图时,这个定位点才有用。 默认情况下,anchorPoint属性设置为(0.5,0.5)或者贴图尺寸的一半。它是 一个抽象的因素,一个乘数,而不是一个特定的像素尺寸。 

    anchorPoint定义的是贴图相对于节点位置的偏移。你可以通过把贴图的宽和高 乘以定位点来得到贴图的偏移值。 

    如果设置anchorPoint为(0,0)的话,你实际上是把贴图的左下角同节点的位 置对齐了。以下代码会把精灵图片完美地同屏幕左下角对齐:

    CCSprite* sprite = [CCSprite spriteWithFile:@”Default.png”]; sprite.anchorPoint = CGPointMake(0, 0);
    [self addChild:sprite]; 

     

    15、动作(Actions) 

    动作是用于在节点上运行某些“动作”的轻量级类。 

    你可以通过动作让节点移 动,旋转,缩放,着色,淡进淡出和干很多其它的事情。因为动作可以用于任 何节点,你可以在精灵,标签,甚至菜单或者整个场景中使用它们! 

    因为大多数动作都是在一段时间内发生的,比如旋转三秒钟,所以通常需要写 一个更新方法,还要添加用于储存中间状态的变量。动作(Actions)把这些逻 辑都包装了起来,用参数化的方法来应用动作:

    // 以下代码会让myNode在3秒钟内从当前位置移动到(100,200)坐标点

    CCMoveTo* move = [CCMoveTo actionWithDuration:3 position:CGPointMake(100, 200)];

    [myNode runAction:move];

    cocos2d的动作可以分为两种类型。

    一种是“即时动作”,它的效果和设定节点 属性一样,例如设定visible或flipX属性。

    另一种是“时间间隔动作”,这种 动作在一段时间之内发生,例如上述代码的移动动作。你不需要在这两种动作 完成以后将它们从内存里清理出去,cocos2d会自动释放动作所占用的内存。 

    重复动作

    你可以让动作或者一系列动作重复运行到永远。你可以通过这个特性生成循环 动画。以下代码会让一个节点永远旋转下去,就像一个永远旋转的轮子: CCRotateBy* rotateBy = [CCRotateBy actionWithDuration:2 angle:360];
    CCRepeatForever* repeat = [CCRepeatForever actionWithAction:rotateBy];

    [myNode runAction:repeat]; 

    舒缓动作

    CCEaseAction类让cocos2d的动作更加有用。“舒缓动作”允许你改变在一段时 间内发生的动作效果。例如,如果你在节点上应用CCMoveTo动作,此节点在整个移动过程中将会保持同一个速度。

    而如果你使用CCEaseAction的话,你就可 以让节点慢慢启动,然后加速向目标移动,或者反过来(快速启动,慢慢减速 到达目标)。或者你也可以让节点移动到超过目的地一些,然后再反弹回来。

    “舒缓动作”可以帮助你创造出通常很费时间才能做出来的动画。

    以下代码演 示了如何应用舒缓动作来改变一个普通动作的行为。rate参数是用来决定舒缓 动作的明显程度。此参数只有在大于1的情况下才能看到舒缓动作的效果:

    // 我想让myNode在3秒钟之内移动到100,200坐标点
    CCMoveTo* move = [CCMoveTo actionWithDuration:3 position:CGPointMake(100, 200)]; // 节点应该慢慢启动,然后在移动过程中减速
    CCEaseInOut* ease = [CCEaseInOut actionWithAction:move rate:4];
    [myNode runAction:ease]; 

     

    16、动作序列 

    当你给一个节点添加多个动作时,它们会在同一时间运行。

    一个动作一个动作的运行会更有用。我们可以使用CCSequence来达到 这个目的。在一个动作序列中,你可以使用任意数量和类型的动作。例如,你 可以让一个节点先移动到目标位置,然后在节点到达目标之后让其旋转,最后 淡出消失。动作一个跟着一个的运行,直到完成整个动作序列。 

    CCTintTo* tint1 = [CCTintTo actionWithDuration:4 red:255 green:0 blue:0];

    CCTintTo* tint2 = [CCTintTo actionWithDuration:4 red:0 green:0 blue:255];

    CCTintTo* tint3 = [CCTintTo actionWithDuration:4 red:0 green:255 blue:0];

    CCSequence* sequence = [CCSequence actions:tint1, tint2, tint3, nil];

    [label runAction:sequence];

    你也可以将动作序列与CCRepeatForever动作结合使用:

    CCSequence* sequence = [CCSequence actions:tint1, tint2, tint3, nil];

    CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequence];

    [label runAction:repeat]; 

    即时动作 你可能会奇怪为什么有基于CCInstantAction的即时动作存在,通过改变节点的 属性不是可以达到一样的目的吗?比如那些用来翻转节点,把节点放置的指定 的地方,还有用于开关节点的可视性属性的即时动作。

    即时动作的存在是为动作序列服务的。有时候在一个动作序列里你必须改变某 些节点的属性,像可视性或者位置,改变完成以后会继续当前的动作序列。即 时动作让这样的应用变得可能。其中用的最多的可能是CCCallFunc动作。

    当你使用一个动作序列时,你可能需要在某个时间得到通知。比如当一个动作 序列完成运行以后,你想知道这个动作序列已经完成,得到通知以后,你就可 以接着继续另一个动作序列。以下代码重写了之前的颜色改变动作序列的例子, 它会在每次完成一个CCTintTo动作以后调用三个CCCallFunc动作中的一个来发 送信息:

    CCCallFunc* func = [CCCallFunc actionWithTarget:self selector:@selector(onCallFunc)];

    CCCallFuncN* funcN = [CCCallFuncN actionWithTarget:self selector:@selector(onCallFuncN:)];
    CCCallFuncND* funcND = [CCCallFuncND actionWithTarget:self selector:@selector(onCallFuncND:data:) data:(void*)self];

    CCSequence* seq = [CCSequence actions:tint1, func, tint2, funcN, tint3, funcND, nil];

    [label runAction:seq];

    上述动作序列将调用以下代码来发送信息。sender这个参数继承自CCNode;这 是运行动作的节点。data参数可以是任何值,结构或者指针。只是你必须正确 地转换data指针的类型。
    -(void) onCallFunc

    {
      CCLOG(@"end of tint1!");

    }

    注:和菜单项一样,一串动作最后总是要用nil来结束。如果你忘记在最后用nil结束参数的话,CCSequence这串代码将会崩溃!
    -(void) onCallFuncN:(id)sender

    {

      CCLOG(@"end of tint2! sender: %@", sender);

    }

    -(void) onCallFuncND:(id)sender data:(void*)data

    {

      // 如下转换指针的方式要求data必须是一个CCSprite
      CCSprite* sprite = (CCSprite*)data;
      CCLOG(@"end of sequence! sender: %@ - data: %@", sender, sprite);

    }

    当然,CCCallFunc也可以和CCRepeatForever一起使用。这样,你所指定的方法 将会被重复调用。 

  • 相关阅读:
    WPF入门教程系列十二——依赖属性(二)
    WPF入门教程系列十一——依赖属性(一)
    WPF入门教程系列十——布局之Border与ViewBox(五)
    WPF入门教程系列九——布局之DockPanel与ViewBox(四)
    WPF入门教程系列八——布局之Grid与UniformGrid(三)
    WPF入门教程系列七——布局之WrapPanel与StackPanel(二)
    WPF入门教程系列六——布局介绍与Canvas(一)
    WPF入门教程系列五——Window 介绍
    WPF入门教程系列四——Dispatcher介绍
    WPF入门教程系列三——Application介绍(续)
  • 原文地址:https://www.cnblogs.com/sell/p/2850758.html
Copyright © 2011-2022 走看看