Отображение анимации по кадрам?

У меня есть анимация, состоящая из 100 изображений. Я хочу иметь возможность отображать анимацию через последовательность кадров. Например, вместо отображения анимации от 0:00 до 0:32 секунд я предпочел бы использовать Frame 0 to Frame 32. Мне нужно сделать это, потому что мне нужно много контроля над анимацией (даже разница в одном frame создаст неправильный результат). Я рисовал каждый кадр лично, поэтому я знаю изображение каждого кадра.

В любом случае я могу сделать это в Cocoa, используя Objective-C? Как мне это сделать? Все, что у меня есть, - это 100 изображений, и я не знаю, как исходить отсюда. А также, будет ли это "лагги" по сравнению с указанием времени?

Я бы предпочел подробный ответ, потому что я не мог найти какие-либо ресурсы в Интернете и совершенно новичок в этом.

Обратите внимание, что я использую OS X, а не iOS, поэтому, пожалуйста, не предоставляйте ответы, связанные с iOS. Я работаю с Xcode 3.2.6 на Snow Leopard.

Ответы

Ответ 1

Путь к максимальному контролю будет заключаться в том, чтобы хранить все ваши изображения в массиве. Если вы назвали свои изображения чем-то вроде "frame.png" (т.е. "Frame1.png" ), вы можете быстро загрузить все свои изображения следующим образом:

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]];
}

То, что это делает, создает массив и заполняет его с помощью NSImage, названного в честь ваших файлов. %i заменяется в строке для текущего значения i.

Теперь вам понадобится другая переменная, чтобы отслеживать текущий кадр. Что-то вроде unsigned int currentFrame;. Это можно либо увеличить каждый цикл обновления, либо установить определенное значение, что даст вам полный контроль над отображаемым фреймом.

Затем в вашем цикле рендеринга вы можете отобразить любой кадр, который в настоящий момент хранится следующим образом:

[(NSImage *)[frames objectAtIndex:currentFrame - 1] drawAtPoint:animPos fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];

где animPos - это NSPoint, содержащий позицию, в которую вы хотите нарисовать. - 1 присутствует в индексе, поэтому вы можете ссылаться на свои фреймы, начиная с 1, а не на 0. Этот код вытягивает изображение в рамке currentFrame и отображает его на экран.

Поскольку вы используете массив, также обязательно проверяйте currentFrame, никогда не равное значению больше максимального количества кадров, например: if (currentFrame > 100) currentFrame = 1;. Это заставило бы анимацию зацикливаться, поскольку, когда она попадает в последний кадр, currentFrame будет возвращаться к начальному индексу массива.

Для получения дополнительной информации о чертеже вы должны посмотреть: Cocoa Руководство по рисованию и Ссылка на класс NSImage. Кроме того, если вам нужна дополнительная информация о том, как обновлять фрейм каждый шаг времени, загляните в NSTimer Class Reference.

Ответ 2

Вы можете использовать этот код для анимации кадра за кадром. Надеюсь, это поможет вам.

 // 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];

Ответ 3

Вы можете использовать CADisplayLink. Вот пример того, как его использовать:

В вашем методе инициализации настройте ссылку на изображение:

CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(playAnimation:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];    

Затем добавьте метод:

- (void)playAnimation:(CADisplayLink*)displayLink
{
    // Set the right image to display
    self.imageToDisplay = TheRightImageBasedOnYourAnimationSequence

    [self setNeedsDisplay];
}

И, наконец, в методе drawRect: отобразите изображение. Класс CADisplayLink имеет атрибуты для управления продолжительностью кадра и т.д.

Ответ 4

Повторить ответ @wquist: -

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]];
}

В прошлом у меня было несколько анимаций с именами файлов, такими как image_00000.png, image_00001.png, и чтобы позаботиться о них, я использовал что-то вроде этого: -

NSString *filename = [NSString stringWithFormat:@"frame%05d.png", i];

Ответ 6

Пробовали ли вы подклассифицировать NSAnimation и изменили ли отображаемое изображение каждый тик?

EDIT:

Сначала вы создаете подкласс NSAnimation. Затем переопределите setCurrentProgress:, вызвав его реализацию суперкласса и изменив представление в зависимости от currentValue. См. Следующее руководство для более подробных инструкций: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/AnimationGuide/Articles/TimingAnimations.html#//apple_ref/doc/uid/TP40003581-SW1