Прямая ссылка на экземпляры класса мувиклипа в Actionscript 2 - PullRequest
2 голосов
/ 22 мая 2009

Вот действительно расстраивающая проблема ActionScript 2, с которой я столкнулся с видеоклипами, связанными с классами, которые созданы на временной шкале и на которые нужно сразу ссылаться в коде:

- в моей библиотеке есть фрагмент ролика "C", связанный с классом "C".

- Класс "C" расширяет MovieClip.

- У меня есть клип на сцене с надписью «Выкл.» И «Вкл.». Этот фрагмент ролика имеет имя экземпляра "mc".

- В кадре «on» есть экземпляр класса «C» с именем экземпляра «inst».

- Конструктор для класса "C" включает в себя оператор трассировки для вывода "Конструктор C!" дайте мне знать, когда будет создан экземпляр на сцене.

Теперь допустим, я запускаю этот код:

mc.gotoAndPlay("on");
var inst_mc:MovieClip = mc.inst;

if (inst_mc){
    trace("inst_mc found!");
}else{
    trace("inst_mc NOT FOUND!");
}

var inst_c:C = C(mc.inst);

if (inst_c){
    trace("inst_c found!");
}else{
    trace("inst_c NOT FOUND!");
}

Кажется, что создание любого объекта в классе, таком как C, не произойдет до тех пор, пока не завершится выполнение всего кода для текущего кадра, потому что вывод будет следующим:

inst_mc found!
inst_c NOT FOUND!
C constructor!

Какого черта здесь происходит? Я явно сказал среде разработки Flash, что фрагмент ролика C связан с классом C, а этот класс C является производной MovieClip. Поэтому в моем коде gotoAndPlay («on») создаст фрагмент ролика «inst», который находится во фрейме «on». Он может найти экземпляр в порядке, но когда я воспринимаю его как тип C, он не работает. И тогда конструктор происходит после всего этого. Как это исправить? Я надеюсь, что после того, как вы что-то измените на временной шкале, соответствующие объекты будут созданы немедленно - и они есть, за исключением случаев, когда они являются явными типами классов. Я могу сослаться на свой экземпляр, но только как мувиклип. Как, черт возьми, я могу это исправить? Должно выдать:

C constructor!
inst_mc found!
inst_c found!

Спасибо за любую помощь!

* ОБНОВЛЕНИЕ * Спасибо за ответы! К сожалению, для моего проекта нет простого решения, которое сейчас велико и не может быть легко реструктурировано (оно также слишком велико, чтобы его можно было преобразовать в AS3). Я думал о том, чтобы сохранить MC в кадре 1 и скрыть их, но я считаю, что это излишне увеличивает накладные расходы. Даже если для _visible установлено значение false, не будет ли оно по-прежнему расходовать ресурсы? (Это другая, но связанная с этим проблема: отличается ли производительность, если у вас есть сложный, стационарный MC, который не является _видимым, по сравнению с тем, когда его вообще нет?)

Моя текущая стратегия выглядит примерно так:

mc.gotoAndPlay("on");
var inst_mc:MovieClip = mc.inst;
var inst_c:C = C(mc.inst);

if (inst_c){
    // Even though I moved to the "on" frame,
    // the object was already initialized/existed already
    // so i can use its class code now
    inst_c.do_something_now();
}else{
    // The class is not accessible, so set a boolean flag
    // which will get dynamically assigned to the *movie clip*.
    // The constructor in class C will look to see if the flag
    // has already been set.  If so, it calls do_something_now()
    // within C's constructor.
    // In class C, trigger_do_something_now is a defined as a 
    // Boolean with no default value.
    // It is not set in the constructor.
    inst_mc.trigger_do_something_now = true;
}

Этот подход беспокоит меня. Это грязно и запутанно. Но я думаю, что это разумный обходной путь. Что, вы парни, думаете? Спасибо!

Ответы [ 2 ]

2 голосов
/ 23 мая 2009

К сожалению, так работает «объектно-ориентированный» код в AS2. Вот как вы должны это визуализировать: когда указатель времени на временной шкале находится на кадре A и вы вызываете gotoAndStop(B), указатель воспроизведения сразу продвигается и все необходимые объекты создаются, но все сценарии на временной шкале, которые есть в кадре B, еще не выполнены , потому что Flash все еще завершает выполнение кадра A. Только в следующей итерации внутреннего цикла Flash обрабатывается кадр B.

Так было с тех времен, когда уроки молочного склянки были просто проблеском. И, как вы можете догадаться, с прикрепленными к классу клипами все работает одинаково - когда вы перемещаете точку воспроизведения в кадр B, Flash создает все в кадре B, но не запускает сценарии, которые находятся в кадре B, - и это включает в себя конструкторы только что созданных клипов - до следующего кадра.

Самый простой обходной путь - это изменить временную шкалу так, чтобы рассматриваемый MC был создан в кадре 1, но оставался скрытым (или неактивным, за кадром и т. Д.) До команды gotoAndStop. Вы можете сделать это, изменив его альфа (и т. Д.) В IDE в данном кадре, или просто вызвав метод init в то же время, когда вы выполняете gotoAndStop. лучший обходной путь - использовать AS3, если это вариант. Есть и другие пути, по которым вы можете пойти, но я бы порекомендовал один из этих двух.

обновление для редактирования: Да, один из «других способов, которыми вы можете пойти», - это поместить параметры в клип или его родительский элемент и заставить клип искать их при инициализации, но тот, кто поддержит ваш код, не поблагодарит вас за это. , Но я конечно сделал это. ;)

В ответ на ваш побочный вопрос клип с _visible = false влечет за собой очень небольшие накладные расходы, если он находится в пределах сцены. Он не рендерится, но есть очень маленький хит, который, я думаю, связан с тем, что Flash рассчитывает свои границы или что-то подобное. (Я провел тест один раз, и вам действительно нужны тысячи из них, чтобы его можно было измерить.) Даже если это влияет на производительность, проблема заключается в том, чтобы убрать невидимые клипы за пределы сцены. Затем они не испытывают никакого попадания процессора, что я обнаружил, они просто используют память скриптов (и если вы откладываете их инициализацию до тех пор, пока они не будут готовы к использованию, очень мало памяти).

Просто помните, что обработчики событий срабатывают независимо от того, является ли клип видимым, поэтому не регистрируйте его для событий, пока вы его не инициализируете. Что бы я сделал, это сделал метод init () со всем, что принадлежит конструктору, и вызвал бы его там, где вы иначе построили бы клип.

1 голос
/ 27 мая 2009

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

Перечитывая эту ветку, я думаю, что все сводится к "ожиданию одного кадра". Это обходной путь, который мне приходилось использовать много-много раз в AS2, поэтому мне кажется странным, что я не заметил, что это было настоящей проблемой здесь.

В любом случае, ваш обходной путь, вероятно, сработает, но, как вы говорите, он может стать кошмаром для техобслуживания очень быстро. Опция Fenomas является еще одним допустимым решением. В этом случае я бы не пошел таким путем, если бы мог избежать этого, но не из-за производительности; скорее, потому что перестановка многих вещей, когда вы уже выложили вещи, может быть большой работой.

Итак, возможно, вы могли бы попробовать что-то действительно простое, как:

mc.gotoAndPlay("on");
this.onEnterFrame = function():Void {
    trace(mc.inst instanceof C);
    delete this.onEnterFrame;
};

И я думаю, что это должно работать. Событие enterFrame будет перехвачено вашим обработчиком через один кадр после регистрации в нем. В этот момент у вас есть готовый фрейм, поэтому вам нужно просто очистить обработчик и сделать то, что вы обычно делаете, сразу после выпуска gotoAndPlay.

Я помню, что когда-то мне приходилось делать что-то очень похожее (это был не совсем тот же сценарий, но он сводился к ожиданию одного кадра), поэтому в то время я написал очень простой класс для централизации этого кода. Это было что-то вроде этого:

class FrameDelay {

    function FrameDelay(scope:Object,callback:Function,args:Array) {
            // get a reference to the current enterFrame handler
            // (if any), so we can restore it back when we're done   
        var oldEnterFrameHandler:Function = _root.onEnterFrame;

    _root.onEnterFrame = function():Void {
        oldEnterFrameHandler();
        callback.apply(scope,args);
        _root.onEnterFrame = oldEnterFrameHandler;
    }
    }

}

И вы бы использовали это так:

mc.gotoAndPlay("on");

new FrameDelay(this,onFrameReady,["a","1"]);

function onFrameReady():Void {
    //  arguments (if any), will be available in the arguments array 
    trace(arguments.length);
    trace(mc.inst instanceof C);
    trace(this); 

}

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

Также обратите внимание, что я использую _root.onEnterFrame. Хотя я делаю «резервную копию» исходного обработчика и восстанавливаю его обратно, когда я закончу, это не обязательно означает, что другие части кода являются такими вежливыми (!). Таким образом, обработчик может быть перезаписан. Если вы думаете, что это может быть проблемой, возможно, вы можете динамически создать мувиклип в корне на некоторой глубине, которую вы знаете, что он не используется, и заменить _root.onEnterFrame на _root.dummy_mc.onEnterFrame.

В любом случае, имейте в виду, что простой встроенный onEnterFrame сделает эту работу, поэтому, возможно, вам даже не понадобится использовать класс для этого.

...