diff --git a/Spriter/Resources/monster-hd.plist b/Spriter/Resources/monster-hd.plist index 136b45b..849914a 100644 --- a/Spriter/Resources/monster-hd.plist +++ b/Spriter/Resources/monster-hd.plist @@ -7,301 +7,301 @@ foot_0.png frame - {{1,100},{264,294}} + {{1,54},{137,149}} offset - {-4,-4} + {-1,-2} rotated sourceColorRect - {{4,17},{264,294}} + {{1,8},{137,149}} sourceSize - {280,320} + {141,161} foot_a.png frame - {{296,100},{244,294}} + {{151,54},{122,148}} offset - {-10,-3} + {-5,-2} rotated sourceColorRect - {{3,19},{244,294}} + {{2,10},{122,148}} sourceSize - {270,326} + {136,164} forearm_0.png frame - {{1,936},{150,190}} + {{1,549},{76,96}} offset - {1,3} + {0,2} rotated sourceColorRect - {{11,11},{150,190}} + {{5,5},{76,96}} sourceSize - {170,218} + {86,110} forearm_a.png frame - {{552,1025},{182,138}} + {{298,540},{91,70}} offset - {-1,-3} + {0,-2} rotated sourceColorRect - {{8,13},{182,138}} + {{5,7},{91,70}} sourceSize - {200,158} + {101,80} hand_0_0.png frame - {{552,870},{186,154}} + {{203,509},{94,77}} offset - {-3,14} + {-1,7} rotated sourceColorRect - {{15,27},{186,154}} + {{8,14},{94,77}} sourceSize - {222,236} + {112,119} hand_0_1.png frame - {{192,975},{188,154}} + {{352,462},{94,77}} offset - {-2,14} + {-1,7} rotated sourceColorRect - {{15,27},{188,154}} + {{8,14},{94,77}} sourceSize - {222,236} + {112,119} hand_0_2.png frame - {{395,838},{188,156}} + {{106,473},{96,79}} offset - {-1,14} + {-1,7} rotated - + sourceColorRect - {{16,26},{188,156}} + {{7,13},{96,79}} sourceSize - {222,236} + {112,119} hand_0_3.png frame - {{860,364},{194,162}} + {{413,380},{98,81}} offset - {1,10} + {1,5} rotated - + sourceColorRect - {{15,27},{194,162}} + {{8,14},{98,81}} sourceSize - {222,236} + {112,119} hand_a_0.png frame - {{739,997},{130,184}} + {{98,553},{66,93}} offset - {-19,0} + {-10,0} rotated sourceColorRect - {{21,22},{130,184}} + {{10,11},{66,93}} sourceSize - {210,228} + {106,115} hand_a_1.png frame - {{1,1087},{140,188}} + {{436,284},{70,95}} offset - {-14,-2} + {-7,-1} rotated - + sourceColorRect - {{21,22},{140,188}} + {{11,11},{70,95}} sourceSize - {210,228} + {106,115} hand_a_2.png frame - {{1,795},{140,196}} + {{436,184},{70,99}} offset - {-16,2} + {-8,1} rotated - + sourceColorRect - {{19,14},{140,196}} + {{10,7},{70,99}} sourceSize - {210,228} + {106,115} hand_a_3.png frame - {{198,838},{136,196}} + {{252,440},{68,99}} offset - {-18,2} + {-9,1} rotated sourceColorRect - {{19,14},{136,196}} + {{10,7},{68,99}} sourceSize - {210,228} + {106,115} head_0.png frame - {{553,611},{258,266}} + {{279,309},{130,133}} offset - {-13,6} + {-7,3} rotated sourceColorRect - {{3,13},{258,266}} + {{1,7},{130,133}} sourceSize - {290,304} + {146,153} head_1.png frame - {{587,364},{246,272}} + {{298,184},{124,137}} offset - {-7,3} + {-4,2} rotated sourceColorRect - {{15,13},{246,272}} + {{7,6},{124,137}} sourceSize - {290,304} + {146,153} head_2.png frame - {{1,548},{246,268}} + {{1,285},{124,135}} offset - {-7,5} + {-4,3} rotated sourceColorRect - {{15,13},{246,268}} + {{7,6},{124,135}} sourceSize - {290,304} + {146,153} head_3.png frame - {{282,478},{246,270}} + {{143,245},{124,135}} offset - {-7,2} + {-4,1} rotated sourceColorRect - {{15,15},{246,270}} + {{7,8},{124,135}} sourceSize - {290,304} + {146,153} pelvis_0.png frame - {{1,365},{182,280}} + {{1,192},{92,141}} offset - {-3,-10} + {-2,-5} rotated sourceColorRect - {{17,20},{182,280}} + {{8,10},{92,141}} sourceSize - {222,300} + {112,151} shadow_idle.png frame - {{1,1},{628,98}} + {{1,1},{317,52}} offset - {1,5} + {0,3} rotated sourceColorRect - {{9,11},{628,98}} + {{3,4},{317,52}} sourceSize - {644,130} + {323,66} shoulder_0.png frame - {{860,559},{208,160}} + {{1,468},{104,80}} offset - {0,4} + {0,2} rotated - + sourceColorRect - {{11,11},{208,160}} + {{6,6},{104,80}} sourceSize - {230,190} + {116,96} shoulder_a.png frame - {{270,725},{112,212}} + {{1,410},{57,107}} offset - {-3,3} + {-2,2} rotated sourceColorRect - {{13,7},{112,212}} + {{6,3},{57,107}} sourceSize - {144,232} + {73,117} thigh_0.png frame - {{296,345},{132,290}} + {{151,177},{67,146}} offset - {1,3} + {0,2} rotated sourceColorRect - {{17,9},{132,290}} + {{8,4},{67,146}} sourceSize - {164,314} + {83,158} thigh_a.png frame - {{820,768},{228,202}} + {{137,370},{114,102}} offset - {-6,7} + {-3,4} rotated - + sourceColorRect - {{7,5},{228,202}} + {{4,2},{114,102}} sourceSize - {254,226} + {128,114} torso_0.png frame - {{630,1},{362,376}} + {{319,1},{182,191}} offset - {0,-10} + {0,-4} rotated sourceColorRect - {{0,20},{362,376}} + {{0,8},{182,191}} sourceSize - {362,396} + {182,199} metadata @@ -311,9 +311,9 @@ realTextureFileName monster-hd.png size - {1024,2048} + {512,1024} smartupdate - $TexturePacker:SmartUpdate:ed4bfc52c7e3af6b78c62ff4259e6568$ + $TexturePacker:SmartUpdate:666a62867467761fed5c96c91f5409a9$ textureFileName monster-hd.png diff --git a/Spriter/Resources/monster-hd.png b/Spriter/Resources/monster-hd.png index 5073d49..248791f 100644 Binary files a/Spriter/Resources/monster-hd.png and b/Spriter/Resources/monster-hd.png differ diff --git a/Spriter/Resources/monster.plist b/Spriter/Resources/monster.plist index 5a97bc0..5f14458 100644 --- a/Spriter/Resources/monster.plist +++ b/Spriter/Resources/monster.plist @@ -7,301 +7,301 @@ foot_0.png frame - {{1,54},{137,149}} + {{1,28},{68,76}} offset - {-1,-2} + {-1,-1} rotated sourceColorRect - {{1,8},{137,149}} + {{1,4},{68,76}} sourceSize - {141,161} + {72,82} foot_a.png frame - {{151,54},{122,148}} + {{78,28},{63,75}} offset - {-5,-2} + {-3,-1} rotated sourceColorRect - {{2,10},{122,148}} + {{0,5},{63,75}} sourceSize - {136,164} + {69,83} forearm_0.png frame - {{1,549},{76,96}} + {{215,166},{40,50}} offset - {0,2} + {0,1} rotated - + sourceColorRect - {{5,5},{76,96}} + {{2,2},{40,50}} sourceSize - {86,110} + {44,56} forearm_a.png frame - {{298,540},{91,70}} + {{51,281},{48,37}} offset - {0,-2} + {-1,0} rotated sourceColorRect - {{5,7},{91,70}} + {{1,2},{48,37}} sourceSize - {101,80} + {52,41} hand_0_0.png frame - {{203,509},{94,77}} + {{205,267},{45,41}} offset - {-1,7} + {1,4} rotated sourceColorRect - {{8,14},{94,77}} + {{7,6},{45,41}} sourceSize - {112,119} + {57,61} hand_0_1.png frame - {{352,462},{94,77}} + {{157,266},{47,41}} offset - {-1,7} + {0,4} rotated sourceColorRect - {{8,14},{94,77}} + {{5,6},{47,41}} sourceSize - {112,119} + {57,61} hand_0_2.png frame - {{106,473},{96,79}} + {{1,294},{47,41}} offset - {-1,7} + {0,4} rotated sourceColorRect - {{7,13},{96,79}} + {{5,6},{47,41}} sourceSize - {112,119} + {57,61} hand_0_3.png frame - {{413,380},{98,81}} + {{1,250},{49,43}} offset - {1,5} + {1,3} rotated sourceColorRect - {{8,14},{98,81}} + {{5,6},{49,43}} sourceSize - {112,119} + {57,61} hand_a_0.png frame - {{98,553},{66,93}} + {{107,266},{34,49}} offset - {-10,0} + {-5,0} rotated sourceColorRect - {{10,11},{66,93}} + {{5,5},{34,49}} sourceSize - {106,115} + {54,59} hand_a_1.png frame - {{436,284},{70,95}} + {{215,217},{38,49}} offset - {-7,-1} + {-4,0} rotated sourceColorRect - {{11,11},{70,95}} + {{4,5},{38,49}} sourceSize - {106,115} + {54,59} hand_a_2.png frame - {{436,184},{70,99}} + {{55,244},{36,51}} offset - {-8,1} + {-4,1} rotated - + sourceColorRect - {{10,7},{70,99}} + {{5,3},{36,51}} sourceSize - {106,115} + {54,59} hand_a_3.png frame - {{252,440},{68,99}} + {{132,229},{36,51}} offset - {-9,1} + {-5,1} rotated sourceColorRect - {{10,7},{68,99}} + {{4,3},{36,51}} sourceSize - {106,115} + {54,59} head_0.png frame - {{279,309},{130,133}} + {{153,99},{66,70}} offset - {-7,3} + {-3,2} rotated sourceColorRect - {{1,7},{130,133}} + {{1,2},{66,70}} sourceSize - {146,153} + {74,78} head_1.png frame - {{298,184},{124,137}} + {{144,166},{62,70}} offset - {-4,2} + {-1,1} rotated sourceColorRect - {{7,6},{124,137}} + {{5,3},{62,70}} sourceSize - {146,153} + {74,78} head_2.png frame - {{1,285},{124,135}} + {{1,145},{62,70}} offset - {-4,3} + {-1,2} rotated sourceColorRect - {{7,6},{124,135}} + {{5,2},{62,70}} sourceSize - {146,153} + {74,78} head_3.png frame - {{143,245},{124,135}} + {{73,128},{62,70}} offset - {-4,1} + {-1,1} rotated sourceColorRect - {{7,8},{124,135}} + {{5,3},{62,70}} sourceSize - {146,153} + {74,78} pelvis_0.png frame - {{1,192},{92,141}} + {{1,97},{47,71}} offset - {-2,-5} + {-1,-1} rotated sourceColorRect - {{8,10},{92,141}} + {{4,4},{47,71}} sourceSize - {112,151} + {57,77} shadow_idle.png frame - {{1,1},{317,52}} + {{1,1},{159,26}} offset - {0,3} + {0,1} rotated sourceColorRect - {{3,4},{317,52}} + {{2,3},{159,26}} sourceSize - {323,66} + {163,34} shoulder_0.png frame - {{1,468},{104,80}} + {{1,208},{53,41}} offset - {0,2} + {0,1} rotated sourceColorRect - {{6,6},{104,80}} + {{3,3},{53,41}} sourceSize - {116,96} + {59,49} shoulder_a.png frame - {{1,410},{57,107}} + {{224,99},{30,56}} offset - {-2,2} + {-1,1} rotated - + sourceColorRect - {{6,3},{57,107}} + {{3,1},{30,56}} sourceSize - {73,117} + {38,60} thigh_0.png frame - {{151,177},{67,146}} + {{78,92},{35,74}} offset - {0,2} + {0,1} rotated sourceColorRect - {{8,4},{67,146}} + {{4,2},{35,74}} sourceSize - {83,158} + {43,80} thigh_a.png frame - {{137,370},{114,102}} + {{72,191},{59,52}} offset - {-3,4} + {-2,2} rotated sourceColorRect - {{4,2},{114,102}} + {{1,1},{59,52}} sourceSize - {128,114} + {65,58} torso_0.png frame - {{319,1},{182,191}} + {{161,1},{86,97}} offset - {0,-4} + {-3,-2} rotated - + sourceColorRect - {{0,8},{182,191}} + {{0,4},{86,97}} sourceSize - {182,199} + {92,101} metadata @@ -311,9 +311,9 @@ realTextureFileName monster.png size - {512,1024} + {256,512} smartupdate - $TexturePacker:SmartUpdate:d7912141bc6dded8e7d4f2aee97d8ce9$ + $TexturePacker:SmartUpdate:32044d1553066c11776146395049de1d$ textureFileName monster.png diff --git a/Spriter/Resources/monster.png b/Spriter/Resources/monster.png index 248791f..65f5e8c 100644 Binary files a/Spriter/Resources/monster.png and b/Spriter/Resources/monster.png differ diff --git a/Spriter/Resources/monster.tps b/Spriter/Resources/monster.tps index b53e1dc..8812515 100644 Binary files a/Spriter/Resources/monster.tps and b/Spriter/Resources/monster.tps differ diff --git a/Spriter/TGSpriterNode.h b/Spriter/TGSpriterNode.h index 5ba5e91..8c0f961 100644 --- a/Spriter/TGSpriterNode.h +++ b/Spriter/TGSpriterNode.h @@ -8,12 +8,11 @@ /* Notes: - + 1.) WARNING - This is a fast and dirty implementation based on the spec for the BETA version. The spec - WILL change with the development of Spriter 1.0 and this code will change with it. This - code should be considered high unstable until the release of 1.0. + This is a fast and dirty implementation based on the spec for the BETA version (V2). + This code will change as features are added to the beta build. 2.) NSXMLParser @@ -22,15 +21,17 @@ was additional dependcies was important to avoid. If this ends up in cocos2d proper and they wish to use a faster parser, then a lot of the parsing code included can be cleaned up. - This isn't the cleanest parsing implementation. I building up a simple tree as the + This isn't the cleanest parsing implementation. I'm building up a simple tree as the SAX parser runs its course, and then I use that tree to build up animations and frames. - 3.) Tweening - - Tweening isn't in the beta spec. + 3.) TGSpriterNode without a sprite sheet + Since I only use this as part of a batch node, that is the only method I am supporting at the + moment. At some point I will add support back in for using this node with out a sprite sheet. + I also plan on adding a method for batching the drawing of this node with other TGSpriterNodes + using the same sprite sheet. Reduce draw calls for the win! -*/ + */ #import #import "cocos2d.h" @@ -90,6 +91,8 @@ CGPoint anchorPoint_; double rotation_; int spin_; + double scaleX_; + double scaleY_; } @property int file; @@ -99,6 +102,8 @@ @property CGPoint anchorPoint; @property double rotation; @property int spin; +@property double scaleX; +@property double scaleY; +(id) spriterTimelineKey; @@ -136,15 +141,33 @@ @end +@interface TGSpriterSprite : CCSprite { + int folder; + int file; + NSString * displayFrameName; +} +@property int folder; +@property int file; +@property (nonatomic, retain) NSString * displayFrameName; + +@end + +#pragma mark - +#pragma mark The Important Stuff +/* + This is the actual node you should use. + */ @interface TGSpriterNode : CCNode { NSXMLParser * parser_; NSString * characterName_; - NSMutableDictionary * animations_; // {name: TGSpriterAnimation,...} - NSMutableDictionary * frames_; // {name: TGSpriterFrame,..} + NSMutableDictionary * animations_; + NSMutableDictionary * frames_; - NSMutableDictionary * files_; + int * folderIndexes_; + int * flattenedFolder_; + NSMutableArray * files_; // an array of arrays, much faster access time [O(1)] than a dict lookup TGSpriterAnimation * curAnimation_; TGSpriterMainlineKey * curKeyFrame_; @@ -161,14 +184,39 @@ // parsing vars TGSpriterConfigNode * configRoot_; TGSpriterConfigNode * curConfigNode_; + + // vars for manipulating scml positions to match game world scale and coords + double sdScale_; + CGPoint offset_; + + BOOL smoothTransitions_; + TGSpriterAnimation * prevAnimation_; + + double playbackSpeed_; + BOOL smoothTransitions; } -+(id) spriterNodeWithFiles:(NSString*)scmlFile; +/* + smoothTransitions is an experimental property for smoothly moving to a new animation when + runAnimation is called in the middle of another animation. This does not work properly at + this time, but it is something I plan on looking into again + */ +@property BOOL smoothTransitions; +@property double playbackSpeed; // defaults to 1, allows you to play the animation faster or slower + +(id) spriterNodeWithFiles:(NSString *)scmlFile spriteSheet:(NSString*)spriteSheet; --(id) initNodeWithFiles:(NSString*)scmlFile; --(id) initNodeWithFiles:(NSString*)scmlFile spriteSheet:(NSString*)spriteSheet; + +// sdScale defaults to 0.5, it assumes that the Spriter assets were made with retina sized assets +// offset lets you correct for spriter animations that were not assembled in the center of the viewport ++(id) spriterNodeWithFiles:(NSString *)scmlFile spriteSheet:(NSString*)spriteSheet sdScale:(double)sdScale offset:(CGPoint)offse; +-(id) initNodeWithFiles:(NSString*)scmlFile spriteSheet:(NSString*)spriteSheet sdScale:(double)sdScale offset:(CGPoint)offset; // call this to run an animation -(void) runAnimation:(NSString*)animation; +// hooks for classes that override this for custom behavior +// should we be message passing to a delegate instead? +-(BOOL) animationEnded; +-(void) animationFrameChanged; + @end \ No newline at end of file diff --git a/Spriter/TGSpriterNode.m b/Spriter/TGSpriterNode.m index a2e5d75..ccfc26f 100644 --- a/Spriter/TGSpriterNode.m +++ b/Spriter/TGSpriterNode.m @@ -30,7 +30,7 @@ +(id) configNode:(NSString*)name { @implementation TGSpriterObjectRef -@synthesize timelineId=timelineId_, timelineKey=timelineKey; +@synthesize timelineId=timelineId_, timelineKey=timelineKey_; +(id) spriterObjectRef { return [[super alloc] init]; @@ -96,7 +96,7 @@ -(void) dealloc { @implementation TGSpriterTimelineKey -@synthesize file=file_, folder=folder_, position=position_, anchorPoint=anchorPoint_, rotation=rotation_, startsAt=startsAt_, spin=spin_; +@synthesize file=file_, folder=folder_, position=position_, anchorPoint=anchorPoint_, rotation=rotation_, startsAt=startsAt_, spin=spin_, scaleX=scaleX_, scaleY=scaleY_; +(id)spriterTimelineKey { return [[super alloc] init]; @@ -131,40 +131,44 @@ -(void) addTimeline:(TGSpriterTimeline*)timeline { @end + +@implementation TGSpriterSprite + +@synthesize folder, file, displayFrameName; + +-(void) dealloc { + if (displayFrameName) { + [displayFrameName release]; + } + + [super dealloc]; +} + +@end + +#pragma mark - #pragma mark TGSpriterNode @implementation TGSpriterNode +@synthesize smoothTransitions, playbackSpeed=playbackSpeed_; -+(id) spriterNodeWithFiles:(NSString*)scmlFile { - return [[[super alloc] initNodeWithFiles:scmlFile] autorelease]; ++(id) spriterNodeWithFiles:(NSString *)scmlFile spriteSheet:(NSString*)spriteSheet sdScale:(double)sdScale offset:(CGPoint)offset { + return [[[super alloc] initNodeWithFiles:scmlFile spriteSheet:spriteSheet sdScale:sdScale offset:offset] autorelease]; } - +(id) spriterNodeWithFiles:(NSString *)scmlFile spriteSheet:(NSString*)spriteSheet { - return [[[super alloc] initNodeWithFiles:scmlFile spriteSheet:spriteSheet] autorelease]; -} --(id) initNodeWithFiles:(NSString*)scmlFile { - return nil; // not implemented - if ( (self = [super init]) ) { - frames_ = [[[NSMutableDictionary alloc] init] retain]; - animations_ = [[[NSMutableDictionary alloc] init] retain]; - files_ = [[[NSMutableDictionary alloc] init] retain]; - - NSString * path = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:scmlFile]; - NSData * scmlData = [NSData dataWithContentsOfFile:path]; - parser_ = [[[NSXMLParser alloc] initWithData:scmlData] retain]; - parser_.delegate = self; - [parser_ parse]; - } - - return self; + return [[[super alloc] initNodeWithFiles:scmlFile spriteSheet:spriteSheet sdScale:0.5 offset:ccp(0,0)] autorelease]; } --(id) initNodeWithFiles:(NSString*)scmlFile spriteSheet:(NSString*)spriteSheet { +-(id) initNodeWithFiles:(NSString*)scmlFile spriteSheet:(NSString*)spriteSheet sdScale:(double)sdScale offset:(CGPoint)offset { if ( (self = [super init]) ) { + sdScale_ = sdScale; // assumes animation was built with retina assets + offset_ = offset; + playbackSpeed_ = 1.0; + frames_ = [[[NSMutableDictionary alloc] init] retain]; animations_ = [[[NSMutableDictionary alloc] init] retain]; - files_ = [[[NSMutableDictionary alloc] init] retain]; + files_ = [[[NSMutableArray alloc] init] retain]; spriterNodes_ = [[[NSMutableArray alloc] init] retain]; useBatchNode_ = TRUE; @@ -216,11 +220,20 @@ -(void) dealloc { -(void) runAnimation:(NSString*)animation { [self unschedule:@selector(update:)]; + //CCLOG(@"running animation: %@", animation); + duration_ = 0; frameIdx_ = 0; + if (smoothTransitions) { + prevAnimation_ = curAnimation_; + } curAnimation_ = [animations_ objectForKey:animation]; - curKeyFrame_ = [curAnimation_.mainline objectAtIndex:0]; - nextKeyFrame_ = [curAnimation_.mainline objectAtIndex:(frameIdx_+1)%[curAnimation_.mainline count]]; + if (smoothTransitions) { + nextKeyFrame_ = [curAnimation_.mainline objectAtIndex:0]; + } else { + curKeyFrame_ = [curAnimation_.mainline objectAtIndex:0]; + nextKeyFrame_ = [curAnimation_.mainline objectAtIndex:(frameIdx_+1)%[curAnimation_.mainline count]]; + } [self schedule:@selector(update:)]; } @@ -228,33 +241,57 @@ -(void) runAnimation:(NSString*)animation { -(void) update:(ccTime)dt { // increment the time duration_ += dt; - int milliseconds = duration_ * 10000; + int milliseconds = duration_ * 1000 * playbackSpeed_; int startTime = curKeyFrame_.startsAt; int endTime = nextKeyFrame_.startsAt; - if (nextKeyFrame_.startsAt == 0) { + BOOL lastFrame = frameIdx_+1 == [curAnimation_.mainline count]; + if (prevAnimation_) { + endTime = MAX(0, prevAnimation_.duration - curKeyFrame_.startsAt); + } else if (nextKeyFrame_.startsAt == 0) { endTime = curAnimation_.duration; } // swap the key frames if we passed the duration if (milliseconds > endTime) { - curKeyFrame_ = nextKeyFrame_; - frameIdx_ = (frameIdx_+1)%[curAnimation_.mainline count]; - nextKeyFrame_ = [curAnimation_.mainline objectAtIndex:(frameIdx_+1)%[curAnimation_.mainline count]]; - startTime = curKeyFrame_.startsAt; - endTime = nextKeyFrame_.startsAt; - } - if (milliseconds > curAnimation_.duration) { - duration_ -= milliseconds * 0.0001; - milliseconds -= 10000; + if (prevAnimation_) { + prevAnimation_ = nil; // transition has occured + frameIdx_ = -1; + duration_ = dt; + milliseconds = duration_ * 1000 * playbackSpeed_; + } + if (!lastFrame || (lastFrame && [self animationEnded])) { + curKeyFrame_ = nextKeyFrame_; + frameIdx_ = (frameIdx_+1)%[curAnimation_.mainline count]; + nextKeyFrame_ = [curAnimation_.mainline objectAtIndex:(frameIdx_+1)%[curAnimation_.mainline count]]; + startTime = curKeyFrame_.startsAt; + endTime = nextKeyFrame_.startsAt; + if (nextKeyFrame_.startsAt == 0) { + endTime = curAnimation_.duration; + } + [self animationFrameChanged]; + } else { + return; // early exit + } + if (milliseconds > curAnimation_.duration && frameIdx_ == 0) { + duration_ -= curAnimation_.duration * (0.001/playbackSpeed_); + milliseconds -= curAnimation_.duration * playbackSpeed_; + } } // hide existing nodes (this is more important later when we have temp objects) - for (CCNode * n in spriterNodes_) { + for (TGSpriterSprite * n in spriterNodes_) { [n setVisible:FALSE]; + n.folder = -1; + n.file = -1; } // interpolation double interpolationFactor = ((milliseconds - startTime)/(1.0*(endTime-startTime))); + if (interpolationFactor == INFINITY) { interpolationFactor = 0.0; } + if (interpolationFactor < 0) { interpolationFactor = 0.0; } + if (interpolationFactor > 1) { interpolationFactor = 1.0; } + if (interpolationFactor == NAN) { interpolationFactor = 1.0; } + // walk through mainline objects for (int keyIdx = 0; keyIdx < [curKeyFrame_.objectRefs count]; keyIdx++) { // look up the current and next timeline keys @@ -263,32 +300,49 @@ -(void) update:(ccTime)dt { TGSpriterTimeline * objectTimeline = [curAnimation_.timelines objectAtIndex:[curObjectRef timelineId]]; - TGSpriterTimelineKey * curTimelineKey = [objectTimeline.keys objectAtIndex:[curObjectRef timelineKey]]; + TGSpriterTimelineKey * curTimelineKey; + if (smoothTransitions && prevAnimation_) { + TGSpriterTimeline * curObjectTimeline = [prevAnimation_.timelines objectAtIndex:[curObjectRef timelineId]]; + curTimelineKey = [curObjectTimeline.keys objectAtIndex:[curObjectRef timelineKey]]; + } else { + curTimelineKey = [objectTimeline.keys objectAtIndex:[curObjectRef timelineKey]]; + } TGSpriterTimelineKey * nextTimelineKey = [objectTimeline.keys objectAtIndex:[nextObjectRef timelineKey]]; // Get the display frame - NSString * displayFrameName = [files_ objectForKey:[NSString stringWithFormat:@"%d-%d", [curTimelineKey folder], [curTimelineKey file]]]; + NSString * displayFrameName = [[files_ objectAtIndex:[curTimelineKey folder]] objectAtIndex:[curTimelineKey file]]; - CCSprite * sprite; + TGSpriterSprite * sprite; // set this data to the first available spriterNode, create a new one if this animation has more objects if (keyIdx >= [spriterNodes_ count]) { - sprite = [CCSprite spriteWithSpriteFrameName:displayFrameName]; + sprite = [TGSpriterSprite spriteWithSpriteFrameName:displayFrameName]; + sprite.displayFrameName = displayFrameName; [batchNode_ addChild:sprite]; [spriterNodes_ addObject:sprite]; } else { sprite = [spriterNodes_ objectAtIndex:keyIdx]; - [sprite setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:displayFrameName]]; + if (![sprite.displayFrameName isEqualToString:displayFrameName]) { + sprite.displayFrameName = displayFrameName; + [sprite setDisplayFrame: + [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:displayFrameName]]; + } } sprite.visible = TRUE; + sprite.folder = [curTimelineKey folder]; + sprite.file = [curTimelineKey file]; sprite.position = ccp([self interpolate:curTimelineKey.position.x b:nextTimelineKey.position.x f:interpolationFactor], [self interpolate:curTimelineKey.position.y b:nextTimelineKey.position.y f:interpolationFactor]); sprite.anchorPoint = ccp([self interpolate:curTimelineKey.anchorPoint.x b:nextTimelineKey.anchorPoint.x f:interpolationFactor], - [self interpolate:curTimelineKey.anchorPoint.y b:nextTimelineKey.anchorPoint.y f:interpolationFactor]); + [self interpolate:curTimelineKey.anchorPoint.y b:nextTimelineKey.anchorPoint.y f:interpolationFactor]); + sprite.scaleX = [self interpolate:curTimelineKey.scaleX b:nextTimelineKey.scaleX f:interpolationFactor]; + sprite.scaleY = [self interpolate:curTimelineKey.scaleY b:nextTimelineKey.scaleY f:interpolationFactor]; double nextRotation = nextTimelineKey.rotation; double curRotation = curTimelineKey.rotation; - if (curTimelineKey.spin == 1 && (nextRotation-curRotation) < 0) { + if (fabs(curRotation-nextRotation) <= 0.001) { // There appears to be a spriter bug related to close FP numbers outputting the wrong spin. This solves that. + nextRotation = curRotation; + } else if (curTimelineKey.spin == 1 && (nextRotation-curRotation) < 0) { nextRotation += 360; } else if (curTimelineKey.spin == -1 && (nextRotation-curRotation) > 0) { nextRotation -= 360; @@ -298,10 +352,7 @@ -(void) update:(ccTime)dt { } -(double) interpolate:(double)a b:(double)b f:(double)f { - if (f == INFINITY) { f = 0.0; } - if (f < 0) { f = 0.0; } - if (f > 1) { f = 1.0; } - if (f == NAN) { f = 1.0; } + if (a == b) { return a; } return a+(b-a)*f; } @@ -315,16 +366,15 @@ -(void)parserDidStartDocument:(NSXMLParser *)parser { -(void)parserDidEndDocument:(NSXMLParser *)parser { // load all frames for (TGSpriterConfigNode * c in [[configRoot_.children objectAtIndex:0] children]) { - CCLOG(@"%@", c.name); + //CCLOG(@"%@", c.name); if ([[c name] isEqualToString:@"folder"]) { + NSMutableArray * folder = [[NSMutableArray alloc] init]; for (TGSpriterConfigNode * file in c.children) { - CCLOG(@"%@: %d-%d => %@", c.name, [[c.properties objectForKey:@"id"] intValue], [[file.properties objectForKey:@"id"] intValue], [[file.properties objectForKey:@"name"] lastPathComponent]); - - NSString * fileKey = [NSString stringWithFormat:@"%d-%d", [[c.properties objectForKey:@"id"] intValue], [[file.properties objectForKey:@"id"] intValue]]; - [files_ setObject:[[file.properties objectForKey:@"name"] lastPathComponent] forKey:fileKey]; - + //CCLOG(@"%@: %d-%d => %@", c.name, [[c.properties objectForKey:@"id"] intValue], [[file.properties objectForKey:@"id"] intValue], [[file.properties objectForKey:@"name"] lastPathComponent]); + [folder addObject:[[file.properties objectForKey:@"name"] lastPathComponent]]; } + [files_ addObject:folder]; } else if ([[c name] isEqualToString:@"entity"]) { // SpriterNode->[TGSpriterAnimation]->[TGSpriterFrame]->[TGSpriteObjectKey] for (TGSpriterConfigNode * animation in c.children) { @@ -334,7 +384,7 @@ -(void)parserDidEndDocument:(NSXMLParser *)parser { spriterAnimation.name = [animation.properties objectForKey:@"name"]; spriterAnimation.duration = [[animation.properties objectForKey:@"length"] intValue]; - CCLOG(@"Parsing Animation: %@ (%d ms.)", spriterAnimation.name, (int)spriterAnimation.duration); + //CCLOG(@"Parsing Animation: %@ (%d ms.)", spriterAnimation.name, (int)spriterAnimation.duration); for (TGSpriterConfigNode * animConfig in animation.children) { // mainline @@ -351,6 +401,7 @@ -(void)parserDidEndDocument:(NSXMLParser *)parser { [mainlineKey addObjectRef:objectRef]; } + mainlineKey.startsAt = [[key.properties objectForKey:@"time"] intValue]; [spriterAnimation addKeyFrame:mainlineKey]; } } else if ([[animConfig name] isEqualToString:@"timeline"]) { @@ -364,11 +415,24 @@ -(void)parserDidEndDocument:(NSXMLParser *)parser { timelineKey.folder = [[object.properties objectForKey:@"folder"] intValue]; timelineKey.file = [[object.properties objectForKey:@"file"] intValue]; - timelineKey.position = ccp([[object.properties objectForKey:@"x"] doubleValue], - [[object.properties objectForKey:@"y"] doubleValue]); - timelineKey.anchorPoint = ccp([[object.properties objectForKey:@"pivot_x"] doubleValue], - [[object.properties objectForKey:@"pivot_y"] doubleValue]); - + timelineKey.position = ccpMult(ccpAdd(ccp([[object.properties objectForKey:@"x"] doubleValue], + [[object.properties objectForKey:@"y"] doubleValue]), offset_), sdScale_); + if ([object.properties objectForKey:@"pivot_x"]) { + timelineKey.anchorPoint = ccp([[object.properties objectForKey:@"pivot_x"] doubleValue], + [[object.properties objectForKey:@"pivot_y"] doubleValue]); + } else { + timelineKey.anchorPoint = ccp(0,1); + } + if ([object.properties objectForKey:@"scale_x"]) { + timelineKey.scaleX = [[object.properties objectForKey:@"scale_x"] doubleValue]; + } else { + timelineKey.scaleX = 1.0; + } + if ([object.properties objectForKey:@"scale_y"]) { + timelineKey.scaleY = [[object.properties objectForKey:@"scale_y"] doubleValue]; + } else { + timelineKey.scaleY = 1.0; + } timelineKey.startsAt = [[key.properties objectForKey:@"time"] intValue]; timelineKey.rotation = [[object.properties objectForKey:@"angle"] doubleValue]; @@ -392,111 +456,6 @@ -(void)parserDidEndDocument:(NSXMLParser *)parser { continue; } - - /* - NSString * spriterFrameName = @""; - TGSpriterFrame * spriterFrame = [TGSpriterFrame spriterFrame]; - - for (TGSpriterConfigNode * frameNodes in c.children) { - if ([frameNodes.name isEqualToString:@"name"]) { - spriterFrameName = frameNodes.value; - } else if ([frameNodes.name isEqualToString:@"sprite"]) { - NSString * img; - double x = 0, y = 0, angle=0, width = 0, height = 0; - int opacity = 255; - BOOL flipX = FALSE, flipY = FALSE; - ccColor3B color = ccc3(255, 255, 255); - for (TGSpriterConfigNode * spriteProp in frameNodes.children) { - //CCLOG(@"\t%@: %@", spriteProp.name, spriteProp.value); - if ([spriteProp.name isEqualToString:@"image"]) { - img = [[spriteProp.value componentsSeparatedByString:@"\\"] lastObject]; - } else if ([spriteProp.name isEqualToString:@"x"]) { - x = [spriteProp.value doubleValue]; - } else if ([spriteProp.name isEqualToString:@"y"]) { - y = -[spriteProp.value doubleValue]; - } else if ([spriteProp.name isEqualToString:@"angle"]) { - angle = -[spriteProp.value doubleValue]; - } else if ([spriteProp.name isEqualToString:@"opacity"]) { - opacity = [spriteProp.value doubleValue] / 100.0 * 255; - } else if ([spriteProp.name isEqualToString:@"flipX"]) { - flipX = [spriteProp.value boolValue]; - } else if ([spriteProp.name isEqualToString:@"flipY"]) { - flipY = [spriteProp.value boolValue]; - } else if ([spriteProp.name isEqualToString:@"color"]) { - int c = [spriteProp.value intValue]; - int red = c / pow(256, 2); - int green = (c - red * pow(256, 2)) / 256; - int blue = c - red * pow(256, 2) - blue * 256; - color = ccc3(red, green, blue); - } else if ([spriteProp.name isEqualToString:@"width"]) { - width = [spriteProp.value doubleValue]; - } else if ([spriteProp.name isEqualToString:@"height"]) { - height = [spriteProp.value doubleValue]; - } - } - CCSprite * sprite; - if (useBatchNode_) { - sprite = [CCSprite spriteWithSpriteFrameName:img]; - } else { - sprite = [CCSprite spriteWithFile:img]; - } - sprite.anchorPoint = ccp(0,1); - sprite.position = ccp(x,y); - sprite.rotation = angle; - sprite.flipX = flipX; - sprite.flipY = flipY; - sprite.color = color; - sprite.scaleX = width / sprite.contentSize.width; - sprite.scaleY = height / sprite.contentSize.height; - sprite.visible = FALSE; - - if (useBatchNode_) { - [batchNode_ addChild:sprite]; - } else { - [self addChild:sprite]; - } - [spriterFrame addSprite:sprite]; - } - } - [frames_ setObject:spriterFrame forKey:spriterFrameName]; - } - */ - - /* - // load all animations - for (TGSpriterConfigNode * c in [[configRoot_.children objectAtIndex:0] children]) { - if (![[c name] isEqualToString:@"char"]) - continue; - - for (TGSpriterConfigNode * charNodes in c.children) { - if ([charNodes.name isEqualToString:@"name"]) { - //CCLOG(@"Character Name: %@", charNodes.value); - continue; - } else if ([charNodes.name isEqualToString:@"anim"]) { - TGSpriterAnimation * animation = [TGSpriterAnimation spriterAnimation]; - NSString * animationName = @""; - for (TGSpriterConfigNode * frames in charNodes.children) { - if ([frames.name isEqualToString:@"name"]) { // animation name - animationName = frames.value; - } else if ([frames.name isEqualToString:@"frame"]) { - NSString * frameName = @""; - double frameDuration = 0; - for (TGSpriterConfigNode * frameProp in frames.children) { - if ([frameProp.name isEqualToString:@"name"]) { - frameName = frameProp.value; - } else if ([frameProp.name isEqualToString:@"duration"]) { - // the spec stats milliseconds, but this doesn't match the reference implementation. - frameDuration = [frameProp.value doubleValue]/100.0;// milliseconds?? should be 1000 - } - } - [animation addFrame:[frames_ objectForKey:frameName] duration:frameDuration]; - } - } - [animations_ setObject:animation forKey:animationName]; - } - } - } - */ // clean up if (configRoot_) { @@ -529,4 +488,7 @@ -(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { } } +-(BOOL) animationEnded { return TRUE;} +-(void)animationFrameChanged {} + @end