如何确定特定动画帧何时运行

时间:2013-07-09 08:38:54

标签: animation cocos2d-iphone

我想知道动画的某些帧何时运行以设置各种条件。在下面的代码中,我如何使用计数器或设置条件来确定特定的动画帧,例如当前正在运行的第3和第8帧?

NSMutableArray *frameArray = [NSMutableArray array];
 for(int i = 1; i < 12; i++) 
{
    CCLOG(@"item %d added", i);
    [frameArray addObject:
     [birdSpriteFrameCache spriteFrameByName:
      [NSString stringWithFormat:@"Sprite%d.png", i]]];    } 

//Starting the Animation
CCAnimation *animation = [CCAnimation animationWithSpriteFrames:frameArray delay: 0.3];

id action =[CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation :animation]];
[firstSprite runAction:action];

2 个答案:

答案 0 :(得分:2)

如果您使用cocos2d 2.0,现在会以每帧为基础提供通知。直接来自文档:

/*******************/
/** Notifications **/
/*******************/
/** @def CCAnimationFrameDisplayedNotification
    Notification name when a CCSpriteFrame is displayed
 */
#define CCAnimationFrameDisplayedNotification @"CCAnimationFrameDisplayedNotification"

创建动画时,您可以向每个帧添加将随通知一起收到的userInfo字典。以下是CCActionInterval的行:

NSDictionary *dict = [frame userInfo];
if( dict )
    [[NSNotificationCenter defaultCenter]       
          postNotificationName:CCAnimationFrameDisplayedNotification 
          object:target_ 
          userInfo:dict
    ];

所以我猜你可以为第3和第8帧添加一个dict对象,并在通知回调中添加'do your thing'。

ob cit:没试过,但应该适合你。

编辑:现在尝试过。我花了一个小时的时间将一个非常笨重的基于时间的算法转换为我的游戏的combatController类中的一个可靠的事件驱动实现。我只收到了2帧的通知:攻击动画的第9帧(我现在可以完美同步播放武器声音)和伤害动画的第11帧(如果受害者死亡,我可以停止动画并慢慢淡出小动物)。去cocos2d团队的方式。不是那么难,API干净清爽。

这是代码的一部分(我的第一个破解,不自豪:)),代码使用了我的其他一些东西,但你应该能够开始使用一般的想法。

-(void) registerForFrames{

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(gotFrame:)
                                                 name:CCAnimationFrameDisplayedNotification
                                               object:nil];
}   

-(void) deregisterForFrames {

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:CCAnimationFrameDisplayedNotification
                                                  object:nil];

}

-(NSDictionary *) frameEventForFrameNumber:(NSUInteger) frameNumber 
                                 animation:(NSString *) animationType {

    return [[FrameEvent frameEventForListener:frameListenerCombatController
                                animationName:animationType
                                  frameNumber:frameNumber] asDictionary];

}

-(FrameEvent*) frameEventForFrame:(NSDictionary *) theDic{
    return [FrameEvent frameEventListenerWithContentsOfDictionary:theDic];
}

-(void) gotFrame:(id) notification{

    NSDictionary *userInfoDictionary =  [notification userInfo];
    FrameEvent *ev = [self frameEventForFrame:userInfoDictionary];
    if (!ev) {
        MPLOGERROR(@"*** userInfo returned nil frameEvent object, bailing out!");
        return;
    }

    if (ev.frameListenerType==frameListenerUnknown){
        MPLOGERROR(@"*** Got served an unknown dictionary, bailing out!");
        return;
    }

    if (ev.frameListenerType==frameListenerCombatController) {

        MPLOG(@"Got Frame %@",ev.description);

        if([ev.animationName isEqualToString:@"attack"]) {
            [self scheduleOnce:@selector(attackTurnAround) delay:0.];
        }

        if ([ev.animationName isEqualToString:@"hurt"]) {
            // more here !
            MPLOG(@"TODO : schedule dead critter method");
        }
    } else {
        MPLOGERROR(@"*** Not processing frame listener of type [%@], bailing out!",
        [GEEngineSpecs frameListenerAsString:ev.frameListenerType]);
    }
}

最后是关键部分,在框架上放置用户信息:

- (CCAction *)attackActionFor:(GEClassAnimationSpec *)animSpec playerAsString:(NSString *)playerKey {

    NSMutableArray *animFrames = [NSMutableArray array];
    for (int i = 1; i <= animSpec.frames; i++) {
        NSString      *sfn = [NSString stringWithFormat:@"%@%@%i.png", animSpec.fileName, playerKey, i];
        CCSpriteFrame *sf  = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:sfn];
        [animFrames addObject:sf];

    }

    float animFrameDelay = 1.0f / K_ATTACK_FRAME_RATE;
    CCAnimation *anim = [CCAnimation animationWithSpriteFrames:animFrames delay:animFrameDelay];
    anim.restoreOriginalFrame = NO;

    CCAnimationFrame * ninth = [anim.frames objectAtIndex:8];
    NSDictionary *ui = [self frameEventForFrameNumber:9 animation:@"attack"];
    ninth.userInfo=ui;

    CCAction *action = [CCSequence actions:
        [CCAnimate actionWithAnimation:anim],
        [CCCallFunc actionWithTarget:self selector:@selector(resumeAttackerIdle)],
        nil
    ];
    return action;
}

答案 1 :(得分:0)

这是在其他人提到的通知中,但作为一个抬头:前一段时间我遇到了一些麻烦,当时我有2个不同的精灵在相同的时间点开始运行相同的动画。

认为每次sprite命中那个帧时都会触发通知,我最终废弃了那块代码,因为我无法弄清楚如何协调哪个sprite触发它,以及用不同的方法结束,因为我无法让它表现出来。

如果你的结果很糟糕,那就值得注意了。