Доступ к мовиклипам после gotoAndStop
В этом посте я хотел рассмотреть одно не очень очевидное поведение флеша при смене кадров с помощью gotoAndStop. А именно если вы пишете чтото вроде:
this.gotoAndStop(2); this.MovieClipFromFrameTwo.x = 90;
То вас ждёт глубокое разочарование – MovieClipFromFrameTwo будет не доступен. Дело всё в том что дочерние объекты нового кадра не доступны до тех пор пока не будет выполнен скрипт нового кадра, а он не будет выполнятся до тех пор, пока не будет завершён текущий, т.е. там где вы писали gotoAndStop(2).
Выход из этого есть – можно использовать два события, и писать в них то, что вы хотели написать после gotoAndStop.
- Использовать событие ADDED
- Использовать событие RENDER
Изменив в них какой либо из параметров клипа можно быть уверенным что на экране отобразится уже клип с изменённым параметром. Если же вы попробуйте использовать событие ENTER_FRAME для этих целей, то увидите, что объект сначала будет появляться на экране без изменённого параметра и лишь после этого, в следующем кадре, он появится уже с изменённым.
Собственно рассмотрим простенький пример:
В примере есть клип goto_example в нём находится два кадра. На каждом кадре находится экземпляр мовиклипа с часами – на первом кадре mc1 и на втором – mc2.
Четыре кнопки управляют переходом между кадрами:
- Первая кнопка просто меняет текущий кадр на следующий. Визуально видно что при этом переходе анимация клипа начинается сначала.
- Вторая кнопка использует событие ADDED, которое изменяет кадр клипа с 0 на текущий. К сожалению на этом этапе мы не можем обратится к нашему мовиклипу по имени т.к. оно ему ещё не присвоено. Но ссылка на наш клип уже доступна и лежит в event.target. Также мы можем проверить имя клипа через свойство name.
- Третья кнопка использует событие RENDER, которое изменяет кадр клипа с 0 на текущий. Обращение по имени к клипу уже возможно, но нам необходимо до вызова gotoAndStop вызвать stage.invalidate() иначе событие RENDER не возникнет.
- Ну и четвёртая кнопка показывает использование события ENTER_FRAME. Думаю невооружённым глазом видно, что при смене кадров наш клип начинает проигрываться заново, но потом резко ставится на нужный кадр. Выглядит всё это очень печально )
Ниже приведён полный код класса goto_example. К клипам mc1 и mc2 я обращаюсь через функцию getChildByName, чтобы не писать никаких проверок )
Код примера:
package { import flash.display.MovieClip; import flash.events.*; dynamic public class goto_example extends MovieClip { //В этой переменной будем хранить номер текущего кадра нашего мовиклипа с часами var curMCFrame:uint=0; public function goto_example() { //Создаём обрабочики событий нажатия на кнопки buttGoto.addEventListener(MouseEvent.CLICK, buttGoto_Click); buttAdd.addEventListener(MouseEvent.CLICK, buttAdd_Click); buttRender.addEventListener(MouseEvent.CLICK, buttRender_Click); buttFrame.addEventListener(MouseEvent.CLICK, buttFrame_Click); } //Кнопка просто меняет текущий кадр на следующий function buttGoto_Click(e : MouseEvent):void { //Меняем текущий кадр на следующий if(currentFrame==1) gotoAndStop(2); else gotoAndStop(1); } //Кнопка меняет текущий кадр на следующий и вызывает событие ADDED function buttAdd_Click(e : MouseEvent):void { //Создаём обработчик события ADDED addEventListener(Event.ADDED, onAdded); //Запоминаем номер текущего кадра мовиклипа с часами curMCFrame=MovieClip(getChildByName("mc"+currentFrame)).currentFrame; //Меняем текущий кадр на следующий if(currentFrame==1) gotoAndStop(2); else gotoAndStop(1); } //Кнопка меняет текущий кадр на следующий и вызывает событие RENDER function buttRender_Click(e : MouseEvent):void { //Внимание! Событие RENDER сработает только если вызвать у стейджа метод invalidate stage.invalidate(); //Создаём обработчик события RENDER addEventListener(Event.RENDER, onRender); //Запоминаем номер текущего кадра мовиклипа с часами curMCFrame=MovieClip(getChildByName("mc"+currentFrame)).currentFrame; //Меняем текущий кадр на следующий if(currentFrame==1) gotoAndStop(2); else gotoAndStop(1); } function buttFrame_Click(e : MouseEvent):void { //Создаём обработчик события ENTER_FRAME addEventListener(Event.ENTER_FRAME, onEntFrame); //Запоминаем номер текущего кадра мовиклипа с часами curMCFrame=MovieClip(getChildByName("mc"+currentFrame)).currentFrame; //Меняем текущий кадр на следующий if(currentFrame==1) gotoAndStop(2); else gotoAndStop(1); } //Обработчик события ADDED function onAdded(event:Event):void { // К сожалению на этом этапе мы не можем обратится к нашему мовиклипу по имени // т.к. оно ему ещё не присвоено. Но ссылка на наш клип уже доступна и лежит в event.target var curchild:MovieClip = (event.target as MovieClip); //К счастью мы можем проверить имя клипа на данном этапе через свойство name if (curchild && curchild.name == ("mc"+currentFrame)) { //Проигрываем с нужного места мовиклип с часами curchild.gotoAndPlay(curMCFrame + 1); //Удаляем обработчик события ADDED removeEventListener(Event.ADDED, onAdded); } } //Обработчик события RENDER function onRender(event:Event):void { //Проверяем доступен ли наш клип, т.к. это может быть onRender предыдущего кадра if (MovieClip(getChildByName("mc"+currentFrame))) { //При событии RENDER клип уже доступен по имени. //Проигрываем с нужного места мовиклип с часами MovieClip(getChildByName("mc"+currentFrame)).gotoAndPlay(curMCFrame + 1); //Удаляем обработчик события RENDER removeEventListener(Event.RENDER, onRender); } } //Обработчик события ENTER_FRAME public function onEntFrame(e : Event):void { //Проверяем доступен ли наш клип, т.к. это может быть onEnterFrame предыдущего кадра if (MovieClip(getChildByName("mc"+currentFrame))) { //Проигрываем с нужного места мовиклип с часами MovieClip(getChildByName("mc"+currentFrame)).gotoAndPlay(curMCFrame + 1); //Удаляем обработчик события ENTER_FRAME removeEventListener(Event.ENTER_FRAME, onEntFrame); } } } }
Весь пример с fla файлом можно качнуть сдесь: http://www.anegmetex.com/devblog/files/sampGotoAndStop.rar

Март 9th, 2010 at 23:45
Нечаянно забрел на блог. Почти никогда не пишу комменты. Но тут жада показать что я самый умный возобладала
Мне кажется удобнее вышеприведенного использовать недокументированную функцию
addFrameScript(numberFrame, functionName)
numberFrame – номер кадра при переходе на который сработает функция functionName. (причем номер нумерация с 0.)
Например чтобы что-то сделать с клипом на втором кадре:
this.addFrameScript(1, onFrame2); //не забываем что нумерация с 0, поэтому 1 – это второй кадр.
this.gotoAndStop(2);
private function onFrame2():void {
this.MovieClipFromFrameTwo.x = 90;
this.addFrameScript(1, null); // таким образом удаляется обработчик перехода на второй кадр, чтобы он не вызывался каждый раз когда происходит переход на второй кадр
}
Надеюсь это информация окажется полезной
Март 10th, 2010 at 0:04
Спасибо за интересную информацию
Однако очень часто в кадрах уже хранятся разного рода скрипты для управления анимаций. Я так понимаю они затрутся?
Также остается открытым вопрос производительности этого метода )
Март 10th, 2010 at 1:26
Да, действительно скрипты затрутся.
Но IMHO лучше скрипты в кадрах не писать
На производительность вроде жалоб не слышал.
В любом случае каждый может выбрать какой метод ему по душе
Март 11th, 2010 at 10:57
Привет. Вот у меня раньше тоже были такие проблемы, что пока скрипт кадра не выполнится, то и мувиклипы будут не доступны. Я думаю, что этого можно всегда избежать, это просто напросто не надо при программинге. Или я не прав. Бывают какие-то случаи, что без этого не обойтись? (сейчас я вообще практически не работаю с флеш средой, художник дает сразу растр, весь код в классах, никаких кадров никаких проблем:)
Март 11th, 2010 at 14:41
Привет. Чёто не очень понятно про что речь – про скрипты в кадрах или про доступ к мовиклипам после gotoAndStop… отвечу для всех случаев )
1) Скрипты в кадрах. Ну самое распространёное – stop() и this.visible=false. Например эффекты удаляются как только они visible=false, эффект отыграл, сделал себе visible=false а класс уровня уже сам удалил его. Также регистрация объектов при добавлении в уровень. Чтото типа “myLevel(parent).objectsArray.push(this)”. Это очень полезно, когда уровни делаются прямо во flash IDE. Также может быть полезно при отыгрывании звуков зависящих от анимации. Например есть анимация падения подстреленного зомби. Звук удара об пол надо добавить например на 10 кадр. Можно конечно отслеживать из основного класса, но гораздо удобней просто добавить код в кадр “sounds.play(’zomb_floorImpact’,this)”.
2) Зачем доступ к мовиклипам после gotoAndStop. Самый простой пример, где без этого обойтись сложновато. Допустим делаем изометрическую игру про космолёты. У нас есть клип космолёта с двумя кадрами:
- на первом кадре располагается клип с 8 направлениями космолёта и с неработающими двигателями
- на втором кадре располагается клип с 8 направлениями космолёта и уже с работающими двигателями.
Естественно по ходу необходимо будет то включать движки то выключать, но направление космолёта должно сохранятся. Соотвественно как только переходим с первого кадра на второй – направление сбрасывается на начальное. Но нам нужно текущие направление. Вот тут то нам и поможет доступ к мовиклипам после gotoAndStop )
По сути похожая ситуация описана в самом посте, только про часы.
Сорри за многобукаф, надеюсь я ответил на твой вопрос
Март 21st, 2010 at 14:46
Спасибо, помучался со своими рендерами для своего листа, пытался изобразить states как во FlexSDK, понял где грабли =)
Декабрь 14th, 2010 at 22:57
Если быстро тыкать любую кнопку кроме первой то вылетает сообщение типа
An ActionScript error has occurred:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at goto_example/buttFrame_Click()
Декабрь 14th, 2010 at 23:04
Хм, странно. У меня всё ок если даже быстро кликать по любой кнопке.
Какой браузер и какая версия флеш плеера?
Декабрь 20th, 2010 at 11:05
тоже самое..ошибка вылетает
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at goto_example/buttFrame_Click()
firefox 3.6.13
10-ый плеер
Декабрь 20th, 2010 at 14:48
Короче, вероятно ошибка в функции buttFrame_Click вот в этом месте:
curMCFrame=MovieClip(getChildByName(”mc”+currentFrame)).currentFrame;
Если быстро кликать наверно getChildByName не успевает подхватить новый кадр и обрабатывает ещё старый, где этого мовиклипа ещё не было. Надо сделать проверочку что-то вроде:
if(getChildByName(”mc”+currentFrame)==null) return;
Если такого клипа ещё нет, выходим из обработчика.