Нам, ActionScript’ерам,
порой приходится делать то же самое: раз мы
создаем новое Flash-приложение, обычные
инструменты программирования (встроенные
объекты Flash) могут не очень-то отвечать
нашим надобностям. В таком случае мы
закатываем рукава и создаем свои собственные
– а значит, вы должны знать механику, общие
принципы того, как работают объекты в
ActionScript.
Внимание! Для работы с этим
уроком необходимы учебные файлы, которые
Вы можете загрузить
здесь.
В этом уроке мы покажем вам, как
работают объекты и как их создавать, так
что впоследствии вы сможете строить
новые классы объектов с нуля, равно как
и расширять возможности существующих.
Разъяснение механики объектов
В Уроке 4 мы познакомили вас
со встроенными объектами Flash (объекты,
перечисленные в инструментальном окне панели
Действия). В том уроке мы с вами создавали
экземпляры встроенных объектов, изменяли их
свойства и использовали различные методы,
заставляя объекты выполнять те или иные
задачи. Думается, в процессе этого вы вошли
во вкус, почувствовали, насколько объекты
облегчают построение интерактивных проектов.
Однако тогда мы не могли объяснить, почему
объекты работают именно так, а не иначе –
сначала вы должны были познакомиться с
функциями, поскольку именно они играют
важнейшую роль в деле создания и
модифицирования объектов. Теперь, изучив
функции (это было в Уроке 5), вы готовы
приступить к кропотливому, но жизненно
необходимому делу создания и изменения
объектов.
Создание экземпляров объектов и классов
объектов – в чем разница?
Чувствую, сейчас кто-нибудь
скажет: "Мы же уже создавали объекты и
модифицировали их в четвертом уроке, теперь
что, опять то же самое?" Да, в Уроке 4 вы
создавали экземпляры встроенных объектов (например,
экземпляр объекта Color) – объектов, которые
созданы заранее, встроены в ActionScript,
которые можно сразу использовать. Мы
показали вам различные классы встроенных
объектов Flash (Array,
Color, Date, String, Sound и т.д.),
но мы не говорили ни о том, как создать свой
собственный класс объектов (то есть новый
инструмент вашей разработки), ни о том, как
расширить возможности существующих.
Возвратимся к прежней аналогии: когда был
изобретен автомобиль, был создан новый класс
объектов – класс Автомобиль. Объекты этого
класса имели двери, руль, фары и все такое
прочее. Теперь, когда новый автомобиль
сходит с конвейера, создается новый
экземпляр класса Автомобиль, но не новый
класс объектов. Новый класс объектов будет
создан тогда, когда кто-нибудь разработает,
например, индивидуальный летательный аппарат,
и назовет этот класс, скажем, Флайер. После
этого с конвейера начнут сходить экземпляры
объекта Флайер. Вскоре мы обсудим эту
концепцию детально, а пока важно уяснить,
что класс – это просто описание того, из
чего состоит объект, как он работает; это,
если хотите, чертеж, проект. По этому
чертежу создаются экземпляры. В конечном
итоге нужно и то, и другое – без чертежа не
создашь экземпляр, но и чертеж без реального
экземпляра бесполезен.
Примечание Экземпляры
объектов часто называют просто объектами.
Как правило, говоря об объектах,
подразумевают именно экземпляры.
Концепция построения и работы
объектов есть вещь довольно-таки абстрактная,
поэтому упражнения в нескольких следующих
разделах будут несколько отличаться от
прочих. В большинстве упражнений из этой
книги вы имеете дело с вещами зримыми и
осязаемыми – обычно это графические элементы,
которыми вы манипулируете в проекте. В
следующих упражнениях действие будет
происходить по большей части за сценой.
Здесь нам придется пользоваться действием
trace и командой меню (в среде тестирования
Отладка > Список переменных (Debug > List
Variables), чтобы увидеть данные,
существующие в фильме в ходе воспроизведения,
но на экране не отображаемые. С действием
trace вам прежде приходилось встречаться, но,
чтобы все было ясно, посмотрите на этот
синтаксис:
trace(_root._totalframes);
Если это действие поместить в
первый кадр фильма, состоящего из 47 кадров,
то при тестировании фильма автоматически
откроется окно Выход (Output)
и в нем отобразится:
47
Это простое действие очень
пригодится нам при изучении работы объектов.
Что ж, начнем.
Примечание Если вы
знакомы с принципами построения и работы
объектов, можете сразу перейти к разделу "Отслеживание
свойств".
Объекты как Контейнеры
Объекты ActionScript
представляют собой, в первую очередь,
контейнеры – хотя и специфические. Будучи
таковыми, они не только содержат в себе
нечто (например, данные или даже другие
объекты – это называют свойствами объекта),
они также могут выполнять специальные задачи
(которые обычно зависят от их содержимого).
Скажем, встроенный объект Flash String
используется для хранения строкового
значения – например, "Сильнее кошки зверя
нет", – однако помимо этого он имеет методы,
позволяющие выполнять различные манипуляции
со своим содержимым; этот принцип мы
продемонстрируем в следующем упражнении.
- Откройте Flash и выполните
команду Файл > Новый (File > New), чтобы
создать новый файл проекта. Откройте
панель Действия, выделите кадр 1 и
ведите следующий скрипт:
person1 = new Object();
Эта строка создает новый
экземпляр родового объекта с именем
person1.
Примечание Объект
Object (использованный здесь) не имеет
специального применения – это родовой
объект. Таким образом, в приведенной строке
фигурирует экземпляр объекта Object (а не
новый класс), который мы создали, чтобы
продемонстрировать роль объекта как
контейнера. В следующем уроке вы узнаете,
что объект Object имеет более широкое
применение.
На данный момент person1 –
никакая не персона, вообще никто: мы не
задали для нее никаких характеристик – ни
пола, ни возраста, вообще ничего.
- Добавьте к предыдущей строке
скрипта следующие:
person1.gender = "male";
trace (person1.gender);
trace (person1.age);
Теперь объект person1 имеет
свойство, под названием
gender, со
значением "male".
Еще мы добавили пару действий
trace, чтобы
посмотреть, какие значения имеются в объекте
person1.
Давайте посмотрим, что у нас есть на данный
момент.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тестирование проекта.
Откроется окно Выход (Output),
и в нем отобразится:
male
undefined
Первое из действий
trace вернуло
значение "male",
поскольку именно такое значение имеет
person1.gender.
Второе действие trace
вернуло значение
undefined для
person1.age – это значение мы не
присвоили, оно не определено.
Давайте добавим в person1 еще
несколько свойств и значений.
- Закройте тестовый фильм и
вернитесь в среду разработки. Выделите
кадр 1. В панели Действия замените три
строки скрипта, добавленные на шаге 2,
следующими строками:
person1 = new Object();
person1.age = 16;
person1.legs = 2;
person1.name = "Justin";
Как видим,
person1
приобретает все больше черт. Теперь она
имеет все самое необходимое, чтобы войти в
общество.
На данный момент любой скрипт
может получить доступ к данным, связанным с
person1 – мы
продемонстрируем это на следующем шаге.
- В конец скрипта добавьте оператор
if:
if (person1.age >= 21){
trace("Я иду на дискотеку");
}else{
trace("Я иду в МакДональдс");
}
Этот оператор проверяет
значение person1.age,
чтобы определить дальнейший образ действий.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход (Output),
и в нем отобразится:
Я иду в МакДональдс
Оператор
if выдает
именно такой результат, поскольку
person1.age
имеет значение 16.
Давайте добавим еще кое-что в
наш объект person1
– продемонстрируем, что объект может
содержать в себе другой объект. Эта
концепция вам, в принципе, знакома –
вспомните вложенные монтажные столы (это
ведь не что иное, как объект
MovieClip
внутри другого объекта
MovieClip).
- Закройте тестовый фильм и
вернитесь в среду разработки. Выделите
кадр 1, в панели Действия замените
оператор if, добавленный на предыдущем
шаге, следующими строками скрипта:
person1.head = new Object()
person1.head.eyes = 2;
person1.head.memories = new Array();
Итак, наша персона
person1
получила (наконец-то!) голову –
head. Это
произошло в результате создания нового
экземпляра объекта Object и помещения его
внутрь объекта
person1. Объекту
head мы дали
свойство eyes,
имеющее значение 2. Мало того, внутрь
объекта head
мы поместили объект
Array (c именем
memories).
Как видите, мы поместили один
объект (memories)
внутрь другого объекта (head),
который, в свою очередь, находится в объекте
person1. Мы
могли бы поместить
memories непосредственно в
person1, вот
так:
person1.memories = new Array();
Мы так не поступили, и на то
были веские причины, о которых мы скоро
расскажем.
Совет Можно поместить
в person1.head
существующий массив – с именем, скажем,
myThoughts (а
не создавать новый, как было показано), для
этого следовало бы использовать такой
синтаксис:
person1.head.memories = myThoughts.
Что ж, давайте добавим
несколько воспоминаний в массив
memories.
- В конец скрипта добавьте
следующие строки:
person1.head.memories[0] = "Я родился, пришел в этот мир.";
person1.head.memories[1] = "Я узнал слово 'НЕТ'. Очень продуктивный день.";
person1.head.memories[2] = "Я узнал, что если малость поору, то получу, что хочу.";
Эти три строки помещают
данные в массив
memories, находящийся внутри
head объекта
person1.
- В конец скрипта добавьте
следующее действие:
trace(person1.head.memories[1]);
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится такая надпись:
Я узнал слово 'НЕТ'. Очень продуктивный день.
Это – значение, которое
содержит
person1.head.memories[1].
- Закройте тестовый фильм и
сохраните свою работу как object1.fla.
Все, что мы
запрограммировали, мы используем в следующем
упражнении. Пока что запомните, что объекты
– это контейнеры, способные хранить не
только простые "порции" данных, но даже
другие объекты. Смысл тут тот же, что при
помещении переменной в экземпляр
фильма-символа, или при создании на
монтажном экземпляра
Color, Sound
или другого объекта (см. Урок 4 – "Объекты и
их использование"). Вы помещаете объект
(скажем, экземпляр объекта
Color) на
монтажный стол (внутрь экземпляра объекта
MovieClip).
Создавая на монтажном столе переменную, вы
на самом-то деле даете новое свойство
объекту MovieClip.
Отношения "родитель-потомок"
Когда вы помещаете монтажный
стол (то есть, экземпляр объекта
MovieClip) на
другой монтажный стол (другой экземпляр) –
вам приходилось делать это с
фильмами-символами – вы создаете отношения
родитель-потомок (см. Урок 3 – Об
адресации). Тот же принцип применим и к
объектам. Продолжая наш предыдущий пример,
давайте рассмотрим следующее:
person1.head.memories
Здесь
head является дочерним объектом по
отношению к person1,
а memories –
потомок и head,
и person1
(таким образом, head
– родитель
memories, а
person1 – предок
head и
memories).
Теперь внимание: следует
помнить о том, что если что-то происходит с
родителем, то же случается и с его потомками
– что мы и продемонстрируем в следующем
упражнении.
- Откройте файл object1.fla из
папки Lesson06/Assets. Откройте панель
Действия, выделите кадр 1. Удалите
действие trace, добавленное на шаге 9
предыдущего упражнения и добавьте в
конец скрипта следующие строки:
delete person1.head;
trace(person1.head);
trace(person1.head.memories[1]);
trace(person1.name);
Первое действие удаляет
head из
person1.
Последующие действия
trace позволяют нам увидеть, как это
повлияло на наш объект.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится:
undefined
undefined
Justin
Мы видим, что
person1.head
теперь undefined,
как и memories[1],
находившийся внутри
head. Будучи потомком
head массив
memories был
удален вместе со своим родителем. Однако на
прочие свойства объекта
person1 это не
повлияло – что и продемонстрировало
последнее действие
trace, вернув значение
person1.name.
Как видите, правильно
сконструированный объект реагирует на
внешние обстоятельства почти так же, как
объекты из реальной жизни. А в соединении с
действиями, вызывающими визуальные изменения
в фильме, (скоро мы до этого дойдем) объекты
способны и на большее.
- Закройте тестовый фильм и
вернитесь в среду разработки. Выделите
кадр 1, в панели Действия добавьте в
конец скрипта, сразу после строки
trace(person1.name); следующие строки:
person1.head = new Object();
trace(person1.head);
trace(person1.head.memories[1]);
Первая строка вновь добавляет
объект head к
person1.
Последующие действия
trace позволяют нам увидеть
результат. Учтите, что эти три строки
скрипта мы поместили после четырех строк,
добавленных на первом шаге. Таким образом,
при тестировании проекта окно Выход вновь
покажет нам результат удаления
person1.head;
но на этот раз за ним будет следовать
результат возвращения свойства
head.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится:
undefined
undefined
Justin
[object Object]
undefined
Три первых выведенных строки
– те же, что мы видели на шаге 2. А две
последние показывают, что получилось после
возвращения person1
свойства head.
В результате
person1.head отображено как
[object Object],
и это значит, что объект
head опять
существует (и является экземпляром объекта
Object), однако
person1.head.memories[1] остался
undefined.
Причина в том, что массив
person1.head.memories
был удален до этого (в результате удаления
person1.head),
и "забыт" – выгружен из памяти компьютера
(вместе, кстати, со свойством
eyes, которое
также было частью объекта
head). Когда
мы вновь добавили свойство
head,
компьютер уже не "вспомнил" ни массива
memories, ни
свойства eyes.
Чтобы восстановить их, пришлось бы создавать
эти элементы заново и заново помещать в них
данные.
- Закройте тестовый фильм и
сохраните свою работу как object2.fla.
Наша работа с этим файлом
завершена. В следующем разделе мы продолжим
изучение объектов – и теперь подойдем с
другой стороны.
Описание нового класса и создание
экземпляров этого класса
До сего времени мы
использовали экземпляр родового объекта
Object для демонстрации принципов действия
нашего объекта person1. Теперь настало время
перейти на следующий уровень механики
объектов: создать собственный класс, который
мы опишем в этом упражнении как класс
Person. Этот
класс послужит образцом, "чертежом" для
создания новых экземпляров "персон" – вы
убедитесь, что такой способ более эффективен
для создания person1
и ему подобных.
Примечание В
предыдущих упражнениях мы использовали
объект Object главным образом ради
демонстрации общей концепции объектов. На
самом деле объект Object может найти
действительно ценное применение в реальных
проектах, хотя это – весьма общее решение,
которое подойдет не для всякого приложения.
Как и в предыдущих
упражнениях, здесь, изучая "теоретическую
часть", мы будем частенько пользоваться
действием trace.
- Откройте Flash и выполните
команду Файл > Новый (File > New), чтобы
создать новый файл проекта. Откройте
панель Действия, выделите кадр 1 и
ведите следующий скрипт:
Person = function(){
}
Да, это очень похоже на
объявление функции – это и называется
конструктор функции, но именно с этого
начинается описание класса.
Примечание В ходе
дальнейшего обсуждения темы термины
конструктор функции и описание класса будут
означать одно и то же.
Обычное объявление функции и
конструктор функции (описание класса) имеют
одинаковый синтаксис, однако доступ к ним
осуществляется по-разному. Через минуту мы
разъясним все это детально, а пока давайте
посмотрим на два примера. В первом случае
функция выполняется как функция; во втором –
создается экземпляр класса
Person:
Person(); //выполняется как функция
person1 = new Person(); //создается экземпляр класса Person
Примечание Хотя
описание класса выглядит, как функция, его
следует рассматривать именно как описание
класса. Использовать один и тот же блок кода
обоими способами не советуем.
Снова повторим, что описание
класса мы рассматриваем как чертеж, или
шаблон, для создания экземпляров этого
класса. Внутри фигурных скобок мы помещаем
характеристики, описывающие этот класс
объектов. Пока что наш класс
Person не
имеет никаких характеристик (свойств),
поэтому давайте опишем этот класс, как в
нашем предыдущем примере.
- Дополните описание класса Person
следующим образом:
Person = function(){
this.name = "Justin";
this.age = 16;
this.legs = 2;
this.head = new Object()
this.head.eyes = 2;
this.head.memories = new Array();
}
Наше описание класса
завершено. Далее мы поговорим о том, как его
использовать и что означает
this в
контексте описания класса. Также мы покажем,
как создавать экземпляры нашего нового
класса.
Совет Возможно, вы
обратили внимание, что в описании имя нового
класса Person начинается с заглавной буквы.
Хотя это не обязательно, такова общая
практика. Имена экземпляров класса,
напротив, начинаются обычно с маленькой
буквы. Соблюдение этого простого правила
позволит сделать ваши скрипты более
удобочитаемыми и понятными.
- Добавьте в конец скрипта
следующую строку:
person1 = new Person();
Этим создается экземпляр
объекта Person
с именем person1.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта. Когда на экране появится
среда тестирования, выполните команду
Отладка > Список переменных (Debug >
List Variables).
Когда откроется окно Выход (Output),
в нем отобразится следующая информация:
Variable _level0.person1 = [object #2] {
name:"Justin",
age:16,
legs:2,
head:[object #3, class 'Object'] {
eyes:2,
memories:[object #4, class 'Array'] []
}
}
Это – информация об
экземпляре person1,
который мы создали действием, добавленным на
предыдущем шаге. Как определяет наш класс
Person, экземпляр person1 имеет имя
"Justin", возраст 16, две ноги и так далее.
Важно понимать, что команда
person1 = new
Person(); создает экземпляр, свойства
которого определены классом
Person. Все
происходит очень просто: выполняя эту строку
кода, ActionScript понимает, что настало
время создать новый экземпляр класса
Person. Тогда
создается временный объект (объект
Activation) и отсылается к описанию класса
Person,
запрограммированному ранее (все это
происходит автоматически и внешне никак не
проявляется). Объект Activation, поначалу
совершенно бесформенный, "проходит" через
описание класса, где получает различные
свойства класса (name,
age, legs и так далее). В ходе этого
процесса временный объект Activation для
адресации пользуется термином
this, как
указано в описании класса. Например, в
описании класса встретилась строка:
this.age = 16;
Это значит, что когда объект
Activation будет проходить через нее, его
свойству age должно быть присвоено значение
16. Процесс создания экземпляра можно
представить как движение объекта Activation
по сборочной линии конвейера – к нему
добавляются по очереди части и он движется
дальше. Когда процесс сборки завершен,
объект активации получает имя (в данном
случае person1),
и все – он готов к использованию.
Итак, мы описали класс Person
и знаем, как создавать его экземпляры,
однако все эти экземпляры будут совершенно
идентичными. Все дело в том, что так был
определен наш класс Person – все его
экземпляры будут иметь имя "Justin", возраст
16 и так далее. Хотелось бы, чтоб наш класс
Person был способен на что-то большее, чем
творить собственных клонов, а потому давайте
внесем в описание класса некоторые
изменения, чтобы можно было создавать
экземпляры с различающимися значениями
свойств.
- Закройте тестовый фильм и
вернитесь в среду разработки. Выделите
кадр 1, и в панели Действия измените
описание класса Person следующим
образом:
Person = function(name, age){
this.name = name;
this.age = age;
this.legs = 2;
this.head = new Object()
this.head.eyes = 2;
this.head.memories = new Array();
}
Подобно объявлению обычной
функции, в нашем описании класса
Person теперь
указано два аргумента:
name и
age. Обратите
внимание – те же самые значения указаны
теперь и в первых двух строках описания
класса. Это позволит нам создавать
экземпляры класса
Person, имеющие уникальные значения
свойств.
- Измените последнюю строку
скрипта, а после нее добавьте еще одну:
person1 = new Person ("Justin", 16);
person2 = new Person ("Derek", 29);
Эти два действия создают два
экземпляра класса
Person. Эти экземпляры имеют разные
значения свойств name
и age –
обновленное описание класса
Person
позволяет это. Чтобы задать свойства нового
экземпляра, мы можем передавать конструктору
строки, числа и даже имена объектов.
Теперь хотелось бы упростить
создание экземпляров класса
Person, чтобы
это можно было сделать с любого монтажного
стола без указания пути, подобно встроенным
объектам Flash. Для этого мы должны сделать
наше описание класса глобальным.
- Измените описание класса Person
следующим образом:
_global.Person = function(name, age){
this.name = name;
this.age = age;
this.legs = 2;
this.head = new Object()
this.head.eyes = 2;
this.head.memories = new Array();
}
Обратите внимание на
_global в
первой строке скрипта: именно это слово
позволит создавать экземпляры класса
Person с
любого монтажного стола, просто используя
такой конструктор:
new Person();
Если бы слова
_global не
было, и описание класса находилось бы,
скажем, на основном монтажном столе, то с
других монтажных столов пришлось бы
использовать следующий синтаксис:
new _root.Person();
Примечание Даже если вы,
используя _global,
сделаете класс доступным для всех монтажных
столов без адресации, отдельные экземпляры
всетаки будут находиться на монтажном столе,
содержащем скрипт, создавший этот экземпляр.
Допустим, вы создаете экземпляр объекта
Person с
именем myBuddy,
причем скрипт, создающий экземпляр,
находится в
_root.myMovieClip. Тогда абсолютный
адрес экземпляра
myBuddy будет _root.myMovieClip.myBuddy.
Конечно, при желании можно сделать
глобальным и экземпляр, использовав такой
синтаксис:
_global.myBuddy = new Person("Dennis", 27);.
- Сохраните свою работу как
class1.fla.
В следующем упражнении мы
продолжим работать с этим файлом. Мы теперь
умеем создавать экземпляры класса Person,
однако этого мало – нужно подумать о его
"тонкой настройке" в случае необходимости, а
для этого требуется освоить такую мощную
штуку, как объект-прототип (звучит
жутковато, правда?).
Генетическое программирование объекта
при помощи прототипа
Когда рождается ребенок, мы
ожидаем, что у него будет голова, два глаза,
два уха, две ноги и так далее. Все это мы
ожидаем увидеть у всех детей, поскольку эти
признаки жестко зашиты в нашем генетическом
коде. То же самое применимо и к объектам,
создаваемым ActionScript. При создании
экземпляров нашего класса
Person имена и
возраст могут варьироваться, но прочие
характеристики являются общими для всех
экземпляров. Давайте, рассуждая таким
образом, внесем еще изменения в описание
нашего класса Person.
- Откройте файл class1.fla из папки
Lesson06/Assets. Откройте панель
Действия, выделите кадр 1. Измените
описание класса Person следующим
образом:
_global.Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.legs = 2;
Person.prototype.head = new Object()
Person.prototype.head.eyes = 2;
Person.prototype.head.memories = new Array();
Здесь, как видите, мы вынесли
некоторые свойства за пределы формального
описания класса и присоединили их к
объекту-прототипу класса
Person –
специальному объекту, связанному с классом.
Этот объект (создаваемый автоматически,
когда описывается новый класс) содержит
свойства и значения, универсальные для
данного класса. Можете считать, что этот
объект – вроде как прицеп к описанию класса,
специальное приложение.
Примечание Все классы
объектов, включая встроенные в ActionScript,
имеют ассоциированный с ними объект-прототип.
В этом уроке мы бегло ознакомим вас с
объектами-прототипами этих классов.
Объект-прототип класса не
будет содержать никаких значений, пока мы их
не запрограммируем – поэтому мы и добавили
этому объекту три свойства (и присвоили им
значения) сразу после описания класса. Эти
свойства объекта-прототипа автоматически
наследуются всеми экземплярами данного
класса, и их значения доступны точно так же.
Очень важно уяснить принцип работы
объекта-прототипа, поэтому давайте об этом
поговорим.
Когда создается новый
экземпляр класса
Person, описание класса придает ему
два свойства – name
и age. Вот
иллюстрация доступа к одному из этих
свойств:
myVariable = person1.age;
Первым делом ActionScript
смотрит в person1,
чтобы определить, имеется ли у этого
экземпляра свойство
age. Видя, что оно есть (это свойство
было присоединено при активации экземпляра –
проходе через описание класса), ActionScript
присваивает значение этого свойства
переменной myVariable.
Теперь давайте рассмотрим такую строку
скрипта:
myVariable = person1.legs;
Вы помните, что описание
класса Person
теперь не включает свойства
legs, тем не
менее, myVariable
получит значение 2. Дело вот в чем: при
таком сценарии ActionScript сначала смотрит
в экземпляр, пытаясь найти у него свойство
legs. Не найдя
такого, ActionScript говорит себе: "Ладно,
ведь person1 у нас – экземпляр класса
Person. Раз я не нашел свойства
legs у этого
экземпляра, надо посмотреть в объекте
Person.prototype,
может, там есть такое". Тут ActionScript
обнаруживает, что
Person.prototype.legs существует и
имеет значение 2, стало быть, и
person1.legs
равно 2. Это значение ActionScript
присваивает переменной
myVariable.
Итак, ActionScript всегда
ищет свойство сначала на уровне экземпляра (термин
уровень экземпляра мы вскоре разъясним).
Если свойство нашлось – поиск прекращается,
если нет – продолжается в объектепрототипе
класса, к которому принадлежит экземпляр.
Считайте, что объект-прототип класса – это
зеркало. Все его свойства отражаются на
каждый экземпляр данного класса. Хотя
экземпляр на самом деле не содержит таких
свойств, ActionScript будет считать, что они
есть.
Вы можете подумать: ведь все
эти свойства прекрасно работали, будучи
частью описания класса (как в предыдущем
упражнении); зачем же морочить голову еще и
прототипами? Дело в том, что объект-прототип
позволяет изменения, распространяющиеся на
весь класс – то есть на все экземпляры этого
класса. Если изменить значение какого-либо
свойства в объекте-прототипе класса, то все
экземпляры этого класса – вновь создаваемые
и существующие – унаследуют это новое
значение (за одним исключением, о котором
чуть позже). Чтобы вы еще лучше усвоили эту
концепцию, давайте немного поупражняемся.
- Добавьте в конец скрипта
следующие строки:
person1 = new Person ("Justin", 16);
person2 = new Person ("Derek", 29);
trace (person1.legs);
trace (person2.legs);
Первые две строки создают два
экземпляра объекта
Person. Действия
trace выводят
значение свойства
legs для каждого экземпляра.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится следующее:
2
2
Как видите,
person1 и
person2 имеют
одинаковое значение свойства
legs. Мы можем
сделать изменение для всего класса (то есть
изменить значение для всех экземпляров),
поменяв значение свойства
legs объекта
Person.prototype.
Давайте так и сделаем.
- Закройте тестовый фильм и
вернитесь в среду разработки. Выделив
кадр 1, поменяйте значение
Person.prototype.legs с 2 на 5. Затем
командой Управление > Проверить фильм
вновь запустите тест проекта.
Откроется окно Выход
(Output), и в нем отобразится следующее:
5
5
Как видите, простое изменение
одного значения в объекте
Person.prototype
привело к тому, что и
person1, и
person2 получили новое значение
свойства legs.
Если бы у нас было сто экземпляров, все они
получили бы это новое значение. Добавление,
удаление или изменение свойств
объекта-прототипа класса немедленно
отражаются на всех экземплярах этого класса
(единственное исключение, о котором мы уже
упоминали, мы обсудим в следующем разделе).
Итак, экземпляры объекта
могут иметь как уникальные свойства, так и
универсальные (одинаковые для всех
экземпляров данного класса). Например, мы
все имеем разные имена и возраст (свойства,
заложенные в описании класса), но также мы
имеем некоторые общие признаки – две ноги,
голова, и на ней два глаза (свойства,
устанавливаемые объектом-прототипом).
- Закройте тестовый фильм и
вернитесь в среду разработки. Выделив
кадр 1, верните Person.prototype.legs
значение 2. Сохраните свою работу как
class2.fla.
Мы продолжим работу с этим
файлом в следующем упражнении.
Свойства уровня экземпляра
Как упоминалось в предыдущем
разделе, при попытке обращения к значению
свойства – например, присвоении
myVariable
значения person1.legs
(myVariable = person1.legs;) –
ActionScript всегда сначала ищет указанное
свойство на уровне экземпляра, и только
после это переходит к проверке
объекта-прототипа. Свойство уровня
экземпляра – это свойство, полученное
экземпляром из описания класса при создании,
либо свойство, значение которого
присваивается путем непосредственной
адресации к экземпляру. Экземпляр
person1,
созданный нами в предыдущем упражнении,
имеет следующие свойства:
name = "Justin" // уровень экземпляра
age = 16 // уровень экземпляра
legs = 2 // уровень прототипа
head = new Object() // уровень прототипа
head.eyes = 2 // уровень прототипа
head.memories = new Array() // уровень прототипа
Почему важно различать
свойства уровней экземпляра и прототипа? В
ситуации, когда свойства уровня экземпляра и
уровня прототипа имеют одно и то же имя,
значение свойства уровня экземпляра
перекрывает значение уровня прототипа (хотя
это относится только к данному экземпляру).
Допустим, экземпляр person1
получает свойство
legs от объекта
Person.prototype,
поскольку при создании экземпляра это
свойство не было присвоено. Однако если мы
присвоим значение этому свойству,
обратившись непосредственно к экземпляру –
например,
person1.legs = 1; – мы создадим у
person1
свойство legs
уровня экземпляра, и его значение перекроет
значение, установленное прототипом. У всех
прочих экземпляров останется две ноги – за
исключением person1,
который теперь имеет только одну. Значение
уровня прототипа попрежнему существует и для
этого экземпляра, только теперь оно
невидимо, перекрыто свойством уровня
экземпляра с таким же именем.
Мы можем удалить свойство
legs уровня экземпляра – вот таким образом:
delete person1.legs;
В результате
person1 вновь
станет отражать свойство
legs (и его
значение), установленное объектом-прототипом
класса Person.
Еще один важный аспект,
касающийся свойств уровня экземпляра – то,
что благодаря им экземпляры могут иметь свои
уникальные свойства вдобавок к полученным из
описания класса и унаследованным от
прототипа. Чтобы добавить такое свойство,
нужно просто обратиться непосредственно к
экземпляру. Например:
person2.favoriteFood = "Pizza";
Теперь
person2 станет единственным
экземпляром, имеющим свойство
favoriteFood.
Конечно, мы можем запросто добавить такое
свойство и прочим экземплярам (присвоив его
напрямую либо добавив в объект-прототип), но
пока мы этого не сделаем, свойство
favoriteFood
будет только у экземпляра
person2.
Примечание Если
добавить свойство в описание класса, то это
свойство будут получать лишь вновь
создаваемые экземпляры, начиная с этого
момента, но не те, что уже существуют.
Причина в том, что конструктор функции
(описание класса) присоединяет свойства к
экземплярам лишь в процессе их создания.
Свойства уровня экземпляра
позволяют индивидуализировать экземпляры, не
принося в жертву преимущества класса
объектов.
Создание подклассов
Наш класс
Person
приобретает все большую функциональность: мы
можем создавать его экземпляры, указывая для
них имена и возраст, и еще они могут
наследовать свойства объекта-прототипа. Но,
как в реальной жизни, могут существовать еще
и классы внутри нашего класса
Person:
доктора, юристы, Flash-разработчики и так
далее – разные подклассы людей. Эти
подклассы разделяют признаки класса
Person (имя,
возраст, ноги, голова), но имеют и свои,
уникальные. Если, например, вы создаете
класс Доктор, он должен обладать такими
особыми характеристиками, как специальность,
звание ("Dr."),
и так далее, и в то же время – всеми
признаками класса
Person.
- Откройте файл class2.fla из папки
Lesson06/Assets. Откройте панель
Действия, выделите кадр 1. Удалите два
действия trace в конце скрипта, затем
добавьте следующее:
_global.Doctor = function(almaMater){
this.almaMater = almaMater;
}
Doctor.prototype.title = "Dr.";
Здесь мы создаем конструктор
функции, описывающий новый класс объектов –
класс Doctor.
Каждый новый экземпляр этого класса объектов
получит свойство
almaMater, значение которого
передается конструктору функции при создании
экземпляра. Также мы придали классу
Doctor
прототип со свойством
title, имеющим значение "Dr.".
Поскольку это звание носят все доктора, мы
можем поместить его в объект-прототип класса
Doctor.
Давайте создадим экземпляр
нашего нового класса.
- Поместите следующие строки в
конец скрипта:
frankenstein = new Doctor("Transylvania");
trace(frankenstein.almaMater);
trace(frankenstein.title);
trace(frankenstein.legs);
Первое действие создает новый
экземпляр класса
Doctor с именем
frankenstein.
Последующие действия trace покажут нам
результат.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится следующее:
Transylvania
Dr.
undefined
Как видите, со свойством
almaMater все
в порядке (это свойство уровня экземпляра, и
оно было добавлено при создании экземпляра),
и со свойством title
тоже (благодаря объекту
Doctor.prototype),
а вот свойство legs
не существует.
Нашей целью было сделать
класс Doctor подклассом класса Person, чтобы
все экземпляры класса
Doctor наследовали такие свойства,
как legs, head
и так далее. Только это не произойдет само
собою – где-либо в нашем скрипте мы должны
сообщить ActionScript о том, что
Doctor есть
подкласс класса
Person.
- Закройте тестовый фильм и
вернитесь в среду разработки. Выделив
кадр 1, поместите следующую строку
непосредственно перед
Doctor.prototype.title = "Dr.":
Doctor.prototype = new Person();
Эта строка скрипта создает
связь между классами
Doctor и
Person. Она указывает, что
объект-прототип класса Doctor должен
наследовать объекту-прототипу
Person.
В результате все экземпляры
класса Doctor
будут отражать свойства не только
Doctor.prototype,
но также и
Person.prototype. Обратное неверно;
экземпляры класса
Person будут отражать только свойства
Person.prototype.
Теперь Doctor
– подкласс Person,
а Person –
надкласс (еще говорят суперкласс) класса
Doctor.
Существует причина, по
которой мы поместили нашу последнюю строку
перед строкой устанавливающей свойство
title объекта
Doctor.prototype.
При выполнении добавленной нами строки
объект
Doctor.prototype, по существу,
полностью очищается, все свойства, которые в
нем могли быть, заменяются свойствами
объекта
Person.prototype. Если бы мы добавили
в объект
Doctor.prototype свойство
title до
этого, оно было бы утрачено. Вместо этого в
нашем скрипте мы добавляем свойство
title после
того, как
Doctor.prototype наследует объекту
Person, и
проблема решена.
В конце нашего скрипта
остались три действия
trace, которые прежде (на шаге 3)
вернули такие результаты:
trace(frankenstein.almaMater);// вернуло "Transylvania"
trace(frankenstein.title); );// вернуло "Dr."
trace(frankenstein.legs);// вернуло undefined
Давайте проверим результаты
теперь.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится следующее:
Transylvania
Dr.
2
Как видите,
frankenstein
имеет теперь две ноги (тогда как прежде это
свойство было не определено –
undefined).
Это результат того, что экземпляры Doctor
теперь наследуют, как мы выяснили на
предыдущем шаге, свойства не только
Doctor.prototype,
но и Person.prototype.
По той же причине
frankenstein имеет теперь голову –
head, а в
голове – memories,
и так далее. Если теперь мы изменим любое
свойство
Person.prototype, эти изменения
отразятся не только на экземплярах класса
Person, но
также и на экземплярах класса
Doctor, ведь
он – подкласс класса
Person. Дело, по существу, обстоит
так. Когда Flash пытается обратиться к
свойству legs
экземпляра
frankenstein (на уровне экземпляра),
он такого свойства не находит. Тогда он
заглядывает в объект
Doctor.prototype (поскольку
frankenstein
есть экземпляр класса
Doctor ). Такого свойства нет и там.
Наконец Flash обращается к объекту
Person.prototype
(поскольку Doctor
есть подкласс класса
Person) – и здесь находит-таки
свойство legs.
Теперь вы, возможно,
заинтересовались такими свойствами класса
Person, как
name и
age. Эти
свойства не входят в объект
Person.prototype,
так будут ли экземпляры класса
Doctor
наследовать их? Нет, не будут. Чтобы
исправить это, нам нужно внести изменения в
описание класса
Doctor.
- Закройте тестовый фильм и
вернитесь в среду разработки. Выделите
кадр 1 и измените описание класса Doctor
следующим образом:
_global.Doctor = function(name, age, almaMater){
this.base = new Person(name, age);
this.name = this.base.name;
this.age = this.base.age;
delete this.base;
this.almaMater = almaMater;
}
Первое, что вы замечаете в
обновленном описании класса
Doctor – то,
что появились два новых аргумента (вдобавок
к almaMater,
который был и прежде):
name и
age. Все три
значения используются в последующих строках,
обеспечивающих создание экземпляров класса
Doctor.
В первой строке используются
значения name
и age,
переданные конструктору функции, для
создания нового экземпляра класса
Person. Да-да,
конструктор функции
Doctor создает экземпляр класса
Person. Как
видите, этот экземпляр имеет имя и путь
this.base.
Видя такую строку, ActionScript создает
экземпляр. При этом
this.base получает свойства
name и
age (как все
экземпляры класса
Person). Следующие две строки
конструктора функции задают значения свойств
name и
age для
будущих экземпляров класса
Doctor (this.name
и this.age),
причем значения берутся у
this.base
(экземпляр класса
Person, созданный в первой строке).
Четвертая строка конструктора функции
удаляет экземпляр
this.base – он был нужен лишь
временно. Пятая строка задает свойство
almaMater для
будущих экземпляров (это уже было на шаге
1).
Примечание Это
показывает, сколь динамичным может быть при
необходимости конструктор функции: функция
может создать временный объект (как было
показано), выполнить математические
вычисления, другие действия, и много чего
еще.
Вы, вероятно, думаете, мол, к
чему такие сложности, разве нельзя было
выполнить описание класса вот так:
_global.Doctor = function(name, age, almaMater){
this.name = name;
this.age = age;
this.almaMater = almaMater;
}
Верно, так было бы проще, и в
большинстве случаев подошло бы, но
представьте себе, что в описании класса
Person с
переданными ему значениями
name и
age
производятся какие-либо действия – скажем,
все буквы name делаются заглавными, или
что-то в этом роде? Тогда в последнем
варианте скрипта экземпляры класса
Doctor
оказались бы лишены этого, тогда как в
первоначальном такого не случится.
- Обновите строку frankenstein =
new Doctor("Transylvania"); таким
образом:
frankenstein = new Doctor("Frankenstein", 85, "Transylvania");
Необходимость такого
изменения вызвана тем, что мы добавили в
конструктор функции
Doctor параметры
age и
name. В
результате экземпляр
frankenstein при создании получит
свойства name со значением
"Frankenstein",
age со значением 85 и
almaMater со значением
"Transylvania".
- В конец скрипта добавьте
следующие строки:
trace(frankenstein.name);
trace(frankenstein.age);
Эти действия позволят нам
увидеть результаты сделанных изменений.
Всего в конце скрипта у нас получилось пять
действий trace
(три должны остаться с шага 2).
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится следующее:
Transylvania
Dr.
2
Frankenstein
85
Экземпляр frankenstein
полностью готов!
Теперь пришло время
представить для рассмотрения свойство
_proto_.
Каждый объект-прототип класса и каждый
экземпляр класса имеют свойство
_proto_. Его
значение показывает, где следует искать
свойства, не обнаруженные у самого
экземпляра. Как мы знаем, для экземпляра
frankenstein в
таком случае поиск будет продолжаться в
объекте
Doctor.prototype. Таким образом,
frankenstein._proto_
имеет значение
Doctor.prototype (экземпляр наследует
этому объекту-прототипу). Поскольку класс
Doctor
наследует классу
Person,
Doctor.prototype._proto_ равно
Person.prototype
(один прототип класса наследует другому
объекту-прототипу класса). Это свойство
_proto_
создается и получает значение автоматически
при создании экземпляра или подкласса, и его
всегда можно изменить, если в том возникнет
нужда. Предположим, мы создали класс
Lawyer и хотим
связать frankenstein
с этим классом (вместо класса
Doctor). Тогда
мы просто используем такую строку скрипта:
frankenstein._proto_ = Lawyer.prototype;
После этого
frankenstein
можно считать экземпляром класса
Lawyer,
наследующим характеристики прототипа этого
класса. При этом его "докторские"
характеристики (унаследованные от объекта
Doctor.prototype)
исчезнут начисто!
Осталось рассказать еще о
двух важных аспектах классов объектов:
составе и наследовании. Их понимание поможет
вам лучше осмыслить концепцию построения
объектов. В принципе, смысл этих двух
аспектов заключается в двух словах: иметь и
быть. Если мы посмотрим на два класса
объектов, созданных нами (Person
и Doctor), то
обнаружим следующее:
- Экземпляр
Person имеет
name: это
состав.
- Экземпляр
Person имеет
head: это
состав.
- Экземпляр
Person имеет
legs: это
состав.
- Экземпляр
Doctor есть
Person:
это наследование.
- Экземпляр
Doctor имеет
title: это
состав.
Эта концепция имеет место и в
реальном мире. Вот, например:
- Чизбургер есть сэндвич есть пища:
это наследование.
- Автомобиль имеет кузов имеет дверь
имеет окно: это состав.
Рассуждая таким образом, вам
будет легко проектировать новые классы
объектов. Когда что-либо является чем-то
более общим (отношение есть), это хороший
кандидат для создания подкласса. Там, где
возникает отношение имеет, появляется
возможность создать свойство объекта.
Наследование также имеет
некоторые свои аспекты, но это уже выходит
за пределы тематики данной книги. Теперь,
когда вы понимаете, как создаются любые
классы объектов (и каково их сходство с
объектами из реальной жизни), вам будет
много проще постигнуть процесс разработки
приложений. К концу следующего раздела мы
обобщим все, что вы узнали нового и
продемонстрируем, как это можно использовать
в проектах.
- Закрыв тестовый фильм, вернитесь
в среду разработки. Сохраните свою
работу под именем class3.fla.
В следующем упражнении мы
продолжим работу с этим файлом.
Создание методов для собственных классов
До сих пор объекты, которые
мы создавали, имели свойства, значения
которых мы могли использовать для вычислений
и тому подобного, однако они не могли сами
выполнять каких-либо задач. Сейчас вы
поймете, что это означает – просто
вспомните, что умеют делать встроенные
объекты Flash:
- Экземпляры фильмов-символов могут
начать воспроизведение –
play(),
остановить –
stop(), загрузить фильм –
loadMovie(),
получить URL –
getURL() и так далее.
- Экземпляры объекта Sound могут
загрузить звук –
loadSound(), установить громкость
– setVolume(),
установить баланс –
setPan() и
тому подобное.
А наши классы
Person и
Doctor ничего
делать не могут, кроме как просто
"присутствовать". В этом разделе мы
постараемся изменить такое положение дел,
создав для наших классов методы – это даст
возможность экземплярам этих классов
выполнять полезные задачи.
Прежде всего следует вам
сказать, что метод – это не что иное, как
функция, связанная с классом и помещенная в
объект-прототип этого класса. Давайте начнем
упражнение, и вы увидите, как это работает.
- Откройте class3.fla из папки
Lesson06/Assets. Откройте панель
Действия, выделите кадр 1. Удалите
действия trace в конце скрипта, затем
добавьте следующее:
Person.prototype.talk = function(){
trace("Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.");
}
Примечание Можно
создать метод другим способом:
//Сначала описываем функцию
function futureMethod(){
trace("Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.");
}
//Назначаем эту функцию методом talk класса Person
Person.prototype.talk = futureMethod;
Вот и создан новый метод для
класса объектов
Person. Функция сделалась методом
класса Person,
поскольку мы поместили ее в объект-прототип
класса Person.
Этот наш метод, который
называется talk(),
может быть использован любым экземпляром
данного класса. У нас имеются экземпляры
person1 и
person2. Любой
из этих экземпляров может использовать наш
новый метод, вот таким образом:
person2.talk();
Этот метод выполняет лишь
одну простую задачу: выводит сообщение в
окне Выход при тестировании фильма. Как вы
видели, в описании метода, в действии
trace,
использованы элементы
this.name и
this.age. Поскольку функция является
методом, this
есть ссылка на экземпляр, который вызвал
этот метод. Таким образом, если этот метод
вызывает person2,
эти элементы будут означать
person2.name и
person2.age.
Примечание Если термин
this
используется в обычной функции, то он
означает ссылку на монтажный стол, на
котором находится функция.
- Поместите в конец скрипта
следующую строку:
person2.talk();
Это приведет к тому, что
person2
вызовет метод talk().
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится следующее:
Привет, меня зовут Derek. Мне 29 лет.
Итак, наш метод
talk() дал
возможность экземплярам нашего класса Person
хоть что-то делать. Давайте немного расширим
возможности этого метода.
- Закрыв тестовый фильм, вернитесь
в среду разработки. Выделите кадр 1 и
измените метод talk() следующим образом:
Person.prototype.talk = function(tone){
var message = "Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.";
if (tone == "крик"){
trace(message.toUpperCase());
}else{
trace(message);
}
}
Как видите, теперь метод
принимает параметр
tone. Метод будет действовать одним
из двух способов в зависимости от значения
параметра. Давайте рассмотрим подробнее
обновленное описание метода.
Первым делом создается
локальная переменная с именем
message,
которая получает значение, полученное из
выражения. Выражение то же самое, которое мы
прежде использовали в действии
trace.
Примечание Как
упоминалось в предыдущем уроке (о функциях),
локальная переменная – это такая переменная,
которая определена только внутри той
функции, где она объявлена. Если бы мы не
сделали переменную
message локальной, она была бы
доступна любому скрипту в любое время.
Будучи локальной, она будет удалена, как
только завершится выполнение метода.
После того, как переменная
message
получила значение, в действие вступает
оператор if.
Он проверяет значение
tone: если оно равно "крик", то
значение message
преобразовывается – все буквы делаются
заглавными – а затем выводится в окне Выход.
Если tone
имеет любое другое значение,
message будет
выведено обычным образом. Использование
метода toUpperCase()
объекта String
демонстрирует, что метод одного класса может
использовать методы других классов. Методы
могут содержать действия, другие методы и
так далее – практически все, что
предоставляет ActionScript.
- Добавьте в конец скрипта
следующую строку:
person2.talk("крик");
В результате
person2
вызовет метод talk(),
передав ему в качестве параметра значение
"крик". Теперь обе последние строки вызывают
метод talk(),
но разными способами (в одном случае
передается значение "крик"); окно Выход
покажет нам оба результата.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится следующее:
Привет, меня зовут Derek. Мне 29 лет.
ПРИВЕТ, МЕНЯ ЗОВУТ DEREK. МНЕ 29 ЛЕТ.
Теперь давайте рассмотрим еще
один интересный аспект нашего метода
talk().
- Закрыв тестовый фильм, вернитесь
в среду разработки. Выделите кадр 1 и
измените вызов метода talk() (две
последние строки скрипта) следующим
образом:
frankenstein.talk();
frankenstein.talk("крик");
Метод
talk() принадлежит классу
Person,
однако, поскольку класс
Doctor есть
подкласс класса
Person, экземпляры класса
Doctor тоже
имеют доступ к этому методу – как вы видите
на примере экземпляра
frankenstein.
Класс
Doctor тоже может иметь свои,
уникальные, методы – достаточно просто
поместить нужные функции в объект
Doctor.prototype,
как мы сделали с объектом
Person.prototype
на шаге 1.
Примечание Если
создать другой метод
talk() – в объекте
Doctor.prototype,
то экземпляр
frankenstein будет использовать
именно этот метод, а не тот, что находится в
Person.prototype.
Ведь frankenstein
– экземпляр класса
Doctor, и поиск его методов
происходит в первую очередь в объекте
Doctor.prototype.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Откроется окно Выход
(Output), и в нем отобразится следующее:
Привет, меня зовут Frankenstein. Мне 85 лет.
ПРИВЕТ, МЕНЯ ЗОВУТ FRANKENSTEIN. МНЕ 85 ЛЕТ.
Наш метод
talk()
выполняет простейшую задачу, вообще же
методы могут быть весьма сложными.
- Закрыв тестовый фильм, вернитесь
в среду разработки. Сохраните свою
работу как class4.fla.
Полученные знания о механике
объектов пригодятся вам в следующих
разделах, когда мы будем изучать способы
расширения функциональности встроенных
объектов Flash. Мы видели, как можно
использовать объекты для организации данных
и работы с ними, однако Flash – в первую
очередь визуальное средство, и вы, возможно,
недоумеваете, какую пользу могут принести
объекты в этой сфере. Что ж, пришла пора об
этом узнать.
Отслеживание свойств
Итак, вы знаете, что объектам
(в том числе фильмам-символам) можно придать
свойства (переменные). В типичных
Flash-приложениях значения этих свойств в
любое время могут быть изменены. Как и у
объектов реального мира, изменение свойства
может привести к тому, что будет затронут не
только объект, свойство которого изменено,
но также, возможно, и ряд других объектов –
произойдет, можно сказать, цепная реакция
изменений. Например, предположим, что погода
– это объект, и что погодные условия
(свойство погоды – погода.условия)
изменились с "ясно" на "дождь"
(погода.условия = "дождь";). В результате
небо потемнеет, у автомобиля включатся фары
и заработают "дворники" на лобовом стекле. В
распоряжении ActionScript имеется метод
watch() для
отслеживания различных свойств (переменных,
как условия в нашем примере) объекта; если
обнаружено изменение значения свойства, то
вызывается некая функция, и ваш проект
реагирует на изменение. Синтаксис применения
этого метода таков:
имяОбъекта.watch("имяСвойства", имяФункции);
Предположим,
weather
(погода) – имя фильма-символа на основном
монтажном столе,
condition (условия) – имя переменной
в этом фильме-символе, а watchCondition() –
имя функции, которая должна быть вызвана при
изменении значения
condition. Тогда мы запишем так:
_root.weather.watch("condition", watchCondition);
Примечание Объект
может инициировать отслеживание внутри себя,
используя синтаксис
this.watch("имяСвойства", имяФункции);.
Функция
watchCondition()
могла бы быть описана, например, так:
function watchCondition(id, oldval, newval){
message.text = id + " было " + oldval + ", а таперь " + newval;
if (newval == "rainy"){
sky.gotoAndStop("dark");
lights.gotoAndStop("on");
wipers.gotoAndPlay("on");
}else if (newval == "sunny"){
sky.gotoAndStop("bright");
lights.gotoAndStop("off");
wipers.gotoAndStop("off");
}
}
Обратите внимание на
параметры id, oldval
и newval,
передаваемые функции. Эти параметры будут
автоматически передаваться функции при ее
вызове в результате срабатывания метода
watch().
Параметр id
представляет собой имя (в виде строкового
значения) отслеживаемого свойства;
oldval –
старое значение этого свойства (до
изменения), а newval
– новое значение, которое привело к
срабатыванию метода
watch(). В приведенном примере вы
видите, как эти параметры используются
внутри функции.
Примечание Хотя в
примере этого не показано, если бы в функции
встречалась адресная ссылка
this, она
означала ссылку на объект, свойство которого
отслеживается.
Примечание Необходимо
описывать функцию, вызываемую методом
watch(), до
того, как будет инициировано отслеживание.
Ведь в качестве параметра метода
watch() вы
должны указать функцию, которую следует
вызвать при изменении свойства, а указать
функцию, которая еще не существует, нельзя.
Если необходимость в
отслеживании свойства отпала, вы можете
отменить его, вызвав метод
unwatch():
weather.unwatch("condition");
После выполнения данной
строки скрипта при изменении свойства
condition
функция
watchCondition() более не будет
вызываться.
Несколько важных моментов,
касающихся применения метода
watch():
- Метод watch()
работает с большинством объектов
ActionScript, имеющих свойства
(переменные), включая объекты, созданные
пользователем (как мы показали в
предыдущем упражнении). Однако нельзя
отслеживать предустановленные свойства
ActionScript, такие, как
_x, _y, _alpha
и тому подобные.
- Для любого свойства можно
инициировать только один процесс
отслеживания. Установка нового
отслеживания для того же свойства
отменит предыдущее.
- Если вы попытаетесь устроить
отслеживание по "принципу домино" –
когда при изменении отслеживаемого
свойства выполняется функция, которая
изменяет другое отслеживаемое свойство,
в результате чего выполняется функция,
изменяющая еще одно отслеживаемое
свойство, и так далее – результаты могут
оказаться непредсказуемыми.
В следующем упражнении мы
создадим сцену, в которой изменение
отслеживаемого свойства будет влиять на
различные элементы фильма.
- Откройте файл objectProject1.fla
из папки Lesson06/Assets.
Большинство элементов этой
сцены мы для вас уже создали и настроили,
так что вы можете сосредоточиться на
ActionScript. Сцена содержит четыре слоя.
Слой Background содержит фоновую заливку и
логотип проекта. В слое Head находятся два
экземпляра фильма-символа Head. Меньший
экземпляр называется smallHead, больший –
bigHead (вскоре мы рассмотрим этот
фильм-символ поближе). Кроме того, слой Head
содержит текстовое поле, расположенное в
нижней части сцены, называется оно
message. В
слой Buttons помещены все используемые в
нашем проекте кнопки. Четыре из них, которые
мы будем использовать в этом упражнении,
расположены справа от экземпляра
bigHead. Имена
кнопок – mouthButton,
noseButton, eyesButton и
glassesButton.
Две другие кнопки, те, что в левом верхнем
углу сцены, мы используем в следующем
упражнении. Слой Actions содержит скрипты (к
которым мы многое добавим по ходу
упражнения).
- Двойным щелчком на экземпляре
bigHead откройте его для редактирования
на месте.
Главное, о чем следует вас
предупредить – это то, что монтажный стол
этого фильма-символа (сам по себе
простенький) содержит экземпляры четырех
других фильмов-символов:
glassesClip, eyesClip,
noseClip и
mouthClip. Что они изображают, можно
догадаться по названиям – очки, глаза, нос и
рот. Мы будем делать эти экземпляры видимыми
и невидимыми, отслеживая изменение значений
свойств. В нашем фильме два экземпляра
фильма-символа Head,
и мы запрограммируем их так, чтобы они
реагировали на события противоположным
образом. Например, если экземпляр
noseClip в
экземпляре bigHead
становится видимым, то
noseClip в
экземпляре smallHead
делается невидимым.
- Вернитесь на основной монтажный
стол. Откройте панель Действия, выделите
экземпляр bigHead и добавьте следующий
скрипт:
onClipEvent(load){
glassesClip._visible = false;
eyesClip._visible = false;
noseClip._visible = false;
mouthClip._visible = false;
glasses = false;
eyes = false;
nose = false;
mouth = false;
}
Здесь использовано событие
load, то есть
скрипт будет выполнен, как только экземпляр
bigHead
появится на сцене – а это произойдет сразу
после начала воспроизведения фильма. Кстати,
большинство скриптов для этого упражнения мы
будем помещать в обработчики события
load. А пока
давайте рассмотрим эти первые несколько
строк.
Четыре первые строки делают
невидимыми экземпляры, о которых мы говорили
на предыдущем шаге. Таким образом, сразу
после загрузки
bigHead лишится лица, а
smallHead
останется без изменений, (то есть, с лицом)
– ведь эти действия затрагивают только
экземпляры, находящиеся в
bigHead.
Четыре следующие строки
присваивают значения переменным. Имена
переменных (и значения) совпадают с текущим
состоянием экземпляров фильмов-символов, о
которых только что шла речь. Эти четыре
переменные (свойства) мы будем отслеживать
на предмет изменений. Сейчас мы присвоили им
начальные значения.
Теперь нам нужно описать
функции, которые будут вызываться при
изменении одного из этих свойств.
- В конце (но внутри) обработчика
события load добавьте описание функции:
function watchGlasses(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
glassesClip._visible = newval;
_root.smallHead.glassesClip._visible = oldval;
_root.glassesButton.enabled = false;
}
Вообще-то мы собираемся
установить наблюдение за переменной
glasses, а эта
функция будет выполняться при изменении
значения. Обратите внимание на три
параметра, передаваемых функции –
id, oldval, newval.
Значения всех трех параметров используются в
функции. Первая строка функции, используя
значение id,
будет выводить сообщение в текстовом поле
message.
Поскольку эту функцию будет вызывать
"наблюдатель" за значением
glasses,
id в данном
случае будет иметь строковое значение
"glasses", а все сообщение будет звучать как
"I now have glasses.". В следующей строке
используется значение параметра
newval. На
предыдущем шаге мы дали очкам начальное
значение false;
это значит, что функция будет вызвана, когда
это значение сменится на
true, а
значит, и newval
будет true –
это ведь не что иное, как новое значение
свойства или переменной. Итак, во второй
строке
glassesClip._visible становится
true, и
glassesClip из
bigHead
отображается на экране. В следующей строке
нашел применение параметр
oldval – в
соответствии с ним устанавливается видимость
glassesClip из
экземпляра smallHead.
А поскольку oldval
имеет значение false
(значение glasses до изменения), то
glassesClip из
smallHead
становится невидимым. Последнее действие
отключает кнопку
glassesButton. Функции этой кнопки мы
пока не запрограммировали, но заранее
скажем, что именно она будет изменять
значение glasses,
что и приведет к выполнению данной функции.
Как видите, изменив это значение, кнопка
отключается, то есть, использовать ее можно
будет лишь один раз.
- В конце (но по-прежнему внутри)
обработчика события load добавьте
описания еще трех функций:
function watchEyes(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
eyesClip._visible = newval;
_root.smallHead.eyesClip._visible = oldval;
_root.eyesButton.enabled = false;
}
function watchNose(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
noseClip._visible = newval;
_root.smallHead.noseClip._visible = oldval;
_root.noseButton.enabled = false;
}
function watchMouth(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
mouthClip._visible = newval;
_root.smallHead.mouthClip._visible = oldval;
_root.mouthButton.enabled = false;
}
Эти три функции будут
обрабатывать изменение переменных
eyes, nose и
mouth,
соответственно. На шаге мы задали для всех
этих переменных начальное значение
false; значит,
соответствующие функции будут выполняться
при изменении значения на
true. Работают
эти функции точно так же, как та, которую мы
создали на предыдущем шаге. Различия
заключаются лишь в именах самих функций, в
именах экземпляров, которые становятся
видимыми (в bigHead)
и невидимыми (smallHead),
и именах отключаемых кнопок.
Итак, функции созданы, пора
заняться и "наблюдателями".
Примечание Помните,
что функции, используемые методом watch(),
должны быть описаны до того, как будет
установлен сам "наблюдатель".
- В конце (но внутри) обработчика
события load добавьте следующие строки:
this.watch("glasses", watchGlasses);
this.watch("eyes", watchEyes);
this.watch("nose", watchNose);
this.watch("mouth", watchMouth);
Эти четыре строки содержат
указания Flash вести отслеживание значений
упомянутых свойств, и в случае изменения
выполнить соответствующую функцию. Первая
строка устанавливает отслеживание свойства
(переменной) glasses.
При изменении значения этого свойства
выполняется функция
watchGlasses(). Обратите внимание на
использование здесь термина
this. Первая
строка, в сущности, гласит: "Отслеживать
изменение значения свойства
glasses в
"этом" (this)
объекте". Если бы переменная
glasses
находилась в другом экземпляре, мы могли бы
попросту использовать адресацию – указать
путь к этому экземпляру (и/или функции), к
примеру, вот так:
_root.myMovieClip.watch("glasses", _root.myFunction);
Остальные три строки скрипта
устанавливают наблюдение за свойствами
eyes,
nose и
mouth, и при
их изменении выполняют соответствующие
функции.
Нам осталось лишь
запрограммировать четыре кнопки, которыми мы
и будем изменять значения четырех свойств.
- Выделите кнопку mouthButton
справа от экземплра bigHead и
присоедините к ней такой скрипт:
on(release){
_root.bigHead.mouth = true;
}
Как только эта кнопка будет
нажата и отпущена, значение свойства
mouth
экземпляра bigHead
изменится с false
на true, и на
это отреагирует "наблюдатель", установленный
нами на предыдущем шаге.
- К остальным кнопкам присоедините
следующие скрипты:
К кнопке noseButton:
on(release){
_root.bigHead.nose = true;
}
К кнопке eyesButton:
on(release){
_root.bigHead.eyes = true;
}
К кнопке glassesButton:
on(release){
_root.bigHead.glasses = true;
}
Все то же самое, что и с
предыдущей кнопкой. Различаются только
свойства, значения которых изменяются при
нажатии каждой из кнопок.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Итак, изначально
bigHead у нас
совершенно "обезличен". Нажатие той или иной
кнопки справа изменит значение
mouth, nose, eyes
либо glasses,
что приведет к выполнению одной из наших
функций. В сцене произойдут изменения – как
мы и запрограммировали.
- Закройте тестовый фильм и
сохраните работу как objectProject2.fla.
На этом упражнение закончено.
Как увидели, как используется отслеживание
свойств – единственное изменение значения
может запустить нужную функцию. Конечно, в
этом упражнении можно было обойтись без
всякого отслеживания – попросту вызывать
нужные функции кнопками. Вообще же
технология отслеживания свойств может быть
очень и очень полезна – особенно в ситуации,
когда изменение единственного свойства
объекта имеет решающее значение для общей
функциональности проекта. В таких случаях
бывает проще сразу установить наблюдение за
нужным свойством, чем потом то и дело
проверять его значение, чтобы вызвать
функцию. Да и вообще со свойствами как-то
удобнее работать.
Усовершенствование методов существующих
объектов
Встроенные во Flash классы
объектов (MovieClip, TextField, XML и тому
подобные) располагают множеством готовых
методов, ими можно обойтись почти в любой
ситуации. Тем не менее, может статься и так,
что вам понадобится что-то большее. Что ж, с
помощью знаний, полученных из этого урока,
вам будет несложно улучшить и расширить эти
методы, получив дополнительные возможности –
практически любые, какие только можно
вообразить.
Усовершенствовать
существующие методы можно двумя способами –
в зависимости от типа объекта. Для некоторых
объектов нельзя создать экземпляры – они
единственны и универсальны для каждого
проекта: это объекты
Mouse, Key, Selection и
Math. К
примеру, вы не можете создать экземпляр
объекта Mouse
– мышь у компьютера только одна, и на экране
только один указатель мыши; вы можете лишь
использовать методы объекта Mouse для
управления этим указателем. А для других
классов объектов вы можете создавать
экземпляры (MovieClip,
TextField и т.п.). Итак, если
экземпляр объекта создать нельзя, то для
усовершенствования метода этого объекта
используется такой способ:
MathPlus = new Object();
MathPlus._proto_ = Math;
MathPlus.oldMax = Math.max;
MathPlus.max = function(x, y, z){
firstComparison = MathPlus.oldMax(x, y);
secondComparison = MathPlus.oldMax(firstComparison, z);
return secondComparison;
}
Object.prototype.Math = MathPlus;
trace(Math.max(23, 56, 4));
Данный скрипт усовершенствует
метод max()
объекта Math,
который в нормальном своем виде находит
большее из двух чисел. Например, в этом
примере myVariable
получит значение 456:
myVariable = Math.max(456, 23);
Приведенный выше скрипт
изменяет этот метод так, чтобы он находил
большее из трех чисел. Вот как это делается:
в первой строке создается родовой объект с
именем MathPlus.
В следующей строке используется свойство
_proto_, чтобы
экземпляр этого нового объекта унаследовал
все функцио-нальные возможности объекта
Math.
Примечание Здесь мы не
можем обеспечить наследование, используя
выражение MathPlus =
new Math(); (как мы делали прежде с
экземплярами Doctor
и Person),
поскольку экземпляр объекта Math создать
нельзя – выражение
new Math() не имеет смысла. Поэтому,
чтобы объект MathPlus
унаследовал возможности объекта
Math, мы
должны использовать свойство
_proto_, как и
показано в скрипте – результат, в принципе,
то же самый, только достигается иным путем
(об этом свойстве мы рассказывали в этом
уроке, в разделе "Создание подклассов").
Теперь
MathPlus "умеет" делать все, на что
способен объект Math.
Например,
Math.round(34.7); и
MathPlus.round(34.7);
дадут один и тот же результат. Почему мы
решили пойти таким путем? Дело в том, что
напрямую манипулировать методами объекта
Math (как и
других объектов, не позволяющих создавать
экземпляры) нельзя. Поэтому мы создаем
объект-посредник (MathPlus),
который наследует возможности объекта Math,
изменяем функции этого посредника, а затем
(такое вот извращение) велим ActionScript
считать объект-посредник (MathPlus)
объектом Math.
Итак, в двух следующих
строках скрипта описываются два новых метода
объекта MathPlus:
oldMax,
который копирует оригинальный метод
Math.max(), и
max, который
станет новым, усовершенствованным методом
max(). Метод
oldMax создать
необходимо, поскольку функции оригинального
метода max()
все же понадобятся новому методу
max(). Однако
мы должны дать оригинальному методу новое
имя, так как имя max
будет теперь принадлежать новому,
измененному методу. Функция, описывающая
новый метод max(),
принимает три параметра (x,
y, z). Значения этих трех параметров
функция использует для нахождения
наибольшего значения из трех. Строка,
следующая за описанием функции, назначает
новый объект MathPlus
методом Math
прототипа класса
Object (это значит, что объект
MathPlus
заместит прежний объект
Math).
Можем заодно
усовершенствовать метод
random()
объекта Math,
чтобы он генерировал случайное число из
заданного диапазона. Это можно сделать с
помощью таких строк (предполагается, что
MathPlus уже
создан и "получил наследство", как показано
в первых двух строках предыдущего примера):
MathPlus.oldRand = Math.random;
MathPlus.random = function(min, max){
return MathPlus.floor(MathPlus.oldRand() * ((max - min) + 1)) + min;
}
Как видите, синтаксически это
похоже на предыдущий пример. С помощью
подобных синтаксических структур можно
усовершенствовать и методы объектов
Mouse, Key, Selection.
Способ изменения методов
таких классов, которые позволяют создавать
экземпляры (например,
MovieClip, TextField и т.п.),
отличается – но не намного. В этом
упражнении мы усовершенствуем метод
duplicateMovieClip().
Используя существующий метод, мы имеем
возможность указать, какой фильм-символ
следует дублировать, какое имя дать
дубликату и на какую глубину следует
поместить дубликат (подробнее об этом методе
см. в Уроке 14 – Динамическое управление
фильмами-символами). А мы усовершенствуем
этот метод, добавив возможность перемещать
оригинальный фильм-символ (тот, который
дублируется) в указанные
xy координаты
по окончании процесса дублирования.
- Откройте файл objectProject2.fla
из папки Lesson06/Assets.
Мы уже работали с этим
проектом в предыдущем упражнении, так что
его элементы должны быть вам знакомы. В этом
упражнении мы присоединим скрипты к кадру 1,
а также к кнопкам (в виде сдвоенных
квадратов), расположенным в левом верхнем
углу сцены.
- Открыв панель Действия, выделите
кадр 1 и введите следующий скрипт:
MovieClip.prototype.oldDuplicateMovieClip =
MovieClip.prototype.duplicateMovieClip;
MovieClip.prototype.duplicateMovieClip = function(name, depth, moveX, moveY){
this.oldDuplicateMovieClip(name, depth);
this._x = moveX;
this._y = moveY;
}
Мы поместили этот скрипт в
кадр 1, значит, как только начнется
воспроизведение фильма, метод
duplicateMovieClip()
будет изменен, после чего, в новом
виде, станет доступен всем экземплярам
фильмов-символов.
Методами объекта
Math мы
напрямую манипулировать не могли (пришлось
использовать объект-посредник), а вот
методами класса
MovieClip – вполне можем, поскольку
эти методы находятся в объекте
MovieClip.prototype,
который нам ничего не стоит изменить.
Первая строка скрипта
копирует оригинальный метод
duplicateMovieClip()
в
oldDuplicateMovieClip(). Это мы
делаем для того, чтобы сохранить его
функциональность, она нам пригодится, когда
мы будем описывать работу
усовершенствованного метода. Со следующей
строки начинается функция, описывающая
новый, измененный метод
duplicateMovieClip().
Этот метод будет принимать два
дополнительных параметра:
moveX и
moveY (сейчас мы поясним, как они
будут использованы). В первой строке функции
используется метод
oldDuplicateMovieClip() – он
выполняет то, что раньше делал оригинальный
метод
duplicateMovieClip(), то есть
дублирует фильм-символ, дает ему имя и
назначает глубину. Примененный здесь термин
this ссылается
на оригинальный экземпляр фильма-символа
(тот, который дублируется). Следующие две
строки, используя значения параметров
moveX и
moveY, перемещают оригинальный
экземпляр в соответствующую координатную
точку. Все – наш новый метод
duplicateMovieClip()
готов к использованию экземпляром любого
фильма-символа!
- Выделите меньшую из кнопок в виде
двойных квадратов и присоедините к ней
скрипт:
on(release){
smallHead.duplicateMovieClip("myHead1", 10, 200, 150);
}
Когда эта кнопка будет нажата
и отпущена, произойдет дублирование
экземпляра smallHead.
Дубликат получит имя
myHead1 и глубину 10. Два других
параметра – это значения, соответственно,
moveX и
moveY, они
используются для перемещения оригинального
экземпляра после дублирования. Таким
образом, smallHead
буде перемещен в точку с координатами 200 по
x и 150 по y.
- Выделите большую из кнопок в виде
двойных квадратов и присоедините к ней
скрипт:
on(release){
bigHead.duplicateMovieClip("myHead2", 10, 100, 200);
}
Здесь (таким же образом)
будет продублирован экземпляр
bigHead.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
При нажатии на каждую из
запрограммированных нами кнопок
соответствующий экземпляр будет дублирован и
перемещен в указанную точку координат.
В нашем примере изменение
существующего метода был простым, однако
такие усовершенствования могут быть весьма
сложными – процесс может включать очень
много действий ActionScript. Таким же
способом, то есть путем изменения функций в
объекте-прототипе, можно усовершенствовать
методы других классов (например,
TextField, Sound,
Color, XML и т.д.).
- Закройте тестовый фильм и
сохраните свою работу как
objectProject3.fla.
Это упражнение завершено, но
в следующем мы продолжим работу над данным
проектом.
Описание новых методов для встроенных
объектов
В предыдущем упражнении вы
узнали, как можно усовершенствовать
существующий метод класса объектов
MovieClip. Но что, если понадобится метод,
совершенно не похожий на существующие?
Создайте новый, свой собственный!
Как и при изменении
существующих, тут подход тоже зависит от
того, создается ли новый метод для
встроенного объекта (Mouse,
Key, Math, Selection) или класса
объектов (MovieClip,
TextField и т.п.).
Чтобы создать новый метод для
встроенного объекта (мы опять возьмем для
примера объект Math) следует использовать
такую синтаксическую конструкцию:
Math.totalWithTax = function(total, taxPercent){
convertedPercent = taxPercent / 100;
totalTax = total * convertedPercent;
return total + totalTax;
}
trace(Math.totalWithTax(24.00, 6)); // Выводит результат: 25.44
Как видите, чтобы добавить
метод (в данном случае –
totalWithTax())
к объекту Math,
мы попросту присоединили к нему функцию.
Теперь этот метод будет доступен в любом
месте фильма. Таким же образом
присоединяются новые методы и к другим
встроенным объектам.
В следующем упражнении мы
покажем, как добавить новый метод к классу
встроенных объектов (для примера будет взят
объект MovieClip,
но описанный способ подойдет и для других
встроенных классов). Метод, который мы
создадим, будет переворачивать экземпляр
фильма-символа относительно вертикальной или
горизонтальной оси – такого встроенного
метода в ActionScript не предусмотрено.
- Откройте objectProject3.fla из
папки Lesson06/Assets.
С этим проектом мы работали,
выполняя два предыдущих упражнения, так что
с его элементами вы знакомы. В этом
упражнении мы добавим скрипт в кадр 1, а
также к кнопкам в виде двусторонних стрелок,
что в левой верхней части сцены.
- Откройте панель Действия,
выделите кадр 1 и добавьте следующий
скрипт после уже имеющегося:
MovieClip.prototype.flip = function(mode){
if (mode.toLowerCase() == "h"){
this._xscale = -this._xscale;
}else if (mode.toLowerCase() == "v"){
this._yscale = -this._yscale;
}
}
Мы поместили этот скрипт в
кадр 1, поэтому он будет выполнен сразу
после начала воспроизведения фильма, и после
этого метод flip(),
добавленный нами в объект MoviClip.prototype
будет доступен экземплярам фильмов-символов
в любой момент. Как видите, этот метод – не
что иное, как функция, принимающая
единственный параметр под именем
mode. Его
значение может быть либо "h", либо "v"
–указание на поворот по горизонтали или
вертикали.
В зависимости от значения
mode функция
решает, как действовать – помогает ей в этом
оператор if. В
первой секции этого оператора значение mode
преобразуется в нижний регистр (строчную
букву), после чего сравнивается со значением
"h". Если преобразованное значение mode
равно "h", значит, нужно переворачивать по
горизонтали. Делается это путем изменения
знака текущего значения
xscale
(масштаб по x) данного экземпляра
фильма-символа (this).
То есть, если это значение равно 75, то
будет –75; если оно равно –34, то будет 34.
Обращение значения выполняется при помощи
оператора "минус" (-). При таком
использовании этот оператор превращает
положительное число в отрицательное и
наоборот. Таким вот образом масштабирование
фильма-символа приспособлено для
переворачивания.
Секция
else вступает в дело, если mode имеет
значение "v". В этом случае осуществляется
вертикальный переворот – меняется на знак
свойства yscale.
Примечание
Преобразование значения mode в символ
нижнего регистра мы предусмотрели на случай
нечаянного ввода "H" или "V" вместо "h" и
"v" – ведь "H" не равно "h". Такое
преобразование помогает снизить вероятность
ошибки при использовании нового метода.
- Выделите кнопку в виде стрелки
вверхвниз (ту, что расположена левее) и
присоедините к ней скрипт:
on(release){
smallHead.flip("v");
}
При нажатии этой кнопки
экземпляр smallHead
будет переворачиваться по вертикали.
- Выделите кнопку в виде стрелки
влево-вправо (ту, что расположена левее)
и присоедините к ней скрипт:
on(release){
smallHead.flip("h");
}
Это будет переворачивать
экземпляр smallHead по горизонтали.
- Присоедините к кнопкам в виде
двойных стрелок (расположенным правее)
скрипты из двух предыдущих шагов,
заменив smallHead на bigHead.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
При нажатии на какую-либо из
этих кнопок тот или иной экземпляр
фильма-символа будет переворачиваться по
горизонтали или вертикали.
Это был простой пример того,
как можно создать свой метод для встроенного
класса объектов. Таким же способом вы можете
создавать объекты для прочих классов (таких,
как TextField, Sound,
Color, XML и т.п.).
- Закройте тестовый фильм и
сохраните свою работу как
objectProject4.fla.
Это упражнение завершено, а
в следующем мы продолжим работу со слегка
модифицированной версией этого проекта.
Совет Отличный
сетевой ресурс, содержащий новые и
усовершенствованные методы ActionScript, вы
найдете по адресу
http://www.layer51.com/proto/.
Совет Вы можете
создать собственную библиотеку
дополнительных методов (а также и классов),
написав нужный код в стандартном текстовом
редакторе и сохранив файл с расширением
.as –
что-нибудь вроде
CustomScript.as – файлы с таким
расширением Flash воспринимает как
содержащие код ActionScript. Впоследствии,
когда вам понадобится воспользоваться своими
классами или методами, вам просто нужно
будет поместить в кадр 1 вашего фильма
действие #include,
указав имя файла, который следует включить в
программу (например,
#include "CustomScript.as"). Это
действие автоматически импортирует и
вставляет код из .as
файла в то место программы, где находится
действие #include.
Таких .as
файлов, содержащих код для повторного
использования, вы можете создать сколько
угодно.
Регистрация классов
Одна из наиболее ценных
новинок ActionScript, появившихся во Flash
MX – это возможность создания подклассов
фильмов-символов. Вы можете создать
собственный класс (этому вы научились в
данном уроке), запрограммировать его
функциональные возможности – используя
обработчики событий клипа, свои методы и
прочее; а потом ассоциировать с этим классом
один или несколько фильмов-символов из
библиотеки – тогда каждый экземпляр этого
символа автоматически унаследует возможности
вашего класса. Хотя на словах это может
звучать немного запутанно, на самом деле
ничего сложного тут нет. Давайте рассмотрим
пример. Для начала предположим, что у нас в
библиотеке имеется пара символов, которым
присвоены имена связи (Linkage identifier)
clipOne и
clipTwo.
Примечание Обратите
внимание, это важно: далее в тексте, говоря
о фильме-символе, мы будем иметь в виду
образец, хранящийся в библиотеке. Экземпляр
фильма-символа или просто экземпляр,
является копией этого образца.
Итак, давайте создадим новый
класс:
_global.CustomClass = function(){
}
Мы создали новый класс под
именем CustomClass.
Далее мы укажем, что этот класс должен
наследовать возможности класса
MovieClip –
для этого сразу после описания класса мы
поместим такую строку:
CustomClass.prototype = new MovieClip();
Теперь придадим нашему
классу дополнительные возможности, добавив
их в объект-прототип:
CustomClass.prototype.onRollOver = function(){
this._alpha = 50;
}
CustomClass.prototype.onRollOut = function(){
this._alpha = 100;
}
CustomClass.prototype.onEnterFrame = function(){
this._rotation += 10;
}
Наш класс получил,
посредством трех методов – обработчиков
событий, три функции интерактивного
взаимодействия. Теперь нужно, чтобы
экземпляры фильма-символа
clipOne могли
наследовать эти возможности. Для этого нам
следует воспользоваться методом
Object.registerClass():
Object.registerClass("clipOne", CustomClass);
Теперь все экземпляры
фильма-символа
clipOne будут реагировать на
rollOver,
rollOut и
enterFrame
так, как предписывает
CustomClass. Технически они более не
считаются экземплярами класса объектов
MovieClip –
вместо этого они стали экземплярами класса
объектов CustomClass.
Примечание При
описании методов – обработчиков событий
использовался термин
this; в итоге он будет адресной
ссылкой на каждый конкретный экземпляр,
унаследовавший функции.
Если вдруг необходимость в
наследовании экземплярами
clipOne
функциональности класса
CustomClass
отпадет, можно применить следующий
синтаксис:
Object.registerClass("clipOne", null);
Регистрация фильма-символа
как класса и ее отмена не имеет эффекта для
существующих экземпляров данного
фильма-символа, она действует только для
новых экземпляров, помещаемых в фильм –
прямо на монтажный стол в процессе
разработки либо динамически, посредством
duplicateMovieClip()
или attachMovie().
Существующие экземпляры сохраняют текущую
функциональность. Чтобы вы лучше поняли этот
принцип, давайте рассмотрим пример.
Допустим, в кадре 1 вашего фильма вы описали
CustomClass. В
этот же кадр вы поместили экземпляр
фильма-символа
clipOne и назвали его
myInstance. В
кадр 2 вы поместили действие, регистрирующее
clipOne как
CustomClass:
Object.registerClass("clipOne", CustomClass);
Теперь, хотя мы
зарегистрировали
clipOne как
CustomClass, экземпляр этого
фильма-символа
myInstance (помещенный в кадр 1) не
унаследует функциональность
CustomClass,
поскольку этот экземпляр появился в фильме
(в кадре 1) до того, как был вызван метод
registerClass()
(в кадре 2). Только те экземпляры, которые
будут помещены в фильм с этого момента –
вручную или динамически – будут наследовать
возможности
CustomClass.
Но что же делать, если
необходимо поместить экземпляры
clipOne в кадр
1, притом так, чтобы они унаследовали
возможности
CustomClass, описанного в этом же
кадре? Об этом вы узнаете, выполнив
упражнение этого раздела. Важно уяснить, как
это работает, в противном случае даже долгие
часы мучений ни к чему не приведут.
Фильм-символ (как тот же
clipOne) может
быть связан (зарегистрирован) только с одним
классом в каждый момент времени. Если вы,
зарегистрировав фильм как класс, затем
регистрируете его как другой класс, то эта
последняя регистрация заменит предыдущую.
Однако это не означает, что
несколько фильмов нельзя связать с одним
классом – это как раз можно. Просто
вызываете опять метод
registerClass() – столько раз,
сколько нужно:
Object.registerClass("clipTwo", CustomClass);
Object.registerClass("myClip", CustomClass);
Object.registerClass("anotherClip", CustomClass);
В результате этих действий
экземпляры фильмов-символов
clipTwo, myClip
и anotherClip
будут наследовать возможности
CustomClass.
Совет Если вы хотите
ассоциировать со своим классом только
отдельный экземпляр фильма-символа, просто
назначьте свойству
_proto_ объект-прототип этого класса:
myMovieClip._proto_ = CustomClass.prototype;
В этом случае экземпляр
myMovieClip
унаследует возможности
CustomClass.
Такое изменение свойства
_proto_
отражается на экземпляре немедленно.
Итак, в следующем упражнении
мы создадим собственный класс, настроим его
функциональные возможности, а затем, с
помощью метода
registerClass(), назначим
фильм-символ из библиотеки объектом нашего
класса.
- Откройте файл registerClass1.fla
из папки Lesson06/Assets.
С этим проектом мы работали
в предыдущих упражнениях, его элементы вам
знакомы. Однако здесь мы несколько изменили
структуру проекта. Теперь проект содержит
две сцены, идентичные во всем, кроме одного:
сцена 1 содержит кнопку, при нажатии которой
происходит переход к сцене 2, а во второй
сцене, наоборот, имеется кнопка для перехода
в сцену 1. Кроме того, в кадр 1 сцены 1
добавлено действие
stop(), чтобы пользователь мог
переключаться между сценами с помощью
кнопок.
Кнопки справа от экземпляра
bigHead теперь
заменены четырьмя экземплярами
фильма-символа Icons.
В среде разработки эти экземпляры выглядят
одинаково (изображения рта), однако на самом
деле фильм-символ
Icons содержит все изображения,
которые мы использовали ранее для кнопок
(глаза, нос, очки). К тому времени, как мы
закончим программировать этот проект и
проиграем его, эти четыре экземпляра будут
выглядеть так же, как те кнопки в предыдущих
упражнениях.
В этом упражнении мы сделаем
так, чтобы фильм-символ
Icons – а
значит, и эти четыре экземпляра –
унаследовали функции нового класса, который
мы создадим. В результате эти четыре
экземпляра станут как бы
усовершенствованными версиями тех кнопок,
которые мы использовали прежде.
Четыре экземпляра имеют
следующие имена (сверху вниз):
mouth, nose, eyes
и glasses.
Запомните эти имена: в построении проекта
они будут важны. Скрипты в этом упражнении
мы будем помещать в основном на монтажный
стол фильма-символа
Icons. Прежде, чем заняться этим,
давайте откроем библиотеку и посмотрим, что
представляет собою этот фильм-символ.
- Командой Окно > Библиотека
(Window > Library) откройте панель
Библиотека. Найдите фильм-символ под
названием Icons. Щелкните на нем правой
кнопкой (или Control - щелчок на Mac) и
выберите в появившемся меню команду
Связи (Linkage).
В открывшемся диалоговом
окне Свойства связи (Linkage Properties) вы
видите, что фильм символ имеет идентификатор
(имя) "icons". Для него установлена опция "Экспорт
в первый кадр" ("Export in first frame") –
это означает, что данный клип (а иногда и
скрипт, который он содержит) будет загружен
прежде, чем что-либо другое в этом фильме,
включая даже первый кадр – запомните это,
данный момент будет очень важен.
Примечание Подробнее
о настройках связи смотри в Уроке 14 –
Динамическое управление фильмами-символами.
- Нажав кнопку Отмена (Cancel),
закройте диалоговое окно Свойства связи.
Закройте панель Библиотека. Двойным
щелчком на одном из находящихся на сцене
экземпляров фильма-символа Icons
откройте его для редактирования на
месте.
Монтажный стол этого клипа
содержит четыре слоя, в каждом из которых –
40 кадров. В слое
Labels имеется четыре ключевых кадра
с метками: mouth,
nose, eyes и
glasses. Каждая из них обозначает
иное графическое изображение (в слое
Graphics),
соответствующее названию метки. Слой Hit
State содержит маленький прозрачный квадрат,
расположенный под изображениями. Этот
квадратик, как мы вскоре увидим, найдет
важное применение. В кадре 1 слоя Actions
будут содержаться все скрипты, которые мы
создадим в этом упражнении.
- Открыв панель Действия, выделите
кадр 1 и введите следующий скрипт:
_global.Custom1 = function(){
}
Custom1.prototype = new MovieClip();
Первые две строки создают
новый класс под названием
Custom1. Как
видите, описание класса предельно простое.
Следующая строка устанавливает наследование,
в результате которого класс
Custom1
получит все функциональные возможности
класса MovieClip.
Теперь мы опишем особые возможности,
присущие классу
Custom1.
- В конец этого скрипта добавьте
следующий:
Custom1.prototype.scale = function(amount){
this._xscale = amount;
this._yscale = amount;
}
Этот блок кода описывает для
класса Custom1
новый метод под названием
scale(). Метод
будет управлять масштабированием экземпляра,
основываясь на значении
amount,
передаваемом методу при вызове. Скоро мы им
воспользуемся.
Примечание В этом
скрипте (и в последующих)
this в итоге
станет ссылкой на тот экземпляр, который
унаследует возможности класса. Так, если
экземпляр с именем
myClip станет наследником
Custom1, действие
myClip.scale(50);
установит масштаб в 50 процентов для этого
экземпляра.
- В конец скрипта добавьте
следующие строки:
Custom1.prototype.onLoad = function(){
this.gotoAndStop(this._name);
}
В этой части скрипта мы
создаем метод - обработчик события
onLoad – он
описывает, что должно происходить при
загрузке экземпляров данного класса.
Действие
gotoAndStop() будет перемещать каждый
экземпляр к метке, соответствующей его
имени. Четыре экземпляра фильма-символа
Icons
унаследуют эту способность класса. Имена
этих экземпляров –
mouth, nose, eyes и
glasses –
такие же, как имена четырех меток на
монтажном столе данного фильма-символа.
Таким образом, при загрузке (onLoad)
экземпляр перейдет к метке, соответствующей
его имени (this._name).
В результате после начала фильма четыре
экземпляра будут выглядеть так же, как
прежние четыре кнопки.
- В конец скрипта добавьте
следующие строки:
Custom1.prototype.onPress = function(){
thisX = this._x;
thisY = this._y;
startDrag(this, true);
this.scale(70);
}
В этой части скрипта
использован метод-обработчик события
onPress, он
устанавливает, что должно происходить с
экземплярами данного класса при нажатии на
них кнопки мыши. Первые две строки фиксируют
текущие координаты экземпляра по
x и
y и помещают значения в переменные с
именами thisX
и thisY.
Следующее действие делает экземпляр
перетаскиваемым. В последней строке
используется метод
scale(), созданный нами на предыдущем
шаге – в данном случае он уменьшает
экземпляр до 70 процентов от нормального
размера.
- В конец скрипта добавьте
следующие строки:
Custom1.prototype.onRelease = function(){
stopDrag();
if (this.hitTest("_root.bigHead")){
this._x = thisX;
this._y = thisY;
_root.bigHead[this._name] = true;
this.scale(100);
this.enabled = false;
}else{
this._x = thisX;
this._y = thisY;
this.scale(100);
}
}
В этой части скрипта
применен метод-обработчик события
onRelease,
описывающий, что должно происходить с
экземплярами данного класса при отпускании
нажатой кнопки мыши. Первым делом
прекращается перетаскивание экземпляра.
Затем вступает в действие оператор
if,
выполняющий один из двух наборов инструкций
в зависимости от того, где оказался
экземпляр в результате перетаскивания. Для
проверки условия использован метод
hitTest() – с
его помощью выясняется, оказался ли
экземпляр после перетаскивания на экземпляре
bigHead. Если
это так, то выполняется первый набор
действий. Два первых действия перемещают
экземпляр в координаты со значениями
thisX и
thisY – то
есть в изначальное положение. В следующем
действии свойству экземпляра
bigHead,
имеющему такое же название, как имя текущего
экземпляра, присваивается значение
true.
Например, если нажатый и отпущенный
экземпляр имеет имя
nose, то данная строка расшифруется
так:
_root.bigHead.nose = true;
Таким образом, изменяется
значение свойства
nose экземпляра
bigHead – а
оно, как вы помните, отслеживается, так что
в результате этого изменения станет видимым
нос на большом лице (мы запрограммировали
это в одном из предыдущих упражнений.
Следующее действие
возвращает экземпляру первоначальный размер,
а последнее – "отключает" экземпляр, так что
он не будет более реагировать на событие
onPress (это
относится только к данному конкретному
экземпляру).
Секция
else
выполняется, если экземпляр в результате
перетаскивания не попал на
bigHead. В
этом случае выполняется всего три действия.
Экземпляр попросту возвращается в начальное
положение и принимает нормальный размер.
После этого его можно снова нажимать и
перетаскивать, и тогда снова оператор
if предпримет соответствующие
действия.
Программирование
функциональных возможностей нашего класса
закончено. Осталось ассоциировать с этим
классом фильм-символ
Icons, чтобы его экземпляры
наследовали функции класса
Custom1.
- Поместите в конец скрипта
следующую строку:
Object.registerClass("icons", Custom1);
Мы регистрируем фильм-символ
Icons (его
идентификатор связи – "icons") как класс
Custom1;
теперь экземпляры фильма будут наследовать
все свойства и методы этого класса.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
С началом воспроизведения
фильма вы обнаружите, что у нас проблемы –
проект не функционирует. Четыре экземпляра
фильма-символа Icons,
похоже, не унаследовали возможности
Custom1, как
мы планировали. Но если нажать кнопку Go to
Scene 2, то обнаружится, что теперь
экземпляры все унаследовали и работают. Если
теперь нажать кнопку Go to Scene 1, мы
увидим, что и здесь они заработали. Теперь
можно сколько угодно переключаться то на
сцену 2, то на сцену 1 – и здесь, и там
экземпляры будут работать правильно. Что же
получается? В самом начале фильма, в сцене
1, метод
registerClass() не имел эффекта, а
после переключения сцен заработал. Ладно,
давайте сперва внесем нужные исправления, а
потом разберем, в чем было дело.
- Закройте тестовый фильм,
вернувшись в среду разработки. Вы должны
снова оказаться на монтажном столе
фильма-символа Icons. Выделите кадр 1 и
в панели Действия поместите перед кодом,
введенным на предыдущих шагах, действие
#initclip, а в самом конце – действие
#endinitclip:
#initclip
// код, введенный на предыдущих шагах
#endinitclip
Эти действия заключают между
собой так называемый блок инициализации. Что
это такое, мы сейчас разъясним, но прежде
давайте проверим, как работает фильм теперь.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
На сей раз вы увидите, что
наши четыре экземпляра функционируют как
должно: работает и перетаскивание, и все
остальное; экземпляры обладают всеми
возможностями класса
Custom1. Если перейти в сцену 2, то и
там все будет в порядке. Так в чем же тут
дело?
Когда мы тестировали фильм в
прошлый раз, эффект регистрации
фильма-символа Icons
как представителя класса
Custom1 явно
не имел места, а теперь все сработало.
Давайте разберем по пунктам, как развивались
события при первом тестировании фильма:
- Фильм (SWF) загружается и
инициализируется.
- Загружается кадр 1.
- Создаются экземпляры, находящиеся в
кадре 1 (в том числе четыре экземпляра
клипа Icons).
- Выполняются действия, присоединенные
к кадру 1 основного монтажного стола.
- Выполняются действия, присоединенные
к кадру 1 экземпляров (как раз они и
включают описание класса
Custom1, и
здесь же находится вызов метода
registerClass()).
- С этого момента новые экземпляры
фильма-символа
Icons будут наследовать
возможности класса
Custom1.
Теперь понятно, почему не
работали четыре экземпляра – они появились
на сцене (третий пункт) до того, как
произошла регистрация (пятый пункт). А, как
вы помните, регистрация и ее отмены не имеет
эффекта для существующих экземпляров
фильма-символа – только для новых.
Существующие экземпляры сохраняют прежние
функциональные возможности. Вот и в нашем
фильме к моменту регистрации четыре
экземпляра уже были созданы, и потому ничего
не унаследовали. Ну, а как же экземпляры во
второй сцене? Они-то работали. Верно,
работали, поскольку переход в сцену 2 (и
создание экземпляров в ней) можно считать
седьмым пунктом в нашем перечне событий.
Регистрация уже состоялась, и на экземпляры
сцены 2 подействовала. Переход обратно в
сцену 1 будет восьмым пунктом; экземпляры
созданы заново, и регистрация тоже дала
нужный результат. Ну вот, мы выяснили,
почему так странно (на первый взгляд)
работал первоначальный тест. А каким же
образом решили проблему два действия,
которые мы добавили?
Нам требовался механизм,
который обеспечил бы наследование четырьмя
экземплярами функций класса
Custom1 – то
есть выполнил регистрацию – до загрузки
кадра 1 и создания этих экземпляров. Так
вот, действия
#initclip и
#endinitclip – и есть такой механизм.
Это специальные команды, которые
используются только в фильмах-символах (и
только в их кадре 1); они позволяют
управлять процессом инициализации фильма –
иначе говоря, порядком выполнения действий.
Вот как это работает.
Помните, в диалоговом окне
Свойства связи (Linkage Properties) для
фильма-символа Icons
(из библиотеки) была установлена опция
"Экспорт в первый кадр" ("Export in first
frame")? Так вот, это означает, что данный
клип будет загружен в память даже раньше,
чем кадр 1 основного фильма. Впрочем, в
первый раз нам это ничем не помогло; зато во
второй – Flash, загружая фильм-символ
Icons,
обнаружил в его первом кадре блок
#initclip –
#endinitclip и
должен был немедленно выполнить все действия
заключенные в этом блоке. Таким образом этот
скрипт был выполнен до загрузки кадра 1
основного монтажного стола, и порядок
событий получился таким:
- Фильм загружается и
инициализируется.
- Выполняются действия в блоке
#initclip –
#endinitclip (в этом блоке как
раз находится описание класса
Custom1, а
также вызов метода
registerClass()).
- С этого момента новые экземпляры
фильма-символа
Icons будут наследовать
возможности класса
Custom1.
- Загружается кадр 1.
- Создаются экземпляры, находящиеся в
кадре 1 (в том числе четыре экземпляра
клипа Icons).
- Выполняются действия, присоединенные
к кадру 1 основного монтажного стола.
- Выполняются действия, присоединенные
к кадру 1 экземпляров (любые действия,
не входящие в блок
#initclip –
#endinitclip).
И в результате процесс
регистрации состоялся (второй пункт) до
того, как были созданы экземпляры кадра 1
(пятый пункт). Наши четыре экземпляра
благополучно унаследовали все, что нужно –
как и показало второе тестирование.
Конечно, не обязательно
программировать создание класса и
регистрацию фильма-символа как объекта этого
класса именно тем способом, который был
описан в данном упражнении. Главное – чтобы
вы понимали порядок выполнения скриптов и
то, как этот порядок повлияет на процесс
регистрации, а способов можно выбрать в
зависимости от обстоятельств.
Примечание Показанную
здесь схему инициализации (с блоком
#initclip –
#endinitclip) используют все
компоненты Flash. Желательно, чтобы и вы,
создавая собственные компоненты,
придерживались этой схемы.
Примечание Если для
клипа, содержащего блок
#initclip –
#endinitclip, не установлена опция
"Экспорт в первый кадр" ("Export in first
frame") в диалоговом окне Свойства связи
(Linkage Properties), то блок инициализации
выполняется не перед первым кадром фильма, а
перед тем кадром, который содержит первый из
экземпляров данного клипа. Например, если
первый экземпляр встречается в кадре 47, то
блок инициализации исполняется перед
загрузкой кадра 47. В результате
экземпляр(ы), находящиеся в этом кадре,
будут зарегистрированы правильно.
Давайте потренируемся еще на
одном примере, добавим в наш проект
последний скрипт.
- Закройте тестовый фильм,
вернувшись в среду разработки. Перейдите
в Сцену 1. Командой Окно > Библиотека
(Window > Library) откройте панель
Библиотека и найдите фильм-символ под
названием HeadClip. Щелкните на нем
правой кнопкой (или Control - щелчок на
Mac) и выберите в появившемся меню
команду Связи (Linkage). В открывшемся
диалоговом окне Свойства связи (Linkage
Properties) установите флажок Экспорт в
первый кадр (Export in first frame) и вы
видите, что фильм символ имеет
идентификатор "headClip".
Этот клип изображает у нас
на сцене головы. Мы присваиваем ему
идентификатор, чтобы можно было его
зарегистрировать как объект еще одного
нового класса, который мы сейчас создадим.
- Выделите кнопку Go to Scene 2 и
добавьте в панель Действия следующий
скрипт – внутри обработчика события
on(release), перед строкой nextScene();:
_global.Custom2 = function(){
}
Custom2.prototype = new MovieClip();
Custom2.prototype.onEnterFrame = function(){
this._rotation += 5;
}
Object.registerClass("Icons", Custom2);
Object.registerClass("headClip", Custom2);
В шести первых строках
создается новый класс под названием
Custom2; его
экземплярам дается способность
поворачиваться на 5 градусов каждый раз при
наступлении события
enterFrame. Две последние строки
регистрируют фильмы-символы
Icons и
HeadClip как
объекты класса
Custom2 – после нажатия кнопки их
экземпляры унаследуют возможности этого
класса. Заметьте – мы поместили эти действия
перед действием
nextScene(): это сделано для того,
чтобы зарегистрировать клипы до перехода в
следующую сцену, в которой создаются
экземпляры этих символов. Если бы мы
поступили наоборот и поместили этот скрипт
после действия
nextScene(), то сначала произошел бы
переход во вторую сцену, были бы созданы
экземпляры, и только потом состоялась бы
регистрация клипов. При таком порядке
событий регистрация не имела бы эффекта –
ведь экземпляры клипов уже существуют.
Итак, при нажатии этой
кнопки и выполнении скрипта регистрация
клипа Icons
как объекта класса
Custom2 отменяет предыдущую связь его
с классом Custom1
(которую мы создавали и обсуждали в
предыдущих упражнениях). Таким образом,
изначально, в сцене 1, экземпляры клипа
Icons будут
функционировать как объекты класса Custom1,
но после нажатия этой кнопки все последующие
экземпляры будут наследовать
функциональность
Custom2 (как и экземпляры
фильма-символа
HeadClip).
Этот скрипт демонстрирует,
что как объявление нового класса, так и
регистрация могут происходить в любом месте
фильма. Соблюдая правильный порядок событий
при регистрации, вы можете осуществлять ее,
когда и как вам удобно.
- Командой Управление > Проверить
фильм (Control > Test Movie) запустите
тест проекта.
Сразу после начала
воспроизведения фильма четыре экземпляра
справа от bigHead
ведут себя как объекты класса
Custom1.
Нажмите кнопку перехода в сцену 2, и эти
экземпляры, как и экземпляры фильма-символа
HeadClip,
станут функционировать, как объекты класса
Custom2. Их
больше нельзя перетаскивать, они просто
вращаются, как предписывает класс
Custom2.
- Закройте тестовый фильм и
сохраните свою работу как
registerClass2.fla.
Уф, даже не верится – этот
урок закончился. Долгим он был, зато вы так
много узнали об объектах, наследовании,
классах, подклассах, методах и о многом
другом. Теперь вам по силам взяться за
разработку весьма сложных проектов. А кроме
того, все эти знания помогут вам в
последующих уроках этой книги.
|