PDA

Просмотр полной версии : AS2: Получить (рекурсивный?) список мувиклипов с полным путем.


xintrea
05.07.2007, 21:27
Здравствуйте!

Инструменты - mtasc, FDT.

В целях облегчения отладки, хочу сделать себе такую функцию, которая бы выводила в trace информацию о всех существующих в данный момент клипах, начиная с _root, с полным путем.

Пока что найденные решения (типа такого http://www.flasher.ru/forum/showthread.php?t=91727) не радуют, потому что

- не показывают полного пути (чтобы виден был _root или там _level0)
- показывают мувиклипы только текущего уровня. Если в мувиклипе содержится еще один мувиклип, внутри которого еще мувиклипы, то их не видно. (возможно я тут не прав, возможно я не вижу "внутреннего" мувиклипа потому что неправильно загружаю, но проверить не могу потому что нет функции отладки, о которой я как раз и говорю)

В общем, нужен аналог списка мувиков (или даже всех объектов), который можно получить в Flash IDE через менюшку List Objects.

Есть уже готовые наработки?

Kikasso
05.07.2007, 22:03
такое сгодится?
function giperTrace(o){
if(typeof(o)!='movieclip' || o.checked ) return;
trace(o);
o.checked = true;
for( var p in o )giperTrace ( o[p]);
}
giperTrace(this);
немного портит клипы.. добавляет свойство, чтобы по перекрестным ссылкам не бегать. Можно и без этого обойтись, создав объект:
var _checked_:Object = {};
function giperTrace(o){
if(typeof(o)!='movieclip' || _checked_[o._target] ) return;
trace(o);
_checked_[o._target] = true;
for( var p in o )giperTrace ( o[p]);
}
giperTrace(this);

xintrea
06.07.2007, 01:57
Нет, не работает оно как надо.

Вот код который у меня получился. Я подгружаю swf-файл, в котором есть три мувиклипа (для каждого мувиклипа прописаны LinkageID и заданы InstanceName).


class Application
{
var _checked_:Object = {};
private var scopeRef:MovieClip;

function Application(scope:MovieClip)
{
Flashout.info("Application constructor start.");

scopeRef = scope;

// Загрузка ресурсов
var dpt=10;
scopeRef.createEmptyMovieClip("resource", dpt++);
scopeRef.resource._x=200;
scopeRef.resource.loadMovie("resource.swf");

// Пробуем сделать список что имеем в ресурсах в данный момент
// но ничего внутри ресурсов не видно
trace("List of resource start");
for(var prop in scopeRef.resource)
trace("Prop "+scopeRef.resource[prop]);
trace("List of resource end");

// Программно создаем текстовое поле
scopeRef.createTextField("tf", 0, 100, 100, 800, 600);
scopeRef.tf.text = "Hello flasher!";
}

// Функция рекурсивной печати имеющихся мувиклипов
public function giperTrace(o)
{
if(typeof(o)!='movieclip' || _checked_[o._target] ) return;
trace(o);
_checked_[o._target] = true;
for( var p in o )
giperTrace ( o[p]);
}

// --- Main Entry Point
static function main()
{
trace("Run main function");

// Запускаем инициализацию приложеня
var appinst:Application = new Application(_root);

// Печатаем список всех мувиклипов
appinst.giperTrace(_root);
}
}


В результате выполнения имеем лог такой


Run main function
Application constructor start.
List of resource start
List of resource end
_level0
_level0.resource


То есть виден только мувик "resource". Внутри него ничего не видим. А если скомпилированный swf-файл запустить в Flash IDE и посмотреть Object List, то увидим такое


Level #0: Frame=1
Edit Text: Target="_level0.tf" Variable= Visible=true Text = Hello flasher!"
Movie Clip: Frame=1 Target="_level0.resource"
Movie Clip: Frame=1 Target="_level0.resource.mcCellCommandPlayer"
Shape:
Movie Clip: Frame=1 Target="_level0.resource.mcCellEnemyPlayer"
Shape:
Movie Clip: Frame=1 Target="_level0.resource.mcCellBall"
Shape:
Shape:
Edit Text: Target="_level0.resource.instance1" Variable= Visible=true Text = CellCommandPlayer"
Edit Text: Target="_level0.resource.instance2" Variable= Visible=true Text = CellEnemyPlayer"
Edit Text: Target="_level0.resource.instance3" Variable= Visible=true Text = CellBall"


то есть на самом деле внутри мувика "resource" есть три мувиклипа, но они почему-то недоступны. Кстати, внутри конструктора Application я тоже пытаюсь распечатать содержимое приаттаченого swf-файла. Но даже с прямым указанием мувика, который нужно обработать, мувики внутри него не видятся. Мувик подгружается нормально, и его, и мувики внутри него из которых он состоит, я вижу в окне флешплейера при запуске. Но мувики внутри подруженного как мувик swf-файла - недоступны программно, хотя для них в serource-фале указаны (как писал выше) LinkageID и InstanceName.


Поэтому вопрос - почему Flash при печати Object List видит мувики внутри swf-файла, а программно они недоступны? И как сделать чтоб они стали доступными, например, для копирования? Чтобы можно было копировать эти встроенные мувики через attachMovie() или там duplicateMovieClip()? Или это принципиально в AS2 невозможно? И единственный выход - помещать каждый мувик в отдельный SWF-файл?

lowka
06.07.2007, 02:25
Функция рабочая (только добавьте проверку на нужные типы). Вы бы загрузки-то дождались, а потом и посмотрели, что у вас там.

xintrea
06.07.2007, 04:18
Мда.. Еще бы еще ктонить рассказал, как делать проверку на загруженность в AS2 средствами mtasc онли. Или это есть зло?

В данный момент добавил пару методов


public function checkLoaded(target_mc:MovieClip)
{
var pctLoaded:Number = target_mc.getBytesLoaded()/target_mc.getBytesTotal()*100;
trace("target_mc "+target_mc);
trace("target_mc.getBytesLoaded() "+target_mc.getBytesLoaded());
trace("target_mc.getBytesTotal() "+target_mc.getBytesTotal());
trace("pctLoaded "+pctLoaded);

if (!isNaN(pctLoaded) && (pctLoaded==100))
{
trace("clearing interval");
clearInterval(myInterval);
target_mc.onLoad = doOnLoad;
}
}

public function doOnLoad()
{
trace("Movie load full");
}


Вызов загрузки делаю так


scopeRef.createEmptyMovieClip("resource", dpt++);
scopeRef.resource.loadMovie("resource.swf");
myInterval = setInterval(checkLoaded, 100, scopeRef.resource);


Получаю такой лог


Run main function
Application constructor start.
List of resource start
List of resource end
_level0
_level0.resource
target_mc _level0.resource
target_mc.getBytesLoaded() 1440
target_mc.getBytesTotal() 1440
pctLoaded 100
clearing interval


Как видно, назначить мувику метод onLoad() не получается. Лог доходит до строчки "clearing interval", дальше по идее должа появиться строчка "Movie load full". Но ее в логе нет.

Подозреваю, что назначить метод мувику нельзя потому, что функция onLoad() в моем коде является методом класса Application, который не является мувиклипом вообще. Но как по-другому назначить мувику onLoad-метод, сообразить не могу.

PS: Размер загружаемого swf в байтах - 1087. А в логе пишется что загружается 1440 байт. Запуск swf обычный, локальный. Откуда берутся лишние байты в размере загружаемого файла?

etc
06.07.2007, 08:47
MovieClipLoader в помощь, без извращений и интервалами.

з.Ы. onLoad так не используется и работать не будет.

з.Ы.Ы. Байты берутся из расжатого swf.

xintrea
06.07.2007, 17:18
Сделал через MovieClipLoader, но не хватает последнего штриха. А именно - не могу из onLoadInit() вызвать метод нужного мне объекта.

У меня есть инстанс объекта, который именуется appinst. У этого объекта есть метод onLoadResourceMovie(). Но когда я пытаюсь его вызвать из onLoadInit() моего листенера через команду

appinst.onLoadResourceMovie(target_mc);

то компиляция не проходит, и на этой строке мне выдается ошибка

type error Unknown variable appinst

Вот код двух моих классов, из которых и состоит проект.


class Application
{
var _checked_:Object={};
private var scopeRef:MovieClip;

function Application(scope:MovieClip)
{
Flashout.info("Application constructor start.");
scopeRef = scope;
}

function addTextLabel()
{
// Программно создаем текстовое поле
scopeRef.createTextField("tf", 0, 100, 100, 800, 600);
scopeRef.tf.text = "Hello flasher!";
}

function initLoadResouceMovie()
{
// Загрузка ресурсов
var dpt=10;

// Инитим загружающие мувик листенеры
var my_mcl:MovieClipLoader = new MovieClipLoader();
var listenerinst:LoadListener = new LoadListener(my_mcl);
my_mcl.addListener(listenerinst);

// Запускаем процесс загрузки ресурсов
scopeRef.createEmptyMovieClip("resource", dpt++);
my_mcl.loadClip("resource.swf", scopeRef.resource);
}

public function giperTrace(o)
{
if(typeof(o)!='movieclip' || _checked_[o._target] ) return;
trace(o);
_checked_[o._target] = true;
for( var p in o )
giperTrace ( o[p]);
}

public function onLoadResourceMovie(target_mc:MovieClip)
{
trace("Movieclip load complete "+target_mc);
giperTrace(_root);
}

// --- Main Entry Point
static function main()
{
trace("Run main function");
var appinst:Application = new Application(_root);

appinst.addTextLabel();
appinst.initLoadResouceMovie();
}
}



class LoadListener {

var mcl:Object;

public function LoadListener(target_mcl:Object)
{
mcl=target_mcl;
}

public function onLoadStart(target_mc:MovieClip)
{
trace("********* LoadListener Start *********");
trace("Your begin load movie clip = "+target_mc);
var loadProgress:Object = mcl.getProgress(target_mc);
trace(loadProgress.bytesLoaded+" = bytes loaded at start");
trace(loadProgress.bytesTotal+" = bytes total at start");
}

public function onLoadProgress(target_mc:MovieClip, loadedBytes:Number, totalBytes:Number)
{
trace("********* LoadListener Progress *********");
trace("onLoadProgress() called back on movie clip "+target_mc);
trace(loadedBytes+" = bytes loaded at progress callback");
trace(totalBytes+" = bytes total at progress callback");
}

public function onLoadComplete(target_mc:MovieClip)
{
trace("********* LoadListener Complete *********");
trace("Your load is done on movie clip = "+target_mc);
var loadProgress:Object = mcl.getProgress(target_mc);
trace(loadProgress.bytesLoaded+" = bytes loaded at end");
trace(loadProgress.bytesTotal+" = bytes total at end");
}

public function onLoadInit(target_mc:MovieClip)
{
trace("********* LoadListener Init *********");
trace("Movie clip = "+target_mc+" is now initialized");

// теперь можно применять любые установки
appinst.onLoadResourceMovie(target_mc); // <-- Эта строка не компилится
}

public function onLoadError(target_mc:MovieClip, errorCode:String)
{
trace("********* LoadListener Error *********");
trace("ERROR CODE = "+errorCode);
trace("Your load failed on movie clip = "+target_mc+"\n");
}
}


Вооот. Я честнагря в объектном программировании не силен, поэтому понять не могу, почему инстанс appinst объекта Application неизвестен в методе onLoadInit().

Посему тупой вопрос - как правильно вызвать метод одного объекта из другого?

Kikasso
06.07.2007, 17:50
Я могу ошибаться, но _checked_ = new Object(); надо писать внутри конструктора, а в шапке только объявить его тип:var _checked_:Object;
appinst в вашем втором классе не значится. Передавайте параметр при иннициации, вы же scoope передаете в конструктор первому классу.
Я честно говоря не понимаю, нафига вам вообще этот LoadListener отдельным классом. Дописать onLoadInit в базовый класс, добавить слушателем себя, добиться нормальной работы, а потом уже мудрить.

xintrea
07.07.2007, 03:33
Я могу ошибаться, но

_checked_ = new Object();

надо писать внутри конструктора, а в шапке класса только объявить его тип:

var _checked_:Object;

Да, вы совершенно правы. Кроме того, метод giperTrace() пришлось доработать, так как воспользоваться им можно было только один раз. При следующем вызове этого метода, в объекте _checked_ уже отмечено, что данный мувик был обработан, и его имя повторно не выводится в лог. Я внес те исправления что выше написаны и сделал из одного метода giperTrace() два вот таких


public function giperTrace(o)
{
delete _checked_;
_checked_ = new Object();
giperTraceRecurse(o);
}

public function giperTraceRecurse(o)
{
if(typeof(o)!='movieclip' || _checked_[o._target] ) return;
trace(o);
_checked_[o._target] = true;
for( var p in o )
giperTrace ( o[p]);
}


Теперь вызов giperTrace() всегда работает как надо. Не знаю, красивое ли решение (может можно как-то очищать объекты, а не удалять и заводить новые), но оно работает.


appinst в вашем втором классе не значится. Передавайте параметр при иннициации, вы же scoope передаете в конструктор первому классу.

Да, спасибо за подсказку. Почему-то думал, что флеш хранит список инстансов, и к ним можно просто обращаться. А оказывается ООП во флеше почти такое же низкоуровневое как в сиплюсплюсе. Сделал с передачей параметра appinst, и вызов appinst.onLoadResourceMovie() заработал.

Теперь метод giperTrace(), вызваный из onLoadResourceMovie(), показывает все мувиклипы.


_level0
_level0.resource
_level0.resource.mcCellBall
_level0.resource.mcCellEnemyPlayer
_level0.resource.mcCellCommandPlayer



Я честно говоря не понимаю, нафига вам вообще этот LoadListener отдельным классом. Дописать onLoadInit в базовый класс, добавить слушателем себя, добиться нормальной работы, а потом уже мудрить.

Отдельным классом - это задел на будущее. Да и по логике вещей, листенеры - это вещь в себе, т.е. это такие объекты, которые не входят в состав других объектов. А значит это отдельный класс.

lowka
07.07.2007, 04:02
Да, спасибо за подсказку. Почему-то думал, что флеш хранит список инстансов, и к ним можно просто обращаться. А оказывается ООП во флеше почти такое же низкоуровневое как в сиплюсплюсе.
А чем выше уровень ООП, то тем меньше в нем места для инкапсуляции? :bye:

Kikasso
07.07.2007, 04:18
Да и по логике вещей, листенеры - это вещь в себе, т.е. это такие объекты, которые не входят в состав других объектов.
Да-да, в эту яму можно падать бесконечно.

etc
07.07.2007, 11:44
Отдельным классом - это задел на будущее. Да и по логике вещей, листенеры - это вещь в себе, т.е. это такие объекты, которые не входят в состав других объектов. А значит это отдельный класс.
Отдельный класс нахрен не нужен, но если садо-мазо и нужны проблемы с областями видимости — пожалуйста.
«Listener» — это просто объект с объявлеными методами-событиями, и не более того. Хелп флеша в изобилии набит этими листенерами, из-за чего в мозгу возникает стойкое убеждение, что для подписки на событие обязательно нужно создавать ещё один мусорный объект. А то, что можно подписать весь экземпляр текущего класса (методы-события должны быть объявлены, безусловно), как-то никто не догадывается.
В общем, послушайте Kikasso.