Как сделать полный stop() мувиклипу

Однажды столкнулся я с проблемой — нужно остановить мувиклип перед тем, как положить его обратно в пул. Чтобы гад не крутился там в небытии и не жрал ресурсы. Однако метод stop() останавливает только таймлайн самого мувиклипа, а все мувиклипы-дети, которые на нем крутятся — продолжают крутиться. Например, если у вас есть клип «танк», в котором есть анимация кручения гусеницами, но дополнительно в нем есть клип «человечек в люке», который машет флажком. То после метода stop() на танке гусеницы остановятся. А человечек все машет и машет, машет и машет. Танк может уже удалили, но пока его не зачистил гарбадж коллектор — он машет и машет, проц напрягается и напрягается. А если у вас реализован пул танков, то сотни танков в пуле и на каждом человечек машет и машет флажком… Ужос!


Понятно, что надо делать метод, который иерархически обегает все это хозяйство и делает ему stop(). В случае танка, кстати, теоретически, если у вас архитектурно-оопешно в коде все правильно сделано, то, по идее, танк знает о человечке и стоп должен быть неуниверсальный для всех клипов, а реализован в классе танк. Ведь, возможно надо кроме стопа анимации человечка дополнительно отключить ему мозги (отвязаться от квантов или энтерфрейм у кого похуже сделано). Мозги то тоже жрут ресурсы в небытии.

Однако, есть масса мест, где хорошо бы иметь универсальный метод остановить намертво все анимации. Например, если у вас view для юнита не связано с логикой. Ну типа это художник так сделал, как удобно — не все анимации в одном таймлайне и гусеницы и флажок. А иерархически. Тут можно порассуждать про Graphic vs MovieClip во FlashIDE, но это отдельная тема. Достаточно того, что полно ситуаций когда вариант view сложносоставное, но с логикой не связанное. Например в сложной системе анимированных меню.

И вот, наконец. Мы можем сделать универсальный статический утилити метод в каком-нибудь классе MovieClipUtility который использовать каждый раз, когда надо намертво застопорить мувиклип. А можно добавить самому в класс MovieClip такой недостающий метод stopAll() (ау Адоби!). Я делаю это так — метод upgradeMovieClip дергаю в самом начале конструктора главного класса игры. И все. Теперь можно дергать у клипов метод stopAll().

Может кому-то пригодится. Или покритикуйте.


protected function upgradeMovieClip() : void {
        MovieClip.prototype.stopAll = function(dObjCont : DisplayObjectContainer = null) : void {
                if (dObjCont != null) {
                        var mc : MovieClip = dObjCont as MovieClip;
                        if (mc != null) {
                                mc.stop();
                        }
                        
                } else {
                        stop();
                        dObjCont = this;
                }
                var contLength : int = dObjCont.numChildren;
                for (var i : int = 0; i < contLength; i++) {
                        var dObjChildCont : DisplayObjectContainer = dObjCont.getChildAt(i) as DisplayObjectContainer;
                        if (dObjChildCont != null) {
                                this.stopAll(dObjChildCont);
                        }
                }
        };
}
  • +10

Комментарии (28)

0
Интересно, возьму себе в копилку. Хотя расширять MovieClip — не самая хорошая идея, мне кажется. Могут возникнуть проблемы с совместимостью в будущих версиях апи.
0
ну обзови метод не stopAll а myStopAll, customStopAll или даже elmortemHardStopAll :) — конфликта точно избежишь.
0
Не, я запихал всё это дело в утилитный класс, мне так ближе. Спасибо.
Кстати, буквально недавно сталкивался с подобной проблемой. Только там у меня ещё и звук в кадрах был. Не сразу понял, что за фигня. (:
0
Штука наверное полезная, но появился вопрос: танчик действительно будет крутить и махать если он удален из дисплейлиста?
+1
Ага. Поставь в кадры таймлайна трейс и смотри в аутпут.
0
вот ведь подстава какая. Получается перед запуливанием в пул на повторное использование мувклипы надо совсем хардкорно ресетить :)
Спасибо. бум знать.
+1
Еще нужен код для запуска всех деток. Пример использования — пауза в игре.
Натыкался на проблему, что некоторые детки могут быть в состоянии «стоп» до нажатия паузы, и соответствено при при запуске клипа им нужно это состояние сохранить.
0
Проблема относится не только к MovieClip, а ко всем видам DisplayObject. И лучше все-таки реализовать этот метод, как простой статический, а не как прототип, хотя бы для того чтобы с intellisense он был доступен.

И еще: можно сделать все проще: в базовом классе всех игровых объектов подписаться на REMOVED_FROM_STAGE и там вызывать stop() (а на ADDED_TO_STAGE gotoAndPlay() если надо).
0
Совершенно не понял о чем это. Только мувиклип имеет таймлайн, методы play() stop() итд.

Про «проще сделать» тоже не понял. Пробежаться по всей иерархии и всех подписать на REMOVED_FROM_STAGE? Это-ж жесть.
0
1) Да, stop() есть только у MovieClip, но в целом сам анимационный объект может быть любого типа. Например танк может являться спрайтом, а гусиницы у него — MovieClip. В этом случае, чтобы не искать, какие именно составляющие у танка клипы, а какие нет, можно сделать статический метод, принимающий на вход любой DisplayObject.

2) Нет, просто наследовать все игровые объекты не от MovieClip, а от некоего BaseObject, который сам подписывается на REMOVED_FROM_STAGE при инициализации. Причем на базе общего предка можно организовать и универсальную автоматическую отписку от ENTER_FRAME.
0
2) Хотя этот способ не охватывает мелкие анимационные клипы, из которых делать символы — не выход. Для таких случаев конечно придется пробегать по иерархии и подписывать. :)
0
1) Тогда речь не о DisplayObject, а DisplayObjectContainer. Метод stopAll принимает в качестве параметра любой DisplayObjectContainer. Но мне ни разу не потребовалось. Просто если у вас есть view, то правильно будет если его художник компонует (размещает гусеницы, шассии итд). И скорее всего он будет это делать в FlashIDE. А оттуда приходят к нас Мувиклипы. Еще, в таком варианте метод можно дергать и из кадров в IDE. Правда работать он будет уже только в компиляции.

2) Ок — пусть у меня есть TankMC — мувиклип из библиотеки. Там внутри художник в иерархии использовал мувиклип флага. Пусть как вы просите — у меня игровой объект Tank наследован от BaseObject, он подписывается на REMOVED_FROM_STAGE и все такое. При удалении из дисплейлиста Tank сделает stop() своему TankMC. А флаг продолжает махаться. То-есть все равно надо пробегаться по иерархии TankMC и гасить stop() всем кто попался. Либо заранее один раз пробежать и навесить каждому листенер на REMOVED_FROM_STAGE что жесть.
0
2) А почему жесть то? Вполне себе в пределах разумного, особенно если подписывать не все MovieClip, а только те, у которых totalFrames > 1. Таких клипов совсем немного, да и подписка и исполнение достаточно быстры.
0
>да и подписка и исполнение
>достаточно быстры.

я конечно не проверял, но думаю что это весьма не быстрые операции, особенно если объектов много — и листенеров много. Кучи операций выделения памяти (не думаю что для листенеров есть пулы), вставка\удаление… всё это печалит процессор.
0
Кроме оверхеда, могу привести пример. Предположим, у вас есть самолет. Он летит-летит и садится на авианосец. В коде вы его отстегнули от глобального рута и пристегнули к авианосцу. Теперь куда-бы не поплыл авианосцу — самолет остался стоять на посадочной площадке. Логично? Логично. В вашем подходе в момент removeChild все анимации самолета замрут. Нам это не надо.
0
мне кажется можно чуток подсократить, вместо


  if (dObjCont != null) 
{
  var mc : MovieClip = dObjCont as MovieClip;
         if (mc != null) {mc.stop();}
 }


написать


if (dObjCont is MovieClip) dObjCont.stop();
0
Не вкурил, у DisplayObjectContainer есть метод stop()? Или мне надо апгрейдить понимание синтаксиса АС3? :)
0
if (dObjCont is MovieClip) MovieClip(dObjCont).stop();
0
Так можно, теоретически. Однако в нашем случае все равно заменить, как предложил Олег — нельзя. Проверка на null нужна полюбому. Ибо когда в stopAll передают null (по дефолту), то мы начинаем с самого мувиклипа. В другом случае идет пробег по иерархии начиная с параметра, что нужно чтобы рекурсивно по детям идти.
0
проверку на нуль оставляем, я имел ввиду заменить объявление лишней переменной
var mc: MovieClip = dObjCont as MovieClip;
if (mc != null) {mc.stop();}

на

if (dObjCont is MovieClip) MovieClip(dObjCont).stop();
0
Вот так можно. Но я бы так не делал :) Мое ощущение, что мой вариант быстрее. Ибо «as» заменяет собой «is» и кастинг MovieClip() вместе взятые. А по скорости вроде как мне помнится «as» быстрее сейчас как ни странно. Плюс выделение на стеке переменной. ХЗ вобщем :)

Может даже проверю на досуге.
0
Да, точно, забыл приведение типа.
0
Я чуть выше написал что так как ты предложил замену нельзя сделать, ибо важно else этой проверки на null. И else должно быть именно на null как переданный через параметр метода, а не проверка на мувиклип.
0
Кстате резонный вопрос, неужели флеш просчитывает анимацию удаленных со сцены клипов? Код в кадрах он исполняет — это факт, но код и без того легко контролировать, вставив проверку на stage где надо.
Но я сомневаюсь, что скрытые анимационные клипы сами по себе нагружают процессор, т.к. нагружает процессор лишь отрисовка, а без неё там наверняка простой currentFrame++.
+1
Не рисует да. Но перфоманс жрет. Я читал перфоманс типсы в разных источниках — все говорят что в эта возня в бекграунде — зло. Более того, советуют не использовать мувиклипы там, где можно обойтись спрайтами, ибо обвеска класса Мувиклипа очень жирная. Навскидку — там далеко не только currentFrame++. Попробую пример привести. Представте таймлайн ФлешИДЕ. Первый слой — с первого по пятый кадр на сцене синий круг. А со второго по одиннадцатый — серый квадрат. При переходе по кадрам Мувиклип манипулирует контентом, одни объекты удаляются из дисплейлиста, другие добавляются. Даже в бекграунде, ибо добавив Мувиклип в любой момент обратно — вы увидите нужную картину в текущем кадре. Как на таймлайне. Я могу ошибаться насчет реализации. Я не уверен как работают gotoAndPlay — для их реализации нужно для каждого кадра строить заранее какие-то списки итд. Тем не менее — обвеска жирная. Если работать с пулами объектов (а в играх это маст, ибо гарбадж коллектор все испортит) — в пулах десятки, сотни объектов. И все они буду крутиться.

Код в кадрах не советую контроллировать таким методом. Наплодите лишних проверок (-перфоманс), легко ошибиться в любом из кадров и забыть проверить. Плюс куча граблей вас ждет по пути, ибо исполнение кода в кадре это довольно недетерменированная вещь.
+1
Вряд ли там какие-то оптимизации есть, это ж Адоби. ): Звук на фреймах в удалённом мувике точно играет — проверено.
+5
Картинка в посте навеяла
  • adzh
  • adzh
0
:)
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.