Тема 13. Объекты и классы


  1. Введение
  2. Создание объектов
  3. Прототип и наследование
  4. Практикум
  5. Создание нового класса
  6. Классы ActionScript 2.0

1. Введение

Мы уже немного работали с объектами, теперь пришло время поговорить о них более подробно и обобщить всю информацию.

Объект — это нечто, имеющее свойства («знания») и методы («умения»).

Каждый объект относится к некоторому классу, класс описывает общие свойства и методы группы объектов. Отдельные представители класса называются экземплярами класса или просто объектами. Например, собака Бобик является экземпляром класса собака, то есть, имеет все свойства и умения, присущие собакам.

Мы уже работали с готовыми классами объектов Flash:

Базовым объектом является «родовой» объект Object. Это «объект вообще», для которого не определены ни свойства, ни методы.

В среде Flash есть так называемые глобальные объекты, они существуют только в единственном экземпляре. Например, это объекты

Для обращения к свойствам и методам объекта используется точка, например, qq.x обозначает свойство x объекта qq, а запись
qq.move();
вызывает его метод move.
к началу К началу страницы

2. Создание объектов

Одиночный объект

Для создания одиночного объекта достаточно присвоить ему свойства и методы:
var vasya = new Object();
vasya.name = "Вася";
vasya.age = 16;
vasya.say = function() {
  trace(vasya.name + ", " + vasya.age + " лет")
}
vasya.say();
Здесь создается объект vasya, задаются два его свойства (name — имя и age — возраст) и определяется один метод say, который выводит в окно Output информацию об объекте. В последней строке этот метод вызывается.
  Создайте новый документ, выделите кадр 1 и добавьте к нему код, показанный выше, и запустите ролик на выполнение. В окне проигрывателя выберите пункт меню Debug—List variables (клавиши Ctrl+Alt+V).
Эта команда означает «вывести информацию обо всех переменных в окно Output». Там должно появиться следующее:
Level #0:
Variable _level0.$version = "WIN 9,0,45,0"
Variable _level0.vasya = [object #1, class 'Object'] {
    name:"Вася",
    age:16,
    say:[function 'say']
  }
На уровне _level0 две переменных: символьная строка $version (версия Flash-проигрывателя) и объект vasya.

Свойством объекта может быть другой объект. Например, добавим Васе свойство head (голова).

  Добавьте в кадр 1 следующий код:
 vasya.head = new Object();
 vasya.head.eyes = 2;
 vasya.head.memory = new Array();
Голова (свойство head) имеет два глаза (eye) и память (memory) — массив. В память можно записать некоторые данные:
vasya.head.memory[0] = "Я родился.";
vasya.head.memory[1] = "Я начал ходить.";
vasya.head.memory[2] = "Я пошел в школу.";
Информация об объекте vasya должна выглядеть вот так:
Variable _level0.vasya = [object #1, class 'Object'] {
    name:"Вася",
    age:16,
    say:[function 'say'],
    head:[object #3, class 'Object'] {
      eyes:2,
      memory:[object #4, class 'Array'] [
        0:"Я родился.",
        1:"Я начал ходить.",
        2:"Я пошел в школу."
      ]
    }
  }
Свойства можно не только добавлять, но и удалять. Учитывайте, что при удалении свойства-объекта мы удаляем все его внутренние свойства и методы. Если добавить в кадр 1 код
delete vasya.head;
trace(vasya.head);
trace(vasya.head.memories[1]);
trace(vasya.name);
и запустить фильм, в окне Output мы увидим
undefined
undefined
Вася
Слово undefined означает, что запрошенное свойство не найдено.

Объект класса

Если нужно использовать несколько однотипных объектов, удобно создать новый класс, в котором описать их общие свойства.

Для создания объектов, относящихся к некоторому классу, используется специальная функция, которая называется конструктором. Конструктор вызывается с помощью ключевого слова new, например:

A = new Array();
Если мы хотим ввести свой класс, нужно задать для него конструктор. Например, введем класс person, который будет описывать человека:
Person = function() {
  this.name = "Вася";
  this.age = 16;
  this.legs = 2;
  this.say = function() {
    trace(this.name + ", " + this.age + " лет")
  }
}
vasya = new Person();
vasya.say();
Сначала вводится класс Person (человек). Каждый объект этого класса имеет имя (name), возраст (age), ноги (legs) и метод say. В строчке
vasya = new Person();
создается новый объект vasya класса Person, для которого далее вызывается метод say.
  Удалите весь код из кадра 1 и замените его на код, приведенный выше. Проверьте работу программы.
Хотелось бы, чтобы при создании объекта можно было сразу определить его имя и возраст. Для этого нужно изменить конструктор так:
Person = function(name, age) {
  this.name = name;
  this.age = age;
  this.legs = 2;
  this.say = function() {
    trace(this.name + ", " + this.age + " лет")
  }
}
а объект vasya создавать так:
vasya = new Person ( "Вася", 16 );
Теперь легко создавать новые объекты:
petya = new Person("Петя", 26);
petya.say();
Объект petya относится к классу Person и поэтому уже имеет две ноги и умеет выполнять метод say.

Свойства отдельных объектов можно менять:

petya.legs = 4;
При этом у Васи по-прежнему останется две ноги.

Если вызывать такой конструктор с другого монтажного стола (из клипа), нужно указывать его адрес:

kuzma = new _root.Person("Кузьма", 18);
Второй вариант — сделать описание класса глобальным, добавив к имени описатель _global:
_global.Person = function(name, age) {
  ...
}
Наблюдатели свойств
Метод watch позволяет реагировать на изменение свойств объекта (установить «наблюдателя»). Команда
vasya.watch ( "age", changeAge );
приводит к тому, что при любом изменении свойства age объекта vasya будет вызываться функция changeAge, в которой можно отменить операцию или изменить устанавливаемое значение. Это удобно, когда нужно не допустить запись неверных данных. А вот и сама функция:
function changeAge ( prop, oldAge, newAge ) {
  if ( newAge == oldAge+1 ) {
     trace("С днем рожденья!");
     return newAge;
  }
  else {
     trace ("Ошибка в программе?");
     return oldAge;
  }
}
Она должна принимать три параметра: Результат, который возвратит функция, будет фактически записан в переменную объекта. Если новое значение больше предыдущего на 1 (был день рождения), возвращается новое значение (все правильно). Если это не так, скорее всего, случилась ошибка и значение свойства не изменится, поскольку функция changeAge вернула старое значение oldAge.

После установки такого обработчика код

vasya.age = 21;
trace(vasya.age);
vasya.age = 17;
trace(vasya.age);
vasya.age = 15;
trace(vasya.age);
приведет к появлению сообщений
Ошибка в программе?
16
С днем рожденья!
17
Ошибка в программе?
17
В первом и третьем случаях (при ошибках) значение свойства не изменилось, а во втором — поменялось.

Отменить контроль за изменением свойства можно с помощью метода unwatch:

vasya.unwatch("age");
к началу К началу страницы

3. Прототип и наследование

Прототип

Каждый класс имеет специальный объект, называемый прототип, в котором «собраны» общие свойства и методы объектов данного класса. Прототип класса Person имеет имя Person.prototype. Если добавить свойства или методы к этому объекту, все созданные после этого объекты класса Person будут обладать этим свойством (методом).
  Замените код в кадре 1 на следующий:
Person = function(name, age) {
   this.name = name;
   this.age = age;
 }
 vasya = new Person ( "Вася", 16 );
 vasya.say();
 Person.prototype.say = function() {
     trace(this.name + ", " + this.age + " лет")
   }
 petya = new Person ( "Петя", 26 );
 petya.say();
и проверьте его работу.
В окне Output мы увидели только информацию о Пете. Это значит, что объект vasya не умеет выполнять метод say.

Это происходит потому, что объект vasya был создан раньше, чем к прототипу класса Person был добавлен метод say:

Person.prototype.say = function() {
    trace(this.name + ", " + this.age + " лет")
  }
Все объекты, которые созданы после этого кода, умеют «говорить».

Зачем же нужен прототип в практических задачах? Дело в том, что мы не можем изменить конструкторы встроенных объектов, таких как Sound, MovieClip и другие. Но зато можем добавить к прототипу новые свойства и методы, которые будут у всех создаваемых далее объектов:

MovieClip.prototype.say = function () {
  trace(this._name);
}
Все объекты класса MovieClip, которые будут созданы после выполнения этого кода, будут уметь выполнять метод say.

Напомним, что добавление к прототипу «срабатывает» только для вновь создаваемых объектов. Поэтому этот метод не подходит для объектов, которые существуют в единственном экземпляре, например, для Math.

к началу К началу страницы

Наследование

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

Вот как выглядит описание класса Student, который является потомком базового класса Person:

Student = function(name, age, vuz) {
  super(name, age);
  this.vuz = vuz;
}
Student.prototype = new Person();
Конструктор принимает три параметра: кроме уже известных name и age, добавлено еще свойство vuz (ВУЗ, институт). Наследование устанавливается в строчке
Student.prototype = new Person();
Фактически здесь сказано, что прототипом Student является Person.

Первая строчка в конструкторе:

super(name, age);
вызывает конструктор базового класса. Слово super в ActionScript используется для ссылки из класса-наследника к методам базового класса.

Теперь можно создать объект нового класса:

sema = new Student("Сема", 19, "МГУ");
Вот информация об объекте sema, полученная при просмотре переменных (клавиши Ctrl+Alt+V в режиме просмотра ролика):
Variable _level0.sema = [object #10] {
    name:"Сема",
    age:19,
    legs:2,
    vuz:"МГУ"
  }
Видно, что объект sema наследует свойства прототипа (и методы, хотя они не показываются), поэтому код
sema.say();
trace(sema.legs)
выдает правильные данные:
Сема, 19 лет
2
несмотря на то, что мы не определяли свойство legs и метод say для этого объекта.
к началу К началу страницы

4. Практикум

Наблюдатели свойств

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

  Здесь использован переработанный пример из учебного курса фирмы Adobe, опубликованного на сайте www.intuit.ru.

С помощью кнопок с двойными стрелками в правой части можно получить отражение большой головы в вертикальном и горизонтальном направлениях.

Щелкнув по кнопке , можно «запомнить» состояние большой головы — в правом нижнем углу появится ее копия. Если щелкнуть мышью по этой копии, она исчезнет и можно запоминать новое состояние.

  Откройте файл PRACTICE\13\face.fla, и посмотрите (с помощью панели Properties), какие символы размещены на сцене.
Вы найдете два клипа типа Голова, которые называются smallHead (маленькая голова) и bigHead (большая голова). В нижней части — клипы Глаза, Очки, Нос и Рот. Кнопки в правой части мы будем использовать чуть далее.

Сначала мы добавим большой голове свойства hasEyes (имеет глаза), hasGlasses (очки), hasNose (нос) и hasMouth (рот). Это свойства объекта, а не класса — у маленькой головы таких свойств нет.

Кроме того, мы установим «наблюдатель», отслеживающий изменение свойства hasGlasses — он будет передавать очки от большой головы к маленькой или наоборот. Все это удобно сделать в обработчике события load клипа bigHead.

  Выделите клип bigHead и добавьте к нему код:
 onClipEvent (load) {
   function watchGlasses(prop, oldVal, newVal) {
     glasses._visible = newVal;
     _root.smallHead.glasses._visible = oldVal;
     return newVal;
   }
   glasses._visible = false;
   eyes._visible = false;
   nose._visible = false;
   mouth._visible = false;
   hasGlasses = false;
   hasEyes = false;
   hasNose = false;
   hasMouth = false;
   this.watch("hasGlasses",watchGlasses);
   }
 }
Если посмотреть внутрь символа Голова, мы увидим, что в него входят символы с именами glasses (очки), eyes (глаза), nose (нос), mouth (рот). В этом обработчике все они делаются невидимыми (свойство _visible устанавливается в false). При изменении свойства hasGlasses будет вызываться функция watchGlasses, которая «переставляет» очки между большой и маленькой головами.
  Выделите клип-очки на сцене и добавьте к нему обработчик, который изменяет логическое свойство hasGlasses объекта bigHead на обратное (! hasGlasses):
 on ( release ){
   with ( _root.bigHead ) {
     hasGlasses = ! hasGlasses;
   }
 }
Запустите клип и пощелкайте на очках.
В принципе можно сделать то же самое и для остальных свойств (поставив отдельные «наблюдатели» для каждого свойства). Однако можно обойтись всего одной функцией, используя грамотно составленные имена свойств и объектов.

Вспомним, что первый параметр prop, передаваемый в функцию-наблюдатель, это название изменяемого свойства. Как из строки "hasGlasses" получить название объекта "glasses"? Надо «обрезать» первые три буквы и преобразовать строку к нижнему регистру (маленьким буквам):

objName = prop.substr(3);
objName = objName.toLowerCase();
Тогда «наблюдатель» для всех свойств будет выглядеть так:
function watchAll(prop, oldVal, newVal) {
  objName = prop.substr(3);
  objName = objName.toLowerCase();
  this[objName]._visible = newVal;
  _root.smallHead[objName]._visible = oldVal;
  return newVal;
}
  Исправьте обработчик load для клипа bigHead:
 onClipEvent ( load ) {
   function watchAll(prop, oldVal, newVal) {
     objName = prop.substr(3);
     objName = objName.toLowerCase();
     this[objName]._visible = newVal;
     _root.smallHead[objName]._visible = oldVal;
     return newVal;
   }
   glasses._visible = false;
   eyes._visible = false;
   nose._visible = false;
   mouth._visible = false;
   hasGlasses = false;
   hasEyes = false;
   hasNose = false;
   hasMouth = false;
   this.watch ( "hasGlasses", watchAll );
   this.watch ( "hasEyes", watchAll );
   this.watch ( "hasNose", watchAll );
   this.watch ( "hasMouth", watchAll );
 }
Добавьте обработчики события release для остальных кнопок и проверьте результат.

Добавление метода классу

Добавим к классу MovieClip функцию flip, которая выполняет вертикальное отражение (при передаче ей параметра "v") и горизонтальное отражение (если параметр равен "h"). Оказывается, чтобы «отразить» объект относительно оси X нужно только поменять знак у свойства _xscale.
  Добавьте новый слой Программа, выделите в нем кадр 1 и введите код
 MovieClip.prototype.flip = function ( mode ) {
   if (mode.toLowerCase() == "h") {
       this._xscale = - this._xscale;
   } else if (mode.toLowerCase() == "v") {
       this._yscale = - this._yscale;
   }
 }
Этим мы добавили новый метод к прототипу класса MovieClip.
  Добавьте обработчики события release для двойных стрелок в правой части сцены. Для вертикальной стрелки:
 on ( release ){
   bigHead.flip("v");
 }
и аналогично для горизонтальной:
 on ( release ){
   bigHead.flip("h");
 }
Проверьте работу стрелок в фильме.

Изменение существующего метода

Теперь изменим метод duplicateMovieClip так, чтобы при копировании клипа можно было задать его координаты на экране. При этом нам надо будет сначала вызвать старый метод, а потом изменить координаты нового клипа.
  Выделите кадр 1 слоя Программы и добавьте код
 MovieClip.prototype.oldDMC = MovieClip.prototype.duplicateMovieClip;
 MovieClip.prototype.duplicateMovieClip = function ( name, depth, newX, newY ){
   this.oldDMC ( name, depth );
   with ( eval(name) ) {
      _x = newX;
      _y = newY;
   }
 }
Добавление и изменение методов класса выполняется через прототип. Сначала мы создаем у прототипа новое свойство oldDMC (old Duplicate Movie Clip) и запоминаем в нем адрес существующего метода. Затем обновляем метод, присваивая ему функцию с четырьмя параметрами: название нового клипа, глубина и новые координаты.

Внутри обновленного метода сначала вызывается старый обработчик, ссылка на который хранится в свойстве oldDMC. Затем новому объекту присваиваются новые координаты. Поскольку параметр name — это символьная строка, чтобы получить ссылку на новый клип используется функция eval.

  Выделите кнопку и добавьте обработчик события release:
 on ( release ){
   bigHead.duplicateMovieClip("head2", 10, 460, 260);
   with ( head2 ) {
     _xscale = 70;
     _yscale = 70;
   }
 }
Запустите клип и проверьте работу кнопки. При нажатии на нее в правом нижнем углу появляется уменьшенная голова (ее размер — 70% от размера большой головы). Напомним, что оператор with(head2){...} говорит о том, что изменения масштаба применяется именно к объекту head2.
Теперь нужно сделать, чтобы на новом клипе были видимы те же элементы, что и на большой голове. Например, для очков требуется код
head2.glasses._visible = bigHead.hasGlasses;
Кроме того, при появлении объекта head2 на экране надо заблокировать кнопку. На панели Properties ей задано имя btnDup, поэтому блокировка выполняется строчкой
btnDup.enabled = false;
Чтобы при щелчке мыши на новой голове она исчезала и снова включалась кнопка btnDup, новому объекту надо добавить обработчик события release в виде функции
head2.onRelease = function() {
  removeMovieClip ( this );
  btnDup.enabled = true;
}
Метод removeMovieClip удаляет из памяти объект head2, а следующая строчка включает кнопку в активное состояние.
  Измените обработчик события release таким образом:
 on ( release ){
   bigHead.duplicateMovieClip("head2", 10, 460, 260);
   with ( head2 ) {
     _xscale = 70;
     _yscale = 70;
     glasses._visible = bigHead.hasGlasses;
     eyes._visible = bigHead.hasEyes;
     mouth._visible = bigHead.hasMouth;
     nose._visible = bigHead.hasNose;
   }
   head2.onRelease = function() {
     unloadMovie(this);
     btnDup.enabled = true;
   }
   btnDup.enabled = false;
 }
и проверьте работу клипа.
Несмотря на наши усилия, новая копия не запоминает состояние большой головы — на ней нет ни одного элемента. Дело в том, что новый клип унаследовал все свойства и методы объекта bigHead, в том числе и обработчик load, где все части лица скрываются. И этот обработчик срабатывает после выполнения написанного выше обработчика release. Чтобы этого не произошло, можно сказать, чтобы часть команд выполнялось только для объекта с именем bigHead:
if ( _name == "bigHead" ) {
  ...
}
  Выделите большую голову и измените ее обработчик load, поместив последние команды внутрь условного оператора:
 if ( _name == "bigHead") {
   glasses._visible = false;
   ...
   this.watch("hasMouth",watchAll);
 }
Проверьте работу клипа и сохраните его.
к началу К началу страницы

5. Создание нового класса

Предварительная подготовка

Остается сделать так, чтобы элементы можно было перетаскивать на большую голову. Таким образом, четыре кнопки должны вести себя одинаково. Конечно, можно написать отдельные обработчики событий press и release для каждой кнопки отдельно, но это не очень грамотное решение с профессиональной точки зрения.

Хотелось бы описать нужные методы только один раз. Конечно, можно добавить их к прототипу класса MovieClip, но в этом случае перетаскиваться будут все клипы, что нам совершенно не нужно. Мы введем новый класс, который наследует все свойства MovieClip и допускает перетаскивание.

Все кнопки будут принадлежать одному классу, то есть, будут представителями одного символа. Однако они должны иметь различные рисунки. Для этого можно сделать внутри временной шкалы клипа несколько областей с соответствующими метками, в каждой — новое изображение. При создании клипа он направляется на нужную метку командой gotoAndStop.

  Создайте новый символ Значок типа MovieClip и переименуйте слой Layer 1 в Рисунки. В кадр 1 вставьте клип Глаза так, чтобы его центр находился примерно в точке регистрации (крестик на поле). Для точного размещения используйте клавиши-стрелки.
  Добавьте новый пустой ключевой кадр 10 (клавиша F7) и добавьте на сцену клип Очки. Аналогично в кадры 20 и 30 добавьте клипы Нос и Рот.
  Создайте новый слой Метки, вставьте ключевые кадры 1, 10, 20 и 30. Разместите в них (с помощью панели Properties) метки Eyes, Glasses, Nose и Mouth (начинаются с заглавной буквы!).
  Создайте слой Программа и добавьте к кадру 1 код
 stop();
для того, чтобы клип в самом начале остановился.
  Вернитесь к редактированию сцены и перейдите на панель Properties. Для каждого из клипов-элементов
  • выделите клип;
  • щелкнув по кнопке Swap (замена) на панели Properties, замените тип клипа на Значок;
  • дайте клипу имя, совпадающее с нужной меткой внутри клипа Значок.
Вы увидите, что все клипы остановились на кадре 1 и на сцене видны 4 пары глаз.

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

Описание класса

Подготовительные операции сделаны, нужно создавать класс и «учить» клипы правильному поведению.

Вот объявление класса Parts — наследника MovieClip:

Parts = function(){}
Parts.prototype = new MovieClip();
При загрузке (событие load) нужно перейти на метку, совпадающую с именем экземпляра клипа (свойство _name):
Parts.prototype.onLoad = function(){
  this.gotoAndStop(this._name);
}
Удобно будет добавить новый метод scale (изменение масштаба сразу по вертикали и горизонтали):
Parts.prototype.scale = function(amount){
  this._xscale = amount;
  this._yscale = amount;
}
Метод onPress определяет, что делать при нажатии этой кнопки:
Parts.prototype.onPress = function(){
  x0 = this._x;
  y0 = this._y;
  startDrag (this );
  this.scale ( 70 );
}
Это значит, что при начале перетаскивания объект запоминает свои начальные координаты и уменьшается до 70% от обычного размера.

При отпускании мыши (событие release) перетаскивание останавливается (stopDrag) и клип возвращается в исходное положение (с координатами x0 и y0) и к первоначальному масштабу. Если элемент «бросили» на большую голову (это проверяется с помощью метода hitTest), изменяется соответствующее свойство клипа bigHead, совпадающее с именем перетащенного клипа. Вот полный обработчик события:

Parts.prototype.onRelease = function(){
  stopDrag();
  this._x = x0;
  this._y = y0;
  this.scale(100);
  var obj = _root.bigHead;
  var propName = "has" + this._name;
  if ( obj.hitTest(_root._xmouse, _root._ymouse) )
    obj[propName] = ! obj[propName];
}
Поясним последние 4 строки. Для упрощения записи вместо _root.bigHead мы используем локальную переменную obj. Если на большую голову перетащили, скажем, клип с именем Glasses, нужно изменить свойство hasGlasses у объекта bigHead. Благодаря грамотному выбору имен клипов, название нужного свойства propName строится добавлением «приставки» has к имени клипа this._name.

Учитывая, что при быстрой проводке мыши клип-элемент отстает от курсора, аналогичный обработчик поставим и для события releaseOutside (мышь отпустили вне объекта):

Parts.prototype.onReleaseOutside = Parts.prototype.onRelease;

Регистрация класса

Перед использованием новый класс надо зарегистрировать с помощью команды
Object.registerClass("Element", Parts);
Она говорит о том, что описанный класс Parts будет связан с тем символом, которому присвоено кодовое имя Element.
  Нажмите правую кнопку мыши на названии клипа Значок в библиотеке и выберите команду Linkage из контекстного меню. Отметьте флажок Export for ActionScript и введите в верхнее поле кодовое имя символа (идентификатор) Element. Обязательно оставьте включенным флажок Export in first frame (загрузить в первом кадре).
Понятно, что описание класса должно загрузиться в память до создания первого символа этого типа. Однако обычный порядок действий при загрузке Flash-фильма таков Таким образом, сначала создадутся экземпляры, а только потом зарегистрируется новый класс и ничего не выйдет. Чтобы «обойти» эту проблему, используют блок
#initclip
...
#endinitclip
Его можно вставлять только в клипы и только в кадр 1. Работает он следующим образом: Таким образом, при создании экземпляров класс уже загружен, и они унаследуют его свойства и методы.
  Перейдите в режим редактирования клипа Значок. Поместите в кадр 1 слоя программы и замените код stop() на
 #initclip
 ...
 Object.registerClass("Element", Parts);
 #endinitclip
где вместо многоточия вставьте все блоки описания класса Parts, приведенные в предыдущем пункте. Проверьте работу клипа и сохраните файл.
к началу К началу страницы

6. Классы ActionScript 2.0

Пример

В версии языка ActionScript 2.0 были введены полноценные классы, описание которых хранятся на диске в виде текстовых файлов с расширением *.AS (в той же папке, что и использующий их FLA-файл).
  Откройте клип Значок и перейдите в окно кода, где записан блок #initclip — #endinitclip. Сохраните все содержимое в файле с именем Parts.as, выбрав команду Export Script из меню панели Actions (кнопка в правом верхнем углу). Удалите весь код кадра 1 в окне Actions.
  Откройте файл Parts.as и исправьте код на следующий:
 class Parts extends MovieClip {
   var x0, y0;
   function scale(amount) {
     _xscale = amount;
     _yscale = amount;
   }
   function onLoad() {
     gotoAndStop(_name);
   }
   function onPress() {
     x0 = _x;
     y0 = _y;
     startDrag ( this );
     scale ( 70 );
   }
   function onRelease() {
     stopDrag();
     _x = x0;
     _y = y0;
     scale ( 100 );
     var obj = _root.bigHead;
     var propName = "has" + _name;
     if (obj.hitTest(_root._xmouse, _root._ymouse)) {
       obj[propName] = !obj[propName];
     }
   }
   function onReleaseOutside() {
     onRelease();
   }
 }
Сохраните файл.
Попытаемся тут разобраться. Общая структура файла такова:
class Parts extends MovieClip {
...
}
Класс Parts расширяет (extends) класс MovieClip, то есть является его наследником. Описание всех новых свойств и методов класса находится внутри фигурных скобок.

Переменные x0 и y0, в которых запоминается начальное положение клипа, нужно объявить как свойства объектов класса Parts. Методы записаны как функции, их внутреннее содержание осталось прежним.

  При обращении к свойствам и методам класса из методов этого же класса не нужно указывать ссылку на объект this, это делается автоматически.

Способ связывания клипа Значок с классом Parts несколько изменился: в свойствах надо заполнить поле Class:

При загрузке программа сама будет искать файл Parts.as и загрузит его в память. Сообщения об ошибках выдается в окно Compiler Errors.

  Нажмите правую кнопку мыши на клипе Значок в библиотеке, выберите команду Linkage и введите имя класса Parts, как показано на рисунке выше. Проверьте работу клипа и сохраните его с именем face2.fla.

Теория

Построим «с нуля» класс Car (автомашина).
  Создайте новый FLA-файл и сохраните его в папке PRACTICE\13 под именем car.fla.
  Нажав клавиши Ctrl+N создайте новый файл-скрипт (тип файла — ActionScript File и сохраните его в той же папке под именем Car.as. В этом файле введите код:
 class Car {
   var _x: Number = 0,
       _y: Number = 0,
       vx: Number = 0,
       vy: Number = 0;
   function Car ( x: Number, y: Number ) {
     _x = x;
     _y = y;
   }
 }

У объектов этого класса четыре числовых переменные: координаты _x и _y, а также vx и vy — проекции вектора скорости на оси X и Y. В них записываются нулевые начальные значения.

При создании классов считается хорошим стилем строго задавать тип данных:

Это несколько увеличивает текст программы, но позволяет выявить некоторые ошибки и опечатки на ранней стадии. Кроме того, если тип переменной точно указан, при наборе ее имени в редакторе кода появляется окно с подсказками, в котором перечислены все свойства и методы, применимые к этому типу данных.

Функция Car (ее имя совпадает с названием класса) — это конструктор, он вызывается при использовании оператора new. Конструктор имеет два параметра и позволяет установить начальное положение.

  Добавьте к описанию класса функцию toString, которая возвращает информацию об объекте в виде текстовой строки:
 function toString(): String {
   var s: String;
   s = "x=" + _x + " y=" + _y;
   return s;
 }
и сохраните файл.
Функция toString — особая функция, которая вызывается всегда, когда нужно преобразовать объект в символьную строку, например, при вызове функции trace.

Слово String после двоеточия в заголовке функции говорит о том, что результат функции — символьная строка. Это тоже строгая проверка типов данных.

  Перейдите к ролику car.fla, выделите кадр 1 и добавьте к нему код
 car1 = new Car(100, 100);
 trace(car1);
Запустите ролик.
В окне Output вы должны увидеть
x=100 y=100
Теперь сделаем так, чтобы при нажатии клавиш-стрелок скорость машины изменялась в нужном направлении. Проблема в том, что новый объект должен как-то получить сообщение о нажатии клавиши. Для этого есть механизм «слушателей» (listener).
  Сделайте объект слушателем глобального объекта Key, добавив в конструктор строчку
 Key.addListener ( this );
Кроме того, добавьте в класс новый метод для обработки нажатий клавиш (событие keyDown):
 function onKeyDown(): Void {
   switch ( Key.getCode() ) {
     case Key.LEFT:  vx --; break;
     case Key.RIGHT: vx ++; break;
     case Key.UP:    vy --; break;
     case Key.DOWN:  vy ++;
   }
   trace ( "vx=" + vx + " vy=" + vy );
 }
Запустите ролик и посмотрите, что будет при нажатии на клавиши-стрелки.
Слово Void (пустой) означает, что функция не возвращает никакого результата, а просто выполняет действия.

Метод Key.getCode() возвращает код нажатой клавиши. В зависимости от этого кода оператор switch выбирает один из вариантов. Если нажата не клавиша-стрелка, скорость не меняется. Новые составляющие скорости выводятся в окно Output. Обратите внимание: объект невидим, но уже реагирует на нажатия клавиш!

Чтобы машина стала видимой, сделаем класс Car наследником MovieClip. Поскольку клипы уже имеют внутренние переменные _x и _y, их надо убрать из объявления класса Car.

  Измените объявление класса таким образом:
 class Car extends MovieClip {
    var vx: Number = 0,
        vy: Number = 0;;
    function Car() {
      Key.addListener ( this );
    }
    function onKeyDown(): Void {
     witch ( Key.getCode() ) {
       case Key.LEFT:  vx --; break;
       case Key.RIGHT: vx ++; break;
       case Key.UP:    vy --; break;
       case Key.DOWN:  vy ++;
     }
   }
   function onEnterFrame(): Void {
     _x += vx;
     _y += vy;
   }
 }
Здесь добавлен обработчик события enterFrame (новый фрейм), в котором изменяются координаты машины (она двигается).

Теперь построим изображение.

  Перейдите к файлу car.fla и создайте новый символ Машина типа MovieClip, нарисуйте в нем круг. Свяжите этот символ с классом Car так же, как и в предыдущем примере (команда Linkage и контекстного меню, далее см. рисунок ниже).

  Добавьте в центр сцены один клип типа Машина и удалите весь код, связанный с кадром 1. Запустите фильм и проверьте, как круг реагирует на нажатия клавиш.

Некоторые свойства и методы можно сделать закрытыми («частными»), поставив перед ними слово private (частный):

class Zz {
  private var qq: String;
  private function foo(): Void { ... }
}
Свойство qq и метод foo будут доступны только из методов-функций класса Zz, методы объектов других классов их «не видят». Это делается для того, чтобы случайно не испортить значения.

Переменная, описанная с помощью слова static — это переменная класса (статическая переменная). Она одна на все экземпляры этого класса. Вот так можно считать объекты класса Mumu

class Mumu {
  static var count: Number = 0;
  function Mumu() {
    count ++;
    trace(count);
  }
}
В конструкторе увеличивается счетчик объектов count.

Считается хорошим стилем запрещать прямой доступ ко всем свойствам, делая их частными (закрытыми). Тогда чтение и запись значений возможно только с помощью методов. В среде Flash есть два специальных метода get (получить) и set (установить), которые удобно применять для работы со свойствами. Покажем это на примере. Вот определение класса:

class Qq {
  private var angleRad: Number = 0;
  function get angle(): Number {
    return angleRad*180/Math.PI;
  }
  function set angle(a: Number): Void {
    while ( a > 360 ) a -= 360;
    while ( a < 0 )   a += 360;
    angleRad = a*Math.PI/180;
  }
}
Внутренняя закрытая (private) переменная angleRad хранит значение угла в радианах. Объект имеет общедоступное свойство angle — угол в градусах. Для его чтения используется функция get angle, которая принимает значение угла в градусах, переводит его в радианы и полученное число записывает в свойство angleRad.

Метод set angle вызывается при записи значения в свойство angle. Заданный угол приводится к диапазону от 0 до 360 градусов, переводится в радианы и записывается в переменную angleRad.

Метод set может использоваться для проверки правильности данных (если задается неверное значение, оно корректируется или команда вообще не выполняется).

А вот код, использующий этот класс:

q = new Qq();
q.angle = 180;
trace ( q.angle );
q.angle = 710;
trace ( q.angle );
В окне Output мы увидим:
180
350
Во второй раз заданный угол 710° был уменьшен до 350° в функции set angle.
к началу К началу страницы


Оглавление
 Видео Назад В начало Вперед Рисование из программы


© 2007  К. Поляков


Сайт создан в системе uCoz