Анимация в Java аплети

Тази статия описва как да внедрите анимация с помощта на API на Java аплета. Той описва често използваните техники и дава прост пример за илюстриране на всяка техника.

Основни техники за анимация

В Java са възможни много форми на анимация. Общото между всички тях е, че те създават някакъв вид движение на екрана, като рисуват последователни кадри с относително висока скорост (обикновено около 10-20 пъти в секунда).

Ще започнем, като създадем прост шаблонен аплет за правене на анимации и бавно го разработваме, докато стигнем до доста пълен аплет.

Използване на конец

За да актуализирате екрана няколко пъти в секунда, трябва да създадете нова нишка на Java, която съдържа анимационен цикъл. Анимационният цикъл е отговорен за проследяване на текущия кадър и за искане на периодични актуализации на екрана. За да внедрите нишка, трябва или да създадете подклас на интерфейса, Threadили да се придържате към него Runnable.

Често срещана грешка е поставянето на анимационния цикъл в paint()метода на аплета. Това ще има странни странични ефекти, тъй като тя задържа основната AWT нишка, която отговаря за цялото рисуване и обработка на събития.

Като пример съм написал малък аплет за шаблон, наречен Example1Applet, който илюстрира общия контур на анимационен аплет. Example1Applet показва как да създадете нишка и да извикате repaint()метода на фиксирани интервали. Броят на кадрите в секунда се определя чрез предаване на параметър на аплет. Ето пример за това, което бихте въвели във вашия HTML документ:


  

Ето Пример1Аплет.

Забележка:

Този аплет всъщност още не рисува нищо на екрана. Рисуването на екрана е обяснено по-късно. Обърнете внимание също, че аплетът унищожава своята анимационна нишка всеки път, когато потребителят напусне страницата (което води до stop()извикване на метода на аплета ). Това гарантира, че аплетът няма да губи време на процесора, докато страницата му не се вижда.

Поддържане на постоянна честота на кадрите

В горния пример аплетът просто спи за определен период от време между кадрите. Това има недостатъка, че понякога чакате твърде дълго. За да получите 10 кадъра в секунда, не трябва да чакате 100 милисекунди между кадрите, защото губите известно време, просто пускайки нишката.

Следващият аплет, Example2Applet, показва как да запазите по-добро време. Той просто изчислява правилното закъснение между кадрите, като следи началното време. Той изчислява очакваното необходимо забавяне между кадрите въз основа на текущото време.

Тук е Example2Applet.

Боядисване на всяка рамка

Това, което остава, е да нарисуваме всяка рамка. В предишните примери извикваме repaint()всеки кадър, което кара paint()метода на аплета да бъде извикан. Example3Applet има paint()метод, който извежда номера на текущия кадър на екрана.

Тук е Example3Applet в действие, последван от списък с кодове.

Забележка:

Ако посочите честотата на кадрите да бъде много висока (да речем 100 кадъра в секунда), run()методът ще извиква repaint()100 пъти в секунда. Това обаче не винаги ще доведе до 100 обаждания paint()в секунда, защото когато издадете заявка за пребоядисване твърде бързо, те ще бъдат свити в актуализация на един екран. Ето защо ние проследяваме текущия номер на кадъра в run()метода, а не в paint()метода.

Генериране на графики

Сега нека анимираме нещо, което е малко по-трудно да се направи. Example4Applet рисува комбинация от синусоиди. За всяка координата x тя изчертава кратка вертикална линия. Всички тези редове заедно образуват проста графика, която се променя за всеки кадър. За съжаление ще установите, че този подход причинява много мигане. Ще обясним причината за мигането и някои решения в следващия раздел.

Тук е Example4Applet в действие, последван от списък с кодове.

Избягване на прекомерно мигане

Мигането, което виждате в Example4Applet, има две причини: боядисването на всеки кадър отнема твърде много време (поради количеството изчисления, което се изисква по време на пребоядисването) и целият фон се изчиства преди да paint()бъде извикан. Докато продължава изчислението на следващия кадър, потребителят вижда фона на анимацията.

Това кратко време между изчистването на фона и рисуването на синусоида се разглежда като светкавица. На някои платформи като PC мигането е по-очевидно, отколкото при X Windows. Причината е, че графиките на X Windows са буферирани, което прави светкавицата малко по-къса.

Можете да намалите значително мигането, като използвате два прости трика: внедряване на update()метода и използване на двойно буфериране (понякога известно като използване на обратен буфер ).

Замяна на метода update ()

Когато AWT получи заявка за пребоядисване за аплет, той извиква update()метода на аплета . По подразбиране update()методът изчиства фона на аплета и след това извиква paint()метода. Като заместваме update()метода, за да включим чертожния код, който беше в paint()метода, избягваме да изчистваме цялата площ на аплета с всяко пребоядисване.

Сега, когато фонът вече не се изчиства автоматично, трябва да го направим сами в update()метода. Вече можем да изтрием всяка вертикална линия на графиката поотделно, преди да нарисуваме новата линия, като елиминираме мигането напълно. Този ефект е показан в Example5Applet.

Тук е Example5Applet в действие, последвано от списък с кодове.

Забележка:

Всеки път, когато замените update()метода, все още трябва да внедрите paint(). Това е така, защото paint()методът се извиква директно от системата за рисуване AWT, когато възникне „повреда“ в областта за рисуване на аплета - например, когато прозорец, закриващ част от областта за рисуване на аплета, се премахне от екрана. Вашата paint()реализация може просто да се обади update().

Двойно буфериране

Друг начин за намаляване на мигането между кадрите е използването на двойно буфериране. Тази техника се използва в много анимационни аплети.

Общият принцип е, че създавате изображение извън екрана, изчертавате рамка в изображението и след това плесвате цялото изображение на екрана с едно обаждане до drawImage(). Предимството е, че по-голямата част от чертежа се прави извън екрана. Окончателното рисуване на изображение извън екрана на екрана обикновено е много по-ефективно от рисуването на рамката директно на екрана.

Аплетът със синусоида с двойно буфериране е показан в Пример 6Аплет. Ще видите, че анимацията е доста гладка и нямате нужда от специални трикове, когато рисувате рамката. Единственият недостатък е, че трябва да разпределите изображение извън екрана, което е толкова голямо, колкото площта за рисуване. Ако зоната за рисуване е много голяма, това може да изисква доста памет.

Тук е Example6Applet в действие, последван от списък с кодове.

Забележка:

Когато използвате двойно буфериране, трябва да замените update()метода, тъй като не искате фонът на аплета да бъде изчистен, преди да рисувате рамката. (Вие сами изчиствате фона, като рисувате към изображението извън екрана.)

Използване на изображения

Сега ще пренапишем paintFrame()метода с метод, който анимира някои изображения. Това добавя някои малки усложнения към проблема. Изображенията са доста големи и се зареждат постепенно. Изтеглянето на изображенията може да отнеме много време, особено когато ги зареждате през бавна връзка. Това е причината drawImage()методът да приема четвърти аргумент, обект ImageObserver. Наблюдателят на изображения е обект, който се уведомява, когато пристигне повече от данните за изображението. За да получим изображенията, използваме getImage()метода.

Преместване на изображение през екрана

Този първи анимиращ изображението аплет, Example7Applet, използва следните две изображения:

world.gif: car.gif:

Световният образ се използва като фон, а изображението на колата се рисува върху него два пъти, създавайки анимация от две коли, препускащи по целия свят.

Тук е Example7Applet в действие, последвано от списък с кодове.

Показване на последователност от изображения

Example8Applet показва как да създадете анимация, използвайки отделни изображения за всеки кадър. Ето 10 кадъра, които се използват:

T1.gif: T2.gif: T3.gif: T4.gif: T5.gif:

T6.gif:

T7.gif:

T8.gif:

T9.gif:

T10.gif:

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

Тук е Example8Applet в действие, последван от списък с кодове.

Забележка:

Когато показвате последователности от изображения, трябва да внимавате, за да ги подравните правилно. Най-лесният начин е да се уверите, че всички изображения са с еднакъв размер и могат да бъдат изчертани на една и съща позиция. Ако случаят не е такъв, вашият аплет ще трябва да нарисува всеки кадър с различно отместване.

Използване на MediaTracker за избягване на постепенно показване

Когато програма Java зарежда изображение, тя може да покаже изображението, преди изображението да се зареди напълно. Потребителят вижда изображението, което се изобразява първо непълно, а след това постепенно все по-пълно, докато изображението се зарежда. Този инкрементен дисплей дава обратна връзка с потребителя (подобряване на възприеманата производителност) и позволява на програмата лесно да изпълнява други задачи, докато изображението се зарежда.

Що се отнася до анимацията, инкрементното показване на изображения може да бъде полезно за фонови изображения, но може да бъде много разсейващо, когато се използва за анимираните изображения. Ето защо понякога е желателно да изчакате, докато се зареди цялата анимация, преди да я покажете.

Можете да използвате MediaTrackerкласа на Джим Греъм, за да проследявате изтеглянето на изображения, като забавяте показването на анимацията, докато целият набор от изображения бъде напълно изтеглен. Example9Applet показва как да използваме MediaTrackerкласа за изтегляне на изображения за размахването на анимацията на Duke.

Тук е Example9Applet в действие, последван от списък с кодове.

Добавяне на звук

Лесно е да добавите звук към анимация. Можете да използвате getAudioClip()метода, за да получите AudioClip обект. По-късно можете да възпроизвеждате клипа или като непрекъснат цикъл, или като единичен звук. Пример 10 Аплетът показва как се възпроизвежда непрекъснат фонов звук, както и повтарящ се звук по време на анимацията.

Тук е пример 10Applet в действие, последван от списък с кодове.

Забележка:

Когато възпроизвеждате непрекъснат звук, не забравяйте да го спрете, когато потребителят напусне страницата (т.е. направете го по stop()метода на вашия аплет ).

Друга бележка:

Непрекъснатият звук може да бъде много досаден. Добра идея е да предоставите на потребителя начин да изключи звука, без да напуска страницата. Можете да предоставите бутон или просто да изключите звука, когато потребителят щракне в аплета.

Съвети за по-бързо зареждане на изображения

Изтеглянето на анимация, която използва много изображения, ще отнеме много време. Това се дължи главно на факта, че се прави нова HTTP връзка за всеки файл с изображение и осъществяването на връзка може да отнеме няколко секунди, дори когато има голяма честотна лента.

В този раздел ще ви разкажем за два формата на изображения, които вашият аплет може да използва, за да ускори изтеглянето на изображения.

Използване на лента за изображения

Можете да подобрите производителността на изтегляне, като използвате едно изображение, съдържащо няколко кадъра от анимация. Можете да изобразите единичен кадър от изображението, като използвате clipRect()оператора. По-долу е даден пример за лента с изображения, която се използва в аплета UnderConstruction.

Аплетът създава ефект на пробиване, като не изтрива предишните кадри. Фонът се изчиства само на всеки толкова често.

Ето UnderConstruction в действие, с връзка към изходния код.

Компресия между кадрите с помощта на Flic

Ако наистина искате да подобрите производителността на изтегляне на анимация, състояща се от множество кадри, тогава трябва да използвате някаква форма на междукадрово компресиране.

Инструменти за анимация

Към този момент (януари 1996 г.) са налични малко инструменти, които да ви помогнат да създавате Java анимации. Най-добрият инструмент, който успях да намеря, е лесният аниматор (TEA) на DimensionX (известен преди като JAM). Позволява ви да създавате анимации интерактивно. Бихме искали да насърчим разработчиците да напишат повече инструменти за създаване на анимации в Java.

Ако имате няколко готови изображения за показване, можете да използвате аплета Animator. Animator има много параметри, които ви позволяват да укажете непрекъснати звуци, специфични за кадъра звуци, индивидуално време и позиции на кадрите, начално изображение, подреждане на кадрите и т.н.

Трябва също да разгледате страницата Gamelan Animation, за да намерите много аплети, които използват анимация.

Заключение

Надявам се, че тази статия ще помогне на разработчиците на аплети да напишат повече и по-добри апликационни аплети. Също така се надявам скоро да станат достъпни по-добри инструменти.

Артър ван Хоф доскоро беше старши кадрови инженер в Sun Microsystems и участва в разработването на езика Java от 1993 г. Той е автор на първия Java компилатор, написан изцяло на Java. Наскоро напусна Sun, за да създаде нова компания заедно със Сами Шайо, Ким Полезе и Джонатан Пейн. Новата компания ще се фокусира върху изграждането на Java приложения. Кати Уолрат е техническа писателка в Sun Microsystems. Тя е част от екипа на Java от 1993 г. Понастоящем тя работи с Мери Кампионе по The Java Tutorial: Object-Oriented Programming for the Internet, подобрен на аплет урок за изучаване на езика Java, програмиране на аплети и Java GUI програмиране . Освен че е достъпен онлайн, Урокът за Java ще бъде публикуван и това лято като част от Java Series Addison-Wesley.

Тази история „Анимация в аплети за Java“ първоначално е публикувана от JavaWorld.