Книги, научные публикации Pages:     | 1 | 2 | 3 | 4 | 5 |   ...   | 8 |

F L A S 100 советов и рекомендаций профессионала O'REILLY С&ППТЕР Шам Бхангал F L A S H H A C K S Sham Bhangal O'REILLT Beijing Х Cambridge Х ...

-- [ Страница 3 ] --

Рис. 4. 2. Персонаж стоит на месте, указатель мыши находится справа Рис. 4. 3. Эффективный цикл ходьбы В окончательном варианте Скриббл двигался очень быстро - слишком быстро, чтобы зритель мог заметить сокращенное количество кадров в анимации. Кроме того, он много времени проводил в воздухе, сводя к минимуму эффект скольжения. Конечно, если два дизайнера работают над одним проектом, простыми решениями дело никогда не ограничивается. В данном случае было решено, что анимацию следует осуществлять в трех измерениях. Скриббл и указатель мыши должны были двигаться в псевдотрехмерном мире. Вроде бы такой подход должен существенно усложнить анимацию, верно? Чтобы реализовать достаточно плавный эффект трехмерного вращения текста, необходимо до 20 кадров. Трехмерный цикл ходьбы потребует гораздо больше кадров, потому что меняется не только направление/ориентация объекта, но и сам объект (Скриббл). На рис. 4.4 показано, как Адам решил эту задачу. На радиальных линиях представлены циклы ходьбы Скриббла в соответствующих направлениях. Чтобы свести к минимуму великое множество кадров, задействованных в реализации трехмерного цикла ходьбы, Адам использовал ряд неочевидных приемов: Х Цикл ходьбы влево представляет собой зеркальное отражение цикла ходьбы вправо. Это стало возможным благодаря тому, что раскраска изображения Скриббла не зависит от направления - фигура в любом случае остается черной. Таким образом, три фазы ходьбы влево представляют собой зеркальные отражения трех фаз ходьбы вправо. Чтобы создать зеркальное отражение графического объекта или символа, вызовите панель Transform Глава 4. Анимация (Window Х Design Panels Х Transform), снимите флажок Constrain и измените знак масштабного коэффициента по горизонтали или вертикали (в зависимости от того, по какой оси строится отражение). Например, чтобы создать зеркальное отражение символа по вертикальной оси, задайте горизонтальный масштабный коэффициент -100.

Рис. 4.4. Трехмерный цикл ходьбы Графика ходьбы вверх совпадает с графикой ходьбы вниз;

отличие только в глазах (либо они рисуются на голове, либо нет).

Альтернативные средства построения анимации Х Каждый цикл ходьбы содержит множество повторяющихся элементов. Кадры, на которых левая нога стоит на земле, фактически, повторяют кадры с опущенной ногой в зеркальном отражении, поэтому между этими кадрами может выполняться оптимизация. Движение Скриббла ограничивается восемью основными направлениями. Используемая анимация (размашистая подпрыгивающая походка) нормально смотрится на разных скоростях. В реальном мире движения при ходьбе отличаются от движений при беге, поэтому мы специально разработали стилизованную походку, подходящую для любой скорости. Проделать нечто подобное для реалистичной анимации лошади не удастся, потому что шаг, рысь и галоп выглядят по-разному.

Х Х С учетом всех оптимизаций полный трехмерный цикл ходьбы состоит всего из 10-20 кадров. Объем пересылаемых данных снижается до минимума!

Итоги Хотя идеи, заложенные в основу этого трюка, после прочтения кажутся тривиальными, в веб-анимации они встречаются не так уж часто. Аналогичная методика применима к любой циклической анимации (например, полету птицы). Идеально плавная анимация нужна не всегда. Более того, довольно часто снижение количества кадров лучше передает движение и делает его более оригинальным. Конечно, не стоит забывать и о таких положительных аспектах, как сокращение времени загрузки и объема работы! Если вам захочется узнать, как выглядит Скриббл в деле, ознакомьтесь с одной из наших ранних работ scribbleWalk.fIa на сайте книги. Также обратите внимание на код кадра 1 уровня actions в загруженном файле. В этом коде также применен ряд других оптимизаций, но их поиск поручается читателю для самостоятельной работы.

ТРЮК № Альтернативные средства построения анимации Как бы мы ни любили Flash, это не единственная среда разработки. Существуют и другие программы для построения анимационной графики.

Flash занимает первое место среди анимационных и мультимедийных платформ, ориентированных на Веб, но существуют и другие варианты. Даже если исключить программы среднего звена вроде Toonboom ( и Macromedia Director ( а также специализированные приложения вроде генераторов текстовых эффектов и Swift 3D ( остается ряд программ, заслуживающих вашего внимания. Пакет Processing ( интересен тем, что он ориентирован на создание сценарной анимации, но, в отличие от Flash, не отягощен листорическим наследием анимации на временной диаграмме.

Глава 4. Анимация Koolmoves ( специализированный пакет для анимации персонажей, обладает рядом возможностей, отсутствующих в Flash. Оба пакета вполне могут рассматриваться в качестве альтернативы Flash разработчиками и аниматорами, работающими в специфических областях. Processing Сейчас Flash двигается в том же направлении, что и Веб в целом. Macromedia усиленно продвигает концепцию разработки R1A (Rich Internet Application), указывает на повсеместное распространение Flash, на простоту перехода на эту платформу и ее практичность. Конечно, так было не всегда - когда-то Flash считался выбором неформалов в области веб-дизайна. Я до сих пор хорошо помню свои посещения первых конференций Flash Forward ( на которых демонстрировалось множество замечательных, творческих и совершенно некоммерческих разработок. Как ни печально, Flash уже не является неоспоримым королем в области независимых цифровых мультимедийных технологий. Возможно, корона скоро перейдет к Processing ( прикладному интерфейсу графического программирования, который без малейшего труда может быть освоен любым программистом ActionScript. На рис. 4.5 приведены примеры работ Алессандро Капоццо (Alessandro Capozzo) ( *& Рис. 4.5. Графика, созданная в Processing Processing работает на базе Java. Это язык программирования и среда разработки, при проектировании которых ставились две основные цели: Х Х обучить сообщество художников и дизайнеров азам программирования на визуальном уровне;

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

Альтернативные средства построения анимации Самые замечательные свойства Processing - быстрота, бесплатное распространение и работоспособность в любом браузере с поддержкой Java. Возможно, в секторах некоммерческой веб-графики и математических исследований этот пакет превзойдет Flash по популярности. Может, вы считаете, что я преувеличиваю? Приведу список имен, известных в сообществе Flash, которые уже активно используют Processing: Х Cinema Redux: Брендан Доус ( Х Х Х Х Gallery of Computation: Джаред Тарбелл ( Point Man: Энт Идеи ( Sonic wire sculpture: Амит Питару ( Кейт Питере ( Как видите, Processing все активнее вторгается в нашу жизнь. KoolMoves На первый взгляд, KoolMoves ( представляет собой усеченную, дешевую версию среды разработки Flash, по набору возможностей находящуюся где-то между Flash 3 и Flash 4, - однако этот пакет обладает рядом возможностей, которые более нигде не встречались. Особого внимания заслуживает скелетная анимация (рис. 4.6), позволяющая определять нетривиальные иерархические связи между анимируемыми элементами, - сделать нечто подобное в Flash очень трудно. Учитывая, что KoolMoves стоит всего $49, к тому же существует пробная бесплатная версия (которая не позволяет сохранять результаты, но во всем остальном абсолютно функциональна), с этим пакетом стоит познакомиться поближе, особенно если вам приходится часто создавать анимации без сценариев. Поклонников сценарной анимации KoolMoves вряд ли заинтересует (тем более что они сейчас наверняка отложили книгу и загружают Processing).

Й Рис. 4.6. KoolMoves поддерживает скелетную анимацию, имитирующую строение тела персонажа Итоги У среды разработки Flash существует немало альтернатив;

одни используют формат SWF (и требуют Flash Player для воспроизведения), другие работают Глава 4. Анимация с собственными форматами. Проект Adobe LiveMotion прекратил существование и больше не поддерживается. Среди других вариантов стоит особо выделить следующие: Х Ming ( - библиотека функций С, которые могут использоваться из распространенных сценарных языков, работающих на стороне сервера (таких как Perl, Python, PHP и Ruby), для динамического построения SWF-файлов. Результат загружается в клиентский SWF в виде вложенного клипа или используется для замены текущего SWF в Flash Player. Обратите внимание: Ming только генерирует новые SWF, но не модифицирует уже существующие файлы;

Х Flash является самой распространенной платформой для создания мультимедийного содержания в Веб, однако SWF - далеко не единственный формат итогового документа. SVG (Scaleable Vector Graphics), как и Flash, является векторной средой. Графика SVG строится на базе текстовой разметки XML, а это означает, что содержимое файла более доступно для поисковых систем, чем двоичный формат SWF. Документы SVG молено создавать в любом текстовом редакторе. Сейчас существует уже несколько систем разработки SVG, но все они значительно уступают Flash MX 2004. Тем не менее, большинство браузеров не распознает содержание SVG (см. software/player_census/flashplayer), к тому же модуль SVG занимает больше места, чем Flash Player, и обычно его бывает труднее установить. Из-за повсеместного распространения Flash Player SVG скорее рассматриваться может как интересная альтернатива для особых случаев, нежели как полноценная замена Flash и формата SWF. Знакомство с SVG лучше всего начать со списка SVG FAQ ( и SVG Cafe ( на этих сайтах вы получите информацию от людей, которые знают SVG лучше, чем кто-либо другой. Будьте готовы к каверзным вопросам или обвинениям по поводу Flash/SWF (а также неистовой дезинформации со стороны людей, которые подобным образом самоутверждаются). На октябрьской конференции МАХ в Солт-Лейк Сити, штат Юта, фирма Macromedia продемонстрировала версию Flash Player с ограниченной поддержкой SVG. Из этого можно сделать вывод, что в будущем Flash Player будет поддерживать отображение контента SVG, хотя и не в полном объеме спецификации SVG;

Flex ( - сервер представления от Macromedia, динамически генерирует SWF-файлы на XML-подобном языке (называемом MXML). Flex также использует ActionScript 2.0 для вывода возможностей управления сценариями за пределы стандартных средств временной диаграммы. Flex ориентируется на коммерческих Java-разработчиков. Этот продукт должен избавить среду Flash от основных недостатков, на которые чаще всего указывают разработчики, программирующие на стороне сервера, - а именно реализовать возможность разработки на текстовом, а не визуальном уровне, а также использования редактора и системы управления исходными текстами по выбору разработчика. Иначе говоря, Flex сочетает многие сильные стороны SVG и Flash.

Х Принцип Deja New Принцип Deja New Повторяющиеся анимации экономят ресурсы, но быстро приедаются. Создавая неповторяющиеся циклы, вы сделаете анимацию более разнообразной, не повышая затрат ресурсов и не прибегая к ActionScript. Кадрированная анимация хорошо подходит для аниматоров, не владеющих навыками программирования, но она страдает от целого ряда ограничений. Самое очевидное ограничение - Пониженная интерактивность по сравнению со сценарной анийацией. В некоторых приложениях Flash это не создает проблем, особенно в неинтерактивных мультфильмах. В анимации часто используется еще одна возможность, которая в общем случае не реализуется без сценариев: случайное или не повторяющееся движение. Кадрированная анимация проходит по строго фиксированному плану, никогда не содержит случайных элементов, а все повторения полностью совпадают. Предположим, имеются две кадрированных анимации в двух разных клипах, на 10 и на 20 кадров. Если в цикле запустить их в рамках одной общей анимации, ролик будет повторяться каждые 20 с. Анимация становится однообразной, а это ослабляет впечатления пользователя. Другой пример: допустим, вы хотите создать 30-секундный ролик с движущимися облаками. Конечно, можно построить 30-секундную анимацию, но это займет много времени. Вместо этого лучше создать анимацию с одним или несколькими облаками, несколько раз продублировать ее и позаботиться о том, чтобы изображение не повторялось. Но если эта 30-секундная циклическая анимация будет использоваться в качестве фона для 5-минутного ролика, она снова начнет повторяться. В идеале небо должно состоять из множества отдельных анимированных клипов, формирующих почти не повторяющееся изображение. Стандартное решение этой проблемы - запереться на неделю в комнате, изучая ActionScript и возможности Math.random(). Умное решение - использовать простые числа в кадрированных анимациях. Простым называется число, которое нацело делится только на 1 и на само себя. Простые числа в интервале от 1 до 100: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89 и 97. Если потребуется дополнить этот список, проведите поиск в Веб по ключевым словам prime number или prime series. Главной особенностью простых чисел является их неделимость. С точки зрения математики это означает, что наименьшим общим кратным двух простых чисел является их произведение. А что это означает для нашей ситуации? Если имеются две анимации длиной п и т, где питпростые числа, то третья анимация, содержащая первые две, не будет повторяться первые п х т кадров. Следовательно, если изменить две кадрированные анимации так, что их длина в кадрах будет выражаться простыми числами (например, 11 и 19), анимация, содержащая первые два клипа, не будет повторяться на промежутке максимальной длины, определяемой произведением длин двух исходных клипов. Повторение происходит только один раз Глава 4. Анимация каждые 209 кадров (20,9 с при частоте смены кадров 10 fps), а не каждые 20 кадров (2 с),, как при использовании клипов длиной 20 и 10. Неплохо, если учесть, что более длинная анимация состоит из 19 кадров. Итак, если каждая кадрированная анимация, выполняемая в цикле, имеет длину, выраженную простым числом (и отличную от длины других анимаций), повторения будут разделены максимальными промежутками. Наверное, лучшим примером использования простых чисел в FLA служит мой самый первый Flash-файл. Мне хотелось создать анимированную картинку отдыхающей бабочки. Движения бабочек, как и большинства одушевленных объектов, хаотичны и непредсказуемы: Х Х Х крылья периодически складываются и раскрываются, даже если бабочка не собирается взлетать;

усики двигаются;

все тело насекомого слегка подрагивает.

На рис. 4.7 представлено несколько начальных кадров ролика - моего первого опыта в компьютерной анимации.

Рис. 4.7. Анимация бабочки Но я понятия не имел, как сделать анимацию бабочки случайной и жизненной, в отличие от механических, явно искусственных кадрированных циклов. Тогда мой опыт работы с Flash составлял всего пару часов, и я ничего не знал о языке ActionScript (который, впрочем, в той версии был весьма ограничен - в тот же день я освоил все 15 или 20 возможных операций!). В итоге я решил использовать разные анимации для трех частей: Х Х Х анимация крыльев - 97 кадров;

анимация усиков - 31 кадр;

анимация подрагивания тела бабочки - 41 кадр.

Таким образом, между повторениями анимации проходило 97 х 31 х 41 кадров, или приблизительно 3 часа (при 12 fps). Конечно, никто не будет рассматривать анимацию так долго, но поскольку это была моя первая Flash-анимация, я был не так уж далек от этой цифры.

Как попасть в Матрицу Чтобы увидеть окончательный вариант анимации с бабочкой, загрузите файл butterfly.fla с сайта книги.

Итоги Хотя для создания неповторяющейся анимации проще воспользоваться Action Script, некоторые анимации просто лучше работают в кадрированном варианте. Конечно, основные проблемы с кадрированием возникают из-за фиксированного, циклического характера анимации. Немного математики, нестандартный подход к вопросу - и проблема легко решается.

Как попасть в Матрицу Реконструкция знаменитого эффекта падающего зеленого текста из фильма Матрица Предположим, мы хотим создать быструю версию водопада из зеленых букв из кинотрилогии Матрица без применения сценариев. В эффекте задействованы случайно падающие символы катаканы (о том, что такое катакана, можно узнать по адресу Первый символ text представляет собой простую временную диаграмму с тремя ключевыми кадрами (рис. 4.8).

Рис.

4. 8. Временная диаграмма с тремя ключевыми кадрами для символа text Три ключевых кадра отображают три статических текстовых поля, показанных на рис. 4.9. Хотя цикл анимации получается крайне однообразным, никто этого не заметит, потому что все происходит очень быстро. Эффект зеленого водопада в Матрице состоит из множества букв, случайным образом падающих по экрану. Чтобы создать действительно случайное падение текста, нам пришлось бы полностью запрограммировать эффект на сценарном уровне;

поверхностное же знакомство с Flash-сообществом показывает следующее: 1) далеко не каждый знает, как это делается;

2) многие люди хотят воссоздать эффект из Матрицы, но считают, что для этого обязательно нужно знать язык сценариев. Что ж, создать действительно случайную анимацию из кадрированных заготовок невозможно, но представленная далее анимация повторяется только один Глава 4. Анимация раз через каждые 14 535 931 кадров (примерно две недели при 12 fps), так что может считаться почти случайной. В моем варианте реализации используется пять кадрированных анимаций, каждая из которых выделена в отдельный клип. В каждом клипе текст падает сверху вниз (рис. 4.10).

j щ л Х.

п| тж т з| : я&я.

\ы \4\ \п\ h ] ж№\ d m s |э| a wи * 1*1* Ж Рис. 4.9. Три статических текстовых поля, используемые в качестве основы для построения анимации Анимации имеют разную длину в кадрах. Клип waterfall 19 содержит 19 кадров, а клип waterfall37 - 37 кадров. Имена всех пяти клипов в библиотеке изображены на рис. 4.11. На рис. 4.12 изображены три последовательных кадра в созданном эффекте. Каждый столбец представляет один из пяти одновременно запущенных клипов. Клипы воспроизводятся циклически, поэтому изображение в конечном счете повторяется. Но поскольку длины всех пяти анимаций представлены простыми числами (см. трюк 30), до повторения должно пройти 19 х 23 х 29 х 31 х 37 кадров - весьма долгий промежуток времени. Если вам нравится ощущение дежа вю, ждать придется довольно долго. Впрочем, любого поклонника первого фильма трилогии дежа вю ни к чему хорошему не приведет, и от него лучше держаться подальше. При желании добавьте звук падающей воды, чтобы еще точнее воспроизвести эффект из Матрицы.

Как попасть в Матрицу Рис. 4. 1 0. Кадрированная анимация с текстом, падающим сверху вниз Movie Clip Movie Clip Movie Clip Movie Clip Рис. 4. 1 1. Библиотека с символами клипов, длины которых выражаются простыми числами Глава 4. Анимация Рис.

4.12. Три кадра итоговой анимации Итоги Повторение циклов анимации довольно часто встречается даже в коммерчески успешных анимационных проектах. Например, главный герой едет на машине, и в окнах подозрительно часто мелькает одна и та же комбинация уходящих вдаль деревьев, пригородных домов и припаркованных машин. А когда видишь, что в саду перед домами с номером 56 играют одни и те же дети, все становится ясно. Такое происходит из-за того, что даже в высокобюджетных проектах анимация обходится дорого, поэтому постановщик старается по возможности сэкономить. И все же подобные ухищрения не должны бросаться в глаза, а трюк с простыми числами помогает скрыть циклическое повторение отдельных анимаций. Реализм компьютерных спецэффектов растет, и даже сцены из реальной жизни содержат повторяющиеся элементы. Например, огромная толпа в Вашингтоне из фильма Форест Гамп в действительности состоит из маленькой группы людей, многократно повторенной. В фильме Шоу Трумэна обитатели фальшивого городка регулярно повторяют одни и те же действия (машины кружат по кварталу и т. д.), причем главный герой в исполнении Джима Керри замечает это. Возможно, на уроках математики вы думали, что с простыми числами имеют дело только математики. Оказывается, даже Тому и Джерри не обойтись без них.

Анимация персонажа, №32 сгенерированного компьютером ТРЮК Если вам некогда рисовать персонажей для анимаций Flash вручную, воспользуйтесь приложением Poser фирмы Curious Labs. Ветераны помнят, какой эффект произвело появление первой псевдотрехмерной анимации на сайтах Flash в эпоху расцвета Flash 4. Первые пользователи Анимация персонажа, сгенерированного компьютером этой методики (такие как Мануэль Клемент - создавали все вручную, но с появлением специализированных модулей экспортирования 30-графики в Flash и автономных приложений (прежде всего Swift 3D - www.swift3dd.com) появилась возможность автоматизации процесса. Приложения вроде Swift 3D хорошо подходят для создания элементов трехмерных интерфейсов и других объектов, имеющих правильную форму. Конечно, это полезная возможность, и все же в повседневной работе Flash-дизайнерам обычно приходится решать несколько иные задачи. Во многих Flash-роликах задействованы псевдотрехмерные персонажи, которые почти всегда реализуются по старинке, то есть рисуются от руки.

Автоматизация анимации персонажа в Poser Приложение Poser 5 фирмы Curious Labs ( ранее называвшейся MetaCreations, предназначено специально для создания трехмерных фигур. В отличие от других программ создания трехмерных персонажей (таких, как пакет Character Studio от 3D Studio Max), оно довольно просто осваивается. Любой, кому доводилось работать в Swift 3D, быстро начнет работать в Poser 5. Кроме того, в отличие от многих специализированных программ анимации персонажей, Poser стоит относительно недорого. Возможно, вам придется потратить некоторое время на изучение программы, но зато вам не придется вручную рисовать все анимации персонажей. На ручное создание типичной анимации Flash уходят месяцы, поэтому затраченное время быстро окупается. Не падайте духом, если в Веб вам встретятся плохие отзывы о Poser. Они относятся в основном к нулевой версии Poser 5, которая действительно имеет целый ряд проблем. После установки всех исправлений Poser становится надежным и устойчивым инструментом. Существует два способа использования Poser совместно с Flash: Х создание реалистичных анимированных фигур, которые берутся за образец при построении ваших собственных анимаций. Именно для этой цели изначально создавался Poser - как электронная версия деревянных манекенов, используемых некоторыми художниками;

Х непосредственное создание анимаций и их экспортирование в Flash в формате SWF (по аналогии с экспортированием трехмерной анимации в Flash из Swift 3D). Наше знакомство с Poser начнется с рассмотрения второго способа, поскольку в нем задействованы многие возможности, встроенные в Poser специально для экспортирования в Flash.

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

Глава 4. Анимация При запуске Poser 5 на экране отображается стандартная модель в стандартной позе, как показано на рис. 4.13.

.

Х :Х:Х.....

Х :-.

Х :

Х :Х s Х Х Х Рис. 4.13. Начальное окно Poser Выделите любую часть стандартной фигуры и нажмите клавишу Delete, чтобы удалить ее. Когда на экране появится диалоговое окно с предложением подтвердить операцию, щелкните на кнопке Yes. В библиотеке Poser (если библиотека не отображается справа, выполните команду Window Х Libraries) выполните команду Figures Х Additional Figures ХCartoons;

на экране появляются персонажи, изображенные на рис. 4.14. Щелкните на фигуре Минни (Minnie). Минни появляется в главном окне в стандартной для трехмерных моделей персонажей Т-образной позе (корпус выпрямлен, руки вытянуты в стороны). Мы хотим создать для нее анимацию. Выполните в библиотеке команду Pose Х Cartoon Poses Х Minnie. Выберите позу Point, изображенную на среднем кадре р и с 4.15. Анимация позы состоит из 28 кадров (на это указывает число в правом верхнем углу эскиза позы).

Анимация персонажа, сгенерированного компьютером Рис. 4. 1 4. Встроенные персонажи Poser: Дедушка, Мик и Минни Изображение Минни в главном окне изменяется и приводится в соответствие с выбранной позой, как показано на рис. 4.15.

Рис. 4. 1 5. Эскиз позы Point (слева): трехмерная модель Минни изображает первый кадр позы в главном окне (справа) Глава 4. Анимация Возможно, фигуру Минни стоит немного сдвинуть назад на сцене Poser, чтобы она полностью помещалась на сцене Flash при анимации в формате SWF (дело в том, что сцена Poser имеет квадратную форму, тогда как сцена Flash обычно представляет собой прямоугольник, и при просмотре в Flash верхняя и нижняя части фигуры будут усечены). Для этого найдите на экране кнопки Editing Tools (если они не отображаются, выполните команду Window Х Editing Tools). Щелкните на кнопке Translate In/Out (рис. 4.16) и перетащите указатель мыши вверх или вниз, чтобы передвинуть Минни назад или вперед.

Рис. 4.16. Кнопка Translate In/Out в Poser Чтобы экспортировать анимацию в Flash SWF, выполните команду Animation Х Make Movie;

на экране появится диалоговое окно Make Movie, показанное на рис. 4.17. Выберите в списке Sequence Type строку Macromedia Flash (.swf). В группе Frame Rate установите переключатель Use This Frame Rate и выберите частоту смены кадров для ролика SWF. По умолчанию в Poser используется частота смены кадров 30 fps. При существенном снижении частоты ниже уровня 30 fps анимация становится излишне дерганой, поэтому лучше задать частоту смены кадров на уровне 30 fps или около того.

Movie: |Untitled j l :

.

Х :Х' :.

sh ! s Ж-! Х > Х'Х U s e t h i s ;

f r a m e r a t e :

4.fiiSlijtton.: |Жй;

:Ш^.-..

:

Х, w] :.

.-Х...:Х - Tiroe Sp-an: SUrt 0 B E ' Quality 111 Ш : i ^l Х anrr 0 00 01 R a s h S e t t i n g s C a n c e l Рис. 4.17. Диалоговое окно Poser Make Movie Щелкните на кнопке Flash Settings в диалоговом окне Make Movie. Параметры диалогового окна Flash Export (рис. 4.18) определяют способ преобразования графики в векторный формат. Параметры, представленные на рис. 4.18, подходят для большинства оптимизируемых SWF (4 цвета, Quantization=AII Frames, флажки не устанавливаются). Выбор оптимального количества цветов зависит от созданного персонажа. Палитра Минни сильно сокращена, поэтому персонаж экспортируется в векторный формат с минимальным количеством цветов. Помните: чем больше цветов выбрано, тем больше создается векторов.

Анимация персонажа, сгенерированного компьютером tSH Export Number of colors: Quantization:

(* M Frames Г" Overlap Colors Хbetter.maybe larger) f Draw outer lines' ~ Рис. 4.18. Диалоговое окно Poser Flash Export Щелкните на кнопке OK, чтобы подтвердить параметры в окне Flash Export. Повторный щелчок на кнопке ОК создает SWF-ролик. Чтобы импортировать сгенерированный ролик в Flash, создайте новый или откройте существующий файл FLA и выполните команду File Х Import Х Import to Stage. Анимация отображается в виде серии ключевых кадров на одном слое. Графические формы объединяются в группы. Как и при всех операциях экспортирования, следующим шагом должна стать оптимизация анимации. Оптимизация данных Poser очень близка к оптимизации данных Swift 3D. Возможно, вам помогут следующие рекомендации: Х Х отмените группировку содержания каждого кадра, затем выполните оптимизацию (Modify Х Shape Х Optimize) и/или сглаживание (Modify Х Shape Х Smooth);

Poser, как и Swift 3D, не преобразует часто используемые фигуры в символы для повторного использования. Вы должны сами найти такие фигуры и решить проблему. Например, ноги и юбку Минни явно можно заменить небольшим количеством символов, которые могут многократно использоваться в разных кадрах.

На рис. 4.19 представлен процесс оптимизации с применением развертки контуров, а также стандартных приемов и графических инструментов Flash. Более опытные Flash-дизайнеры предпочитают импортировать изображения из Poser в виде опорных растровых изображений и трассировать их в векторную форму, как показано на рис. 4.20. Я тоже предпочитаю этот способ;

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

Глава 4. Анимация ^ Рис. 4.19. Оптимизация анимации, экспортированной из Poser в Flash Рис. 4.20. Выходные данные Poser могут импортироваться в Flash и использоваться как основа для построения анимации Итоги Poser часто используется совместно с Photoshop для построения любой графики, в которой задействованы модели персонажей. Совместно с Flash эта программа используется довольно редко. Хотя создание трехмерных персонажей и их анимация в Poser с последующей ручной оптимизацией каждого кадра в Flash занимают довольно много времени, по сравнению с ручной реализацией эта процедура способна сэкономить целые месяцы тяжелой работы (особенно при наличии опыта работы в Swift 3D).

Эффекты частиц 1G Аниматоры традиционной школы, предпочитающие рисовать своих персонажей вручную, смогут при помощи Poser быстро и легко создавать прототипы для последующей работы.

ТРЮК № Эффекты частиц Эффекты частиц (взрывы, искры, дождь, снег и т. д.) делают анимацию более реалистичной.

При создании кадрированной анимации очень трудно реализовать стандартные эффекты частиц - снег, звездные потоки, переливающуюся воду и т. д. Flash не имеет встроенной поддержки эффектов частиц, а анимация тысяч крошечных клипов в разных направлениях займет много времени. Но в действительности создавать разнообразные эффекты проще, чем кажется на первый взгляд. В простейшем варианте частица движется в одном направлении - сверху вниз. Начнем с моделирования падающих частиц с использованием механизма вложения клипов. Поворот клипа позволяет создать множество разнообразных эффектов без особых усилий с вашей стороны. function mover():Void { this._y += this.speed;

if (this._y > 400) { this._y = 0: this.speed = Math.random()*10+5:

} var path:MovieClip = this.createEmptyMovieClipC'path". 0): var dot:MovieClip = this.path.createEmptyMovieClipC'dot". 0);

dot.lineStyleCO. 0x0. 100);

dot.moveTo(0. 10);

dot.lineTo(0. 15): dot.speed = Math.randomO * 10 + 5;

dot.onEnterFrame = mover;

Приведенный фрагмент создает клип path, в котором создается другой клип с именем dot. Затем клип dot перемещается от верхнего края экрана вниз. Возникает резонный вопрос: для чего мы создали path? В этом и заключается суть трюка - поворачивая path, можно изменить направление перемещения dot. Попробуйте добавить следующий фрагмент в конец предыдущего листинга и посмотрите, как это делается.

// Перемещение клипа в точку (100.100), чтобы он // оставался видимым даже при повороте, path._x = path._y = 100;

path._rotation = 50;

Поворачивая path, мы изменяем направление движения dot. Более того, масштабированием path в процессе перемещения dot можно создавать сложные эффекты ускорения без лишней работы.

Глава 4. Анимация Звездный поток В следующем листинге приведена измененная версия этого кода. Обратите внимание: операции масштабирования и поворота выделены жирным шрифтом. Программа предполагает, что сцена была окрашена в черный цвет (выполните команду Modify Х Document, щелкните на образце и выберите черный цвет). function moverО { // Перемещение частиц во времени this._y += this,speed: this.yscale =+= 10;

this.speed++: i f (this._y > 500) { this._y = 0: this.speed = Math.random()*10;

this._yscale = 100: function starField(x, y. n) { / Псреи зедоо птк здны рзео и п зед / отоне взнг ооа аанх амрв з вз f r ( a i = 0: i < n: i++) { o vr v r s a = t i. r a e m t M v e l p ' t r + i i): a tr hscetEpyoiCiCsa". v r d t = s a. r a e m t M v e l p " o " 0): ao trcetEpyoiCi(dt. s a. r t t o = Math.randomQ * 360;

tr_oain s a. x = x;

tr_ s a. y = y;

tr_ d t l n S y e O O F F F. 100);

o.ietlC. xFFF d t m v T (. 10): o.oeo0 d t l n T (. 15): o.ieo0 dtoEtrrm = mvr o.nneFae oe: dot.speed = Math.random()*10;

} starField(275. 200. 100): В этом коде мы создаем множество клипов star, каждый из которых принимает на себя роль клипа path, рассмотренного в предыдущем примере. Каждый клип star поворачивается под случайным углом (рис. 4.21). Клипы star также подвергаются масштабированию;

растяжение траектории (клип star) во время движения dot ускоряет перемещение и создает иллюзию того, что при приближении к зрителю объект движется быстрее. Трюк с масштабированием path во время движения dot может использоваться для создания эффекта гравитации (см. трюк 38). В следующем листинге те же приемы имитируют Движение воды: function mover():Void { this._у += this.speed: this._yscale =+= 10: this.speed++: i f (this._y > 500) { this._y = 0: this.speed = Math.randomO * 10:

Эффекты частиц this, yscale = 100;

function waterfall(xl. x2. n) { for (var i = 0;

i < n;

i++) { var star:MovieClip = this.createEmptyMovieClipC'star" + i. i) star._x = Math.randomO * x2 + x l ;

star._y = 0;

var dot = star.createEmptyMovieClip("dot", 0);

dot.lineStyle(0. OxFFFFFF, 100);

dot.moveTo(0. 10);

dot.lineTo(0. 15);

dot.onEnterFrame = mover;

dot.speed = Math.random()*10: } waterfall(200. 300. 800);

Рис. 4. 2 1. Звездный поток, созданный с применением эффектов частиц Последний эффект, приведенный далее, имитирует снегопад. Произвольное изменение угла траектории частиц создает хаотический набор движущихся частиц, похожих на снежинки: function mover() { this._y += this.speed;

i f (this._y > 500) { this._y - 0;

this.speed = Math.randomO * 10 + 5;

} function snow(a. n) 1В4 for Глава 4. Анимация (var i = 0;

i < n: i++) { var star:MovieClip = this.createEmptyMovieClipC'star" + i. i ) ;

star._x = Math.randomО * 550;

star._rotation = (Math.randomO * 2 * a) - a star._y = 0;

var dot:MovieC1ip = star.createEmptyMovieClip("dot". 0): dot.lineStyleCO. OxFFFFFF. 100);

dot.moveTo(0. 10);

dot.lineTo(0, 15);

dot.onEnterFrame = mover: dot.speed = Math.random()*10 + 5;

} snow(30. 800);

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

это способствует увеличению количества частиц и упрощает реализацию эффекта.

ТРЮК Морфинг сложных фигур Морфинг относительно редко применяется на практике, поскольку его результаты часто оказываются непредсказуемыми. Проблема решается упрощением фигур и выравниванием количества замкнутых контуров.

№ Одним из простейших и часто используемых применений морфинга, то есть анимации изменения формы фигур, является преобразование текста. Чтобы создать анимацию такого рода, активизируйте инструмент Text и введите какуюнибудь букву (например, ле) в кадре 1. Вставьте ключевой кадр (F6) в кадре 10, удалите в нем букву ле и замените ее буквой с. В каждом из ключевых кадров выделите букву и выполните команду Modify Х Break Apart, чтобы преобразовать текст в векторную форму (если текст состоит из нескольких букв, команда Modify Х Break Apart должна выполняться дважды). Выделите один из промежуточных кадров и выберите команду Shape, в раскрывающемся меню Tween на панели свойств. Переместите индикатор текущей позиции по временной диаграмме и проследите за тем, как происходит морфинг. Превращение ле в с вроде бы должно выполняться тривиально, но Flash сбивается с толку и создает лишние промежуточные петли (рис. 4.22). Алгоритм морфинга, используемый в Flash, не любит, когда одна фигура содержит внутренние замкнутые области (например, верхнюю петлю буквы ле), а в другой фигуре таких областей нет.

Морфинг сложных фигур Из рисунка понятно, что происходит при морфинге. Flash вполне разумно пытается преобразовать внешний контур ле во внешний контур с, но не знает, что делать с внутренним контуром, поскольку у буквы с такого контура нет. Таким образом, без введения дополнительных линий сделать ничего не удастся.

Рис. 4. 2 2. Некачественный морфинг ле в с происходит из-за того, что фигуры имеют разное количество замкнутых контуров Ранее уже был описан прием создания прорезей в фигурах (см. трюк 20). Используя его, вы, фактически, обманываете Flash и заставляете его думать, что сложная фигура (с несколькими замкнутыми контурами) имеет более простую форму. Аналогичный трюк может использоваться и для сближения разнородных фигур, обеспечивающего более гладкий морфинг. Применяя ту же методику, мы просто надрезаем петлю буквы ле, чтобы обе буквы имели одинаковое количество замкнутых контуров. Местонахождение разреза выбирается таким образом, чтобы Flash было проще преобразовать одну фигуру в другую. Возможно, с выбором придется немного поэкспериментировать, но вскоре у вас разовьется необходимая интуиция. В нашем случае разрез лучше всего расположить в любом месте вдоль горизонтальной линии, образующей нижнюю сторону петли в букве ле. Выберите волосяную линию в свойствах инструмента Line на панели свойств и проведите линию в выбранной точке. Преобразуйте линию командой Modify Х Shape Х Convert Lines to Fills и удалите ее. Контур замкнутой области сливается с внешним контуром, и фигура становится одноконтурной. Как видно из рис. 4.23, на этот раз морфинг дает гораздо лучший результат. Лишние петли исчезают, a Flash принимает вполне разумные решения при управлении переходом. Теперь Flash правильно преобразует контур ле в контур с, поскольку каждая буква имеет только один замкнутый контур.

Рис. 4.23. Выравнивание количества замкнутых контуров в крайних точках анимации существенно повысило качество морфинга Глава 4. Анимация Проблема несовпадения количества замкнутых областей объясняет, почему Flash так плохо преобразует буквы с изолированными элементами (такие как i и j) в монолитные буквы вроде t. Проблема представлена на рис. 4.24.

Рис. 4. 2 4. Некачественный морфинг i в t Обходное решение - удалить точку из i непосредственно перед морфингом, как показано на рис. 4.25.

Ь'-\ Рис. 4.25. Качественный морфинг i в t обеспечивается удалением точки непосредственно перед преобразованием Морфинг сложных фигур.

Но если преобразование выполняется в обратном направлении, то есть от t к i, вам придется либо использовать трюк с прорезью и разбить t на две замкнутые области, либо дождаться завершения морфинга и добавить точку над i.

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

ГЛАВА Трехмерная графика и физика Трюки № 35- Flash Player не блещет особой скоростью обработки данных и построения анимаций. Из-за этого в Flash возникают проблемы с анимацией, требующей интенсивного обсчета, - в частности, с трехмерной графикой. Flash не содержит специализированных команд ActionScript и не использует аппаратную поддержку трехмерной графики в отличие от пакета Macromedia Director (поддерживающего формат Shockwave 3D). Тем не менее, если хорошо знать систему и ее ограничения, многие препятствия удается обойти - просто нужно воспользоваться нестандартным подходом или упростить проблему. И если уж на то пошло, возможность поворачивать и масштабировать трехмерную модель наручных часов перед покупкой выглядит впечатляюще, но большинство покупателей получает больше впечатлений от хорошей фотографии и графического дизайна в описании продукта. Трехмерную графику лучше использовать не саму по себе, а совместно с другим^ технологиями. Например, трехмерную анимацию можно объединить с традиционной анимацией, в которой эффект объема создается способностями художника. Хо*рошим примером объединения двухмерной и трехмерной анимации служит Flashкомикс HitchHiker Part Two на сайте Bitey Castle ( hh2Window.html). Анимация автомобиля была создана в Swift 3D ( www.swift3d.com), а затем наложена на двухмерную фоновую анимацию. Псевдотрехмерные эффекты поддерживаются графическими интерфейсами большинства современных операционных систем, и в Flash предусмотрена возможность создания псевдотрехмерных окон и кнопок. Трехмерные эффекты в полной мере используются на таких сайтах, как layerbit ( Впрочем, реклама Phaeton на английском сайте Volkswagen является примером гораздо более умеренного применения сценарных трехмерных эффектов - она выходит на грань возможностей работы с трехмерной анимацией во Flash в реальном времени, но не переступает ее.

Имитация трехмерной графики.

ТРЮК Имитация трехмерной графики Flash не обладает полноценной поддержкой трехмерной графики, но чтобы добиться желаемого эффекта, можно разместить фрагменты двухмерного изображения так, словно они находятся в трехмерном пространстве.

№ Flash не обладает поддержкой трехмерной графики, однако объемный эффект можно имитировать различными способами. Одно из решений - разбить двухмерное изображение на срезы и расположить их так, чтобы создать иллюзию объема. Примеры использования данного трюка встречаются на многих сайтах - например, трехмерная голова на сайте sofake (зайдите на сайт щелкните на десятом белом квадратике в нижней части основной страницы и откройте ссылку haircut.swf), а также Предводитель свободного мира Энди Фулдса (откройте страницу и щелкните на восьмом квадрате над заголовком Amusements). Тем не менее, впервые я столкнулся с ним еще во времена Flash 4;

тогда его использовали Руйен Зарин ( и Энт Идеи ( Энт Идеи разработал превосходный механизм для работы с трехмерными срезами, расширяющий концепцию поворота и масштабирования (щелкните на ссылке 3D на странице Он позволяет перемещать срезы в трехмерном, а не в двухмерном пространстве, как в этом трюке. Ранее говорилось о том, как для создания анимации импортировать GIF-файл в Flash и воссоздать его с расширенными возможностями анимации (см. трюк 4). В этом трюке используется талисман издательства O'Reilly - долгопят с обложки книги Learning the vi Editor. Мы перенесем его в трехмерный мир. На рис. 5.1 долгопят не выглядит объемным;

чтобы полностью оценить эффект, загрузите файл critterO2.fla с сайта книги.

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

Глава 5. Трехмерная графика и физика O'REILLYо ШШШШШШЩШШШШ&ШШШ O'REILLY й O'REILLYо Рис. 5. 1. Объемный долгопят Долгопят в нарезке Для создания срезов мы воспользуемся универсальным инструментом - масками. Подумайте, как будет выглядеть долгопят, если его разделить на поперечные срезы, а потом снова собрать их вместе: вместо внутренних органов будет видна только шкурка. Примерный вид срезов показан на рис. 5.2.

Рис. 5.2. Срезы Улавливаете? Перед вами простая маска в форме круга, центр которой находится на носу долгопята (ближайшая к зрителю точка в трехмерном пространстве). Тем не менее, если наложить срезы друг на друга на плоском экране, никакой иллюзии объема не появится;

все, что мы увидим, - это исходное изображение Имитация трехмерной графики долгопята. Чтобы добиться желаемого эффекта, мы имитируем то, что происходит при повороте тела. Если долгопят поворачивается направо (влево по отношению к зрителю), то срезы тоже поворачиваются и открывают его левый бок (справа от зрителя). При небольшом угле поворота трехмерное движение можно имитировать простым сдвигом срезов. Верхний, то есть ближний к зрителю срез слегка смещается влево, а нижний срез (дальний от зрителя) слегка сдвигается вправо. На этом базовом принципе основана работа трюка - стоит понять его, и все остальное станет относительно простым. Чтобы создать срезы, мы кадрируем расширяющийся круг и используем его последовательные состояния для применения масок к исходному клипу с долгопятом. Анимация маски останавливается на кадре 19, так что на кадре 20 мы видим последний срез с полным изображением зверушки без маски. Чтобы увидеть срезы, загрузите файл critterO2.fla с сайта книги, включите редактирование символа critter in 3D в библиотеке и переместите индикатор текущей позиции по временной диаграмме символа, как показано на рис. 5.3.

ХХияЯтт о ' к аХ. Х аи.

я >.

.: * % I.-. li, - >tj:: I S Ж Ш -Х Рис. 5.3. Анимированная круглая маска последовательно открывает изображение долгопята Глава 5. Трехмерная графика и физика Чтобы отобразить на сцене 20 срезов, свяжите 20 экземпляров клипа critter in 3D с главной временной диаграммой. Временная диаграмма каждого среза останавливается на одном из 20 кадров анимации. this.attachMovieC'menu". "menujnc". 1000);

// Размещение заголовка O'Reilly menujnc._x = 200;

menujnc._y =270;

// Создание объекта инициализации для клипов. // размещаемых на сцене. var init:Object = new ObjectO;

i n i t. _ x = 200;

i n i t. _ y = 200;

var siiceDepth:Number = 2 0 : // К сцене присоединяются 20 экземпляров клипа, остановленных // на разных кадрах для создания срезов. for (var i:Number = 1: i < sliceDepth + 1: i++) { var vritterName:String = " c r i t t e r " + (sliceDepth - i ) : var critter:MovieClip =this.attachMovie("critter". critterName. sliceDepth - i.. i n i t ) ;

critter.gotoAndStop(i);

c r i t t e r. o f f s e t = ((sliceDepth - i ) - (sliceDepth / 2)) * 0.05;

critter.onEnterFrame = i n i t C l i p ;

} Свяжите этот код с кадром 1 главной временной диаграммы. 20 срезов (с именами от critter20 до critteri) размещаются на глубинах от 20 до 1 в позиции (200, 200). Пока ничего необычного. Обратите внимание на следующую строку: critter.gotoAndStop(i);

Она останавливает каждый экземпляр на одном из 20 кадров анимации, создавая таким образом 20 уникальных срезов. Еще одна важная строка: critter.offset = ((sliceDepth - i ) - (sliceDepth / 2)) * 0.05;

В каждый клип добавляется свойство offset, значения которого лежат в интервале от -0,5 до 0,5. Свойство offset определяет сдвиг среза влево или вправо в ответ на смещение указателя мыши (наша дрессированная зверушка следит за мышью). Далее приводятся обработчики событий, управляющие перемещением отдельных срезов. Функция initClipQ назначается обработчиком события onEnterFrame для каждого среза;

это простой сценарий инициализации, который удаляет сам себя после одногократного выполнения. initClip = function () { // Инициализация среза this.startX = this._x: this.startY = this._y: delete (this.onEnterFrame);

this.onMouseMove = mover;

}: mover = function 0 { Имитация трехмерной графики // Перемещение среза с учетом его позиции в стопке срезов, var distX = t h i s. o f f s e t * (_xmouse - this._x) / 50: var distY = t h i s. o f f s e t * (_ymouse - this._y) / 50;

this._x = this.startX + distX;

this._y = this.startY + distY;

updateAfterEventO;

}:

^_ После завершения инициализации функция mover{) будет выполняться при каждом перемещении указателя мыши. Приведенный обработчик события onMouseMove практически идентичен коду движения глаз долгопята в обработчике onEnterFrame (см. трюк 4). Главное различие заключается в том, что в имитации трехмерности свойство offset слегка смещает каждый срез. Вот и все, что необходимо сказать об эффекте трехмерности. Концепция на удивление проста, реализация - тоже. Почему в новом варианте программы используется не то событие, которое использовалось ранее для анимации глаз? Во время движения мыши Flash генерирует события onMouseMove с максимально возможной частотой. События onEnterFrame генерируются с частотой смены кадров. Когда мышь остается неподвижной, события onEnterFrame все равно генерируются, но в процессе движения события onEnterFrame генерируются реже событий onMouseMove. Чтобы убедиться в этом, попробуйте запустить оба обработчика одновременно: пусть обработчик onMouseMove увеличивает переменную при каждом вызове, а обработчик onEnterFrame эту же переменную уменьшает. Вы увидите, что во время перемещения указателя мыши счетчик растет, потому что в этом время onMouseMove вызывается чаще onEnterFrame. Итак, мы выбираем событие, способное обеспечить максимально быстрый отклик в той области, на которую направлено внимание пользователя;

по этой причине основные вычислительные мощности (см. трюк 71) концентрируются в трехмерной анимации, потому что мы предполагаем, что пользователь будет внимательно смотреть на нее. Анимация глаз слишком тривиальна, и выделение дополнительных ресурсов не улучшит ее вида, поэтому в данном случае мы использовали событие onEnterFrame для снижения нагрузки на процессор. Ширина и высота срезов также могут меняться в процессе перемещения. При повороте объекта влево или вправо ширина среза отличается от той, которая видна спереди. Аналогично при повороте объекта вверх или вниз изменяется высота среза. Включение фрагмента, выделенного жирным шрифтом, в основной сценарий анимации иногда (но не всегда!) делает эффект более реалистичным: mover = function О { // Перемещение среза с учетом его позиции в стопке срезов, var distX = t h i s. o f f s e t * (_xmouse - this._x) / 50;

var distY = t h i s. o f f s e t * (_ymouse - this._y) / 50;

this._x = this.startX + distX;

this._y = this.startY + distY;

this._width = 100 - Math.abs(distX) this.Jieight = 100 - Math.abs(distY) updateAfterEvent( );

Глава 5. Трехмерная графика и физика Итоги Теперь, когда вы уловили общий принцип, наш эффект имитации трехмерности можно было бы усовершенствовать. Фактически, базовая методика отображает срезы изображения на конус. Если отвести указатель мыши очень далеко от долгопята, конус становится четко различимым, как показано на рис. 5.4.

Рис.

5.4. Отображение срезов на конус создает искажения Маска состоит из простых кругов с последовательно изменяющимся диаметром, поэтому из периметров срезов формируется конус. Ручная прорисовка срезов с использованием периметров, более точно передающих структуру трехмерного объекта, позволяет создать эффект, который лучше смотрится при больших углах поворота и позволяет поворачивать объект почти на 180 (полный поворот на 180 невозможен, потому что толщина срезов в пределе стремится к нулю. Вам придется создать другой набор срезов, полученных при просмотре объекта под другим углом). Чем больше срезов содержит эффект, тем лучше он смотрится, но за это приходится расплачиваться быстродействием. В тех местах, где контур трехмерной фигуры обладает наибольшим искривлением, количество срезов можно увеличить. В этом случае смещение придется изменять нелинейно, чтобы отразить расхождения в частоте срезов по глубине.

ТРЮК Панорамные изображения Немного изобретательности Ч и вы сможете создавать трехмерные панорамы, имитируя эффект присутствия.

№ Панорамные изображения - прием визуализации, при котором зритель как бы стоит в центре трехмерного окружения. Изображение можно поворачивать, причем для имитации визуальной глубины в текстурное изображение вносятся деформации. Методика панорамирования получила широкое распространение с возникновением таких технологий, как QuickTime VR ( quicktime/qtvr/authoringstudio) от Apple. Панорамный контент встречается повсеместно, особенно на сайтах турагентств и бюро путешествий. Тем не менее, большинство решений основано на применении Java или сторонних модулей, что снижает вероятность их просмотра зрителем, не говоря уже о лицензиях на разработку и развертывание. Хотя на практике встречается несколько способов панорамирования (сферическое, кубическое и т. д.), наибольшее распространение получили цилиндрические панорамы, в ко Панорамные изображения торых текстура проецируется на стены круглой комнаты. Цилиндрические панорамы просты в создании, к тому же, они быстро строятся, что позволяет Flash Player отображать их с приемлемым быстродействием. Хотя Flash Player не обладает возможностями и быстродействием некоторых инструментов просмотра панорамных изображений, он не требует установки дополнительного программного обеспечения (a Flash Player поддерживается чаще, чем Java или любой другой сторонний модуль). Кроме того, он позволяет управлять панорамным изображением прямо из Flash-ролика, что позволяет добавить интерактивные элементы или интегрировать его с другим контентом. Более того, разработка и распространение не потребуют дополнительных лицензионных платежей.

Создание панорамных изображений Панорамное изображение (или просто панорама) представляет собой длинную горизонтальную полосу с видом окружения (рис. 5.5).

Рис.

5.5. Панорамное изображение Подобные изображения обычно создаются многократной съемкой с одной точки поворачивающейся камерой, закрепленной на треноге. Как правило, полученные кадры сшиваются для создания плоского цилиндрического вида. Существуют различные способы создания и редактирования панорам - от камер, автоматически создающих панорамные фото, до специализированных программ. Хотя методика создания панорамных изображений выходит за рамки книги, на самом деле все не так сложно, как может показаться. Тема подробно рассматривается на таких сайтах, как Panoguide.com ( здесь вы найдете краткое руководство, по программному обеспечению для редактирования панорам ( и галерею с готовыми панорамами ( Работу над панорамным изображением в Flash мы начнем с файла pano.jpg, загружаемого с веб-сайта книги (в файле pano.fla содержится итоговый вариант Flash-ролика).

Обработка изображения в Flash Чтобы имитировать трехмерную панораму, мы нарежем панорамное изображение на полоски (рис. 5.6.) разных размеров и масштаба, соответствующих разным глубинам. Для решения задачи будут использоваться множественные экземпляры и маски (см. трюк 35).

Глава 5. Трехмерная графика и физика Хотя на стадии выполнения отображается только область, выделенная на рис. 5.6 внутренним контуром, каждая полоска проходит независимое масштабирование для получения изображения, показанного на рисунке. При отсечении лишних частей сверху и снизу, а также частей, выходящих за пределы области видимости зрителя, происходит окончательное формирование субъективной глубины изображения. Масштабирование создает иллюзию трехмерного вида - увеличение периферийных полос по сравнению с центральными имитирует отображение панорамного изображения на внутреннюю поверхность цилиндрической стены, при этом точка наблюдения находится в центре круга.

Ч|-| г-| Ч1,,Д,Д., LJ Рис. 5.6. Вертикальные срезы панорамы Для работы с панорамными изображениями обычно используется формат JPEG. Создайте панораму (или загрузите файл pano.jpg - с сайта книги либо из галереи Panoguide) и импортируйте файл в Flash (команда File Х Import Х Import to Library). Начнем с определения простых переменных, которые будут использоваться в клипе с изображением: var viewWidth:Number = 450: var viewHeight:Number = 400;

var precision:Number - 8: var viewFOV:Number = 60: где: viewWidth - ширина исходного панорамного изображения;

viewHeight - высота исходного панорамного изображения;

precision - ширина каждой полосы изображения. Присваивание этой переменной значения 1 гарантирует максимальную точность, но требует слишком больших вычислительных ресурсов, непозволительных для Flash. Оптимальное значение подбирается вручную посредством тестирования, но начинать рекомендуется с высокого качества (малых величин) - например, 8. Увеличивайте ширину полос только в том случае, если эффект кажется слишком медленным. Чтобы лучше понять, как работает эффект, попробуйте задать высокое значение (скажем, 50) - в этом случае становятся хорошо видны полосы, образующие эффект;

viewFOV - поле зрения в градусах. Переменная определяет величину искажения, обусловленного масштабированием полос при отходе от центра изобра Панорамные изображения жения. Величина напрямую зависит от размера и форматного соотношения изображения, поэтому ее и приходится задавать вручную. На практике обычно используются значения в интервале от 60 до 80. Значение Г соответствует отсутствию трехмерного эффекта, то есть, фактически, наложению графики на плоскость вместо искривленной поверхности. Значение 180 означает аномально высокое искривление (эффект рыбьего глаза). После задания всех значений изображение необходимо нарезать на полосы. Для начала нам понадобится функция, генерирующая полосы, которые будут использоваться в качестве масок: this.createBox = function (name:String, x:Number, у:Number. w:Number, h:Number, target :MovieClip):!iovieClip { // Функция создает прямоугольники масок i f (target == undefined) { target = t h i s : } var box:MovieClip = target.createEmptyMovieClip(name. this.currentDepth++);

box._x = x: box._y = y;

// Р с в н е п я о г л н к с е с в м D a i g A I иоаи рмуоьиа рдтаи rwn P bxlnSyeudfnd;

o.ietl(neie) b x m v T (. 0): o.oeo0 b x b g n i 1 0 0 0 0 0 30): o.eiFl (x.00. b x l n T (. 0);

o.ieo w b x l n T (. h): o.ieo w b x l n T (0. h);

o.ieo b x l n T (0. 0);

o.ieo box.endFill();

r t r (box): eun } : Затем создаются новые изображения, каждое в своей позиции в соответствии с заданными параметрами (viewWidth, viewHeight, precision и viewFOV). Мы дублируем исходное изображение (предполагается, что оно было предварительно импортировано в библиотеку) и создаем для него маску. Файл pano.fla на сайте книги содержит полностью прокомментированную версию кода (приводится в сокращенном виде для экономии места): var xpos:Number = 0: var currentDepth:Number = 100: var photoList:Array = [ ] ;

Х while (viewWidth^precision != 0) { viewWidth++: } var boxCount:Number = 0;

var stripMask.-MovieClip: var stripPhoto:MovieClip;

var posX:Number;

var ang:Number: var h:Number: var viewTotal-.Number = (viewHeight * 180) / viewFOV:

Глава 5. Трехмерная графика и физика for (var i = 0;

i < viewWidth;

i+= precision) { // Вычисление высоты и масштаба для текущей полосы posX = ((viewWidth / 2) - ( i + (precision / 2 ) ) ) : ang = Math.asin(Math.abs(posX / (viewTotal / 2 ) ) ) ;

h = (Math.cos(and) * (viewTotal / 2) - viewTotal / 2) * - 1 ;

// Создание области маски stripMask = this.CreateBox("box_" + boxCount. i. s, precision. viewHeight);

// Дублирование полосы stripPhoto= this.photo.duplicateMovieClip("photo_" + boxCount. 1000 + boxCount);

stripPhoto._y = -h: stripPHoto._xscale = ((viewHeight + h * 2) / photo._height) * 100;

stripPHoto._yscale = stripPhoto._xscale: stnpPhoto.setMask(stripMask);

photoList.push({photo:stripPhoto. mask:stripMask. scale:stripPhoto._xscale / 100});

boxCount++;

} photo._visible = false;

Итак, изображение нарезано на полосы, каждой полосе назначен клип маски, и полосы масштабированы с правильными коэффициентами. Сцена содержит большое количество отдельных клипов, каждый из которых виден сквозь прорезь клипа маски при его перемещении слева направо (то есть клип маски остается неподвижным, пока перемещается маскируемое изображение). Ссылки на клипы были помещены в массив photolist для упрощения доступа. Теперь нам понадобится код, который разместит все изображения в правильных позициях и сформирует изображение, показанное на рис. 5.6. this.redrawStrips = functionO { // Перерисовка (повторное позиционирование) всех полос. // Маски остаются на своих местах, var tpos:Number;

var epos:Number = 0;

// Создание локальных переменных для обработки // свойств каждой полосы // шх: свойство _х клипа маски // mw: свойство _width клипа маски // pw: свойство _width клипа полосы // s: коэффициент масштабирования полосы var mx:Number;

var mw:Number;

var pw:Number;

var s:Number;

for (var i = 0;

i < this.photoList.length;

i++) { Х mx = photoList[i].mask._x: m = photoList[i].mask._width;

w pw = photoLi st[i].photo._width;

s = photoList[i].scale;

tpos = mx - ((epos + xpos) * s);

// Обновление позиции полосы.

Панорамные изорражения // При необходимости происходит циклический возврат к началу, while (tpos > mx + mw) { tpos -= pw;

} while (tpos + pw < mx) { tpos += pw;

} // Заполнение промежутка между началом и концом панорамы i f ( (tpos > mx) & (tpos < mx + mw) ) { & // Дублирование для заполнения (влево) var alt:MovieClip = photoList[i].photo.duplicateMovieClipC "alternatePhoto". 998);

a l t. _ x = tpos - pw;

var altM:MovieClip = photoList[i].mask.duplicateMovieClip( "alternateMask". 997): alt.setMask(altH);

} else i f ( (tpos + pw > mx) & (tpos + pw'< m + mw) ) { & x // Дублирование для заполнения (вправо) var alt:MovieClip = photoList[i].photo.duplicateMovieClip( "alternatePhoto". 998);

a l t. _ x = tpos + pw: var altM:MovieClip = photoList[i].mask.duplicateMovieClip( "alternateMask". 997);

alt.setMask(altM): } / П р м щ н ек и а т к щ й п л с / е е е е и п еуе ооы p o o i t i. h t. x - tpos;

htLsC]poo_ eo + m / s ps = w ;

Фрагмент получает переменную смещения позиции xpos и перемещает все изображения вверх или вниз в зависимости от масштаба каждой полосы. В конце процесса каждая полоса перемещается вверх или вниз так, чтобы полосы приняли вертикальное расположение, показанное на рис. 5.6. Наконец, мы задаем исходную позицию и производим перерисовку, чтобы полосы заняли положенные места: this.xpos = 0 ;

this.redrawStripsQ;

Пока что программа строит статический панорамный вид, но мы предоставим пользователю возможность поворачивать поле обзора, чтобы он ощутил эффект глубины и мог лучше рассмотреть панораму. Существует несколько типичных пользовательских интерфейсов, обеспечивающих возможность прокрутки панорамы. Например, пользователь может нажать кнопку мыши и прокрутить изображение (другой вариант - использовать нажатие кнопок мыши для прокрутки влево и вправо). В нашем примере панорама прокручивается в зависимости от позиции указателя мыши. Если указатель находится слева от центра экрана, то при нажатии кнопки мыши панорама прокручивается влево. Прокрутка вправо выполняется аналогичным образом. Скорость прокрутки зависит от того, насколько удален от центра указатель мыши. В итоговом FLA-ролике указатель Глава 5. Трехмерная графика и физика мыши заменяется простым клипом в виде стрелки arrow, показывающим направление прокрутки. В предыдущем фрагменте перед вызовом метода redrawStrips(), управляющего позицией полос, а следовательно - и прокруткой, присвается значение переменной xpos. Заменяя в ролике Flash указатель мыши клипом arrow, необходимо включить в него и код обработки перемещений мыши. arrow.onMouseMove = function О { t h i s. i s l n s i d e = this._parent._xmouse > 0 & & this._parent._xmouse < this._parent.viewWidth & & this._parent._ymouse > 0 & & this._parent._ymouse < this._parent.viewHeight;

i f (this.islnside) { // Указатель мыши находится над панорамой - заменить // стандартный указатель стрелкой, i f (!this._visible) { Mouse.hideO;

thos._vi sible = true;

} i f (this._visible) { / Оорзт кд с срло,нпално вео ии врв / тбаиь ар о текй арвенй в л пао / вз в с м с и о т к щ г п л ж н я у а а е я / аииот т еуео ооеи к з т л. t i. o o n S o ( t i. p r n. x o s < t i. p r n. i w i t / 2) ? hsgtAdtp(hs_aet_mue hs_aetveWdh " e t : "right");

lf" } } es { le / У а а е ь м ш р с о о е н н дп н р м й - о о р з т / к з т л ыи аплжн е а аоао тбаиь / садрнй уааеь мш вет плзвтлсоо / тнаты кзтл ыи мсо оьоаеькг, i (hs_iil) { f ti.vsbe Mouse.show();

this.visible = false;

} arrow.onMouseDown = function О { // Если кнопка мыши нажата, изменить xpos для создания эффекта прокрутки // при вызове redrawStripsO this.onEnterFrame = function О { i f (this.islnside) { this._parent.xpos -= ((this._parent.viewWidth / 2) - t h i s. j O / 10;

// Максимальная скорость перемещения this._pa rent. redrawStripsO;

} arrow.onMouseUp = functionO { this.onEnterFrame = undefined: }: Приведенный код реализует только панораму с возможностью прокрутки влево/вправо, а прокрутка по вертикали не поддерживается;

однако он достаточно прост и при необходимости легко модифицируется.

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

Итоги Flash не может конкурировать со специализированными программами просмотра панорам по качеству и скорости, но возможность интеграции панорам в ролики и управление ими на уровне ActionScript является большим преимуществом. Простейшие панорамы - не более чем основа;

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

Зе Фернандо Оптимизированный трехмерный №37 плоттер ТРЮК Создание компактного и быстрого механизма для рисования простых трехмерных объектов на сцене Flash. Полноценная трехмерная графика возможна лишь при подаче двух разных изображений в глаза зрителя. На основании различий в изображениях мозг зрителя вычисляет пространственную глубину каждого объекта (так называемое стереоскопическое зрение). Например, в очках для просмотра стереофильмов используются красные и синие фильтры (или фильтры с вертикальной и горизонтальной поляризацией), при помощи которых на каждый глаз подаются разные изображения - на киноэкран проецируются два разных изображения со смещением, а мозг конструирует из них единую картинку. Однако в так называемой трехмерной компьютерной графике вместо вывода разных изображений трехмерный объект проецируется на плоскость. Если закрыть один глаз, рисунок не изменится, просто мозг зрителя делает разумные предположения относительно глубины элементов изображения на основании масштаба и положения теней. Создать простейший механизм трехмерного вывода не так сложно, как может показаться. В этом трюке приведена математическая база для написания простого трехмерного плоттера, преобразующего координаты точки в трехмерном пространстве (х, у, z) в двухмерные координаты (х, у) на плоскости сцены Flash. Как и большинство графических программ, Flash использует систему координат, показанную на рис. 5.7: ось Y направлена сверху вниз (а не снизу вверх, как в декартовой системе координат). Ось X идет в обычном направлении, то есть слева направо. Flash поддерживает только двухмерную графику (оси X и У). Чтобы имитировать ось Z, мы аппроксимируем глубину применением масштабирования. На рис. 5. Глава 5. Трехмерная графика и физика куб уменьшается при удалении по оси Z. В зависимости от угла перспективы смещение по оси Z также может привести к изменению позиций X и Y.

Рис. 5.7. Ось У в системе координат Flash направлена сверху вниз г Рис. 5.8. Перемещение куба вдоль оси Z Масштабный коэффициент для координат х и у на расстоянии z при просмотре через камеру с фокусным расстоянием / 0 равен /Д/( / 0 + z). Для воспроизведения точки трехмерного пространства (х, у, z) на плоскости (х, у) с масштабом 5 можно воспользоваться следующими аппроксимациями: scale = f 0 / (fo+z) xLoc = x * scale yLoc = у * scale s = 100 * scale Хотя масштаб полноценного трехмерного объекта изменяется в зависимости от глубины (ближние грани выглядят более крупными, чем дальние), простоты ради мы интерпретируем объект как существующий в одной точке пространства. Такая аппроксимация обеспечивает приемлемую точность (кроме очень больших или очень близких к камере объектов). Данная аппроксимация легко реализуется на программном уровне - фактически, программа превращается в простейший трехмерный плоттер. Создайте новый ролик FLA со стандартными размерами сцены (550 х 400), назначьте ему Оптимизированный трехмерный плоттер частоту смены кадров 24 fps. Свяжите следующий код с кадром 1 главной временной диаграммы: function moveSpheresO { // Функция перемещения сферы for (var i:Number = 0: i < n: i++) { pX[i] += pXS[i];

i f (Math.abs(pX[i]) > wSize) { pXS[i] = -pXS[i]: } pY[i] +л pYS[i];

i f (Math.abs(pY[i]) > wSize) { pYS[i] = -pYS[i];

} pZ[i] += pZS[i] * scale;

i f (Math.abs(pZ[i]) > wSize) { pSZ[i] = -pZSCi];

} threeDPlotter(i);

} function threeDPlotter(i) { scale = flength/(flength + p Z [ i ] ) ;

world["p"+i]._x = (pX[i] * scale);

world["p"+i]._y = (pY[i] * scale);

world["p"+i]._xscale = world["p"+i]._yscale = 100 * scale;

} // ОСНОВНОЙ К Д О var fLength:Number = 150;

var wSize:Number = 100;

var centerX:Number = 275: var centerY:Number = 200;

var n:Number = 30;

var pX:Array = n w ArrayO;

e var pX:Array = n w ArrayO;

e var pY:Array = n w ArrayO;

e var pZ:Array = n w ArrayO;

e var pXS:Array = n w ArrayO;

e var pYS:Array = n w ArrayO;

e var pZS:Array = n w ArrayO;

e // Построение м д л трехмерного м р оеи иа t i s.createEmptyMovi e l p " o l d. 0);

h Ci (wr " world._x = centerX;

world._y = centerY;

// Инициализация сфер for ( a i N m e = 0;

i < n;

i++) { vr :ubr world.createEmptyMovieClipC'p" + i. i ) ;

world["p"+i].lineStyle(10. 0x0. 100): world["p"+i].moveTo(0, 0):

184 world["p"+i].lineTo(l. 0): pX[i] = pY[i] = pZ[i] = 0;

pXS[i] - Math.randomO * 5;

pYS[i] = Math.randomO * 5;

pZS[i] = Math.randomO * 5: threeDPlotter(i):

Глава 5. Трехмерная графика и физика } // Настройка обработчика события onEnterFrame this.onEnterFrame = moveSpheres;

При выполнении этого кода в трехмерном мире перемещаются 30 сфер (рис. 5.9). Вскоре вы поймете, почему сферы рисуются в виде простых черных кругов, без эффектных бликов.

Х Х Х < Х Х Х Х Х Х Х л Х Х Х Х Рис. 5.9. Точки в трехмерном мире {два разных момента времени) Проанализируем некоторые участки основного кода. Сначала в нем определяются следующие переменные: Х fLength - фокусное расстояние;

Х centerX, centerY - положение начала координат трехмерного мира (относительно сцены Flash);

Х п - количество точек в анимации;

Х wSize - расстояние от начала координат до каждой грани трехмерного куба, определяющего границы трехмерного мира. Поскольку начало координат находится в центре куба, ребра куба имеют длину 2*wSize. Структура трехмерного мира изображена на рис. 5.10. В трехмерном мире местонахождение и скорость точек задаются следующими параметрами: Х рХ, pY, pZ - координаты (х, у, г) точек анимации;

Х pXS, pYS, pZS - скорость и направление (то есть вектор скорости) каждой точки. Затем мы создаем анимационный клип с именем world, в котором создаются клипы всех сфер, от р, до р. Функция moveSpheresO постоянно обновляет позиции сфер и заставляет их отражаться от граней нашего кубического мира. Она также вызывает функцию Оптимизированный трехмерный плоттер threeDPIotterQ, которая преобразует пространственные координаты (х, у, z) в позицию на плоскости (х, у) и вычисляет масштабный коэффициент, необходимый для построения плоской проекции трехмерного представления.

Рис. 5.10. Трехмерный мир p o s i t i o n ( p X, pY, p Z ) velocity (pXS, pYS, pZS) Рис. 5. 1 1. Местонахождение и скорость точки в трехмерном мире Использование черных кругов (то есть кругов с однородной заливкой) позволяет обойтись без хранения информации об относительной глубине точек, то есть без буфера глубины, поскольку зритель не может определить, какая точка Глава 5. Трехмерная графика и физика находится спереди, а какая - сзади. Тем самым сокращается объем вычислений, необходимых для построения динамической трехмерной сцены. Трехмерная сцена строится внутри клипа world, чтобы позицию начала координат можно было изменять простым перемещением клипа world (при этом не приходится вносить дополнительные смещения на каждом кадре вычислений, что привело бы к замедлению построения сцены).

Итоги Несмотря на свою тривиальность, представленный механизм способен стать начальной точкой для построения более совершенных механизмов трехмерной графики: Х Х соединяя точки отрезками, можно строить трехмерные векторные модели (см. трюк 85);

введение буфера глубины (z-буфера) повысит реалистичность представления трехмерного мира. В частности, это позволит использовать сферы с бликами вместо простых черных кругов.

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

ТРЮК Гравитация и трение Многие явления реального мира (такие как гравитация и трение) влияют на скорость перемещения объектов во времени. Простые уравнения с ускорением позволяют моделировать эти изменения и создавать реалистичные эффекты движения объектов в имитируемых физических условиях.

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

Гравитация и трение Такие силы, как сила притяжения, создают постоянное ускорение. Иначе говоря, скорость объекта изменяется во времени с постоянным приращением. Скорость объекта в конце интервала равна сумме его начальной скорости и произведения значения ускорения на продолжительность интервала:

n w = o d + (ускорение * время) eV lV Позиция объекта в конце интервала равна сумме начальной позиции и скорости, умноженной на продолжительность интервала: newPos = oldPos + (newV * время) Если позиция и скорость вычисляются настолько часто, что скорость при каждой итерации может считаться постоянной, а продолжительность интервала принимается за 1, то уравнение позиции упрощается до следующего вида: newPos = oldPos + newV Проще говоря, это означает: Чтобы вычислить новую позицию, следует взять старую позицию и прибавить к ней текущую скорость. Например, если машина едет со скоростью 60 км/ч, то за один час она сместится на 60 км от исходной позиции. Таким образом, в каждом кадре анимации мы смещаем позицию на небольшую величину в направлении вектора скорости. В конце интервала вычисляется новое значение скорости, учитывающее ускорение. Уравнение скорости по знакомому принципу упрощается до вида newV = oldV + ускорение Переведем: Чтобы вычислить текущую скорость, следует взять исходную скорость и прибавить к ней значение ускорения. Например, если машина едет со скоростью 60 км/ч, и за одну секунду ее скорость увеличивается на 10 км/час, то через одну секунду ее скорость составит 70 км/ч. В каждом кадре анимации к скорости прибавляется величина приращения в направлении вектора ускорения (для силы притяжения этот вектор обычно направлен вниз). Ускорение, обусловленное силой притяжения Земли, составляет примерно 9,8 м/с 2. Впрочем, если вы не пытаетесь моделировать конкретные физические условия, для ускорения можно использовать любую постоянную величину. Сила трения покоя (сила трения, действующая на неподвижное тело) должна быть больше силы трения качения (которая действует, скажем, на катящийся мяч). Сила трения воздуха обычно возрастает пропорционально скорости объекта, поэтому мы воспользуемся простым коэффициентом трения, считая итоговое воздействие пропорциональным текущей скорости. Следующая программа генерирует несколько частиц (см. трюк 33) и анимйрует их, заставляя двигаться под воздействием силы притяжения и трения воздуха: function f a l l ( ) { // Прибавление ускорения, обусловленного гравитацией this.speedY +- GRAVITY: // Учет воздействия силы трения this.speedY *= FRICTION:

Глава 5, Трехмерная графика и физика // Предполагается, что.обе силы // воздействуют исключительно в направлении Y. this._y += this.speedY;

// При соприкосновении с "полом" клип подпрыгивает вверх i f ( t h i s. _ y > 400) { this._y = 400: this.speedY = -this.speedY * ELASTICITY;

function dragO { / К г а пользователь щелкает н клипе, активизировать р ж м / од а еи / перетаскивания и прекратить его анимацию / / н базе события onEnterFrame. /а this.startDragO ;

delete this.onEnterFrane: / Чтобы сэкономить н вызове функции, можно воспользоваться / а // вызовом следующего вида: / this.onMouseMove = updateAfterEvent: / // поскольку обработчик onMouseMoveO в з в е только одну функцию. ыыат // Т м н менее, м используем следующее определение функции ее ы / н случай, если потребуется реализовать дополнительные возможности /а / (скажем, проверку границ, предотвращающую перетаскивание к и а / п / з пределы сцены). /а this.onMouseMove = functionO { updateAfterEventO:

} function dropO { / Инициализация анимации и в х д и р ж м перетаскивания клипа. / ыо з еиа / Исходная скорость п о и Y р в а нулю, / ос ан this.speed.у = 0: this.stopDragt): this.onEnterFrame = fall;

/ ОСНОВНОЙ К Д / О / Создание 20 клипов с сферами / о f r ( a i = 0: i < 20;

i++) { o vr var ball:MovieClip = this.createEmptyMovieClipC'ball" + i. i ) : ball.lineStyle(6. 0x0. 100): ball.moveTo(0. -3): ball.UneToCl. -3): ball._x = Math.randomO * 550;

ball._y = Math.randomO * 200: ball.speedY = 0: ball.onEnterFrame = fall;

ball.onPress = drag;

ball.onRelease = ball.onReleaseOutside = drop;

} // Инициализация физических констант v r GRAVITY:Number = 0 5 a.;

v r FRICTION:Number = 0.995;

a Гравитация и трение var ELASTICITY:Number =0.85: // Рисование линии нулевого уровня this.lineStyleCO. O D D D. 100);

xDDD this.moveTo(0. 400): this.lineTo(550. 400);

Чтобы имитировать эоздействие гравитации, мы выполняем для каждого кадра следующую строку, изменяющую скорость в направлении Y: this.speedY += GRAVITY: Если шар падает, гравитация увеличивает скорость и ускоряет падение. Если шар поднимается, гравитация уменьшает скорость и замедляет набор высоты. Словом, все происходит точно так же, как с мячом, ударившимся о землю. Воздействие трения имитируется следующей строкой: this.speedY *= FRICTION: Если коэффициент FRICTION меньше 1, то шар замедляет движение независимо от его направления (как под воздействием настоящей силы трения). Если он равен 1, сила трения отсутствует. Наконец, если FRICTION больше 1, то на тело действует лотрицательная сила трения, и оно получает импульс наподобие реактивного, в результате чего движение тела не замедляется, а ускоряется. Когда мяч ударяется о пол и отпрыгивает, желательно имитировать потерю энергии, типичную для подобных столкновений. Коэффициент эластичности применяется в строке this.speedY *- ELASTICITY: Если коэффициент ELASTICITY меньше 1, то при столкновении шар теряет часть энергии, как и в реальном мире. Если он равен 1, шар абсолютно эластичен (при отсутствии силы трения он бы вечно подпрыгивал на одну и ту же высоту). Если задать коэффициенту ELASTICITY значение больше 1, то с каждым ударом шар будет подпрыгивать все выше и выше.

Итоги Поскольку анимация выполняется на уровне кадров, уравнения движения в ней выглядят проще, чем те уравнения, которые мы изучали на уроках физики. Движение на уровне кадров всегда описывается линейными уравнениями, благодаря чему сценарии становятся короткими и эффективными. Попробуйте реализовать движение не только по оси У, но и по оси X (подсказка: гравитация по оси X не действует, поэтому шару необходимо придать начальную горизонтальную скорость). В анимацию можно внести всевозможные дополнения: попробуйте изменить коэффициент гравитации (и даже сделать его отрицательным, чтобы объекты не притягивались, а отталкивались). Если задать нулевой уровень гравитации, картина приобретет такой вид, словно камера находится над бильярдным столом. Разместите на сцене бегунки для управления гравитацией, силой трения и эластичностью - и экспериментируйте! При желании добавьте звуки или реализуйте деформацию шара при ударе.

Глава 5. Трехмерная графика и физика ТРЮК Имитация броска Чтобы сделать интерфейс более реальным, попробуйте реализовать эффект броска: объект перетаскивается мышью, а при отпускании кнопки продолжает двигаться в прежнем направлении.

№ Как было показано ранее, воздействие ускорения (см. трюк 38) и силы трения, включая сопротивление воздуха, гораздо проще имитировать на уровне отдельных кадров, а не посредством реализации физических уравнений. В этом трюке представлен простой способ имитации бросания объектов. В момент броска определяется воображаемый вектор силы. Направление вектора определяет начальное направление перемещения объекта, а его длина пропорциональна силе броска (и, соответственно, определяет дальность полета брошенного объекта). В Flash сила броска неизвестна, так как в этом виртуальном мире не существует понятий силы и массы, поэтому мы имитируем движение другим способом, который выглядит довольно реалистично. Как это обычно бывает при имитации физических процессов в Flash, модель должна быть близка к идеалу лишь до той степени, до которой ее эффект выглядит реалистично. На практике сгенерированное движение не является абсолютно точным, но, по крайней мере, близко к нему. На рис. 5.12 изображен шар в момент броска. Пользователь молсет щелкнуть на шаре и протащить его по сцене (шар двигается в направлении, в котором двигался указатель мыши в момент отпускания кнопки).

Рис. 5.12. Бросание объекта Если измерить расстояние, на которое шар передвигается за один кадр, то сила броска Flash пропорциональна расстоянию d между двумя последними позициями шара (предполагается, что позиция шара измеряется с постоянными интервалами - например, с частотой смены кадров). Расстояние между двумя позициями пропорционально скорости перетаскивания. Когда вы отпускаете кнопку мыши, шар следует в направлении броска. Программная реализация эффекта броска приведена в следующем листинге (строки пронумерованы для удобства). Функция drawPipQ рисует шар, а функция throwClipQ определяет обработчики событий, управляющие инициализацией броска. Обработчик onMouseMove отслелшвает разность между последней известной и текущей позициями шара и сохраняет их в виде вектора (dirX, dirY). Вектор определяет как направление, так и силу броска. Чтобы увидеть наглядное представление вектора силы, раскомментируйте все закомментированные строки между двумя наборами комментарием "^Диагностика** (строки 24-26 и 50-52). Так вы легко поймете, как работает этот код.

Имитация броска Цикл анимации начинается с события onPress (строка 15) при нажатии кнопки мыши на клипе ball;

обработчик события активизирует режим перетаскивания. Точка отпускания обнаруживается либо обработчиком события onRelease (цикл нажатие кнопки мыши - перетаскивание - отпускание), либо обработчиком события onReleaseOutside, если указатель мыши вышел за пределы сцены (в этом случае вы просто роняете шар, не бросая его). Обработчики событий onRelease и onReleaseOutside начинаются в строке 31, они создают интервальный таймер, который вызывает функцию moverQ для управления анимацией шара после броска. Функция mover() использует переменные dirX и dirY для управления перемещением шара. С течением времени вертикальная составляющая вектора наращивается константой GRAVITY, а составляющие dirX и dirY уменьшаются с коэффициентом FRICTION. Инертная масса шара косвенно моделируется константой MOMENTUM. Чем больше значение MOMENTUM, тем больше амплитуда вектора силы. Интервал сбрасывается (строка 17) в функции onPress (начало - строка 15) при повторном щелчке на шаре. В этот момент цикл броска начинается заново. 1 // Код ActionScnpt 2.0 2 function drawPip(clip:MovieClip. clipName:String, clipDepth:Number. 3 x:Number, у:Number):MovieClip { 4 var pip:MovieClip = clip.createEmptyMovieClip(clipName, clipDepth);

5 pip.lineStyle(20. 0x0. 100);

6 pip.moveTo(0, 0);

7 pip.lineToCl. 0);

8 pip-_x = x: 9 pip._y = y: 10 return pip;

11 } 12 function throwClip(clip:MovieClip):Void { 13 clip.oldX = clip._x;

14 clip.oldY = clip._y;

15 clip.onPress = functionO { 16 clip.startDrag(true. -265. 190. 265. -200);

17 clearlnterval(clipMove): 18 clip.onMouseMove = functionO { 19 clip.dirX = M MNU * (clip._x - clip.oldX);

O E TM 20 clip.dirY = M MNU * (clip._y - clip.oldY): O E TM 21 clip.oldX = clip._x;

22 clip.oldY = clip._y: 23 // ** Диагностика ** 24 // clip.line = clip.createEmptyMovieClipC'line". 0): 25 // clip.line.lineStyle(4. 0x0. 100);

26 // clip.line.lineTo(5 * clip.dirX. 5 * clip.dirY);

27 /7 ** Диагностика ** 28 updateAfterEventO;

29 };

30 };

31 clip.onRelease = clip.onReleaseOutside = functionO { 32 clip.stopDragO;

33 delete clip.onMouseMove;

192 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 }:

Глава 5. Трехмерная графика и физика clipMove = setlnterval(mover, 1. clip);

} function mover(clip):Void { i f (Math.abs(clip._x) > 265)' { clip.dirX = -clip.dirX;

} i f (clip._y > 190) { clip.dirY = -clip.dirY: clip._y = 190: } clip.dirX = clip.dirX * FRICTION: clip.dirY = (clip.dirY * FRICTION) + GRAVITY: clip._x += clip.dirX;

clip._y += clip.dirY;

// ** Диагностика ** // clip.line = clip.createEmptyMovieClipC'line". 0): // clip.line.lineStyle(4. 0x0. 100);

// clip.line.lineTo(5 * clip.dirX, 5 * clip.dirY): // ** Диагностика ** updateAfterEvent(): } var MOMENTUM:Number * 0.8;

var GRAVITY:Number =0.5: var FRICTION:Number = 0.99: this._x = 275: this._y = 200: this.lineStyle(20. 0x0. 100);

this.moveTo(-275. -200): this.lineTo(-275. 200);

this.lineTo(275. 200);

this.lineTo(275. -200);

var ball :MovieCl.ip = drawPipCthis. "ball". this.getNextDepthO. 0, 190);

throwClip(ball);

Итоги Программистам, работающим в стиле Flash MX, стоит обратить внимание на то, что в обработчиках событий никогда не используется текущий объект this. Вместо этого имя целевого клипа передается в аргументах функций. Такое решение работает гораздо эффективнее - чтобы убедиться в этом, попробуйте заменить clip на this в самом часто выполняемом коде события (обработчик onMouseMove, строки 18-30). Flash Player 7 оптимизирован для быстрой обработки данных, передаваемых в аргументах (см. трюк 100);

данный стиль кодирования является предпочтительным для кода ActionScript 2.O. Оптимизация основана на том факте, что Flash Player сохраняет данные в нескольких аппаратных регистрах (вместо обращения к переменным в памяти). Если заменить ссылки на клипы, передаваемые в аргументах, псевдопеременной this, Flash Player не применяет регистровую оптимизацию, поэтому программа работает медленнее.

Обнаружение множественных столкновений Обнаружение множественных №40 столкновений ТРЮК Обнаружение столкновений используется в играх и имитациях. Попробуем усовершенствовать стандартный механизм обнаружения коллизий Flash для нетривиальной анимации.

Flash позволяет обнаруживать столкновения между двумя клипами методом MovieClip.hitTest(). Метод возвращает true, если столкновение было обнаружено, или false при отсутствии столкновений. Само понятие столкновение тоже понимается по-разному. Столкновением можно считать контакт точки с краем клипа, а также ограничивающими прямоугольниками двух клипов (то есть прямоугольниками, окружающими клипы при их выделении в среде разработки). Обе ситуации будут рассмотрены далее. Предположим, на сцене находятся два клипа: clipA и clipB. Следующий код активизирует режим перетаскивания клипа clipA и выводит значение true для каждого кадра, в котором ограничивающие прямоугольники clipA и clipB перекрываются. В противном случае выводится значение false.

clipA.onEnterFrame = function О { hit = clipA.hitTest(clipB): trace(hit): }: cl ipA.startDrag(true);

Данный способ обнаружения столкновений имеет один серьезный недостаток: столкновения обнаруживаются при соприкосновении ограничивающих прямоугольников даже в том случае, если пикселы внутри клипов не перекрываются. На рис. 5.13 две круговые области клипов не имеют общих точек, но метод hitTest() из предыдущего листинга возвращает true, потому что ограничивающие прямоугольники перекрываются.

Рис.

5.13. Метод hitTest() возвращает true, если перекрываются ограничивающие прямоугольники Первое возможное решение - обнаруживать столкновения вручную. Для кругов это делается легко: если расстояние между центрами кругов меньше суммы радиусов, значит, круги перекрываются. Следующий фрагмент проверяет Глава 5. Трехмерная графика и физика столкновения между кругами, при этом расстояние между точками вычисляется по теореме Пифагора: function circleHHTest (circlel. circle2) { var a = circlel._x - circle2._x: var b = circlel._y - circle2._y: clist = Math.sqrt(Math.pow(a. 2) + Math.pow(b, 2));

return dist < Math.abs(circlel._width/2 - circle2._width/2): } В другом варианте используются графические объекты почти прямоугольной формы, практически целиком заполняющие ограничивающую область клипа. Идея не столь глупа, как может показаться на первый взгляд: она часто применялась при программировании первых видеоигр (кстати, именно из-за этого корабли космических агрессоров бывали более или менее прямоугольными). Обнаружение столкновений также часто производится по отношению к точке и клипу. Следующий фрагмент проверяет, принадлежит ли текущая позиция указателя мыши клипу clipA: this.onEnterFrame = functionO { hit = clipA.hitTest(_xmouse. _ymouse. true): trace(hit): }: Фрагмент возвращает true, если указатель мыши находится на одном из пикселов клипа clipA (включая пикселы с нулевым альфа-каналом и даже скрытые, если клип был скрыт кбмандой clipA._visible=false). ActionScript не содержит встроенных средств проверки столкновений между отдельными пикселами двух клипов. Проверка столкновений возможна либо между ограничивающими прямоугольниками двух клипов, либо между точкой и пикселами клипа. Хотя теоретически можно проверять столкновения между любыми двумя клипами, на практике количество клипов ограничивается в зависимости от того, насколько быстро Flash может выполнять вычисления. При взаимодействии большого количества клипов процессор просто не успевает перебрать тысячи возможных комбинаций. Встроенных событий, оповещающих о столкновениях, не существует, поэтому, чтобы узнать о произошедших столкновениях, вам придется постоянно проверять их вручную. При сколько-нибудь значительном количестве клипов такие операции выполняются очень медленно. И все же спасительный выход существует (а не будь его, разве можно было бы назвать это трюком?) Многие разработчики не понимают, что метод MovieClip.hitTest() при проверке столкновений распознает вложенные клипы. Если упорядочить временные диаграммы в лиерархию столкновений вложенных клипов, столкновение между одним клипом и сотней других может быть обнаружено единственной проверкой. Также возможен вариант создания оптимизированного механизма проверки столкновений, который работает только в том случае, если некоторые столкновения уже произошли (вместо проверки всех возможных столкновений в каждом кадре). Давайте посмотрим, как это делается.

Обнаружение множественных столкновений Иерархия столкновений На практике обычно требуется обнаружить столкновения между одним объектом и группой других объектов, будь то молекулы газа в физической имитации, орда космических агрессоров или стены лабиринта. Допустим, мы хотим выявить столкновения с одним графическим объектом, представляющим игрока (игрового персонажа, находящегося под управлением пользователя). В медленном способе проверки столкновений каждый клип рассматривается как отдельная сущность. Таким образом, если в видеоигре на экране находится одновременно 20 инопланетных кораблей, придется проверять столкновения между кораблем игрока и каждым инопланетянином по отдельности. Вместо этого правильнее объединить всех инопланетян в один клип (например, alienSwarm). Полученная иерархия изображена на рис. 5.14.

J v Ss e S amaes e e -S n w r. f rJ Рис.

5.14. Иерархия инопланетян внутри клипа alienSwarm После этого столкновения между инопланетянами и кораблем игрока проверяются как столкновения между клипами alienSwarm и ship независимо от количества инопланетных кораблей. Еще важнее то, что процесс обнаружения не замедляется даже при большом количестве инопланетян в группе! Чтобы опробовать эту методику, создайте клип с именем ship и убедитесь в том, что его точка регистрации находится возле вершины (рис. 5.15). Треугольник представляет космический корабль игрока.

Рис.

5.15. Точка регистрации находится возле носа космического корабля Создайте второй клип с именем asteroid (рис. 5.16), присвойте ему идентификатор компоновки asteroid. Положение точки регистрации в этом клипе несущественно.

Глава 5. Трехмерная графика и физика Разместите клип ship в нижней части сцены, где обычно находится корабль игрока в классической игре Space Invaders.

Рис. 5.16. Символ клипа asteroid Добавьте следующий код в первый (и единственный) кадр временной диаграммы. Как обычно, этот код рекомендуется разместить на отдельном уровне actions, выделенном специально для этой цели: function shipMoveO { // Обнаружение нажатий клавиш и соответствующее перемещение клипа ship, i f (Key.isDown(left)) { ship._x -= playerSpeed: } i f (Key.isDown(right)) { ship._x += playerSpeed: } / Поек сокоеи / рвра тлнвнй if (asteroidBelt.hitTest(ship._x, ship._y. true)) { traceC'collision"): } updateAfterEventO;

f n t o asteroidMoveO { ucin / Прмщне атриа / ееееи сеод ti.y+ ti.seodpe: hs_ = hsatriSed i ( h s _ > 400) { f ti.y t i. x = Math.randomO * 550: hs_ t i. y =0 hs_ : } function createAsteroidsO { // Создание анимационного клипа asteroidBeit. Внутри клипа // создаются 20 вложенных клипов, представляющих астероиды: // от asteroidO до asteroidl9. this.createEmptyMovieClip("asteroidBelt". 0): initAsteroid = new ObjectO;

Хs for (i = 0: i < 20: i++) { initAsteroid._x = Math.randomO * 550: initAsteroid._y = Math.randomO * 400: initAsteroid.asteroidSpeed = Math.round(Math.random() * 3) + 2 ) : initAsteroid.onEnterFrame = asteroidMove;

asteroidBelt.attachMovieC'asteroid". "asteroid" + i. i. initAsteroid) } / Ииилзця / нцаиаи lf =K y L F : et e.ET Обнаружение множественных столкновений right = Key.RIGHT: playerSpeed = 10;

asteroidSpeed = 3;

shiplnterval = setlnterval(shipMove. 10): createAsteroidsO:

Функция createAsteroidsO создает клип с именем asteroidBelt, содержащий 20 вложенных клипов с именами asteroidO- asteroid 19. Астероиды постепенно перемещаются по экрану. Клип ship перемещается клавишами < и ->. Цель игрока - уклониться от столкЧ новения с астероидами. Анимация корабля управляется событием setlnterval() и получает гораздо большую долю ресурсов Flash Player (см. трюк 71). Астероиды управляются обработчиками onEnterFrame, поэтому их анимация выполняется на более низкой частоте (частоте смены кадров). Если корабль игрока сталкивается с одним или несколькими астероидами, на панели Output выводится слово collision'для каждого кадра, в котором такое столкновение произошло. Ключевой фактор, обеспечивающий высокое быстродействие, - единая проверка столкновений для всего пояса астероидов: i f (asteroidBelt.hitTest(ship._x. ship._y. true)) { trace("collision"):

Столкновения в обратной иерархии Иерархии столкновений позволяют существенно повысить быстродействие, но работают только в одном направлении. Например, можно обнаружить столкновение между поясом астероидов и кораблем игрока, но нам не удастся обнаружить столкновение между лучом лазера и отдельными астероидами (такая проверка необходима, чтобы астероиды взрывались при попадании в них). Но даже в таких ситуациях иерархия столкновений оказывает неоценимую помощь - она сообщает о самом факте столкновения. Наличие такой информации позволяет оптимизировать код обнаружения столкновений - функция обнаружения столкновений между лазером и отдельными астероидами заведомо выполняется только в том случае, если столкновение уже произошло. Для примера рассмотрим один из возможных сценариев. Сначала проверяется столкновение между лазером и поясом астероидов. Если столкновение обнаружено, мы знаем, что лазер попал в какой-то астероид, хотя пока неизвестно, в какой именно. Сравним свойства _х или _у отдельных астероидов с позицией лазера и исключим астероиды, находящиеся слишком далеко. Тем самым из проверки будут исключены почти все астероиды. Для оставшихся астероидов проводится индивидуальная проверка столкновений (функцией MovieClip.hitTestO). Вторичная функция проверки столкновений (для отдельных астероидов) будет выполняться гораздо реже первичной функции hitTestO (для всего пояса астероидов).

Глава 5. Трехмерная графика и физика Выполнение функции означает, что как минимум одно столкновение уже произошло и код можно оптимизировать с учетом этого обстоятельства.

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

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

ТРЮК Поворот к заданной точке Во многих играх и имитациях требуется повернуть графический объект, представляющий игрока, к заданной точке. Используйте угловое перемещение для разворота спрайтов в нужном направлении.

№ Компьютерная графика во многом отличается от реальной жизни. Графические объекты могут двигаться в произвольном направлении, тогда как в реальной жизни объект обычно сначала поворачивается в некотором направлении и только потом двигается. Более того, если реальный объект уже двигается, он не может мгновенно сменить направление. Из-за инерции поворот в новом направлении требует некоторого времени, поэтому объект двигается по криволинейной траектории, пока не будет ориентирован в нужном направлении. Повысить реализм движения компьютерных объектов можно несколькими способами: Х придайте графическому объекту такой вид, как будто он всегда ориентирован в правильном направлении (например, используйте шар, обладающий свойством радиальной симметрии, или летающую тарелку, которая может двигаться в любом направлении без разворотов);

постоянно перемещайте целевую точку, к которой стремится ваш анимированный клип. Если точка движется по сложному закону, зрителю покажется, что клип тоже движется по аналогичным правилам. Приложение проектируется таким образом, что сам код не обладает особой сложностью или разумным поведением. В большинстве видеоигр противники преследуют персонажа, представляющего игрока. Поскольку игрок (будем надеяться!) двигается разумно, перемещения противников тоже выглядят разумно;

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

Преследование движущейся цели Следующий фрагмент постоянно наводит клип на заданную точку (в данном случае - в текущей позиции указателя мыши): // Создание клипа ball this.createEmptyMovieClip<"ball", 0);

ball.lineStyle(50. 0x0. 100);

ball.moveTo(0. 0);

ball.lineToCO. 1): // Анимация ball для преследования указателя м ш ыи ball.onEnterFrame = function О { this._x -= (ball._x - _xmouse) / 4;

this._y -= (ball._y - _emouse) / 4;

}: Программа создает универсального преследователя с эффектом инерции (разность координат делится на 4, чтобы избежать мгновенного перемещения шара в текущую позицию указателя мыши). Шар перемещается к последней позиции мыши по прямой линии (или серии отрезков, если цель передвигается). Анимация основана на упоминавшемся ранее свойстве радиальной симметрии шара (последнее означает, что шар не имеет четко выраженного направления). Мы рассмотрели минимальный код для создания реалистичного эффекта движения, который на первый взгляд решает проблемы изменения направления и ориентации (хотя на самом деле не делает ни того, ни другого). Этот простой трюк молено расширить и изменять ориентацию движущегося объекта не моделированием поворота, а простым переключением между несколькими готовыми изображениями или анимационными последовательностями. Например, если персонаж двил<ется в трехмерном мире, следует заготовить разные варианты анимации для каждого возможного направления (см. трюк 28).

Разворот к точке Представьте, что корабль должен развернуться к цели перед выстрелом (предполагается, что орулше всегда стреляет в текущем направлении). Следующий код рисует отрезок прямой и разворачивает его в направлении текущей позиции указателя мыши: // Создание анимационного клипа tracker var tracker:MovieClip = this.createEmptyMovieClipC'tracker". 0): // Рисование линии в клипе tracker tracker.lineStyle(0. 0x0. 100): tracker.moveTo(0. 0): tracker.lineTodOO. 0): tracker, x = Stage.width / 2:

Глава 5. Трехмерная графика и физика tracker._y = Stage.height / 2: // Определение множителя для преобразования радианов в градусы // var RAD_DE6:Number = 180/Math.PI: tracker.onMouseMove = functionО { // Поворот клипа в направлении указателя м ш ыи var angle:Number = Math.atan2(_ymouse - this._y. _xmouse - t h i s. _ x ) : t h i s. _ r o t a t i o n = angle * RAD_DEG;

updateAfterEventO: }:

Метод Math.atan2(), использованный в этом фрагменте, возвращает угол, на который нужно повернуть линию, чтобы она была обращена к точке, заданной координатами X и Y (обратите внимание: в первом параметре передается расстояние У, а не X). Геометрическая интерпретация представлена на рис. 5.17.

target s t a r t position Рис. 5. Все тригонометрические функции Flash возвращают углы в радианах, поэтому результат нужно преобразовать в градусы - единицы, используемые свойством MovieClip._rotation. Приведенный ранее код мгновенно разворачивает клип к текущей позиции указателя мыши. Чтобы замедлить поворот, достаточно ограничить скорость в обработчике события onMouseMove (в следующем примере скорость ограничивается приращениями 5): tracker.onMouseMove = functionO { // Поворот клипа в направлении указателя м ш ыи var targetAngle:Number = Math.atan2(_ymouse - this._y. _xmouse - t h i s. _ x ) ;

var errorAngle:Number = targetAngle * RAD_DEG - t h i s. _ r o t a t i o n ;

i f (Math.abs(errorAngle) > 5) { i f ( ((errorAngle > 0) && (errorAngle < 180)) || (errorAngle < -180) ) { t h i s. _ r o t a t i o n += 5: } else { t h i s. r o t a t i o n -= 5;

Поворот к заданной точке Многоуровневая команда if в этом фрагменте проверяет переменную errorAngle, потому что значения свойства _rotation лежат в интервале от -180 до +180, а не от 0 до 360. Если увеличивать свойство _rotation на 1 для каждого кадра, то в процессе полного оборота оно будет изменяться следующим образом: 1. 2. 3 179. 180. -179. -178 -2. -1. О Таким образом, команда if заставляет клип tracker поворачиваться к нужному направлению по кратчайшей дуге. Иначе говоря, если клип направлен на ^ ч а сов, и его нужно развернуть на 9 часов, то поворот будет выполняться на 90 против часовой стрелки, а не на 270 по часовой.

Инерция Допустим, мы хотим имитировать эффект инерции, чтобы клип двигался к точке назначения по дуге. Для этого следует добавить перемещение в том направлении, куда клип направлен в каждый момент времени. Опробуйте следующее решение: function drawBlip(clip) { // Рисование линии в клипе clip.lineStyleCO. 0x0. 100);

clip.moveTo(0. 0): clip.lineTodO. 0): clip._x = Math.randomO * Stage.width: clip._y = Math.randomO * Stage.height: } function r a MoveО { el // Вычисление расстояния м ж у т к щ й позицией к и а ед еуе п // и целевой позицией ( к з т л м мыши). уааее this.xDist = _xmouse-this._x;

this.yDist = _ymouse-this._y;

/ Вычисление угла м ж у текущей и ц л в й позициями клипа. / ед еео // а также разности м ж у желаемым направлением ед // и текущим углом (errorAngle). var targetAngle:Number = Math.atan2(this.yDist. this.xDist): var errorAngle:Number = targetAngle * RAD_DEG - this._rotation: // Вычисление угла. о о о а по значению errorAngle. пврт if (Math.abs(errorAngle) > 10) { if ( ((errorAngle > 0) && (errorAngle < 180)) j | (errorAngle < -180) ) { this._rotation += 10: } else { this.rotation - 10: = // Перемещение к и а с учетом текущего угла. п this._x += Math.cos(this._rotation / RAD_DEG) * 20: this._y += Math.sin(this._rotation / RAD_DEG) * 20: } Глава 5. Трехмерная графика и физика // Определение множителя для преобразования радианов в градусы // var RAD_DEG:Number - 180/Math.PI: // Создание клипов tracker for (var i:Number = 0;

i < 100;

i++) { var tracker:MovieClip = this.createEmptyMovieClip("tracker" + i. i ) : drawBlip(tracker);

tracker.onEnterFrame = realMove;

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

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

ГЛАВА Текст Трюки № 42- Средства работы с текстом в Flash не ограничиваются выводом статических сообщений, единственный смысл которых - оставаться на своем месте и быть прочитанными пользователем. Flash позволяет интерпретировать текст как набор векторных фигур или серию анимационных клипов;

оба варианта обладают тем преимуществом, что они позволяют анимировать текст. Достаточно взглянуть на титры некоторых фильмов, чтобы понять, насколько выразительным может быть анимированный текст и до какой степени движение может способствовать передаче смысловой нагрузки. Более того, появляющиеся и исчезающие текстовые сообщения иногда используются для создания подтекста (по аналогии с тем, как жестикуляция создает подтекст для произносимых слов). Далее перечислены некоторые сайты Flash, демонстрирующие возможности работы с текстом: Х Typorganism ( - сайт демонстрирует так называемое кинетическое оформление, то есть эффекты анимации текста, вносящие дополнительный смысл посредством движения. Х Overage4Design ( - современный дизайн, в котором текст интерпретируется как графический объект, а не как нечто предназначенное для простого чтения. Mono-craft ( Юго Ыакамуры (Yugo Nakamura) - один из первых сайтов с новаторскими и интерактивными возможностями работы с текстом. Mono-craft версии 2.0 считался одним из законодателей моды золотого века Flash 4 и 5, когда первая волна Flash-дизайнеров на базе современной методики ActionScript создавала концепции, которые в наше время считаются стандартными. Saul Bass on the Web ( - сайт поддерживается хорошо известным Flash-дизайнером Бренданом Доусом (Brendan Dawes). Он отдает дань уважения Солу Бассу (1920-1966) - одному из первых людей, использовавших текст как динамическую графику в фильме. Басе также применял текст для графического оформления плакатов к фильмам, как это делали его предшественники-модернисты из Советской России в 1920-х годах.

Х Х Глава 6. Текст Flash превосходит стандартный HTML в области работы с текстом не только возможностью анимации, но и более широкими средствами настройки. В частности, при работе с векторным текстом Flash поддерживаются следующие возможности: Х отображение текста под любым углом и любого размера;

Х рисование дополнительных знаков и использование сглаживания для всех шрифтов;

Х использование любых шрифтов, в том числе и не установленных на компьютере пользователя (для этого в SWF-файл включается контурное описание шрифта);

Х возможность разбиения знаков и их модификации внутри текста (команда Modify Х Break Apart) для быстрого создания логотипов и других изображений на текстовой основе. Вы даже можете использовать морфинг для преобразования одних символов в другие;

Х поддержка традиционного форматирования CSS и текста HTML (см. трюк 46) даже в том случае, если содержание отображается вне браузера (например, в автономном Flash Player или при экспортирований данных Flash в исполняемый файл).

Шрифты Flash поддерживает две категории шрифтов: системные и встраиваемые. При использовании одного из трех стандартных шрифтов (л_jsans, л_serif или л.typewriter) Flash Player на стадии выполнения подбирает наиболее похожий шрифт в системе пользователя. Шрифт л_sans (эквивалент sans-serif в HTML/ CSS) обычно соответствует Arial в системе Windows или Helvetica на Мае. Вместо шрифтов serif и л^typewriter (эквиваленты serif и mono в HTML/CSS) Flash обычно использует Times или Courier или другие подходящие шрифты. Контурное описание шрифта может быть сохранено в SWF-файле для выполнения различных текстовых эффектов (см. трюк 48) - скажем, поворота текста. Встраивать стандартные шрифты в SWF не обязательно, но системные шрифты не содержат векторной информации, поэтому Flash не может интерпретировать текст, выведенный с применением системного шрифта, как графический объект. Из-за этого текст, оформленный системным шрифтом, обычно исчезает со сцены при повороте или масштабировании клипа. При встраивании стандартных шрифтов (л_sans, л_serif или л_typewriter) Flash использует ближайший шрифт, обнаруженный в системе на стадии компиляции. На стадии выполнения такой текст успешно поворачивается, масштабируется и анимируется, поскольку Flash использует встроенное описание шрифта, а не системный шрифт, находящийся на компьютере пользователя. Трюки, представленные в этой главе, помогут сделать текст более разборчивым, реализовать текстовые поля с автоматическим заполнением, импортировать текст со сложным форматированием, использовать CSS, а также применять такие специфические возможности, как всплывающие подсказки. В других главах ветре Сохранение разборчивости текста чаются и другие трюки, применяемые при работе с текстом, - например, создание переходов с использованием текста и масок (см. трюк 2).

ТРЮК Сохранение разборчивости текста Удобочитаемость текста зависит от шрифта, размеров кегля и текущих размеров экрана. Оперативное измерение параметров шрифта позволит нормально читать текст сайта на экранах разных размеров.

№ На сайтах Flash обычно используются мелкая графика и текст. Что же, выглядит эффектно - если не считать того, что текст, нормально читающийся на одном компьютере, может оказаться неразборчивым на другом. Например, текст размером 8 пунктов может стать нечитаемым на Macintosh iBook. С другой стороны, текст, который нормально смотрится на мониторе с разрешением 1024 х 768, становится нечитаемым разрешении 1600 х 1200 (пикселы становятся меньше при более высоких разрешениях). А если сайт проектировался для рабочего стола 1600 х 1200, то на мониторе с разрешением 800 х 600 текст будет выглядеть непропорционально большим. В принципе, можно создать несколько версий сайта для разных разрешений, но существует более разумный путь - определить размер экрана пользователя и оперативно изменить текстовые метрики Flash в соответствии с размерами сцены.

Определение размеров экрана Процедура определения разрешения экрана пользователя сводится к простому запросу свойств System.capabilities. Следующий фрагмент выводит разрешение экрана: trace(System.capabiIities.screenResolutionX): trace(System.capabilities.screenResolutionY);

На моем компьютере этот фрагмент выводит числа 1600 и 1200 - как и следовало ожидать, на рабочем столе выставлено разрешение экрана 1600 х 1200. Так что же можно сделать с этими числами? Например, изменить размер выводимого текста. Но сначала нужно создать сам текст. Следующий код (файл varyText.fla на сайте книги) динамически создает текстовое поле с небольшим сообщением. 1 var txtFmt: Text Format = new TextFormatO ;

2 var myStr:String = "Hello and welcome to Flash Hacks. " + 3 "by S a Bhangal and a host of co-contributors": hm 4 // Выбор шрифта и кегля 5 txtFmt.font = "_sans": 6 txtFmt.size = 12;

7 var metrics:Object = txtFmt.getTextExtentCmyStr. 200): 8 // Добавление текстового поля 9 this.createTextField("my_txt", 1, 100. 100. 10 metrics.textFieldWidth. metrics.textFieldHeight): 11 my_txt.wordwrap - true;

206 12 my_txt.border = true;

13 my_txt.text r myStr: 14 my_txt.setTextFormat(txtFmt):

Глава 6. Текст Строки 1 и 2 создают экземпляр TextFormat с именем txtFmt, используемый для отображения текста myStr. В строке 4 выбирается шрифт л_sans - один из трех стандартных шрифтов, поддерживаемых Flash. Затем в строке 7 метод TextFormat.getTextExtent() создает элемент Object с именем metrics. Метод возвращает объект с двумя свойствами, textFieldWidth и textFieldHeight, определяющими ширину и высоту текстового поля, необходимого для отображения сообщения myStr с разрывом текста после 200 пикселов. Вторая часть приведенного кода динамически строит текстовое поле my_txt с глубиной 1 и позицией (100, 100);

ширина и высота поля определяются значениями упоминавшихся ранее двух свойств metrics. Последние четыре строки просто задают свойства текстового поля после его создания. В частности, они заносят в него наш текст myStr и применяют к полю объект TextFormat с именем txtFmt, определяющий шрифт и его кегль. Особенно важна строка 6: txtFmt.size = 12: Если присвоить txtFmt.size любое другое значение (и применить его вызовом setTextFormatO), текст будет отображаться другого кегля, а высота текстового поля автоматически подгоняется под высоту текста, как показано на рис. 6.1, для значений txtFmt.size, равных 8, 12 и 16 соответственно.

Hello and welcome to Flash Hacks, by Sham Bhangal and a host of cocontributors Hello and welcome to Flash Hacks, by Sham Bhangal and a host of co-contributors Рис. 6. 1. Автоматическое масштабирование текстового поля для трех вариантов кегля Вероятно, вы уже уловили, к чему я клоню: если задавать свойство size в зависимости от разрешения экрана пользователя, проблему удобочитаемости текста можно считать решенной. Попробуйте заменить строку 6 следующим фрагментом: i f (System.capabi1iti es.screenResol uti onX 800) { txtFmt.size = 12: } else { txtFmt.size = 10:

Автоматическое завершение текста Обратите внимание: проверять размеры обеих сторон не обязательно. Как правило, для получения информации о разрешении достаточно проверить только одну сторону, поскольку экраны мониторов обычно имеют форматное соотношение 4 : 3. У некоторых дизайнеров может возникнуть неприятная мысль: Все это, конечно, хорошо, но не отразится ли изменение размера шрифта на моем тщательно спроектированном макете сайта? Отразится, но обычно изменения ограничиваются очень небольшими значениями (2 пункта, как в рассмотренном примере), не приводящими к радикальному изменению структуры сайта. В крайнем случае можно использовать текстовые поля фиксированного размера и снабдить их полосами прокрутки для больших текстовых сообщений или уменьшить расстояние между строками при увеличении размера текста (мелкий текст обычно разделяется более широкими межстрочными интервалами, чем крупный).

Итоги Необходимость поддержки разных размеров экранов (для мобильных телефонов, карманных компьютеров и других нестандартных устройств) становится все более насущной, поэтому требования к удобочитаемости текста выходят на первый план. Тривиальное решение проблемы - создать разные версии для всех основных категорий устройств, но таких категорий немало (причем даже устройства одной категории могут различаться). Лучше реализовать возможность динамического управления текстовыми метриками. Также существует возможность динамического изменения размеров сцены Flash и/или находящихся на ней текстовых экземпляров в соответствии с разрешением экрана (и размером окна браузера). Зная соотношение между размером сцены Flash и общими размерами экрана, вы получаете дополнительные возможности для динамического масштабирования содержания Flash. Реализация динамического изменения текстовых метрик и размеров сцены (см. трюк 92) позволит вам в значительной степени контролировать адаптацию SWF к разным условиям времени выполнения. Другое полезное применение кода, приведенного в настоящем трюке, - создание всплывающих подсказок. Код для оперативного создания легко перемещаемого и масштабируемого текста легко адаптируется для создания текста справки (см. трюк 47), который всегда масштабируется и позиционируется возле указателя мыши, но внутри сцены. Иногда при изменении размеров текста бывает желательно сменить шрифт. Например, чтобы текст хорошо читался при малых размерах, следует использовать пиксельные шрифты (см. трюк 67).

ТРЮК № Автоматическое завершение текста Автоматическое завершение ускоряет ввод данных на экранных формах.

Большинство из нас предпочитает вводить ровно столько текста, сколько абсолютно необходимо. А для некоторых категорий пользователей (детей, пользователей ;

Глава 6. Текст с ограниченной дееспособностью, владельцев мобильных устройств с неудобными клавиатурами и т. д.) экономия нажатия клавиш является делом первоочередной важности. Один из способов экономии времени основан на автоматическом завершении часто используемых слов до того, как пользователь завершит их ввод (данная функция называется автоматическим завершением). На страницах и excerpts/xcommon.html приводятся списки 300 слов, чаще всего используемых в письменном английском языке. Другие аналогичные списки можно найти в специализированной литературе. При первом знакомстве с этими ресурсами у меня сразу возникла мысль: Хмм... Неплохое начало для реализации автоматического заполнения. На рис. 6.2 показан результат удаления всех слов, состоящих из двух и менее букв (для которых автоматическое заполнение не дает сколько-нибудь существенной экономии времени), и сортировки полученного списка по алфавиту.

called с*ие can chance children city ccae oeoi could cstuitry d&y dY SiS oiCferenc doea dcr.'t dour, during Рис. 6.2. Самые распространенные слова в письменном английском языке, содержащие не менее трех букв Если вы предпочитаете извлечь слова из другого списка, приведу код, который я использовал: function textLoader(data) { // Разбиение текстового файла по пробелам dictionary = data.splitC " ) : // Сортировка списка doctionary.sortO;

// Вывод отсортированного списка, разделенного пробелами. trace(dictionary.join(""));

} var myText:LoadVars = new LoadVarsO: var dictionary:Array - new ArrayO: myText.1oad("commonWords.txt"): myText.onData = textLoader;

Предполагается, что слова хранятся в файле commonWords.txt и разделяются пробелами. Измените имя файла и разделитель (например, символ новой строки) в соответствии с тем списком, который имеется у вас.

Автоматическое завершение текста Мой усеченный список слов занимает всего лишь 1,5 Кбайт, причем его можно усечь до 990 байт (большая выгода!) копированием выходных данных предыдущего сценария в листинг ActionScript. Как вы вскоре убедитесь, полная версия выглядит не так компактно: var myText:String = "about above across... years you young your";

var dictionary:Array = new ArrayO;

dictionary = myText.splitC " ) ;

dictionary. sortO;

delete (myText);

При публикации SWF Flash сжимает текст определения словаря. На сайте я обнаружил весьма изобретательный трюк для реализации автоматического завершения. В нем используются два текстовых поля, одно над другим. Верхнее поле предназначено для ввода данных, а нижнее, динамическое поле содержит рекомендации для автоматического завершения. Допустим, у вас уже имеется слой с именем actions, с первым кадром которого связан предыдущий фрагмент кода. Добавьте два новых слоя с именами entered text и completed text (см. рис. 6.3). л3D illllQ *Х5.. Х i Х entered text Ij? c m l t text:: o pee ;

Рис. 6.З. Создание слоев для реализации автоматического завершения текста На слое entered text создайте многострочное текстовое поле без бордюра и присвойте ему имя myText_txt (см. рис. 6.4).

ж rayText_txt 309 0 И: 170.8 х: у;

ты sn.s e iiifcl;

^ 6. 3 ш А |Ми1Шпе !V| ! M ;

|vl :

И|;

* | Normal v| ;

| <> D Var:

18 | 13.

Рис. 6.4. Настройка параметров текстового поля для слоя entered text Теперь скопируйте текстовое поле, заблокируйте слой entered text и вставьте текстовое поле на слой completed text (клавиши Ctrl+Shift+V в системе Windows или

;

:

:ХХ i Dynamic Text A b v '.A ;

| _sans vl V completejxt jj 309. :

Ш67.0 Щ 118. |!;

1А|,::|Ми1Шпе v ;

A0 <Ь П Van H i 1*3.3 t Рис. 6.5. Изменение свойств текстового поля на панели свойств Программа Введите в сценарии, связанном со слоем actions, следующий фрагмент, содержащий как программный код, так и предварительно инициализированный словарь: function autoCompleteO { // Функция подтверждает текущий предложенный вариант // автоматического завершения при нажатой клавише CONTROL, if(Key.isDown(Key.CONTROL)){ myText_txt.text = complete_txt.text + " ";

Selecti on.setSelecti on(myText_txt.text. 1 ength, myText_txt.text.length): function fieldChangeO { / Функция д я п и к и отоображения в р а т / л оса аина // автоматического завершения строки, match = "": // Получение последнего незавершенного слова в текстовом поле startOfWord = this.text.lastlndexOf(" ") + 1: lastWord = this. text. substring(startOfWord, this.text.'length);

/ Поиск в словаре кандидатов д я последнего незавершенного слова, / л if(lastWord.length > 1){ f r ( a i = 0;

i < dictionary.length;

i++) { o vr if ( a t o d == (dictionary[i].substr(0. lastWord.length))){ lsWr // Е и совпадение обнаружено, сохранить в переменной match. сл match = d c i onary[i];

it search = i;

break;

} else { search = 0;

Автоматическое завершение текста // Отображение текущего совпадения в автоматически завершаемом // текстовом поле. complete_txt.text = this.text.substrCO. startOfWord) + match;

// Инициализация var myText:String = "about above across after again against air all almost along also always and animals another answer any are around asked away back because been before began being below best better between big both boy boys but bye called came can change children city come cool could country day days did different does don't down during each earth email end enough even ever every example eyes far father feet few find f i r s t five following food for form found four from get give going good goodbye got great had hand hard has have head hear heard hello help her here high him himself his home house how however html important into i t ' s its just keep kind knew know land large last learn left let l i f e light like line l i t t l e live long look looked made make man many may means men might miles more most mother much must name near need never new next night no not now number off often old once one only other others our out over own page paper part parts people picture place play point put read right room said same saw say school sea second see sentence set several she should show side since small some something sometimes soon sound s t i l l story study such sun sure take t e l l than that the their them then there these they thing things think this those thought three through time times today together told too took top toward try turned two under until use used using usually very want was water way ways web website well went were what when where which while white who whole why will with without word words work work world would write year years you young your";

var dictionary:Array = new ArrayO;

var search:Number = 0;

var lastWord:String = "": var startOfWord:String = "";

var control:Object = new ObjectO;

// Построение словаря dictionary = myText.splitC " ) ;

dictionary. sprtO: // Настройка событий и слушателей myText_txt.onChanged = fieldChange;

control.onKeyDown = autoComplete;

Key.addLi stener(control);

.Х Основное место в этом сценарии занимает функция fieldChange(), выполняемая каждый раз, когда пользователь изменяет содержимое текстового поля. Сначала функция очищает содержимое переменной match, содержащей текущий вариант автоматического завершения. Затем она получает все символы последнего слова, введенного пользователем;

для этого мы находим последнее вхождение пробела и берем текст от следующего знака до конца текста. Например, в предложении The cat sat последний пробел находится перед группой sat (а его индекс равен 7, если начать отсчет с 0). Увеличение индекса на 1 дает позицию первого символа слова sat. Используя эту информацию Глава 6. Текст (startOfWord), мы извлекаем подстроку sat из предложения. Последняя буква sat соответствует концу нашего предложения на данный момент. Для удобства далее приводится соответствующий фрагмент листинга: function fieldChangeO { match = "";

startOfWord = this.text.lastlndexOfC" ") + 1;

lastWord = this.text.substring(startOfWord. this.text.length);

Pages:     | 1 | 2 | 3 | 4 | 5 |   ...   | 8 |    Книги, научные публикации