I have a animation consisting of 100 images. I want to be able to display the animation through a sequence of frames. For example, instead of displaying the animation from 0:00 seconds to 0:32 seconds, I would rather use Frame 0 to Frame 32. I need to do this because I need a LOT of control over the animation (even the difference of one frame would create the wrong result). I have drawn each frame personally, so I know the image of each frame.
Is there anyway I can do this in Cocoa, using Objective-C? How would I do this? All I have is 100 images and I have no idea how to proceed from here. And also, would this be "laggy" compared to specifying by time?
I would prefer a detailed answer because I couldn't find any resources online and am completely new to this.
Note that I am using OS X, not iOS, so please do not provide iOS-related answers. I'm working with Xcode 3.2.6 on Snow Leopard.
To create a frame-by-frame animation, define each frame as a keyframe and create a different image for each frame. Each new keyframe initially contains the same contents as the keyframe preceding it, so you can modify the frames in the animation incrementally.
Throughout most of the 20th century, animating frame-by-frame was the only method of producing animation. Although it's no longer necessary, it's still used by many animators for the following two purposes: Tradition: To replicate the traditional qualities of hand-drawn animation.
Frame-by-frame animation is more commonly known as stop-motion animation. It is achieved by manipulating a physical object and making it appear to move on its own by shooting one frame, manipulating the object, then shooting another frame, and so on.
Insert frames and keyframesClick and hold the mouse on the Keyframe icon to view and select the options. Select a frame in timeline and click Keyframe icon to insert frame, keyframe, or blank keyframe as per the recent selection. Inserting keyframe or blank keyframe in Timeline panel now has a visual feedback.
The way to have the most control would be to store all of your images in an array. If you named your images something like "frame.png" (i.e. "frame1.png") then you can quickly load all of your images like this:
frames = [[NSMutableArray alloc] init]; // In header: NSMutableArray *frames;
for (int i = 1; i < 100; i++) // 100 can be replaced for any number of frames.
{
NSString *filename = [NSString stringWithFormat:@"frame%i.png", i];
[frames addObject:[NSImage imageNamed:filename]];
}
What this does is creates an array and fills it with NSImage
s named after your files. The %i
is substituted in the string for the current value of i
.
Now, you'll need another variable to keep track of the current frame. Something like unsigned int currentFrame;
. This can either be incremented every update loop, or set to a specific value, giving you complete control over the frame displayed.
Then, in your render loop, you can render whichever frame is currently stored like this:
[(NSImage *)[frames objectAtIndex:currentFrame - 1] drawAtPoint:animPos fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
where animPos
is an NSPoint containing the position you wish to draw at. The - 1
is there in the index so you can refer to your frames starting at 1 rather than 0. This code pulls the image at frame currentFrame
and renders it to the screen.
Since you are using an array, also be sure to check currentFrame
is never equal to a value greater than the max number of frames, something like: if (currentFrame > 100) currentFrame = 1;
. This would cause the animation to loop, since when it gets to the last frame, currentFrame would be set back to the beginning index of the array.
For more information on drawing, you should look at: Cocoa Drawing Guide and NSImage Class Reference. Also, if you need more information on how to update the frame every increment of time, look into NSTimer Class Reference.
You can use this code for your frame by frame animation. Hope this will help you.
// create the view that will execute our animation
UIImageView* imageListView = [[UIImageView alloc] initWithFrame:self.view.frame];
// load all the frames of our animation
imageListView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:@"campFire01.gif"],
[UIImage imageNamed:@"campFire02.gif"],
[UIImage imageNamed:@"campFire03.gif"],
[UIImage imageNamed:@"campFire04.gif"],
[UIImage imageNamed:@"campFire05.gif"],
[UIImage imageNamed:@"campFire06.gif"],
[UIImage imageNamed:@"campFire07.gif"],
[UIImage imageNamed:@"campFire08.gif"],
[UIImage imageNamed:@"campFire09.gif"],
[UIImage imageNamed:@"campFire10.gif"],
[UIImage imageNamed:@"campFire11.gif"],
[UIImage imageNamed:@"campFire12.gif"],
[UIImage imageNamed:@"campFire13.gif"],
[UIImage imageNamed:@"campFire14.gif"],
[UIImage imageNamed:@"campFire15.gif"],
[UIImage imageNamed:@"campFire16.gif"],
[UIImage imageNamed:@"campFire17.gif"],
//all your 100 images// , nil];
// all frames will execute in 1.75 seconds
imageListView.animationDuration = 1.75;
// repeat the annimation forever
imageListView.animationRepeatCount = 0;
// start animating
[imageListView startAnimating];
// add the animation view to the main window
[self.view addSubview:imageListView];
You might consider using CADisplayLink. Here's an example on how to use it:
In your initialization method, setup the display link:
CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(playAnimation:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
Then add the method:
- (void)playAnimation:(CADisplayLink*)displayLink
{
// Set the right image to display
self.imageToDisplay = TheRightImageBasedOnYourAnimationSequence
[self setNeedsDisplay];
}
And lastly, in the drawRect: method, display the image. The CADisplayLink class has attributes to control the frame duration, etc...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With