Created
June 28, 2011 21:53
-
-
Save JoeOsborn/1052332 to your computer and use it in GitHub Desktop.
SpriteLayer, a CALayer subclass for spritesheet-based animation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SpriteLayer.h | |
// | |
// Created by Joseph C Osborn on 2011.01.12. | |
// Copyright 2011 Universal Happy-Maker. All rights reserved. | |
// This code is made available under the 3-clause BSD license: | |
// http://www.opensource.org/licenses/BSD-3-Clause | |
// | |
#import <Foundation/Foundation.h> | |
#import <QuartzCore/QuartzCore.h> | |
extern const int SpriteAnimEnd; | |
@interface SpriteLayer : CALayer { | |
CGSize frameSize; | |
NSString *currentAnimation; | |
int currentFrame; | |
NSMutableDictionary *animations; | |
} | |
@property (readwrite, assign, nonatomic) CGSize frameSize; | |
@property (readonly, copy, nonatomic) NSString *currentAnimation; | |
@property (readonly, assign, nonatomic) float framesWide, framesHigh; | |
- (void)addSpriteAnimationNamed:(NSString *)n atFramerate:(float)fps looping:(BOOL)loop withFrames:(int)frame1, ...; | |
- (void)play:(NSString *)anim; | |
- (void)play:(NSString *)anim restart:(BOOL)restart; | |
- (CAAnimation *)CAAnimationNamed:(NSString *)n; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SpriteLayer.m | |
// | |
// Created by Joseph C Osborn on 2011.01.12. | |
// Copyright 2011 Universal Happy-Maker. All rights reserved. | |
// This code is made available under the 3-clause BSD license: | |
// http://www.opensource.org/licenses/BSD-3-Clause | |
// | |
#import "SpriteLayer.h" | |
const int SpriteAnimEnd = -1; | |
@interface SpriteAnim : NSObject { | |
BOOL loop; | |
float framerate; | |
NSArray *frames; | |
NSMutableArray *times; | |
} | |
@property (readonly, assign, nonatomic) BOOL loop; | |
@property (readonly, assign, nonatomic) float framerate; | |
@property (readonly, copy, nonatomic) NSArray *frames; | |
- (id)initWithFramerate:(float)fps looping:(BOOL)loop frames:(NSArray *)frames; | |
- (CAAnimation *)animation; | |
@end | |
@implementation SpriteAnim | |
@synthesize loop, framerate, frames; | |
- (id)initWithFramerate:(float)fps looping:(BOOL)looping frames:(NSArray *)frameArray { | |
if((self = [super init])) { | |
framerate = fps; | |
loop = looping; | |
frames = [frameArray copy]; | |
times = [[NSMutableArray alloc] initWithCapacity:frames.count]; | |
for(int i = 0; i < frames.count; i++) { | |
[times addObject:[NSNumber numberWithFloat:i / (float)frames.count]]; | |
} | |
[times addObject:[NSNumber numberWithFloat:1.0]]; | |
} | |
return self; | |
} | |
- (void)dealloc { | |
[frames release]; | |
[times release]; | |
[super dealloc]; | |
} | |
- (CAAnimation *)animation { | |
CAKeyframeAnimation *a = [CAKeyframeAnimation animationWithKeyPath:@"contentsRect"]; | |
a.values = frames; | |
a.keyTimes = times; | |
if(loop) { | |
a.repeatCount = HUGE_VALF; | |
} | |
a.fillMode = kCAFillModeForwards; | |
a.removedOnCompletion = NO; | |
a.duration = frames.count / framerate; | |
a.calculationMode = kCAAnimationDiscrete; | |
return a; | |
} | |
@end | |
@interface SpriteLayer() | |
@property (readwrite, nonatomic, copy) NSString *currentAnimation; | |
@end | |
@implementation SpriteLayer | |
@synthesize frameSize, currentAnimation; | |
@dynamic framesWide, framesHigh; | |
- (id)init { | |
if((self = [super init])) { | |
animations = [[NSMutableDictionary alloc] initWithCapacity:8]; | |
currentAnimation = nil; | |
frameSize = CGSizeZero; | |
NSMutableDictionary *actions = [NSMutableDictionary dictionaryWithDictionary:self.actions]; | |
[actions setObject:[NSNull null] forKey:@"position"]; | |
[actions setObject:[NSNull null] forKey:@"contentsRect"]; | |
self.actions = actions; | |
self.magnificationFilter = kCAFilterNearest; | |
self.minificationFilter = kCAFilterNearest; | |
} | |
return self; | |
} | |
- (void)dealloc { | |
[currentAnimation release]; | |
[animations release]; | |
[super dealloc]; | |
} | |
- (void)addSpriteAnimationNamed:(NSString *)n atFramerate:(float)fps looping:(BOOL)loop withFrames:(int)frame1, ... { | |
NSMutableArray *frameArray = [NSMutableArray arrayWithCapacity:8]; | |
int framesWide = (int)ceilf(self.framesWide); | |
//int framesHigh = (int)ceilf(self.framesHigh); | |
double fw = self.frameSize.width/CGImageGetWidth((CGImageRef)self.contents); | |
double fh = self.frameSize.height/CGImageGetHeight((CGImageRef)self.contents); | |
va_list args; | |
va_start(args, frame1); | |
int f = frame1; | |
while(f != SpriteAnimEnd) { | |
int col = f % framesWide; | |
int row = f / framesWide; | |
CGRect cBounds = CGRectMake(fw*col, fh*row, fw, fh); | |
[frameArray addObject:[NSValue valueWithCGRect:cBounds]]; | |
f = va_arg(args, int); | |
} | |
va_end(args); | |
SpriteAnim *anim = [[[SpriteAnim alloc] initWithFramerate:fps looping:loop frames:frameArray] autorelease]; | |
[animations setObject:anim forKey:n]; | |
} | |
- (void)play:(NSString *)anim { | |
[self play:anim restart:NO]; | |
} | |
- (void)play:(NSString *)anim restart:(BOOL)restart { | |
if(!restart && [self.currentAnimation isEqualToString:anim]) { return; } | |
CAAnimation *caAnim = [self CAAnimationNamed:anim]; | |
if(!caAnim) { return; } | |
if([self animationForKey:@"sprite-animation"]) { | |
[self removeAnimationForKey:@"sprite-animation"]; | |
} | |
self.currentAnimation = anim; | |
caAnim.delegate = self; | |
[self addAnimation:caAnim forKey:@"sprite-animation"]; | |
} | |
- (CAAnimation *)CAAnimationNamed:(NSString *)n { | |
return [[animations objectForKey:n] animation]; | |
} | |
//- (void)animationDidStart:(CAAnimation *)anim { | |
// | |
//} | |
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { | |
if([self animationForKey:@"sprite-animation"] == anim) { | |
self.currentAnimation = nil; | |
} | |
} | |
- (float)framesWide { | |
return CGImageGetWidth((CGImageRef)self.contents)/frameSize.width; | |
} | |
- (float)framesHigh { | |
return CGImageGetHeight((CGImageRef)self.contents)/frameSize.height; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment