![]() |
|||||
| Процесори :: Дънни платки
:: Видео :: Мултимедия
:: Носители :: Периферия Комуникации :: Софтуер :: Технологии :: Links & Downloads :: Форум |
|||||
| Създаване на Java аплети | |||||
|
|
|||||
|
Java е създаден за програмиране в Интернет. Той е платформено-независим, което го прави идеален за програмиране в web. Независимостта му от платформата се осъществява чрез непълното му компилиране и последващо интерпретиране. Java се компилира до .class файл - този class файл в последсвие се интерпретира от някоя програма. Практически всеки съвременен браузър има вграден интерпретатор на Java, а ако няма - потребителя може да си свали и инсталира такъв. Тъй като Java е компилиран до едно междинно ниво, той е по-бързо изпълним от обикновените скриптови езици. Това, че Java се интерпретира, го прави неподходящ за създаване на самостоятелни приложения, тъй като те трябва да се разпространяват със съответния интерпретатор. Но Java не е и предназначен за създаване на самостоятелни приложение. Днес Java намира най-голямо приложение в писането на web-базирани игри и създаването на web-базиран чат. Какво е Java aплет. Аплет попринцип е много малко приложение. Java аплетите са програми, писани на Java и вградени в web-страниците. Тъй като Java аплетите се изпълняват на машината на клиента, не е необходимо специално разрешение от страна на сървъра за тяхното стартиране. Макар че Java e базиран на C++, създателите му са имали за цел да го опростят и са изключили множество мощни, но иначе сложни и объркващи функции. По този начин Java се превръща в лесен за научаване, платформено независим език за писане на приложения за web - Променливи Преди да бъде използване една променлива, тя трябва да бъде декларирана. Спазвайки стила на совя предшественик С++, в Java променливата се декларира като първо се задава типа на променливата, а след това името й. Променливата може да бъде инициализирана при създаването си; за разлика от С++ обаче, Java инициализира всяка неопределена променлива и й дава стойност 0. Следните типове данни се отнасят към простите типове данни: - byte - 8-bit На всеки от тези типове съответства определен клас, съществуват и класове като String, които нямат съответсващ прост тип. Класовете прибавят функционалност към простите типове данни, но те ще бъдат разгледани по-нататък - Коментарите Коментарите са неразделна част от един език за програмиране. Това са
части от кода, които се игнорират от компилатора (а също така и от интерпретатора,
ако езика се явява интерпретируем). Коментарите служат единствено за удобство
на програмистите - с тях се пояснява написания код. Те са особено полезни,
ако вашия код ще бъде редактиран от друг човек или ако самите вие по-късно
редактирате кода. В Java коментарите се означават с две наклонени черти
(//), които могат да бъдат поставени на произволно
място в реда. Всичко след тях се приема за коментар - Основни оператори Всеки език за програмиране поддържа основните аритметични и логически оператори. Java е запазил операторите от C++, така че ако знаете C++ изключително лесно ще научите Java. Основните оператори са: + събиране Последните три оператора обикновено се използват в условните оператори, за които ще научите след малко. Операторите "и" и "или" се поставят между две сравнения, наричани операнди (всеки оператор работи с една или повече операнди). && връща вярно, ако и двете операнди са вярни, || връща вярно ако която и да е операнда е вярна. Накрая ! обръща стойността на операндата - ако е вярна, то тя става невярна и обратното. - Условни оператори: Оператор IF Всеки език има някакъв оператор за условие. В езика Java съществува операторът IF, който има следния синтаксис: if(expression) Ако условието expression е изпълнено, изпълнява се първия блок от кода, в противен случай се изпълнява втория. Важно е да се запомни, че условието е в обикновени отваряща ( и затваряща ) скоби, докато блоковете - в отваряща { и затваряща } фигурни скоби. Частта else не е задължителна. Освен това в условието могат да се използват логическите оператори && (и) и || (или). Ако имате оператор if(val1>10 && val2>10) кода в blok1 ще се изпълни само ако и двете променливи - val1 и val2 са по-големи от 10. Ако в този код замените && с ||, кода ще се изпълни ако която и да е от двете променливи е по-голяма от десет. Избор между повече от два варианта Ако вашата програма трябва да избере между повече от един варианта, можете да използвате няколко вградени оператора if по този начин: if(expression1) Също така можете за по-ясен код да не използвате else, а да сложите няколко условия: if(expresion1){blok1} При този метод, програмата ще провери всички условия, дори и първото да е изпълнено. За да избегнете тези проблеми, Java ви предоставя оператора switch, който има следния синтаксис: switch(variable) Операторът break служи за излизане от оператора switch. Съответно ако е променливата variable има стойност value1 се изпълнява blok1, ако има стойност value2 - blok2 и т.н. Ако н нито една от стойностите не е вярна, изпълнява се блокът на default. За какво е нужен операторът break? Ако го няма, след като се достигне вярното условие, ще се изпълнят всички следващи блокове. Това понякога може да е полезно, ако искате да имате един и същи блок за определен обхват от стойности, или да изпълните определена част от кода - примерно да отпечатате само информацията за деня, за деня и седмицата, за деня, седмицата и месеца или за деня, седмицата, месеца или годината. За тази цел слагате най-отгоре отпечатването на информацията за годината, под него за месеца, на трето място за седмицата и накрая за деня и само при деня слагате break (иначе ще се изпълни и блокът default) - Циклични оператори Когато трябва да изпълните многократно един и същи код, дори и с малки промени, напомощтави идват циклите. Java, подобно на C++, от който произлиза, ви предоставя 3 цикъла, разгледани подробно по-нататък: = цикъл while Синтаксис: while(expression) Този цикъл се повтаря докато условието expression е изпълнено. Първо се извършва проверката - ако е изпълнено условието expression, изпълнява се код в следващия блок. Нещо в този код трябва да променя при определени условия expression за да се даде шанс на цикъла да завърши. За разлика от него, цикълът do-while първо изпълнява кода, след това проверява условието и ако то е изпълнено, отново се връща в началото на кода. Това ви гарантира, че кода ще бъде изпълнен поне веднъж, независимо условието изпълнено ли е преди началото на цикъла; при цикъл while може и никога да не изпълните блока, ако в началото условието не е изпълнено. Синтаксис: do = цикъл for Този цикъл ви дава по-усъвършенстван контрол над цикъла. Той ви предоставя инициализация на контролиращата променлива, условие контрол над условието в едно: for(start; expression; end;) Преди да започне цикъла се изпълнява инициализацията start. Обикновено
start се използва за присвояване на първоначална стойност на променливата,
в същия този момент тя може да бъде и декларирана. Операторът в start
се изпълнява само веднъж преди началото на цикъла. for(int i=0; i<10; i++) - Прекъсване на цикъл или прескачане на част от него Заедно с циклите, Java ви предоставя два оператора за по-добър контол над тях. Първия от тях е break. Този оператор незабавно прекратява цикъла, независимо от това дали е изпълнено условието. За разлика от него continue връща в началото на цикъла, като кодът след него просто се прескача. Когато ползвате оператора continue вие просто прескачате в края на цикъла, ако сте в цикъл for се изпълнява частта end. Условието се проверява отново и ако е изпълнено, цикълът продължава. Внимание! Никога не използвайте тези два оператора самостоятелно - използването на break ще обезмисли цикъла, защото той ще се изпълни само веднъж, докато операторът continue обесмисля следващия код, защото той никога няма да бъде изпълнен. Използвайте тези оператори в блока на условни оператори. Също така имайте предвид, че ако имате няколко вградени един в друг цикъла, тези оператори въздействат само на цикъла, в който са - те не водят до прекратяване на цикъла, в който се намира текущия. - Писане на функции Функциите са блокове от код, които могат да се извикат от всяко едно място в кода. Те са изключително полезни, когато един и същи код се повтаря на много места в кода. Възможността им да приемат аргументи и да връщат стойности ги прави изключително гъвкави. Те са удобни дори и когато кода във вашите програми не се повтаря - просто за да резделите дългите кодове на малки, логически блокове код. Както и променливата, всяка функция има тип - това е типа на върнатата от нея стойност. Ако функцията не връща стойност, то тя се декларира като void; в противен случай се декларира със типа на върната стойност. Валидни са всички типове на променливи, в това число и String. Дефиницията на функция представлява тип на функцията, име на функцията и приемани аргументи. След дефиницията следва тялото на функцията, затворено във фигурни скоби. Типична декларация на фунцкия е: void functionname(){ Това декларира функция, която не приема аргументи и не връща стойност. Извикаването на фунцкия става просто чрез исписването на нейното име: functionname(); Когато функцията трябва да върне стойност се използва ключовата дума return. В скобите след името на функцията се вписват всички приемани аргументи, заедно с техните типове. Пример за една такава функция е: float addnums(float num1, float num2){ Тази функция приема два аргумента - и двата от тип float, и връща техния сбор. Обърнете внимание, че променливите num1 и num2 са локални за функцията - достъпни само в нея, и съдържат подадените им аргументи - ако функцията е извикана с две променливи, то num1 и num2 ще съдържат техните стойности и промяната им няма да се отрази на оригиналните променливи. - Въведение в обектно ориентираното програмиране - обекти, свойства и методи Java е обектно-ориентиран език за програмиране. Ето защо е важно да се разберат някои от ключовите концепции на обектно-ориентираното програмиране. Обектът е екземпляр на клас; свойство се нарича всяка променлива на обект, а метод - всяка негова функция. Следователно, в основата на всичко стоят класовете. Класът е дефинирана от програмиста (а в случая от създателите на Java) структура от данни. Няма разлика между дефинираните от програмиста класове и вградените такива - те функционират от един и същи тип. Класът се изгражда от променливи, всяка от които със свой собствен тип, и фунцкции. Всяка функция или променлива може да се определи като public (общодостъпна - всички функции имат достъп до нея) и private (защитена - само функциите от този клас имат достъп до нея (private функция означава, че тя може да се извика само от друга функция в класа)) или protected (до променливата имат достъпъ само този клас и неговите наследници. За наследяването на класове ще стане дума по-късно). Декларацията на клас става чрез ключовата дума class, имато на класа и фигурни скоби около елементите му. Ето един пример: class CommunityMember { За да създадете обект, трябва да използвате ключовата дума new. Т.е.
ако искате да създадете обект от типа CommunityMember с име theFirstMember,
трябва да използвате кода: Защо е наложително да се използва командата new. new отделя необходимата памет за обекта (Java има автоматичен мениджмънт на паметта и няма явен начин да отделите или освободите памет). Тя инициализира всички променливи (в нашия случай Posts на 0 и Rank на 1, а също така извиква конструктора, за който ще научите след малко. Всъщност няма нужда да се инициализират променливи със стойност 0, тъй като Java приема всяка неинициализирана променлива като 0 или празен низ (когато става дума за символни типове) - Използване на конструктор Конструкторът е метод, който се извиква при създаването на обекта. Той е подходящ за инициализиране на променливите в класа. В нашия пример ние инициализираме в класа променливите Posts и Rank, но бихме искали при създаване на нов обект да определяме променливите Name и Family. Тъй като искаме всеки обект да има различни стойности за Name и Family, то не е подходящо да се инициализират в самия клас. Вместо това ни трябва инициализация при създаването на обекта. Конструкторът се декларира по абсолютно същия начин, както и останалите методи, с тази разлика че конструкторът не връща стойност и името му съвпада с това на класа. Нека допълним нашия пример с конструктор: class CommunityMember { Сега вече при създаването на нашия обект, можем да подадем три аргуманта и по този начин да инициализираме неговото име, фамилиа и възраст: CommunityMember DarkLight = new CommunityMember("Maxim", "Krizhanovsky", "17"); Достъпът до членовете на един клас - негови променливи или функции, се осъществява, като пред името на променливата се слага името на обекта и точка след него. Така непример за обекта DarkLight бихме могли да извикаме метода IcreasePost(): DarkLight.IncreasePost() - Инхериденция Инхериденцията (или наследяването) е едно от основните понятия в обектно-ориентираното програмиране, макар и в различните езици да е реализирана различно. Наследяването е метод да създадете нов клас разширявайки стария. Вместо да пренаписвате цялата дефиниция, вие дефинирате един клас като родител на друг; по този начин втория клас наследява всички свойства и методи на първия. Може да добавите нови свойства и методи, можете да отмените или промените старите, като ги дефинирате наново. Т.е. взимате готов клас и дефинирате разликите в двата класа. В Java се използва ключовата дума extends за да се отбележи наследяване. Нека вземем примера с CommunityMember и да направим под-клас CommunityModerator. Нашия нов клас ще има всички свойства и методи на предишния, но ще добави и свойството type, съдържащо информация дали съответния човек е глобален или локален модератор. class CommunityModerator extends CommunityMember{ Ако по-някаква причина трябва да се обърнете към родителския клас, можете да използвате ключовата дума super. Това обикновено се прави, когато предефинирате някой метод на класа (като декларирате метод на подкласа със същото име), който обаче повтаря кода от родителския метод и добавя нов. За да не пишете два пъти един и същи код, вие първо извиквате родителския метод, а после добавяте новия код. - Глобални методи и променливи Глобалните методи и променливи са такива членове на един клас, които са достъпни за всички негови обекти. Т.е. те не са локални за дадения обект. Глобалната променлива има една и съща стойност за всички обекти на класа, независимо че всеки обект може да има свой собствени стойности за всяка променлива. Една от причините да използвате глобални променливи е дефинирането на константи, като например числото "пи". По-добрата причина е използването на обща променлива, която може да бъде променена от всеки обект но има една и съща стойност за всички обекти. Дефинирането на метод или променлива като глобална става чрез използването на ключовата дума static пред името й. Ако искате наистина да декларирате константа, можете да добавите ключовата дума final пред името й. Една променлива може да е едновременно и глобална, и непроменива (пример: static final float pi = 3,14) - Програмите в Java След цялата тази информация, вие все още не знаете нищо за програмите в Java. Причината чак сега да разглеждаме самата програма е, че всяка програма в Java е клас. Т.е. написването на една програма означава написването на поне един клас. Изпълнението на програмата започва от фунцкията main() на класа, като тази функция има вида void main(String args[]). args[] е масив, съдържащ command-line параметрите, подадени на програмата. Тъй като тази статия не е за писането на програми, а на аплети, то тука няма да разглеждаме нито функцията main(), нито каквото и да е специфично за програмите. Все пак всеки аплет сам по себе си също е клас. Типичен код на аплет започва така: import java.awt.Graphics; Ключовата дума import се използва за включване на външни модули. По-този начин можем да използваме всички класове и функции, дефинирани в java.awtGraphics, java.awt.Font и java.awt.Color. При писането на аплети често се включват модулите от пакета java.awt - включването на външни модули в програмата спестява изключително много работа. - Работа масиви и низове В Java масивите са обекти. Макар обектите да ги разглеждаме в подробности
по-късно, тук ще покажем как се създава масив. На първо място, всеки обект
се създава чрез ключовата дума int. Масивите в Java се отбелязват чрез
слагането на [] след името на масива; достъпът до стойностите в масива
се осъществява чрез слагането на индекса на в квадратни скоби [] след
името на масива. Масивите имат две изключително полезни свойства: възможността
да се използва променлива като индекс на масива и възможността да се мине
през всички стойностти на масива чрез един единствен цикъл. Декларирането
на масива е обрътзано с декларирането на неговия тип и на количеството
стойности в масива. Ето два примера: Забележете че в Java всяка команда завършва с точка и запетая (;) Низовете се декларират по-лесно. Всъщност се декларират точно така, както и обикновените променливи. String s = "Моя първи низ" Java предоставя множество функции за манипулация на низове. Характерно за тях е, че те не работят директно с низа, който им е подаден, а връщат нов низ. Така че резултатат от всяка манипулация с низ трябва да бъде присвоен на низова проеменлива. Това не означава че не можете да присвоите резултата от манипулация с низова променлива на същата тази променлива. Можете да "събирате" низвое с оператора +. Така от два отделни низа правите един цял. Освен това, валиден е и оператора += - така прибавяте към дадена низова променлива друг низ. По-интересни обаче са методите на обекта String и следователно на всяка променлива от типа String. Метода length() връща дължината на низа. Например s.length() ще върне 13 - толкова символа се съдържат в "Моя първи низ". Метода substr() от своя страна. Метода valueOf e глобален за обекта String и следователно може да се прилага без променлива от тип String. Той взима за аргумент числена стойност (или числена променлива) и връща съответния низ. Ето един пример: int i = 13;
- Java в web - писане на аплети Аплетите са програми, създадени да се изпълняват каво част от web страница. Създаването на аплети е доста различно от създаването на програми. Тъй като аплетите се стартират от web страница, те нямат main() функция, а множество други функции, които ще разгледаме по-нататък. Освен това имат някои ограничения свързани със сигурността на компютъра на който се стартират. Тъй като са предназначени за разпространяване в web, обикновено са много по-малки от обикновена програма, за да се намали времето за сваляне. При аплетите няма нужда от интерпретатор - за тази цел служи браузъра. И отново поради разпростанението като част от web страница, аплета няма команден ред - при него съществуват други начини за подаване на параметри. Всички аплети са подкласове на класа Applet, който е част от пакета класове java.applet и съответно техния код трябва да почва с public class AppletName extends java.applet.Applet. За разлика от програмите, които започват изпълнението си от функцията main(), аплетите имат няколко различни метода, възникващи в отговор на определени събития. Това са: - init() - този метод се извиква само веднъж, когато аплета се страртира. Това е подходящ момент за инициализация на различни променливи и свойства на обекти. - paint() е част от всеки аплет, тъй като нищо не може да се покаже на екрана без него. Винаги, когато нещо има нужда да се покаже не екрана или картината да се обнови, се извиква метода paint(). Освен това, можете да извикате метода paint() чрез функцията repaint(). Метода paint() се извиква с един аргумент, който е от класа Graphics. Пример за този метод е следния код: - public class paint(Graphics screen) { - start() се изиква всеки път, когато аплета започне изпълнението си. Когато за първи път се стартира аплета, след метода init() се извиква start(). - stop() се извиква, когато аплета спре да се изпълнява. Това обикновено е при напускане на страницата, която го съдържа, но може да възникне и ако метода stop() се извика директно от програмата. Методите start() и stop() най-често се използват при създаване на анимация - destroy() възниква точно преди окончателното затваряне на аплета и се използва в случаи, когато нещо е било променено и трябва да се възтанови в оригиналния му вид. Винаги имайте предвид, пространството, в което е видима една променлива. Декларирайте необходимите ви променливи преди инциализация - всяка променлива, декларирана в инициализационната част е достъпна само по време на инициализацията - Основни функции Всичко което се изписва (или изрисува на екрана) се осъществява в метода onpaint(). Тъй като рисувате в прозореца на аплета, вие използвате специални функции за целта. Всички останали функции за обработка на данните (като се изключи приемането на параметри, описано в "Параметри") са същите, като при програма на Java (някои функции, като тези за работа с файлове, не са позволени заради спецификациите за сигурност на аплета). Тук са разгледани основните функции за определяне на фон, шрифт, цвят на изрисуване, изписване на текст. Специфичните функции са описани в малко по-нататък. Всички описани функции са методи на обекта Graphics; при извикване на метода onpaint() се подава параметър от този клас, обикновено с име screen. Именно screen ще използваме като обект от типа Graphics, подаден като параметър на метода. - Изписване на текст Една основна функция както при създаване на програма, така и при аплет, е изписването на текст. Аналогично на System.out.println() в аплетите се използва функцията screen.drawString(). Като параметри й се подават текста, който да се изпише, който може да е комбинация от низове и променливи, обединени чрез оператора +, х-позицията и у-позицията, от която да започе изписването на текста (спрямо горния ляв ъгъл на аплета). Съответно, синтаксиса е следния: screen.drawString(text, x, y); - Шрифтове Можете да определяте шрифта, с който да се изпише текста, с помощта на класа Font. За целта трябва да създадете нов обект от този клас. При създаването му определяте името на шрифта, стила (удебелен, курсивен или и двете) и неговия размер: Font currentFont = new Font(FontFace, Font.Style, Size); - FontFace е името на шрифта (трябва да е в кавички), Style може да бъде PLAIN(нормално), ITALIC (курсивно) или BOLD (удебелено), а Size е размера на шрифта в pt. За да създадете шрифт, който да е удебелен и курсивен, използвайте декларацията Font.ITALIC+Font.BOLD След като сте създали обект, съдържащ информацията за шрифта, вие трябва да определите текущия шрифт, с който обекта screen да изписва текста. Това става чрез функцията screen.setFont() на която се подава единствен параметър - обект от типа Font. - Определяне на цветовете Вие можете да смените цвета на фона и цвета за изрисуване. Цвета за изрисуване определя както цвета на буквите, така и на линиите и на запълване (виж "Графики"). За целта се позлват функциите: screen.setColor() определя цвета за изрисуване screen.SetBackground() определя цвета на фона И на двете функции се подава обект от типа Color. Вие можете да използвате следните константи: Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink, Color.red, Color.white и Color.yellow. Ако искате да използвате друг цвят, трябва да създадете нов обект от класа Color и да му подадете RGB кода на цвета под формата на 3 аргумента - всеки отговарящ на стойността на една от трите компоненти: Color yourcol = new(red, green, blue); Съгласно спецификацията на RGB всеки от параметрите red, green и blue, може да приеме стойност от 0 до 255 - Графики Всичко, което се показва на екрана, се изрисува в метода onpaint(). На този метод му се подава параметър от класа Graphics, обикновено името на обекта от този тип е screen. В примерите по-нататък именно screen ще се използва като обект от типа Graphics, подаден на метода onpaint() - Изрисуване на линия Всички линии са с дебелина 1 пиксел. Изрисуването на линия става чрез функцията screen.drawLine(x1,y1,x2,y2); където х1 и у1 са координатите на началната точка на линията, а x2 и у2 - на крайната. Функцията изрисува линия между двете точки в текущия цвят - Изрисуване на правоъгълници Правоъгълниците могат да бъдат запълнени или незапълнени и техните краища могат да бъдат заоблени или да не бъдат. Следващите функции осигуряват тези възможности: - screen.drawRect(x1,y1,width,height); създава незапълнен правоъгълник с горен ляв ъгъл с координати х1 и у1, ширина width и височина height. Цвета на рамката е текущия цвят за изрисуване - screen.fillRect(x1,y1,width,height); е аналогична на предишната функция, с тази разлика че целия правоъгълник, а не само рамката, е в текущия цвят - screen.drawRoundRect(x1,y1,width,height,h1,v1) създава правоъгълник със заоблени краища, координатите на горния ляв ъгъл на който са х1,у1, ширината - width, височината - height, заоблянето по хоризонтала започва на h1 пиксела от ъгъла, а по вертикала - на v1 пиксела. Цвета на рамката е текущия цвят на изрисуване - screen.fillRoundRect(x1,y1,width,height,h1,v1) е аналогична на предишната но запълва целия правъгълник в текущия цвят - Изрисуване на овали Кръговете и овалите се създават с една и съща функция (кръга е частен случай на овала). Те също могат да бъдат запълнени или незапълнени. Това се осъществява чрез двете функции: - screen.drawOval(x1,y1,height,width) създава овал, като x1 и y1 са най-малките стойности на x и y координатите на точките, които съставят овала (респективно горния ляв ъгъл на правоъгълника, ако овала беше ограден от такъв) със ширина width и височина height (aко те са равни, получава се кръг). Цвета на рамката е текущия цвят на запълване - screen.fillOval(x1,y1,height,width) е аналогична на drawOval, с тази разлика, че целия овал е оцветен в цвета на запълване - Изрисуване на полигони Овалите и правоъгълниците са лесни и удобни за използване, но са много ограничени. Java ви предоставя възможността да рисувате полигони. За създаване на полигон, всички х-координати на точките се слагат в масив, съответните им у-координати - в друг масив. След това се извиква функцията - screen.drawPolygon(xPoints, yPoints, points); където xPoints е масив, съдържащ х-координатите, yPoints - масив, съдържащ y-координатите, а points - броя на елементите във всеки масив. Тази функция само очертава многоъгълник с цвета на запълване - screen.fillPolygon(xPoints, yPoints, points); е аналогична на предишната, но не само рамката, а и целия многоъгълник се запълва в цвета на запълване. При тези две функции, първата точка се свързва с втората, втората - с третата и т.н., а последнта - с първата. Можете да изрисувате произволна линия, като използвате функция за полигон, която обаче не свързва последната линия с първата. Функцията е: - screen.drawPolyline(xPoints, yPoints, points); - аргументите са същите като за изрисуването на многоъгълник. Линията е с текущия цвят - Копиране и изтриване Java предоставя и функции за копиране и изтриване на част от аплета. - screen.copyArea(x,y,width,height,newX, newY) копира правоъгълник с горен ляв ъгъл с координати x,y, със широчина x и височина y. Параметрите newX и newY указват координатите, на които да се сложи копираното изображение - screen.clearRect(x,y,width,height) оцветява правоъгълник с координати на горния ляв ъгъл x,y, ширина width и височина height с текущия фонов цвят Накрая, функциите size().width и size().height връщат ширината и височината на прозореца на аплета - Изображения Освен да изрисувате полигони, можете да използвате в аплетите си готови JPEG или GIF изображения. За целта трябва да направите две неща - да заредите изображението от външен файл и да го изрисувате в аплета. Зареждането на изображение става чрез функцията getImage() която приема за аргумент адреса и връща изображението. Следователно ще имате нужда и от променлива от тип Image в която да се съхранява изображението. Синтаксиса на функцията е: Image getImage(URL url, String name) Ако за url определите пълния адрес на изображението, включително името на файла, можете да не използвате втори аргумет. От друга страна, можете да определите само част от URL, за поддиректорията и името на файла да подадете като аргумент name. Това е изключително полезно, защото стаднартния функцията се използва по следния начин: Image img = getImage(getCodeBase(), "directory/filename") Функцията getCodeBase() връща директорията, в която се съдържа аплета. Ако изображението е поддиректория на тази директория, задавате тази под директория като direcory. Разбира се, ако зареждате изображение от адрес, различен от този на вашия аплет, ще искате да определите целия URL Втората стъпка е изрисуването на изображението. Това става в метода paint() на аплета чрез функцията drawImage(), коиято има четири задължителни и няколко оптионални аргумента. Задължителните параметри са изображението, X координатата, Y координатата и типа на файла. Съответно стандартния вид на функцията е: screen.drawImage(img, 10, 10, this) В случая изображението се поставя на координати (10,10) но можете да използвате произволни числа или променливи. Вида на функцията е boolean drawImage(Image img, int x, int y, [int width], [int height], [Color bgColor], ImageObserver observer) за observer използвайте this Ако включите параметрите width и height вие ще определите размера на изображението, а самото изображение ще бъде мащабирано за да запълни този размер. Добавянето на bgColor ще определи фонов цвят, с който ще бъдат запълнени всички прозрачни области от изображението - Интерфейс С Java можете да създавате графичен интерфейс, който включва бутони, полета за отмека, текстови полета, диалогови прозорци, падащи менюта и други. Този графичен интерфейс можете да използвате в свойте аплети без да нарушавате тяхната платформена независимост. За да изпозлвате някой от тези компоненти, вие трябва да създадете обект и да използвате метода add() за да го добавите към аплета. Например създаването на бутон с надпис "Panic" става чтез следния код: import java.awt.*; public void init() { Този код показва, че за създаването на бутон, трябва да създадете обект от типа Button и като аргумент да подаде заглавието на бутона. - Етикети и текстови полета Етикета (Label) е компонент, изобразяващ текст, който не може да бъде променен от потребителя. Този компонент е получил името си от използването им за слагане на обозначаващ етикет на другите компоненти. Често се използва за да индентифицира текстово поле. Текстовото поле (TextFiels) е поле, в което потребителя може да въведе един ред текст. Можеш да определиш вирината на полето, когато го създаваш. Създаването на етикет става чрез командата: Label labelname = new Label("Text to be displayed", Label.ALIGNMENT); labelname съответно е името на създадения обект и може да бъде произволно име, "Text to be displayed" определя текста, който съдъража етикета, а Label.ALIGNMENT определя подравняването на този текст. Съответно вместо ALIGMENT трябва да използвате едно от трите подравнявания - LEFT, RIGHT или CENTER. При създаването на текстово поле се определя само ширината му в знаци: TextFiels fieldname = new TextField(symbols); TextField fieldname - new TextFiels("default", symbols); - Полета за отметка Полето за отметка е поле до ред от текст, което може да бъде избрано или не от потребителя. Съществуват два типа поле за отметка: единия е известен като радиобутон и при него само един от бутоните в групата може да бъде избран. Другия, известен като поле за отметка, е независим от останалите полета и всяко поле може да бъде или избрано, или не. Първо ще разгледаме втория тип полета за отметка. Създават се с командата: Checkbox boxname = new Checkbox(caption) caption определя текста който да се изпише до полето. Командата създава поле, което не е отбелязано. Ако искате първоначално да има отметка в полето, трябва да подадете втори аргумент - true За да създадете радиобутон, първо трябва да създадете група, в която той действа. Само един от радиобутоните в групата може да бъде избран, но можете да имате няколко групи радиобутони. След като създадете групата, създавате всеки един радиобутон по отделно, като давате три аргумента - заглавие, състояние (true за отбелязан и falsе за неотбелязан - не забравяйте, че само един от бутоните може да бъде отбелязан) и име на групата, към което той принадлежи. Кода има следния вид: CheckboxGroup group1 = new CheckboxGroup();
Обърнете внимание, че самата група не се прибавя към аплета. - Падащ списък Компонентът Choise представлява падащ списък с възможности, от които само една може да бъде избрана. Той е по-особен заради това, че първо трябва да създадете списъка, а след това елеметите му. Кода има следния вид: Choise listname = new Choise(); При използването на функцията add на компонента Choise се подава аргумент, съдържащ тектът, който се показва за съответния елемент - Многоредово текстово поле Java ви предоставя компонента TextArea, който е тектово поле, позволяващо на потребителя да въведе повече от един ред текст. Можете да определите височината и ширината на полето . Например следния код определя текстово поле с име comments, без текст по подразбиране, със височина 10 реда и ширина 50 символа: TextArea comments = new TextArea("", 10, 50); - Панел Посления от компонентите, които предоставя Java, е Panel. Този обект само по себе си не показва нищо; той се използва като контейнер на други обекти. Целта на панела е да раздели видимата област на няколко групи компоненти. Създаването на панела е съвсем просто: Panel panlname = new Panel() - Параметри Тъй като аплетите се стартират от web страница, не е възможно да се подават параметри от командния ред. За сметка на това, такива параметри могат да се подават от самата web страница чрез специални HTML тагове - <PARAM>. Подаването на параметри от този тип прави аплета по-функционален, тъй като не се налага промяна на кода и прекомпилация, за да се извърши някаква промяна, и един и същи аплет може да се стартира по множество различни начини. За да се създадат параметри, между таговете <Applet> и </Applet> се поставят съответните тагове <PARAM>. Всеки един от тези тагове има два атрибута - NAME, определящ името на параметъра, и VALUE, определящ неговата стойност. Могат да се ползват произволно количество тагове <PARAM> След като са подадени параметрите по този начин, те трябва да бъдат прочетени от аплета. Това обикновено се осъществява в инициализиращата част. Функцията getParameter() взима за аргумент името на параметър и връща съответната стойност. Ако в HTML кода на web страницата не бъде намерен параметър с това име, функцията връща null Трябва да се отбележи, че всички параметри се подават като String, независимо от съдържанието си. В инициализиращата част трябва да се включи и прехвърлянето им в съотвения тип данни - Звук Във вашите аплети вие можете да използвате и звук. За съжаление, Java поддържа само формата AU, който не е никак качествен. От друга страна е подходящ за WEB, защото качествения звук изисква време за да бъде прехвърлен. Java предоставя класа AudioClip за работа със звук. Преди да можете да направите каквото и да било, трябва да създадете звуков обект и да заредите звука от външен файл. Това става чрез функцията getAudioClip(): AudioClip getAudioClip(URL url, String name) Тази функция взима адреса на файл и връща звука, съответно тя трябва да бъде присвоена към променлива от типа AudioClip. Можете да определите url като пълен URL,включващ името на файла, като при това втория аргумент става излишен. Можете също така да определите url като част от URL, например използвайки функцията getCodeBase за получаване на адреса, съдържащ аплета, и да опредеите поддиректорията и името на файла в name. Клипа AudioClip предоставя и функциите play(), loop() и stop(). Първия от тези методи предизвиква изпълнението на звуковия файл, а последния предизвиква неговото спиране. Методът loop() предизвиква безкрайното му изпълнение - когато свърши, започва отначало, докато не бъде извикат методът loop() Ако сте останали с впечатлението, че трябва да използвате обект от типа AudioClip за да изпълнявате звуци, грешите. Истината е, че имате нужда от AudioClip обект за безкрайно повторение на звук. За обикновен звук, можете да използвате метода play() като му подадете като аргумент адреса на звуковия файл. Отново можете да зададете пълния адрес или да го разделите на две части: void play(URL url, String name) - Обработка на събития Какво е програмата, която не реагира на действията на потребителя? Същото,
което и аплет, който не го прави. Ако искате просто да представите красива
анимация във вашата програма, или нещо независимо от потребителя, то на
вас никога няма да ви се наложи да обработвате събитията. Но ако искате
да създадете меню, в което бутоните се променят когато мишката е над тях,
или пък искате да напишете игра (което е най-често срещаното приложение
на Java), то вие трябва да минете през цялата обработка на събития. За
да можете да обработвате събития, първо трябва да включите пакета java.awt.Event
използвайки командата import: При всяко събитие в аплета се извиква съответстващата функция. Ако такава функция не е дефинирана, то събитието не се обработва. Тук ще разгледаме отделните функции и аргументите които и се предават. Общо за тези функции е, че те връщат стойност от тип boolean и един от приеманите аргументи е от тип Event. Обектът Event ще бъде разгледан малко по-нататък. Интересното при Java е, че всяко събитие се обработва от метода handleEvent(), който от своя страна извиква метода за конкретното събитие. Можете да използвате този метод за да създадете своя система за обработка на събития; обикновено обаче това не е необходимо и вие ползвате готовите методи за всяко едно събитие, които се извикват по подразбиране от handleEvent. Все пак, ако някога решите да пишете свой код за метода handleEvent, трябва да знаете, че на метода се подава един единствен аргумент от тип Event. Свойството id на обекта Event съдържа възникналото събутон. Всяко събитие, което обработва Java, има своя стойност за id. Възможните стойности са: - KEY_PRESS възниква (както споменахме преди малко) при натискането на
клавиш от клавиатурата Събитията се делят на два основни типа: събития на клавиатурата и събития на мишката. Всяко натискане на клавиш от клавиатурата предизвиква събитието KEY_PRESS. Самото натискане на клавиш обаче се разделя на две отделни действия - натикането на клавиша и неговото отпускане, които от своя страна предизвикват различни методи: keyDown() и keyUp(). Методите приемат по два аргумента - един тип Event и един тип int, като втория съдържа кода на натиснатия клавиш. Стандартната декларация на методите изглежда така: public boolean keyDown(Event evt, int key){
... } Обектът Event съдържа констати за някои от най-често използваните клавиши и вие можете да ползвате тях, вместо съответните кодове. Това са: - Event.UP Ако имате нужда от повече информация за натиснатото копче, можете да
използвате методите на обекта Event (както отбелязахме, на всички методи
за обработка на събития се подава аргумент от тип Event). Във примерите
по-нататък ще използваме декларацията на методи с параметър Event evt
и съответно evt като подаден параметър тип Event Събитията на мишката са значително повече на брой. Отново съществува събитие за натискане на бутона на мишката, което се дели на натискане и на отпускане на бутона. Освен това съществуват събития на преместване на мишката, преместване на мишката над аплета и преместване на мишката извън аплета. Съществува и събитие на изтегляне, което възниква когато мишката бъде преместена докато бутона й все още е натиснат. Всички методи за обработка на събития на мишката връщат стойност тип boolean и приемат три аргумента: един от тип Event и два от тип int. Аргументите тип int съдържат съответно X и Y координатата на курсора. Стандартна декларация на метод за обработка на събитие на мишката изглежда така: public boolean mouseUp(Event evt, int x, int y) mouseUp() е метода, извикван при отпускане на бутона на мишката, mouseDown() - при натискането на бутона, mouseMove() при преместване на курсора, mouseEnter() - когато курсора се появи над аплета, mouseExit() - когато го напусне и mouseDrag(), когато курсора се премести докато бутона на мишката е натиснат. Така вие можете да обработвате събития, възникнали за аплета. Какво се случва с отделните обекти? В частност, създаването на графичен интерфейс, който не отговаря на събития, е безсмислено. За какво е бутон, който нищо не прави? Събитията в елементите от графичния интерфейс извикват един специален метод: action. Този метод отново връща стойност тип boolean и приема два аргумента: единия тип Event, а другия Object. Както сигурно се досещате, те съдържат информация за обекта и възникналото събитие. Стандартна декларация на този метод изглежда така: public boolean action(Event evt, Object arg)
{ Как се използва втория аргумент? Бутоните подават като аргумент своето заглавие, полетата за отметка (както и радиобутоните) предизвикват събитие, когато са избрани и винаги подават аргумент true; падащите списъци извикват метода, когато е избран елемент и подават като аргумент избрания елемент; текстовите полета извикват метода, когато потребителя натисне Enter, докато полето е активно и подават въведения текст. Обърнете внимание, че метода не се извиква, ако полето просто загуби фокус, само бутона Enter може да извика метода. Така че първото нещо, което трябва да разберете, е кои обект е извикал метода. В Java съществува оператора instaceof, който връща true, ако обектът е екземпляр на определен клас и false, ако не е. Обектът Event има свойството target, съфържащо обекта, който е извикал метода. Ако декларацията на метода е като по-горе, то може да използвате кода: if (evt.target instanceof TextField){ > Анимация Съществуват два различни типа анимация: - базираната на кадри и базирана на спрайтове. Втория тип се използва при писането на игри. Разликата е очевидна: при базираната на кадри анимация потребителя гледа кадър след кадър и не може да промени нищо в анимацията. Това е добре за анимационни филмчета. В базираната на спрайтове анимация съществува фон и отделни графични обекти. Графичните обекти се движат и често тяхното движение зависи от действията на потребителя. Спрайтовете са една от основните техники, изпозлвани при писане на игри, независимо от ползвания език, тъй като главния герой трябва да се движи както поиска потребителя и останалите обекти реагират на неговите действия. Освен това понякога е по-рационално да се прави анимация, базирана на спрайтове, независимо от това, че потребителя няма никакъв контрол над нея. Ще разгледаме последователно анимацията на основата на кадри и тази, на освновата на спрайтове. Но преди това нека разгледаме процесите (threads). Java позволява създаването на многопроцесни приложения. За да използваме анимация, основаваща се на смяната на кадри, ние трябва да създадем процес. Затова ще разгледаме един клас, предоставен от Java, носещ името Thread и предоставящ необходимата ни функционалност. Процеса ни е необходим при създаваме на анимация кадър по кадър поради една много проста причина. Имаме нужда от код, който да се изпълнява непрекъснато и да определя новия кадър. В същото време трябва непрекъснато да изрисуваме новия кадър. Ако просто напишем един безкраен цикъл, в който се изрисуват кадри и се извиква метода repaint(), то нищо няма да се изрисува. Причината е, че цикълът е безкраен и аплета никога не излиза от него. В същото време цикъла взима всички ресурси и аплета не може да изпълни друг метод, докато не завърши цикъла. Затова трябва двете неща да протичат едновременно, а това се осъществява чрез дефинирането на процес. За да го изполваме, трябва да включим обаче интерфейса Runnable. Интерфейса представлява абстрактен клас; неговите свойства и методи не се прилагат на обекти, но са достъпни за всички обекти, независимо от техния клас.Включването на интерфейс става чрез ключовата дума implements в декларацията на класа. Така че вместо public class AppletName extends java.applet.Applet
{ ще имаме дефиницията public class AppletName extends java.applet.Applet
implements Runnable { За да създадем аплет, ползващ процес, трябва да добавим интерфейса Runnable, да създадем обект Thread, да модифицираме метода start() на аплета и да добавим метод Run(). Вече добавихме интерфейса; създаването на обект Thread по нищо не се отличава от създаването на който и да е обект: Thread myAnimation; Кръстих процеса myAnimation защото тука ще използваме процесите за създаване на анимация. В метода start() на аплета трябва да инициализираме обекта (т.е. да създадем обект чрез командата new(), и да стартираме процеса: public void start() { Причината, да не стартираме процеса при инициализация е, че потребителя може да напусне страницата и после пак да се върне; или може по някаква причина временно да спрем процеса. Инициализацията се извършва само веднъж, докато стартирането може да е многократно. Именно поради възможността аплета да бъде спрян поради някакви причини, ние трябва да спрем процеса, за да не отнема от системните ресурси, когато и без това не е необходим. Това се осъществява в метода stop() на аплета: public void stop() { Присвоявайки на myAnimation стойност null, ние не само спираме процеса,
но и унищожаваме обекта, което освобождава заеманата от него памет. Често е по-добре да използваме пауза в процеса. Тъй като в компютъра най-малката единица за време е милисекунда, а на нас не ни трябва да обновяваме екрана всяка милисекунда, то оставяйки процеса да върви непрекъснато, ние ненужно ще натоварим процесора. Вместо това, задавайки пауза от 1000 милисекнди, ще получим обновяване на екрана всяка секунда (подходящо за създаване на часовник за web сайт), а използвайки пауза от 50 милисекунди, ще получим 20 кадъра в секунда. Налагането на пауза се осъществява чрез метода Thread.sleep(pause), където pause е количеството милисекунди, за които трябва да е спрян процеса. Добра практика е този метод да се извика в блок try .. catch за да не се предизвикват грешки... времето на изпълнение на процеса зависи от това доколко е натоварен процесора и понякога паузата може да бъде прескочена. Така че накрая на метода run() винаги можете да добавите кода: try { Thread.sleep(pause); } Казаното дотук далеч не е всичко за процесите. В частност, тук не е разгледано използването на повече от един процес и съответно синхронизация на процесите. Все пак материалът е за анимацията, не за процесите Какво представлява анимацията, базирана на изображения? Това са изображения, които се сменят достатъчно бързо (във филмите обикновено скоростта е 24 кадъра в секунда), за да се възприемат като движение. Т.е. най-простия тип анимация, която можете да създадете, е чрез последователно сменяне на изображения. За да създадете такава анимация, трябва само да създадете масив от тип Image, да заредите в него всички изображения и да преминавате през него в един безкраен цикъл... По-точно в безкрайния цикъл ще контролирате текущия индекс на масива, а в метода pain() ще изрисуваме изображението, отговарящо на текущия индекс. Нека приемем, че трябва да направим анимирано лого за компанията; то се сътои от 40 отделни изображения. Ще сменяме по 10 изображения в секунда (често при създаване на прости анимации се използват скорости между 10 и 15 кадъра в секунда - за да върви анимациата по-бавно. Същия ефект може да се постигне и като се покаже всяко изображение два пъти, но това само излишно ще натовари аплета). Следователно имаме нужда от масив с 40 елемента и пауза от 100 милисекунди. Кода за нашия аплет ще изглежда така: import java.awt.*; public class Animate extends java.applet.Applet implements Runnable { Image[] picture = new Image[40]; public void init() { public void paint(Graphics screen) { public void start() { public void run() { public void stop() { В този аплет използваме 40 .JPG изображения, всяко от които се нарича "image" и съдържа пореден номер (image1.jpg, image2.jpg и т.н.). Ако компилирате и заредите този код в web-браузър, ще забележите обаче един страничен ефект: премигване на изображението. Това е заради метода repain(). Ако смятате, че той извиква метода pain() грешите. repaint() извиква един друг метод на аплета - update(), който изчиства екрана и след това извиква paint(). От своя страна изчистването се осъществява чрез изрисуване на правоъгълник с цвета на фона и размерите на аплета. За да избегнете премигването, трябва да пренапишете метода update(), така че да не изчиства екрана или да изчиства само тази част, която се е променила. Цялостното премахване на изчистването на екрана е полезно, когато имате изображение, което се сменя, но не променя местоположението си, или когато новото изображение напълно покрива старото. Но ако във вашата анимация имате едно изображение, което променя своето местоположение в прозореца на аплета, изчистването на екрана е задължително. Тук ще разгледама и двата метода за избягване на премигването. За да премахнем изцяло изчистването на екрана, трябва просто да създадете метод update, който извиква метода paint(): public void update(Graphics screen) { Но ако трябва да създадете движещо се изображение върху постоянен фон, то вие не можете да избегнете изчистването на екрана. В противен случай предишните позиции на обекта ще останат видими и няма да получите ефект на движение. Тука идва една друга технология, наречена clipping. Това е процес, при който се определя коя част от изображението да се изрисува и коя - не. Това е технология, която често се използва в 3D графиката за определяне на това кое да се изрисува - тъй като изрисуването на цялата сцена може да отнеме твърде много време, а голяма част от нея ще остане невидима. Java предоставя метод на обекта Graphics за определяне на правоъгълен участък, който да бъде изрисуван. Метода е clipRect() и приема четири аргумента: X и Y координатите на горния ляв ъгъл, ширината и височината на правоъгълника. Определяйки по този начин област за изрисуване, дори при извикването на метода pain() няма да бъде изрисуван целия аплет, а само част от него. Това силно намалява премигването и често може да го сведе до незначителен, невидим за човешкото око ефект. Пространството за извисуване трябва да включва обекта и неговото предишно местоположение; за това трябва да поддържате две променливи, следящи координатите на обекта; а също така две променливи, съдържащи предишните координати, а също така две, съдържащи височината и ширината на обекта. Определянето на пространство за изрисуване се осъществява в мето update() - непосредствено след това се извиква paint(). Ако наречем променливите, съдържащи координатите на обекта objX и objY, а тези, съдържащи предишните им координати objXprev и objYprev, съответно objWidth и objHeight - за ширина и височина, то кода за метода update() ще бъде: public void update(Graphics screen) { Съществува и технология, наречена double buffering, или използване на двоен буфер. Какво предтсавлява тя и какво ни дава? Не мога да кажа, че двойния буфер е пряко реализиран в Java, но се осъществява лесно и работи добре. Двоен буфер означава да изрисуваме кадъра на невидим екран и след това да копираме съдържанието му на видимия. Това е особено полезно, когато е необходимо изветсно време за изрисуване на изображението. Без използването на двоен буфер е възможно да се изрисува първо една част от изображенито, която да стане видима за потребителя, после друга. Така потребителя ще може да следи в реално време изрисуването на вашето изображение. Но ако рисувате върху друго изображение, без да изчиствате екрана, ефектът ще е ужасен. Тъй като цялата работа по изрисуването се осъществява чрез обект Graphics
(който се подава като параметър на screen), можем да създадем втори, невидим
екран като просто създадем втори обект Graphics. В метода init() на аплета,
създаваме обект Graphics (ще го нарека offscreen), а в метода pain(),
вместо върху подадения като параметър обект (в нашите примери това е Graphics
screen;) изрисувате върху offscreen. За да осъществите двойния буфер обаче,
трябва да асоциирате изображение с offscreen, в което да се запазва всичко
изрисуване - т.е. то ще е вашия невидим екран. След като приключите с
изрисуването, всичко което трябва да направите е да изрисувате изображението,
асоциирано с offscreen. Image workspace; След това при инициализация на аплета (в неговия метод init() )създаваме изображение с размери равни на размерите на аплета и го асоциираме с offscreen: workspace = createImage(size().width, size().height); Накрая, в метода paint() вместо да използваме
screen за изрисуване, използваме offscreen, като накрая добавяме кода: - Спрайтове Спрайтовете са технология, която често се използва в игрите, но е полезна и при създаване на анимационни филмчета. Спрайтът е графичен обект, който може да се движи независимо от фона. Всъщност той се движи независимо от всички останали обекти. Така вместо да изрисувате кадър след кадър фона и всички обекти върху него, вие създавате спрайт за всеки движещ се обект и оставяте фона непроменен. Причината да се ползват спрайтове всъщност е, че можете лесно да промените анимацията според определени параметри - най-често действията на потребителя. За да осъществите това, Java предоставя специален клас, наречен Sprite. Когато се използват спрайтове е важно да се разбере един елемент: прозрачността (transparency). Изображението винаги е правоъгълно, макар и обектът да може да има произволна форма. Тъй като обектът често не е правоъгълен, то около него винаги остава празно пространство. Ако фона е едноцветен, проблемът се решава лесно - запълвате празното пространство със същия цвят. Но ако не е? Бихте могли да запълните празното пространство със фона - така то няма да се вижда. Но какво става, когато обектът се движи? Трябва да запълвате празното пространство във всеки един кадър с различен фон. Затова в спрайтовете много често се използва цвят на прозрачност - това е цвят, който се приема за прозрачен и всички пиксели в този цвят съответно също са прозрачни. Когато имате прозрачен цвят, то цвета на съответния пиксел се заменя с цвета на съответната точка във фона и така се постига необходимия ефект. Java поддържа прозрачните цветове, също така както те се поддържат и от формата GIF. Друг важен елемент е подреждането на обектите в дълбочина - по оста Z. Така се определя кои спрайтове покриват другите, ако те се застъпват. Особено важен в игрите, но често използван в анимацията, е така наречения Collision Detection - определяне кога два спрайта се сблъскали. Това е и едно от най-трудните неща при писането на игри. Най-простия тип Collision Detection е използването на правоъгълниците, които ограждат спрайта (както вече споменахме всяко изображение всъщност е правоъгълнало) и проверяване дали част от тях не се застъпват. Но когато изображенията не са правоъгълни, този метод не винаги върши работа. Ако се използва, то ще се предизвиква сблъсък дори когато прозрачните области около спрайтовете, които не би трябвало да са част от тях, се докоснат. Всички тези елементи ще бъдат разгледани подробно по-нататък. Основните класове за създаване на анимация, базирана на спрайтове, предоставени от Java, са Sprite и SpriteVector. Макар да определихме спрайтът просто като графичен елемент, самия спрайт може да представлява базирана на кадри анимация. Всъщност не рядко се използват анимирани спрайтове, особено в игрите. Класът Sprite предоставя поддръжка на анимирани спрайтове под формата на масив, съдържащ изображенията и няколко метода за определяне на текущия кадър. Този клас осигурява поддръжката и на някои други, важни за спрайтовете елементи: позиция, скорост на движение, z-order, пространството, в което спрайтът може да се движи (обикновено това е прозореца на аплета, т.е. неговата площ). Класът Sprite има множество свойства, които съдържат неговите характеристики. Като за начало ще разгледаме една група константи, които дефинират действията при достигане на границата на пространството, в което може да се движи спрайта. Всяка от тях има целочислена стойност, както и буквено означение. Това са BA_STOP (0 - спира на границата), BA_WRAP (1 - преминава от другата страна), BA_BOUNCE (2 - тръгва в противоположната посока) и BA_DIE (3 - спрайтът се унищожава). Масивът image, съдържащ стойноти от тип Image, съдържа всички изображения, ползвани от спрайта. frame съдржа текущия кадър, frameInc определя с колко да нараства frame (това е полезно за да пуснете анимация отзад напред, като зададете стойност -1. Но няма причина, поради която да искате да ползвате стойност, различна от 1 или -1), а FrameDelay - времето между смяната на два последователни кадъра. collision е обект тип Restengular и ви позволява да ползвате най-простия тип collision detection - този, в който се използва правоъгълната рамка около обекта. Булевата свойство hidden определя дали спрайтът е невидим. Свойството position, съдържащ поцицията на спрайта, също е тип Restangle - то съдържа позицията на горния ляв ъгъл на изображението (което е и позицията на спрайта), както и неговата височина и ширина. От своя страна velocity, което определя скоростта на движение на спрайта, е тип Point. Това е така, тъй като скоростта има както големина, така и посока, и е невъзможно да бъде определена с една единствена стойност. Друго свойство на класа Sprite е zOrder, съдържащ неговата z-координата. Колкото по-голяма е стойността на zOrder, толкова по-близо до екрана е спрайтът. Харатерно за класа Sprite е наличието на два конструктора. Първия от тях има вида: public Sprite(Component comp, Image img, Point
pos, Point vel, int z, int ba) { Както можете да видите, той приема като аргументи изображение img, което да се използва за спрайта, позиция pos, скорост vel, z-координатата и стойността за bound action. Втория конструктор се използва за създаване на анимиран спрайт и съответно приема малко повече аргументи: public Sprite(Component comp, Image[] img,
int f, int fi, int fd, Point pos, Point vel, int z, int ba) { Първата разлика е, че img в случая е масив от изображения. f е текущия кадър или текущия индекс на масива; fi е стойността на frameInc, fd - на frameDelay. И в двата случая, за comp се използва ключовата дума this. Константите са декларирани като public; всички свойства обаче са декларирани като protected. Това означава че не можете да променята директно свойствата на обекта. За сметка на това, класът Sprite има множество методи, предоставящи ви възможността да манипулирате неговите свойства. Най-простите от тях са getVelocity(), който връща стойността на свойството velocity, и setVelocity(), който приема като аргумент стойност от тип Point и променя velocity на подадената му стойност. Методът incFrame() се грижи за смяната на кадрите - когато го извикате, той променя текущия кадър, като следи след последния да премине първия (или ако пуснете анимациата отзад напред - след първия да премина на последния). Този метод следи и спазването на времето между два последователни кадъра - ако го извиквате по-често, от колкото трябва да бъдат сменяни кадрите (което се определя от стойността на frameDelay), то това няма да предизвила ускоряване на анимацията Метода setPosition() има две разновидности, първата от които приема стойност тип Point, а втората - Restangle. И двете променят текущата позиция на спрайта. Метода isPointInside() приема аргумент от тип Point и връща true, ако подадения аргумент е точка, намираща се в спрайта и false в противен случай. Основния метод на всеки спрайт е update(), който се грижи за промяната на текущия кадър, позицията на спрайта, както и действията на спрайта при достигане на границата, в която може да се движи. Метода draw() приема аргумент от тип Graphics и изрисува спрайтът вурху него, взимайки предвид текущата позиция и кадър. Метода testCollision() взима като аргумент обект от тип Sprite и връща true, ако двата спрайта са се сблъскали. Забележете, че свойството collision на обекта Sprite е правоъгълник; ако искате да напишете свой метод за ориверка на сблъсъците (тъй като правоъгълникът около изображението може да не върши работа), намалете размерите на collision за да не пречи на вашите собствени методи (тъй като в противен случай ще се предизвиква сблъсък винаги, когато правоъгълникът се докосне до този на друг спрайт) След цялата тази информация може би трябва да изясня в крайна сметка как се изрисува спрайтът. На първо място, както при всяка анимация, имате нужда от процес. Именно в този процес се извиква метода update() на спрайта. В метода paint(), трябва да извикате метода draw() на спрайта, като му подадете като параметър графичен обект - или този, който е подаден на paint(), или дефиниран от вас, ако ползвате двоен буфер. Същестува още един клас, SpriteVector, който помага за управление на множество спрайтове, но също така предоставя някои ключови функции за създаването на анимация. Именно тези негови функции ще разгледаме. Преди всичко ще трябва да създадете обект от този тип. Той има само едно свойство, което е обект Background. Именно благодарение на него, можете да създадете фон, на който спрайтовете да се движат. Всъщност конструктора на SpriteVecror изисква подаването на фон като параметър. Но за да можете да използвате това предимство на SpriteVector, ще трябва да научите няколко класа, всичките от които наследници на класа Background. Неговия конструктор изисква само един обект от типа Component (който все пак обикновено е прозореца на аплета), а неговите свойства са component и size, съдържащи съответно стойността, подадена на конструктора и размера на обекта тип component. Класът има метод getSize(), който връща стойността на size, както и draw(), който запълва прозореца в черен цвят. Както виждате, този клас не ви дава кой знае какво. Важни са обаче неговите наследници. ColorBackground ви дава възможността да запълните прозореца с избран от вас цвят. Конструктора му изисква вече и цвят, а метода getColor връща съответния цвят. Класът предоставя и метод setColor(), в случай че искате да промените цвета на фона - като единствен параметър метода приема новия цвят. Отново съществува метод draw(), който запълва прозореца с избрания цвят. По-интересен е обаче класът ImageBackground, позволяващ да изберете за фон изображение. Конструкторът изисква два обекта - единия тип Component, другия - Image. Методите getImage() и setImage() са аналогични на getColor() и setColor() в ColorBackground, само че работят с изображения. И отново метода draw() изрисува фона. Сега вече можем да се върнем към класа SpriteVector. Той предоставя методите getBackground() и setBackgroun(). На setBackground() се подава обект от типа Bacground, но това означава че можете да подадете обект от всеки клас, наследил Background. Полезен е и метода getEmptyPosition(), който връща обект тип Point. Този обект съдържа координатите на произволно избрана точка, която ако се използва като позиция за спрайта, то той няма да се застъпва с друг. За да осъществите това обаче, трябва да подадете на метода един аргумент от тип Dimension, който съдържа размерите на спрайта. Метода isPointInside е подобен на този в класа Sprite, с тази разлика че проверява всички налични спрайтове и връща спрайтът, съдържащ точката. Ако няма такъв спрайт. то метода връща null. Отново update() е основен метод - той извиква метода update() на всички спрайтове и проверява за възникнали сблъсъци.Метода testCollision приема за аргумент обект тип Sprite и проверява за възникнал сблъсък между него и всички останали спрайтове. Ако има такъв, връща номера на спрайта, с който се е сблъскал подадения, в противен случай връща -1. Ако искате да получите достъп до спрайт на позиция i във списъка на спрайтовете, трябва да използвате конструкцията (Sprite)elementAt(i). Метода draw() изрисува фона, а след това и всички спрайтове. За да управлявате обаче списък от спрайтове, първо трябва да го създадете. Добавянето на нов спрайт към списъка се осъществява чрез метода add(), който приема като единствен аргумент спрайт и връща неговия индекс. Характерно за класа spriteVector е, че той подрежда списъка по z-координатата на спрайтовете и по този начин изрисува най-отгоре спрайтовете с най-голяма стойност на свойството zOrder. Когато използвате spriteVector, вместо метода draw() на конкретен спрайт, трябва да извикате метода draw() на spriteVector - той ще извика методите draw() на всички спрайтове, взимайки предвид стойността на zOrder на всеки и съответно коректно изрисувайки ги в дълбочина. Очаквайте продължение.... |
|||||
![]() |
|||||
| Материалът е публикуван на: 05.01.2003
г. |
Автор: Максим
Крижановски |
||||
| Начало на материала :: Процесори :: Дънни платки
:: Видео :: Мултимедия
:: Носители Периферия :: Комуникации :: Софтуер :: Технологии :: Links & Downloads :: Форум |
|||||
|
|||||