Второе решение.
Вложенные списки
А что нам мешает помещать в конвейер действия, при выполнении которых будут создаваться действия конвейера? Практически ничего не мешает. Но созданные действия таким способом будут помещены уже в конец списка действий. Очень сложно выстроить правильную последовательность при таком подходе. Об этой проблеме я уже писал в «истории создания конвейера». Проблема уже не столько касается механизмов работы конвейера, суть этой проблемы помочь нам максимально упростить работу с большими списками команд. И решение здесь – это использование иерархичных списков.
В чем суть иерархических списков? Суть в том, что часть команд мы наделяем дополнительным поведением, при котором все новые действия созданные при выполнении этих команд не помещались в конец списка команд, а выполнялись «на том же месте» где находилась команда, которая их создала. Уже при описании этого решения могут быть проблемы с пониманием сути. Итак, есть некий большой список. Но некоторые команды – это не просто команды, они хранят инструкции по созданию других списков, и при выполнении данной команды в место одной команды будут помещены элементы только что созданного списка.
То есть, помещаем некоторые действия, которые создадут списки, а действия в тех списках опять же могут создавать вложенные списки.
Для этого, кроме метода put нам необходим метод include. Он будет, так же как и метод put создавать элементы списка. Но будет иметь дополнительные «способности» для создания вложенных списков.
Например, какой-то объект создал список, а один из элементов этого списка был создан с помощью include и должен вызывать у другого объекта метод, при выполнении которого создастся еще несколько элементов списка. Один из этих элементов опять же будет создан с помощью include. И когда действие, содержащееся в этом элементе, выполнится, создадутся еще действия.
Как это работает:
public function include()
{
this.put(this, "bookmark_add", 0);
this.put.apply(this, arguments);
this.put(this, "bookmark_remove", 0);
} |
Как видно из этого кода мы помещаем не один элемент списка, а три. Первый создает метку в списке конвейера. Второй - это то действие, которое помещается методом include. Третий элемент - это метод конвейера, который ищет метку, удаляет ее, но и все действия, которые были перемещены в конец списка (до метки), перемещает вверх (вместо действия помещенного методом include).
Своего рода новый уровень работы конвейера. Для реализации внутреннего механизма конвейер использует свой «внешний» сервис.
Спецификация include:
| Conv.include |
Conv.include( function[, timeout][, argument1, … argumentN] )
Conv.include( function, scope, [, timeout][, argument1, … argumentN] )
Conv.include ( object, methodName[, timeout][, argument1, … argumentN])
function – ссылка на функцию, которую необходимо выполнить.
object – путь к объекту. Он может задаваться в виде ссылки и (если объект еще не существует в момент добавления действия) – в виде строки, например, "_root.any_object".
methodName – строка, имя вызываемого метода объекта
scope – ссылка на объект - пространство имен, то что при выполнении функции будет this.
timeout – строка или номер, задержка после выполнения. Строка – задержка во фреймах. Номер – в миллисекундах.
arguments – аргументы
Ничего
Метод полностью идентичен методу Conv.put . Но все действия, помещаемые в список конвейера действием, созданным этим методом, будут выполнены сразу после выполнения этого действия.
Для создания вложенных списков.
Conv.include(function()
{
trace("Первый вызов");
Conv.include(function()
{
Conv.put(trace, "1", "Второй вызов")
Conv.put(trace, "1", "Третий вызов")
});
Conv.put(trace, "1" , "Четвертый вызов");
});
Conv.put(trace, "1" , "Пятый вызов");
// Первый вызов
// Второй вызов
// Третий вызов
// Четвертый вызов
// Пятый вызов |
А так будет без использования include:
Conv.put(function()
{
trace("Первый вызов");
Conv.put (function()
{
Conv.put(trace, "1", "Второй вызов")
Conv.put(trace, "1", "Третий вызов")
});
Conv.put(trace, "1" , "Четвертый вызов");
});
Conv.put(trace, "1" , "Пятый вызов");
// Первый вызов
// Четвертый вызов
// Пятый вызов
// Второй вызов
// Третий вызов |
|
В принципе include по использованию ничем не отличается от метода put. Можно вообще использовать только один include. Но, во-первых, put – быстрее. Меньше выполняет работы. А во-вторых, нужно понимать различие действий: создание элемента списка (put) и создание элемента списка для создания вложенных списков (include). При таком подходе код будет выглядеть яснее.
Получив новый инструмент, перепишем код рулетки. Так как ставки будут так же работать с конвейером, и создавать свои списки, нам нужно описать и методы ставок.
Сначала метод SPIN:
private function spin()
{
this.lock();
// вычислить игровой результат
this.createGameResult();
// начать движение колеса
// (время 100 кадров)
Conv.put ( this , "startWeel" , "100" );
// остановить колесо
Conv.put(this, "stopWeel", 0);
// озвучить игровой результат
// время – две секунды
Conv.put(this, "gameSounds", 2000);
// анимационно убрать проигравшие ставки
Conv.include ( this , "looses" );
// анимационно показать выигрыш всех
/// выигравших (если такие имеются)
Conv.include ( this , "wins" );
// разблокировать интерфейс
// игрового приложения
Conv.put ( this , "unlock" , 0)
} |
Это практически первый вариант, кроме того, что в список помещается вызов методов wins и looses через include и без указания времени задержки.
Далее опишем методы wins и looses.
private function looses() { for( var i=0; i<this.looses_array.length; i++) { // анимационно убрать // проигравшие ставки Conv.include(this.looses_array[i], "loose"); } } private function wins() { for ( var i=0; i<this.wins_array.length; i++) { // анимационно показать // выигрыш у каждой ставки Conv.include(this.wins_array[i], "win"); this.balance += this.wins_array[i].winsValue; // покажем на дисплее // баланса игрока // увеличение баланса Conv.put(_root.balanceDisplay_mc, "setBalance", this.balance); } } |
Может показаться странным, что мы опять используем вызов include для создания действий ставок (уже во вложенном списке). На самом деле - это суть подхода.
В методе spin мы описываем общую последовательность действий. Во вложенных списках мы описываем уже частные моменты, которые нам нужно показать. И, наконец, спускаемся до самих ставок и это уже анимация. И это самый лучший подход. На каждом этапе мы работаем с небольшим и «обозримым» списком. Во вложенных списках мы работам со более детализированными, но так же «обозримыми» списками. Наша задача – ясный код. А метод, который мы используем для решения этой задачи - это иерархичность.
Распределенные вычисления
Еще небольшое отступление. В данном примере нам не обязательно вкладывать списки друг в друга (код не настолько сложен). Мы могли бы просто вызвать сначала метод looses в головном классе, тот в свою очередь вызвал методы у ставок напрямую, а списки мы создавали бы только в ставках. И список сразу бы содержал все необходимые действия без вложенности. Но представьте, какой длины получился бы список (а ставок в рулетке должно быть около 150, а в некоторых вариантах рулетки даже больше)? И сколько вычислительной работы нам нужно сделать сразу? А вложенными списками мы достигаем распределение вычислений во времени. Это замечательное свойство! Признаюсь, что первоначально include создавался именно для этой цели. В одной игре нельзя было вычислить все действия в один момент - Flash просто «зависал». И пришла идея распределения вычислений во времени. А другие замечательные свойства include стали понятны позже, и возможность распределения вычислений во времени отошла на второй план, но не стала от этого менее ценной.
Предыдущая часть | Следующая часть
[1] [2] [3] [4] [5] [6] [7] |