Rozdział 26. Zaawansowana budowa klipów filmowych
W tym rozdziale:
Przemieszczanie obiektów wzdłuż algorytmicznie opisanych krzywych
Nowe metody tworzenia interfejsów
Odczytywanie bieżącego czasu za pomocą nowych obiektów typu Data Object
Zdecydowaliśmy się zakończyć piątą część książki rozdziałem, w którym zaprezentujemy trzy zaawansowane ćwiczenia. W ćwiczeniach tych zostaną poruszone tematy, o których nie było okazji wspomnieć w innych rozdziałach dotyczących programowania we Flashu. Każde z nich prezentuje inną metodę pracy.
Ćwiczenia z zakresu programowania interaktywnych filmów Flasha
Ten rozdział składać się będzie z trzech ćwiczeń prezentujących zaawansowane metody wykorzystania obiektów i funkcji Flasha. Oto zestawienie zagadnień poruszonych w każdym z tych ćwiczeń.
Ćwiczenie eksperta |
Omawiane funkcje ActionScript |
Wykorzystanie krzywych Béziera w animacji |
onClipEvent(load) onClipEvent(mouseDown) onClipEvent(enterFrame) _xmouse _ymouse Math.sqrt() Math.atan2() Math.pi Math.random() getTimer() |
Programowanie interfejsów |
updateAfterEvent() String.length() MovieClip.attachMovie() substring() onClipEvent(load) funkcje o wielu argumentach |
Jak zrobić zegar za pomocą akcji getTimer() |
getTimer() Date.getSeconds() Date.getMinutes() Date.getHour() |
<szara ramka początek>
Ćwiczenie eksperta:
Wykorzystanie krzywych Béziera w animacji
Autor: Darrel Plant
Programowanie prostoliniowego ruchu obiektów za pomocą ActionScript jest dość proste, lecz czy nie byłoby miło umieć programować także ruch po łuku lub wzdłuż dowolnej krzywej? Ćwiczenie napisane przez Darrela pokazuje, jak wprowadzić do kodu ActionScript równania, reprezentujące krzywe --> Béziera[Author:MMP] [Author ID1: at Thu Nov 22 13:59:00 2001 ].
W otaczającym nas świecie większość przedmiotów nie porusza się po liniach prostych. A nawet jeśli tak się czasem dzieje, to prostoliniowy ruch nigdy nie będzie tak interesujący jak ruch przebiegający wzdłuż zakrzywionej ścieżki. Krzywoliniowa trajektoria wprowadza element nieprzewidywalności i dodaje ruchowi charakteru.
Wiesz zapewne, że we Flashu można animować ruch obiektu wzdłuż krzywej, umieszczonej na warstwie prowadzącej (Guide). Jednak taka krzywa musi być z góry zdefiniowana. A jeśli chcemy, by obiekt poruszał się w sposób losowy? Albo podążał w miejsce wskazane kliknięciem przez użytkownika? A przy tym ten ruch ma być krzywoliniowy... precz z nudą przemieszczania się po prostej!
W tej sytuacji rozwiązaniem jest animacja algorytmiczna — to znaczy, oparta na równaniach matematycznych. Ilość równań, jakie mogą być wykorzystywane w animacji jest praktycznie nieograniczona, lecz każdy taki algorytm musi spełniać kilka podstawowych wymagań.
Animowany ruch musi być krzywoliniowy.
Ruch musi zakończyć się dokładnie w wyznaczonym miejscu.
Ruch musi przebiegać w wyznaczonym odcinku czasu.
Kształt trajektorii powinien być zmienny; obiekty nie powinny poruszać się zawsze w taki sam sposób, chyba że sami sobie tego życzymy.
Krzywa Béziera to jedna z tych parametrycznych krzywych, które spełniają wszystkie powyższe wymagania. Pracując w różnych programach graficznych (w tym we Flashu 5), musiałeś nieraz spotkać się z tymi krzywymi. Krzywe Béziera są wykorzystywane w grafice wektorowej od czasu, gdy firma Adobe wprowadziła Postscript — a był to środek lat osiemdziesiątych. Wiesz zapewne, jakiego typu kształty można odwzorowywać za pomocą krzywych Béziera. Może to być nieomal wszystko, poczynając od prostej linii, a kończąc na pętli przecinającej się z samą sobą. Być może jednak nie zdajesz sobie sprawy, jak prosta jest matematyczna reprezentacja tego tworu.
Krzywą Béziera wyznaczają cztery punkty. Dwa z nich to końcowe punkty krzywej (wierzchołki), a dwa to punkty kontrolne (uchwyty). Punkty te zazwyczaj oznacza się jako p0, p1, p2 oraz p3. Wierzchołki to p0 i p3, natomiast punkty kontrolne to p1 i p2. W większości programów graficznych oprócz właściwej krzywej wyświetlane są dwie pomocnicze linie; jedna z nich jest rysowana od punktu p0 do p1, a druga od punktu p2 do p3. Krzywa rozpoczyna się od punktu p0 i dąży w stronę punktu p1, potem w stronę punktu p2, aby w końcu dotrzeć do p3. Krzywa zawsze przecina punkty p0 i p3, ale nigdy nie dochodzi do punktów p1 i p2. Wielobok, utworzony przez połączenie wszystkich punktów prostymi odcinkami w kolejności zgodnej z ich numeracją, zawsze całkowicie zawiera w sobie krzywą. Każdemu z czterech punktów krzywej przypisane są dwie współrzędne: xn oraz yn.
Do opisania krzywej Béziera potrzebne są dwa równania:
x(t)= axt3 + bxt2 + cxt + x0
y(t)= ayt3 + byt2 + cyt + y0
Przy czym t w każdym równaniu jest liczbą zmiennoprzecinkową z przedziału od 0 do 1. Gdy t równe jest 0, z powyższych równań otrzymujemy parę liczb: x0, y0, czyli współrzędne punktu p0. Jest to, jak pamiętamy, początkowy punkt krzywej. Gdy t równe jest 1, uzyskujemy współrzędne punktu p3: (x3, y3). W tym miejscu krzywa się kończy.
Musimy się jeszcze dowiedzieć, jak określić wartości współczynników ax, ay i tak dalej. Współczynniki te obliczane są na podstawie współrzędnych położenia punktów, opisujących krzywą:
cx = 3(x1 - x0)
bx = 3(x2 - x1)- cx
ax = x3 - x0 - cx - bx
cy = 3(y1 - y0)
by = 3(y2 - y1)- cy
ay = y3 - y0 - cy - by
Ponieważ wartości współczynników b oraz a są zależne od współczynnika c, współczynnik c zawsze powinien być obliczany w pierwszej kolejności.
W programach graficznych krzywa Béziera rysowana jest jako seria krótkich prostych odcinków, łączących punkty obliczone przez podstawianie do równania kolejnych, równomiernie rosnących wartości t. Przykładowo, jeśli za każdym razem będziemy zwiększać założoną wartość zmiennej t o 0,05 (a więc rozwiążemy nasze równania dla t równego 0,00, 0,05, 0,10, 0,15... i tak dalej, aż do t = 1,00) obliczymy współrzędne 21 punktów leżących na krzywej. Łącząc te punkty prostymi odcinkami, uzyskujemy przybliżony obraz przebiegu krzywej. Im mniejszy skok wartości zmiennej t, tym więcej obliczonych punktów. Większa liczba punktów oznacza krótsze odcinki i gładszą krzywą.
Tę samą metodę możemy zastosować w animacji, nakazując obiektowi przemieszczać się do kolejnych obliczonych punktów krzywej. Ponieważ jednak chcemy, aby cała akcja rozegrała się w pewnym ustalonym odcinku czasu (to trzeci punkt na naszej liście wymagań), musimy dodatkowo użyć funkcji getTimer przy obliczaniu przemieszczeń obiektu.
Pierwszy projekt, w którym to wykorzystałem, to wykonany w Directorze stolik do seansów spirytystycznych. Na stoliku znajduje się alfabet oraz rodzaj deseczki do wskazywania liter, która pędzi po scenie w dość zwariowany sposób, reagując na kliknięcia i naciśnięcia klawiszy. (Znajdziesz ten film wraz z innymi przykładami na dołączonym do książki CD-ROM-ie, w katalogu ch26).
Teraz, gdy poznałeś już przyświecającą nam ideę oraz (mam nadzieję) przyjrzałeś się dostarczonym przeze mnie przykładom, możesz przystąpić do wykonania odpowiedniego skryptu we Flashu 5. Najpierw musimy ustalić kilka zmiennych, które zostaną wykorzystane w chwili pierwszego pojawienia się deseczki. Przypiszemy je do detektora zdarzenia onClipEvent(load):
onClipEvent (load){
//ustalenie początkowego stanu aktywności
active = false;
//ustalenie czasu trwania ruchu
period = 2500;
}
Zmienna active jest wykorzystywana do wykrywania, czy deseczka jest w danej chwili w ruchu. Zmienna period wyznacza okres trwania ruchu, mierzony w milisekundach. Wobec tego wartość 2500 oznacza, że cała animacja zajmie 2,5 sekundy. W tej chwili deseczka jest po prostu obecna na scenie, nic więcej.
Zajmiemy się teraz kolejną częścią skryptu, aktywowaną przez następne zdarzenie. To tu wykonamy większą część pracy. Detektor onClipEvent (mouseDown) pozwala wykryć, w którym miejscu ekranu użytkownik kliknął myszą. Ten punkt uczynimy końcowym punktem krzywej Béziera. Potem będzie już można wyznaczyć współczynniki, potrzebne do obliczenia trajektorii ruchu. (W przytoczonym tu skrypcie znaczek ¬ oznacza, że pozostała część kodu z danej linii została przeniesiona niżej, ze względu na ograniczoną szerokość strony; nie wprowadzaj tego znaku, pisząc własny skrypt!).
onClipEvent (mouseDown) {
// sprawdzenie stanu aktywności
active = true;
//ustalenie końcowego punktu krzywej
x3 = _root._xmouse;
y3 = _root._ymouse;
//ustalenie punktu początkowego
x0 = _x;
y0 = _y;
//wektor łączący punkt początkowy z końcowym
vectorX = x3 - x0;
vectorY = y3 - y0;
//wyznaczenie punktu leżącego w 1/3 odległości pomiędzy początkiem a końcem
//dla punktu kontrolnego 1
x1 = x0 + vectorX / 3;
y1 = y0 + vectorY / 3;
//wyznaczenie punktu leżącego w 2/3 odległości pomiędzy początkiem a końcem
//dla punktu kontrolnego 2
x2 = x0 + 2* vectorX / 3;
y2 = y0 + 2* vectorY / 3;
//wyznaczenie stopnia zmienności w zależności od odległości
// między początkiem a końcem
variability = Math.sqrt( vectorX * vectorX + vectorY * vectorY) /1.5
//przemieszczenie punktów kontrolnych w losowy sposób
//przy czym zakres przemieszczeń waha się od -(variability/2)
//do (variability/2)
x1 = x1 + Math.random()*(variability+1) - variability /2;
y1 = y1 + Math.random()*(variability+1) - variability /2;
x2 = x2 + Math.random()*(variability+1) - variability /2;
y2 = y2 + Math.random()*(variability+1) - variability /2;
//obliczenie współczynników do równań krzywej Béziera
cx = 3*(x1 - x0);
cy = 3*(y1 - y0);
bx = 3*(x2 - x1)- cx;
by = 3*(y2 - y1)- cy;
ax = x3 - x0 - cx - bx;
ay = y3 - y0 - cy - by;
//ustalanie czasu
originTime = getTimer();
}
Większa część powyższego kodu to zwykłe przeniesienie naszych wyjściowych równań do środowiska ActionScript. Tylko sposób potraktowania punktów kontrolnych krzywej może wymagać objaśnienia. Oto, jak obliczamy te punkty:
Zaczynamy od punktu p1, ulokowanego między punktami p0 i p3 w jednej trzeciej dzielącej te punkty odległości. Punkt p2 lokujemy, analogicznie, w dwóch trzecich odległości pomiędzy punktami p0 i p3. Przy takim położeniu punktów krzywa Béziera przybiera postać prostej linii (to ten szczególny przypadek, gdy krzywa naprawdę przechodzi przez wszystkie cztery wyznaczające ją punkty).
Ponieważ jednak chcemy uzyskać zakrzywioną trajektorię (warunek 4.) zmieniamy te wyjściowe współrzędne punktów kontrolnych o losową wartość. W naszym przykładzie rozpiętość losowych zmian uzależniona została od odległości pomiędzy punktami p0 i p3. Maksymalny zakres zmian, reprezentowany przez zmienną variability, wynosi dwie trzecie tej wielkości; obliczana na jej podstawie wartość losowa przyjmuje wartości od 0 do 2/3. Zakres od 0 do 2/3 zostaje zmieniony w zakres od -1/3 do +1/3 przez odjęcie połowy wartości zmiennej variability (czyli 1/3). Załóżmy, że koniec krzywej — punkt p3 — znalazł się o 350 jednostek od jej początku, czyli punktu p0. Zakres zmian (variability) w takim przypadku wyniesie 233,333. Do początkowych współrzędnych punktów kontrolnych zostanie wtedy dodana losowo wybrana wartość z przedziału od 0 do 233, 333, po czym od uzyskanego wyniku zostanie odjęta liczba 116,666. Jeżeli początkowa wartość współrzędnej wynosiła 200, to finalna wartość może być dowolną liczbą z przedziału od 83,333 do 316,666. Zwiększając lub zmniejszając wartość variability, możemy uczynić obliczaną krzywą bardziej lub mniej zamaszystą. Gdy wartość zmiennej variability ustalimy na 0, nasza krzywa stanie się linią prostą.
Gdy już ustalimy położenia wszystkich punktów, obliczenie współczynników potrzebnych do układu równań obrazującego krzywą staje się dziecinną igraszką. Ostatnim zadaniem, jakie musimy zrealizować, jest włączenie stopera (getTimer).
Tyle już dokonaliśmy, a nasza deseczka wciąż pozostaje nieruchoma. Aby wprawić ją w ruch, potrzebujemy jeszcze jednego detektora zdarzenia; tym razem będzie to onClipEvent(enterFrame).
onClipEvent(enterFrame) {
// sprawdzenie stanu aktywności
if (active) {
var t;
var dX;
var dY;
var modangle;
//obliczenie t na podstawie czasu jaki upłynął i całkowitego czasu ruchu
t = (getTimer() - originTime) / period;
//sprawdzenie czy minął czas ruchu
if (t >= 1) {
//czas minął; obiekt zajmuje pozycję finalną
_x = x3;
_y = y3;
//koniec stanu aktywności
active = false;
} else {
//obiekt ciągle jest w ruchu; podstawiamy t do równania
newX = ax*t*t*t + bx*t*t + cx*t + x0;
newY = ay*t*t*t + by*t*t + cy*t + y0;
//ustalamy nowe położenie obiektu
_x = newX;
_y = newY;
}
//obracanie obiektu tak by zwrócił się ku klipowi pointto
//obliczanie wektora od obiektu do pointto
dX = _x - _root.pointto._x;
dY = _y - _root.pointto._y;
//obliczanie kąta obrotu z pomocą funkcji atan2
//na podstawie wektora
modangle = 270 + (Math.atan2 (dY, dX) * 360 / 2 / Math.PI);
//obrót głównego obiektu i cienia
body._rotation = modangle;
shadow._rotation = modangle;
}
}
Aby uruchomić odczyt czasu, najpierw sprawdzamy, czy obiekt jest aktywny (czyli w ruchu). Jeśli tak nie jest, nic się nie dzieje.
Gdy obiekt jest aktywny, nasz skrypt działa następująco: wartość, jaką wprowadzamy do równania, to mierzony w milisekundach czas, jaki upłynął od ostatniego kliknięcia myszą. Wartość ta zostaje podzielona przez długość okresu czasu przeznaczonego na ruch (zmienna period). W wyniku tej operacji uzyskamy dodatnią liczbę zmiennoprzecinkową; od zera w górę. Gdy wartość t jest równa 1 lub większa, oznacza to, że czas, jaki upłynął od ostatniego kliknięcia myszą, jest już dłuższy niż czas przeznaczony na ruch. Obiekt zostaje więc ulokowany w finalnej pozycji, a jego aktywność się kończy.
Gdy wartość t jest mniejsza od 1, to po prostu podstawiamy ją do równania. To niemal zbyt proste; potrzeba tylko trochę mnożenia i dodawania. Zauważ, że zamiast podnosić t do sześcianu i kwadratu za pomocą funkcji Math.esp, wolałem zwyczajnie pomnożyć t przez siebie. To łatwiejszy sposób. Na koniec zmieniamy współrzędne położenia obiektu. Gotowe!
Na deser dodałem coś, co nie ma nic wspólnego z animacją ruchu wzdłuż krzywej Béziera. Ostatnia część skryptu służy do obracania naszej deseczki w taki sposób, by zawsze wskazywała pewien określony punkt na scenie. Użyłem do tego niewidzialnego klipu filmowego o nazwie pointto („wskaż mnie”), ulokowanego w górnej części ekranu. Skrypt oblicza kąt między deseczką a obiektem-celem i odpowiednio zmienia orientację samej deseczki i jej cienia. Efekt jest taki, jakby jakiś niewidzialny magnes oddziaływał na poruszający się obiekt.
Cała animacja jest zależna wyłącznie od działań widza. Gdy klikniesz ekran w trakcie ruchu deseczki, jej trajektoria zostanie natychmiast przeliczona na nowo. Animacja jest także niezależna od prędkości odtwarzania. W moim przykładowym filmie ruch trwa zawsze 2,5 sekundy, niezależnie od tego, czy prędkość odtwarzania wynosi 5 czy 100 ujęć na sekundę. Obniżenie prędkości odtwarzania oznacza tylko tyle, że ruch nie będzie wyglądał tak gładko. Zwiększenie tempa wyświetlania nie przyspieszy ruchu, tylko zwiększy jego płynność. (Zwróć uwagę, że możesz z łatwością przyspieszyć lub zwolnić ruch deseczki, zmieniając wartość zmiennej period w trzeciej linii skryptu wprowadzonego pod nagłówkiem onClipEvent(load); zmienisz w ten sposób czas, w którym obiekt przemierza swą trasę).
Animacje oparte na krzywych Béziera można wykorzystać na wiele różnych sposobów. Mój oryginalny projekt, wykonany w Directorze, pozwalał widzom kierować deseczkę ku widniejącym na ekranie literom za pomocą naciskania odpowiadających im klawiszy na klawiaturze. Każdemu z klawiszy przyporządkowałem na scenie punkt o określonych współrzędnych. Dobrym pomysłem mogłoby się okazać zmodyfikowanie wartości zmiennej period w chwili rozpoczęcia ruchu, tak by dopasować czas trwania ruchu do odległości między punktami p0 i p3. Mógłbyś też zrezygnować z detektora zdarzenia onClipEvent i ulokować kod krzywej Béziera w innej funkcji; wtedy obiekt byłby sterowany nie kliknięciami myszy, lecz informacjami przekazywanymi z dowolnego miejsca w filmie.
<drobny druk początek>
Darrel, mieszkaniec Portland w stanie Oregon, powiedział nam o sobie: „Całe moje życie spędziłem w Oregonie; rzadko zdarza mi się opuścić granice miasta, w którym mieszkam”. W roku, w którym ukończył uczelnię, sześć razy obejrzał Alien w teatrze. Jest autorem kilku książek, w tym Shockwave: Breathe New Life into Your Web Pages (wydawnictwo Ventana, 1996) oraz Flash! Creative Web Animation (Berkeley, Peachpit Press, 1997) i redaktorem kilku kolejnych, jak na przykład The Lingo Programmer's Reference (Ventana, 1997) i Special Edition Using Macromedia Flash 5 (New York, Macmillian, Que, 2001). Jednak zapytany o zainteresowania odpowiedział: „Czytać — ale nic związanego z komputerami! Acha, i grać w Battleboty!” A oto, co powiedział na temat genezy swych zainteresowań Flashem: „Przez całą dekadę byłem facetem od wektorów, a programowaniem multimediów zajmuję się od wczesnych lat dziewięćdziesiątych. Nic dziwnego, że zająłem się Flashem, gdy tylko się pojawił”.
<drobny druk koniec>
<szara ramka koniec>
<szara ramka początek>
Ćwiczenie eksperta:
Programowanie interfejsów
Autor: Nik Schramm
W chwili pojawienia się Flasha 4, który położył podwaliny pod język skryptowy tak wspaniale rozwinięty w wersji 5, zarysowała się następująca tendencja w projektowaniu interaktywnych filmów Shockwave, z których większość ma nieomal pustą główną listwę czasową. Skrypty i animację przenosi się zwykle do klipów filmowych. W dziedzinie projektowania interfejsów, gdzie cała akcja rozgrywająca się na ekranie, jest zazwyczaj zależna od działań użytkownika, niełatwo znaleźć inną skuteczną metodę pracy. Ponieważ sam zajmuję się tą właśnie dziedziną, niezwykle ucieszyły mnie ulepszenia, jakie Flash 5 wprowadził w swym języku skryptowym. W tym ćwiczeniu postaram się je zaprezentować.
Tworzenie elementów systemu nawigacji w formie klipów filmowych wielokrotnego użytku stało się teraz naprawdę bardzo proste. Flash 5 jest wyposażony w wiele wygodnych, predefiniowanych akcji; mam na myśli przede wszystkim nowe funkcje i akcje przypisywane obiektom (Object Actions), dzięki którym każdy klip filmowy może inicjalizować własny, niezależny zestaw zmiennych i wykonywać szereg różnych operacji na zdarzeniu typu onEnterFrame. Najważniejsze jest, by w głównym poziomie filmu umieścić uniwersalne procedury, do których będą odwoływać się poszczególne klipy w celu zrealizowania specyficznych dla nich zadań. Przypomina to trochę działanie akcji call z Flasha 4, ale jest znacznie bardziej elastyczne. Aby zilustrować te nowe techniki, dokonam wiwisekcji głównego menu z www.industrial.com. To witryna, którą zaprojektowałem niedawno przy użyciu Flasha 5.
Interfejs przedstawiony na rysunku poniżej został zaprojektowany w Illustratorze 9, wyeksportowany jako plik *.swf i zaimportowany do Flasha. Po zaimportowaniu zoptymalizowałem projekt, usuwając powielające się elementy i zastępując je wielokrotnie używanymi symbolami, gdzie tylko było to możliwe. Zmniejszyło to rozmiar pliku do 19 kB, zostawiając mnóstwo miejsca na moje skrypty. Aby móc w pełni wykorzystać nowe akcje przypisywane do obiektów Flasha 5, wszystkie symbole graficzne zamieniłem na klipy filmowe (Movie Clip).
Zadania interfejsu
Główna listwa czasowa zawiera tylko jedno ujęcie kluczowe, w którym zostają zainicjalizowane najważniejsze procedury skryptowe ze wszystkimi potrzebnymi funkcjami i zmiennymi. Głównym elementem interfejsu jest menu złożone z dziesięciu identycznych przycisków, nad którymi w ślad za kursorem myszy przesuwa się prostokątne szkiełko; przypomina to nieco suwak logarytmiczny. Gdy mysz znajdzie się nad przyciskiem (zdarzenie rollover), wyświetlana jest krótka animacja; numer porządkowy wybranego tematu pojawia się w szkiełku. Równocześnie przewijana jest zawartość dużego kwadratowego wyświetlacza powyżej; w miejsce początkowego symbolu (jest to logo Industrial) pojawia się ten sam numer co w szkiełku. Dla większego efektu zostaje automatycznie wygenerowana linia tekstu, która jest poddawana losowej animacji. Tekst ten pojawia się między logarytmicznym menu a wyświetlaczem. Zdarzenie rollout (cofnięcie myszy) powinno przywrócić całość do stanu początkowego; wszystkie wprowadzone przeze mnie funkcje muszą uwzględniać ten warunek. Przyjrzyjmy się teraz bliżej poszczególnym elementom interfejsu od efektów animacyjnych aż po faktyczne przyciski.
Szkiełko
Szkło powiększające ulokowane jest wewnątrz klipu filmowego o nazwie _root.menuLens. Jak już wiesz, klip ten zaczyna podążać za kursorem, gdy ten znajdzie się w pobliżu głównego menu. Zamiast korzystać z akcji dragMovie, wolałem napisać krótką procedurę na głównym poziomie filmu. Ta procedura dopasowuje położenie klipu filmowego względem osi X do kursora myszy. W ten sposób akcję dragMovie mogę pozostawić sobie na inne okazje; we Flashu 5 często zdarza się, że ten sam efekt można osiągnąć na wiele sposobów. Moja procedura jest bardzo prosta:
function lensMenu() {
//przypisanie zmiennych do obecnego położenia kursora w osi X i Y
mouseX = _root.xmouse;
mouseY = _root.ymouse;
//ograniczenie aktywnego obszaru do obszaru głównego menu
if(mouseY>421 && mouseY<489 && mouseX>43 && mouseX<655) {
//dopasowanie położenia klipu menuLens do położenia kursora
_root.menuLens._x = mouseX
}
//zatrzymanie szkiełka nad przyciskiem 1 gdy kursor jest
//za daleko na lewo
if(mouseY>421 && mouseY<489 && mouseX>9 && mouseX<44) {
_root.menuLens._x = 43
}
//zatrzymanie szkiełka nad przyciskiem 10 gdy kursor jest
//za daleko na prawo
if(mouseY>421 && mouseY<489 && mouseX>654 && mouseX<688) {
_root.menuLens._x = 655
}
}
Oczywiście ta procedura sama z siebie nie działa; klip filmowy menuLens musi ją wywołać, by zaktualizować swe położenie za każdym razem, gdy mysz się poruszy. We Flashu 5 można to zrobić z łatwością; wystarczy przypisać klipowi menuLens następujące akcje:
onClipEvent(mouseMove) {
_root.lensMenu();
updateAfterEvent();
}
Ostatnia linia tego skryptu, updateAfterEvent, powoduje odświeżenie ekranu niezależnie od aktualnej prędkości odtwarzania filmu. Pozwala to uniknąć zwłoki, jaka jest zwykle związana z przeciąganiem myszą jakichkolwiek elementów filmu Flasha.
Animowany tekst
Każde słowo, jakie można przeczytać na ekranie, jest generowane na bieżąco w trakcie korzystania z interfejsu. Używam do tego tylko jednej zgrabnej funkcji, ulokowanej na głównym poziomie filmu. Do tej funkcji, w wyniku następowania rozmaitych zdarzeń, przesyłane są ciągi zmiennych opisujących szczegółowo, co, gdzie i jak ma się pojawić na ekranie. W ten sposób utworzyłem zarówno napisy na wszystkich przyciskach, jak i linię animowanego tekstu, która pojawia się po najechaniu myszą nad obszar głównego menu.
Zacząłem od utworzenia klipu filmowego dla każdej czcionki, jaką wykorzystuję w tym interfejsie. Wewnątrz mojego „czcionkowego” klipu znajduje się puste pole tekstowe, dla którego wybrałem odpowiedni krój, kolor i wielkość liter. Polu tekstowemu przypisałem zmienną letter. Czcionkowe klipy będą w miarę potrzeby tworzone za pomocą funkcji _root.createText, wykorzystującej nową akcję attachMovie. Dlatego też w trakcie projektowania filmu nie umieściłem na scenie ani jednego klonu tekstowego symbolu. Dla wszystkich czcionkowych klipów w bibliotece filmu trzeba za to włączyć opcję eksportu w oknie własności współdzielenia symbolu (Symbol Linkage Properties), aby klipy zostały wyeksportowane z gotowym filmem SWF. Na szczęście to bardzo proste; wystarczy wybrać polecenie Linkage w menu okna biblioteki, nadać każdemu z klipów indywidualną nazwę w polu Identifier i na koniec włączyć opcję Export this symbol.
Uwaga autora. Własności współdzielenia symbolu są omawiane w rozdziale 19., „Sterowanie klipami filmowymi”, oraz w rozdziale 20., „Doczytywanie filmów i biblioteki współdzielone”.
Dobrze, pora zbudować w pierwszym ujęciu naszej głównej listwy czasowej funkcję, która odpowiada za generowanie tekstu. Funkcja ta będzie otrzymywać od wywołującego ją zdarzenia (w naszym przypadku zdarzeniem będzie kliknięcie przycisku) następujące zmienne: ciąg znaków tekstowych do wyświetlenia, informacje na temat bazowego klipu, do którego przyłączony zostanie klip tekstowy, informacje na temat obszaru ekranu, względem którego napis ma być wycentrowany, położenie w osi X, położenie w osi Y, podstawowe informacje na temat kerningu oraz — jak łatwo przewidzieć — nazwę „czcionkowego” klipu, który ma zostać połączony z filmem. Moim zamierzeniem było rozbić napisy na całe serie klipów; każdy z nich wyświetlałby tylko jedną literę. Za odpowiednie pozycjonowanie poszczególnych liter odpowiadają zmienne. W tym akurat interfejsie, wyświetlonym na ekranie literom przypisana jest losowa animacja skali; natychmiast po pojawieniu się litery zaczynają rosnąć lub maleć. Wbudowałem tę animację w moje klipy tekstowe, ale nie będę jej tu bliżej opisywał.
W przytoczonym tu skrypcie znaczek ¬ oznacza, że pozostała część kodu z danej linii została przeniesiona niżej; nie wprowadzaj tego znaku, pisząc własny skrypt!
function createText (text, base, area, xpos, ypos, kern, font) {
//inicjalizacja zmiennych lokalnych
this.text = text;
this.base = base;
this.area = area;
this.xpos = xpos;
this.ypos = ypos;
this.kern = kern;
this.font = font;
var textString = this.text;
//obliczenie ilości znaków
letterCount = textString.length;
//tworzenie nowego szeregu znaków na podstawie wprowadzonego tekstu
arrayNumbering = letterCount - 1;
textArray = newArray(arrayNumbering);
//przypisanie jednego znaku do każdej pozycji
//w ramach szeregu
for (i = 0; i < letterCount; i++) {
textArray[i] = substtring (textString, i+1, 1);
}
//utworzenie jednego klipu dla każdego znaku w szeregu
//z pomocą akcji attachMovie
for (i = 0; i < letterCount; i++) {
eval(this.base).attachMovie (this.font, "clip"+i, i+1);
//wprowadzenie bazowego kerningu 20 pikseli odstępu w osi X
//i ustalenie położenia w osi Y
eval(this.base)["clip" + i]._x = this.xpos + (i*20);
eval(this.base)["clip" + i]._y = this.ypos;
//przypisanie zmiennej letter wewnątrz klipu
//właściwego znaku
eval(this.base)["clip" + i].letter = ¬
substring(textString, i+1, 1);
}
//centrowanie wszystkich klipów w wyznaczonym obszarze ekranu
//i wprowadzenie zadanego kerningu
for (i=0; i < letterCount; i++) {
x = ((this.area - (letterCount*this.kern))/2)+50;
pos = ((x)+(i*this.kern));
eval(this.base)["clip" + i]._x = pos;
}
}
Teraz pozostało nam już tylko zadbać o to, by zdarzenie typu rollOut spowodowało zniknięcie klipów ze sceny:
function removeText() {
//możemy tu użyć zmiennych zdefiniowanych dla funkcji createText()!!
for (i = arrayNumbering; i >= 0; i--){
removeMovieClip (eval(this.base+".clip"+i));
}
}
Uwaga autora. Używając operatorów szeregu, możemy ten sam skrypt oparty na akcji removeMovieClip napisać w następujący sposób:
removeMovieClip(this.base["clip"+i]);
Co za słodka prostota. Później przyjrzymy się, jak przyciski głównego menu wykorzystują te funkcje do wprowadzenia efektów typu rollOver i rollOut.
Wyświetlacz
Wyświetlacz jest tak naprawdę jedenaście razy szerszy, niż mógłbyś przypuszczać, obserwując jego okno widoczne na ekranie. Działa trochę jak rzutnik slajdów, w którym slajdy umieszczane są obok siebie w ramce. Nasz wyświetlacz zawiera ułożone obok siebie numery od 0 do 9 oraz logo Industrial. Gdy mysz znajdzie się nad jednym z przycisków głównego menu (zdarzenie rollOver), zawartość wyświetlacza zostaje przewinięta do odpowiadającego mu numeru. Na zdarzenie on(rollOut) ponownie pojawia się obraz logo. Aby osiągnąć ten efekt, położenie zawartości wyświetlacza względem osi X musi być dynamicznie zmieniane. Obiekt, który reprezentuje zawartość wyświetlacza w tej scenie jest klipem filmowym i nosi miano _root.mask.container.content.
Świetnie, ale skąd ten klip „wie”, jak daleko ma się przewinąć? Każdy z przycisków w menu kontroluje zmienną globalną o nazwie _root.windowTarget, równą numerowi, jaki przyporządkowano do danego przycisku. Zamiast zera mamy tu logo. Wszystko, co trzeba zrobić, to użyć wartości tej zmiennej w skrypcie kontrolującym przewijanie. Oto kod napisany dla klonu symbolu o nazwie _root.mask, umieszczonego w głównym filmie:
onClipEvent (enterFrame) {
//aby wyświetlacz domyślnie pokazywał logo (cel 0)
//odejmujemy połowę liczby możliwych celów
//i zmieniamy odpowiednio zmienną _root.windowTarget
interim = _root.windowTarget - 5.5;
final = 5.5 - interim;
//zmienna final to zmieniony numer celu
//obliczamy nowe położenie w osi x mnożąc zmienną final
//przez widoczną szerokość celu (315.4)
//odejmujemy połowę łącznej szerokości wszystkich 11 celów (1892.4)
xTarget = (final*315.4) - 1892.4;
//ustalamy gdzie jest teraz wyświetlacz
xIs = this.container.content._x;
//jeśli nowe położenie jest bardziej na prawo
if (xTarget > xIs) {
//podkręcamy efekt wprowadzając zmiany tempa ruchu
//ustalamy bazowy współczynnik tempa przewijania na 3,5
speed = (xTarget - xIs)/3.5
//stopniowo zwiększamy położenie klipu w osi x o wartość speed
this.container.content._x += speed
}
//to samo co wcześniej tylko gdy nowe położenie jest na lewo
if (xTarget < xIs) {
speed = (xIs - xTarget)/3.5
//stopniowo zmniejszamy położenie klipu w osi x o wartość speed
this.container.content._x -= speed
}
}
Teraz możemy już zabrać się do programowania naszych przycisków tak, by mogły zapoczątkować łańcuch reakcji.
Przyciski
Każdy z przycisków w głównym menu to klip filmowy, który zawiera niewidzialny symbol typu Button i 15-klatkową animację, odtwarzaną w wyniku umieszczenia kursora nad przyciskiem lub jego cofnięcia. Zaletą takiego rozwiązania jest to, że wszystkie elementy takiego klipu filmowego mogą być kontrolowane za pomocą skryptów. Nasze przyciski pomieszczą skrypty określające nie tylko ich własne zachowanie, ale też zachowanie powiązanych z nimi elementów interfejsu, o których już mówiliśmy.
Do tej pory wprowadziliśmy mnóstwo funkcji i detektorów zdarzeń, które oczekują tylko na hasło „naprzód” podane przez jeden z przycisków. Każdy z przycisków odrobinę różni się od pozostałych i trochę inaczej wpływa na linię informacyjnego tekstu oraz wyświetlacz, gdy umieścimy nad nim kursor myszy. Nie znaczy to jednak wcale, że musimy użyć dziesięciu odrębnych przycisków, z dziesięcioma różnymi skryptami zajmującymi dziesięć razy więcej miejsca w finalnym pliku. Wystarczy nam z powodzeniem dziesięć klonów tego samego przycisku.
Jak to działa? Każdy klon naszego klipu będzie miał przypisane własne akcje, podporządkowane detektorowi zdarzenia onClipEvent. Gdy dla każdego klonu zainicjalizujemy zmienne za pomocą detektora zdarzenia onClipEvent(load), uzyskamy dziesięć osobnych zestawów zmiennych, po jednym dla każdego z przycisków menu. Nasze klipy zostaną tak zaprojektowane, by zmienne nadawały im indywidualny charakter i różniły zachowanie każdego przycisku od jego towarzyszy. Natomiast same akcje będą wspólne dla całego menu i zostaną na stałe wbudowane w wyjściowy klip filmowy — tak by automatycznie pojawiły się w każdym klonie, jaki umieścimy na scenie. Zwróć uwagę, że w ten sposób możemy uzyskać zróżnicowane efekty rollOver, korzystając z klonów tego samego przycisku!
Jakież więc są te akcje, które zakodujemy wewnątrz naszego klipu filmowego, przypisując je do niewidzialnego przycisku? Szkiełko powiększające samo sobie radzi, ale wyświetlacz, aby przewinąć swą zawartość, musi użyć zmiennej _root.windowTarget. Nasze przyciski muszą więc przyporządkować tej zmiennej wartość równą swemu numerowi porządkowemu, aby okno wyświetlacza zadziałało. Moglibyśmy w każdym klonie przycisku wprowadzić zmienną indexNumber, lokujac ją pod nagłówkiem onClipEvent(load), i użyć jej do ustawienia adekwatnej wartości zmiennej _root.windowTarget. To by działało, ale czy nie prościej użyć własności na stałe przypisanych każdemu klipowi?
To bardzo istotna rzecz: każdy klip filmowy we Flashu 5 jest obiektem, a każdy obiekt ma przypisany pewien zestaw własności, do których zawsze można się odwołać — takich jak położenie w osi X i Y (_x, _y) lub nazwa (_name). Ponieważ sprytnie umieściliśmy nasze przyciski wewnątrz klipów filmowych, możemy teraz przypisać nazwy wszystkim klonom. A jeśli przypadkiem okaże się, że nazwa klonu dokładnie odpowiada jego numerowi porządkowemu — dajmy na to, będą to nazwy: _root.1, _root.2 itp. — to nasz problem będzie rozwiązany. W każdej chwili możemy odwołać się do nazwy obiektu, nie korzystając w tym celu z żadnych zmiennych. W ten sam sposób możemy ładować dodatkowe pliki SWF na wyższy poziom filmu, naciskając przycisk; wystarczy, abyśmy odpowiednio je nazwali (content1.swf, content2.swf i tak dalej). Oczywiście, właśnie tak to zostało zrobione.
Przejdźmy teraz do animacji tekstu. Musimy wywołać funkcję _root.createText i posłać jej potrzebne zmienne: text, base, area, xpos, ypos, kern oraz font. Ustalenie właściwych wartości dla zmiennych dotyczących położenia wymaga wykonania szeregu prób i mnóstwa obliczeń, a więc darujmy to sobie. Zamiast tego powiem po prostu, że odpowiednia wartość dla zmiennej area to 600, xpos to 10, ypos to 405, a zmiennej kern przyporządkujemy 12. Moim punktem odniesienia (klipem bazowym — base) będzie główny film: _root. To oznacza, że położenie wszystkich klipów jest ustalane względem całej sceny, a ścieżki dostępu do nich będą następujące: _root.clip0, _root.clip1 itp. Przyłączona czcionka, jakiej chcę użyć, nosi miano twLetterClip. Ponieważ wszystkie przyciski w menu będą korzystać z tej samej czcionki, nie musimy nadawać zmiennej font indywidualnych wartości dla każdego z klonów. Jednak tekst wyświetlany po kliknięciu każdego z przycisków menu oraz ładowana wtedy zawartość (kolejny film SWF) będą zróżnicowane, więc musimy zająć się ich zdefiniowaniem. Każdy klon klipu z przyciskiem menu będzie musiał sam ustalić te zmienne, korzystając przy tym z detektora zdarzenia onClipEvent(load). Zdecydowałem także, że informacyjny tekst przypisany do każdego z przycisków powinien zostać poprzedzony numerem porządkowym danego przycisku, ujętym w nawias „< >”. To także uwzględniłem w moim skrypcie:
onClipEvent (load) {
//zaczynamy kompletować nasz tekst
header = "<";
//dodajemy numer porządkowy
header += this._name;
//zamykamy nawias i dodajemy zasadniczy tekst
//który oczywiście może być całkiem inny dla każdego przycisku
header += "> industriality prezentuje temat nr ";
//jeszcze raz wprowadzamy numer porządkowy
header += this.name;
//i tak oto tekst przypisany do przycisku nr 8 przybrał
// postać "<8> industriality prezentuje temat nr 8"
//ustalamy zmienną określającą który swf z dodatkową zawartością
//będzie ładowany po zwolnieniu klawisza myszy
content = "content";
content += this.name;
content += .swf";
}
Teraz każdy klon przycisku menu definiuje dwie zmienne. Jedna z nich dotyczy wyświetlanego tekstu informacyjnego, druga zaś zawartości, którą chcemy załadować w wyniku kliknięcia danego przycisku. A oto finalny kod, przypisany do niewidzialnego przycisku umieszczonego w każdym klonie (znaczek ¬ oznacza, że pozostała część kodu z danej linii została przeniesiona niżej; nie wprowadzaj tego znaku, pisząc własny skrypt!).
on (rollOver) {
//rozpoczynamy wyświetlanie animacji rollover danego przycisku
play ();
//powiększamy przycisk
this.xscale = 160;
this.yscale = 140;
//zarządzamy przewijanie okna wyświetlacza do numeru przycisku
_root.windowTarget = this.name;
//wywołujemy funkcję generującą tekst informacyjny
_root.createText(this.header, "_root", 600, 10, 405, 12,¬
"twLetterClip");
}
on (releaseOutside, rollOut, dragOver, dragOut) {
//zarządzamy przewijanie wyświetlacza na powrót do logo
_root.windowTarget = 0;
// wywołujemy funkcję usuwającą klipy tekstu informacyjnego
_root.removeText();
// rozpoczynamy wyświetlanie animacji rollout danego przycisku
//i przywracamy początkowy rozmiar przycisku
gotoAndPlay("out");
this.xscale = 100;
this.yscale = 100;
}
on (release) {
//ładujemy SWF w oparciu o zmienną "this.content"
//na poziom który odpowiada numerowi zawartemu w nazwie
// danego klipu (od 1 do 10)
loadMovieNum (this.content, this._name);
//przywracamy początkowy stan przycisku
gotoAndPlay("off");
}
Końcowe uwagi
Użyłem tu metody bardzo zbliżonej do metody „inteligentnych klipów”, które odwołują się do uniwersalnych procedur, lecz karmią je własnym zestawem zmiennych, przypisanych przez użytkownika indywidualnym klonom. Gdyby okazało się, że moje przyciski muszą stać się jeszcze bardziej skomplikowane, niż były w tym przykładzie, to z pewnością posłużyłbym się inteligentnymi klipami. Jednak w tym przypadku zwykłe klipy okazały się wystarczające.
Gdy pracujemy we Flashu, opłaca się planować z wyprzedzeniem. Trzeba projektować film tak, by poszczególne fragmenty kodu można było wykorzystywać wielokrotnie; dopóki cały świat nie zacznie korzystać z połączeń szerokopasmowych (co pewnie nigdy nie nastąpi) musimy oszczędzać rozmiar pliku. We Flashu 5 jest to łatwiejsze niż kiedykolwiek. Funkcje, akcje przypisywane do obiektów, inteligentne klipy i wspólne biblioteki — to wszystko pomaga poprawić współczynnik liczby kilobajtów do wartości rozrywkowej filmu. Korzystaj z nich.
<drobny druk początek>
Nik Schramm mieszka w Hamburgu, w Niemczech. Oto, jak opisał początki swej kariery we Flashu: „Zacząłem od wersji OEM Corela 4, nieograniczonego entuzjazmu, MSFrontPage i straszliwej frustracji, gdy okazało się, że nie mogę przenieść moich projektów z jednego środowiska do drugiego. Budowałem wtedy witrynę zajmującą się promocją muzyki; potrzebowałem wyrazistej grafiki, animacji, dokładnej kontroli nad typografią i możliwości integracji dźwięku. Więc wyrzuciłem wszystkie moje dotychczasowe narzędzia i przesiadłem się na Flasha — cóż bardziej naturalnego”. Jego renomę jako projektanta ugruntowały znakomite witryny www.industriality.com oraz www.nae.de. Aby dokładniej datować karierę Nika, spytaliśmy go o główne wydarzenie medialne roku, w którym ukończył szkołę. „Świat się kręcił w rytmie Let's Go Crazy artysty Poprzednio Znanego Jako Prince.” A co najbardziej lubi? „Spędzać czas z moimi synami: Noahem (3) i Rubenem (1). Jak widać, nie wziął do serca rady Prince'a.
<drobny druk koniec>
<szara ramka koniec>
<szara ramka początek>
Ćwiczenie eksperta:
Jak zrobić zegar za pomocą akcji getTimer()
Autor: Jake Smith
Moją pierwszą reakcją na nowy język skryptowy Flasha 5 było przerażenie. Ale wkrótce zaczęły mi chodzić po głowie różne „a gdyby tak...” oraz „ciekawe, czy to by zadziałało?” — i to nawet, gdy w pobliżu nie było żadnego komputera! Ten flashowy zegar to efekt jednego z tych moich „a gdyby tak” połączonego z odrobiną prostej matematyki, niezbędnej, by sfinalizować projekt. Mówię uczciwie; gdy już wymyśliłem zasadę działania mojego zegara, wprawienie w ruch sekundnika zajęło mi nie więcej niż dwie minuty. Reszta jest już prosta, jak sam się za chwilę przekonasz.
Temat pracy domowej: zrobić prawdziwy, działający w czasie rzeczywistym zegar.
Najpierw narysowałem tarczę zegara we FreeHand 9. Nic wymyślnego, ale potrzebowałem wszystkich dwunastu punktów na obwodzie cyferblatu. Narysowałem więc linię przecinającą w poprzek cyferblat, a potem raz za razem kopiowałem ją i zarazem obracałem o 30°. Powtórzyłem tę operację pięć razy, uzyskując w ten sposób dwunastoramienną gwiazdę. Na wierzchu umieściłem mniejsze koło przesłaniające te części linii, które znalazły się w pobliżu centrum tarczy zegarowej. (Obracałem linię po 30°, gdyż na zegarze mamy 12 godzin, a pełne koło ma 360°. Nietrudno obliczyć, że godzinowe cyfry są rozstawione wzdłuż obwodu koła w 30-stopniowych odstępach).
Importowanie narysowanego zegara do Flasha
Wykorzystałem zdolność Flasha 5 do wczytywania plików z FreeHand 9 (polecenie File/Import), by umieścić mój zegar we Flashu. Następnie wycentrowałem cyferblat względem sceny, gdyż potrzebowałem wyrazistego punktu odniesienia przy tworzeniu wskazówek. Na tym etapie pracy masz dwie możliwości: możesz od razu zablokować warstwę z cyferblatem lub też najpierw przekształcić cyferblat w symbol typu Movie Clip i dopiero wtedy zablokować warstwę. Rób, jak chcesz — ale zamiana wszystkiego, co można, w klipy filmowe jest na ogół dobrym pomysłem.
Sekundnik
Teraz utwórz nową warstwę i narysuj wąski, wysoki prostokąt za pomocą narzędzia Rectangle. Sekundnik powinien mieć długość odrobinę przekraczającą promień zegarowej tarczy, od środka do jej krawędzi. Zaznacz prostokąt, który właśnie narysowałeś, i zrób z niego symbol typu Movie Clip. Nadaj mu nazwę Second Hand (sekundnik). Jeśli prostokąt nie jest idealnie wyśrodkowany względem sceny, użyj palety Align do wycentrowania go.
Kliknij raz sekundnik i wciśnij klawisze Ctrl+E (jeśli pracujesz na Macintoshu, Command+E). W ten sposób przejdziesz do trybu edycji symbolu. Prostokąt powinien być wycentrowany względem środkowego punktu sceny symbolu, zaznaczonego cienkim krzyżykiem. Wokół tego właśnie punktu odbywają się wszelkie obroty we Flashu. Jeżeli pozostawimy nasz sekundnik w jego obecnym położeniu, to obracając się, będzie wyglądał jak śmigło. Musimy więc zaznaczyć cały prostokąt i przemieścić go wzwyż, aż cienki krzyżyk znajdzie się w pobliżu dolnej krawędzi prostokąta. Sam zdecyduj, jak daleko przemieścić prostokąt; postaraj się jednak, by pod krzyżykiem pozostał niewielki fragment sekundnika. Dzięki temu zegar będzie wyglądał bardziej autentycznie. (Zwróć uwagę, że nie musisz przemieszczać sekundnika w lewo ani w prawo. Prostokąt został automatycznie wyśrodkowany względem sceny symbolu w chwili, gdy przekształciliśmy go w klip filmowy).
Naciśnij ponownie kombinację klawiszy Ctrl+E (lub Command+E), by opuścić tryb edycji klipu filmowego i powrócić do głównej sceny. Wskazówka powinna w tej chwili wskazywać godzinę 12. Na koniec musimy nadać indywidualną nazwę klonowi symbolu, umieszczonemu w scenie. Mając zaznaczoną wskazówkę, otwórz panel Instance i nadaj klonowi nazwę second_hand.
Niech płyną sekundy
W głównej listwie czasowej filmu utwórz trzy ujęcia na wszystkich istniejących warstwach (jeżeli dotąd nie nadałeś warstwom indywidualnych nazw, uczyń to teraz, abyś później łatwiej mógł je rozpoznać). Utwórz nową warstwę i nazwij ją Actions (akcje). Ta warstwa będzie zawierała wyłącznie skrypty kontrolujące film. Trzymanie wszystkich skryptów na oddzielnej, łatwej do odszukania, warstwie jest bardzo wygodne, gdy projekty zaczynają się rozrastać.
Na warstwie Actions utwórz ujęcia kluczowe w klatce nr 1, 2 i 3. Następnie otwórz panel Frame Actions — pora zająć się techniczną stroną filmu! Jedną z nowych cech Flasha 5 jest umiejętność zwracania wartości odpowiadających aktualnej dacie i godzinie. Dzięki nowym obiektom „datowym” (Data Object) filmy .SWF mogą sprawdzić datę i godzinę obowiązującą w systemie, w którym są odtwarzane. Nasz film będzie pobierał informacje o godzinach, minutach i sekundach — tego właśnie potrzebujemy.
Sprawdź, czy aktywne jest pierwsze ujęcie kluczowe na warstwie Actions i w oknie Frame Actions wpisz co następuje:
mySecs = new Date();
secs = mySecs.getSeconds();
Ten skrypt tworzy nowy obiekt „datowy” o nazwie mySecs i ustala taką wartość zmiennej secs, by równała się liczbie sekund aktualnie podawanej przez komputer, na którym uruchamiamy film. To wszystko, czego potrzebujemy w pierwszym ujęciu filmu; teraz przejdźmy do ujęcia nr 2.
Aby utrzymać zegar w ruchu, użyjemy innej akcji Flasha, zwanej getTimer. Akcja to zwraca ilość czasu, jaki upłynął od chwili uruchomienia filmu. Czas mierzony jest w tysięcznych częściach sekundy. Pozwoli to nam w sposób ciągły zwiększać liczbę sekund, a potem także minut i godzin.
Zaczniemy od utworzenia nowej zmiennej i nadania jej wartości równej liczbie całkowitej, zwracanej przez akcję getTimer. A więc pierwsza linia skryptu z ujęcia 2. powinna wyglądać tak:
myTime = getTimer();
Ponieważ zwracana wartość to liczba milisekund, musimy wykonać prosty rachunek dla obliczenia dokładnej liczby sekund. Oto nasza następna linia:
seconds = myTime/1000;
Nowa zmienna seconds reprezentuje sekundy! Już nie w tysięcznych częściach, lecz zwyczajnie, tak jak podawałby je kwarcowy zegarek z cyfrowym wyświetlaczem. Jest tylko jedno małe „ale”: nasza zmienna nie mierzy faktycznego czasu, a jedynie czas, jaki upłynął od włączenia filmu. Aby to naprawić, wykorzystamy zmienną secs wprowadzoną w pierwszym ujęciu kluczowym. Tamta zmienna zmierzyła czas w momencie uruchomienia filmu. Jeśli dodamy tamtą wartość do wartości zmiennej seconds, uzyskanej za pomocą akcji getTimer, powinniśmy uzyskać prawidłowy wynik.
clocksecs = seconds + secs;
Oto co osiągnęliśmy do tej pory; w pierwszym ujęciu film sprawdza, jaki jest aktualny czas. Jeśli otrzyma od komputera-gospodarza informację, dajmy na to, „30 sekund”, to będzie mógł na podstawie tej informacji ustalić odpowiednie wyjściowe położenie sekundnika. Od tego wyjściowego czasu będą odmierzane kolejne sekundy.
Nasz film „wie”, która godzina, ale to jeszcze nie starczy. Musimy spowodować, by sekundnik obrócił się odpowiednio, tak byśmy mogli sami odczytać czas. Przy tworzeniu elementów filmu zwracaliśmy baczną uwagę na położenie punktu, względem którego obraca się wskazówka; to dlatego, że wykorzystamy obrót do właściwego ustawienia wskazówki. We Flashu wystarczy do tego jedna linia skryptu:
setProperty ("second_hand", _rotation, ((clocksecs/60)*360));
Choć wygląda to na dość skomplikowane, łatwo jest podzielić ten skrypt na części. Akcja SetProperty zmienia własności klonu o nazwie second-hand, a zmienianą własnością jest kąt obrotu (_rotation). Na koniec ustalamy, jaki ma być ten nowy kąt. Aktualny kąt obrotu sekundnika obliczamy z wyrażenia (clocksecs/60)*360. Dlaczego?
Koło ma równe 360°, a sekundnik musi obiec je całe w równe 60 sekund. Aktualny czas — clocksecs — dzielimy najpierw przez 60 (maksymalna liczba sekund to 60; potem liczba ta znów zostaje zresetowana do jedności). Wynik mnożymy przez 360, czyli liczbę stopni pełnego koła. W ten sposób dowiemy się, jak dalece sekundnik powinien być obrócony w danym momencie. I tak właśnie uzyskujemy nasz wzór:
((clocksecs/60)*360)
Na koniec, aby nasz zegar stale szedł, w trzecim ujęciu kluczowym warstwy Actions umieszczamy jeszcze jedną akcję:
gotoandPlay (2);
Teraz możesz przetestować swój film. Zobaczysz, jak wskazówka wędruje wokół tarczy.
A teraz minuty
Pewnie ucieszysz się, gdy usłyszysz, że wskazówkę minutową tworzymy bardzo podobnie jak sekundnik. Wystarczy zmodyfikować kilka zmiennych. Najpierw utwórz nową warstwę o nazwie minutes (minuty) i umieść ją poniżej warstwy z sekundnikiem. Narysuj czarną wskazówkę, długą i cienką, choć nie aż tak długą i cienką jak sekundnik. Przekonwertuj minutową wskazówkę na klip filmowy i wycentruj ten klip na scenie.
Wciśnij kombinację Ctrl+E (lub Command E), by przejść do trybu edycji wskazówki minutowej. Zaznacz wskazówkę i przesuń ją do góry, tak samo jak to było z sekundnikiem — cienki krzyżyk powinien znaleźć się u dołu prostokąta. Powróć do edycji głównego filmu; wskazówka powinna stać na godzinie dwunastej. Nadaj klonowi minutowej wskazówki indywidualną nazwę w panelu Instance; niech to będzie na przykład minute_hand.
W pierwszej klatce warstwy Actions dodaj następujący kod:
myMins = new Date();
mins = myMins.getMinutes();
W ten sposób tworzymy nowy obiekt „datowy” pobierający z systemowego zegara informację o minutach.
Teraz przejdź do drugiego ujęcia kluczowego warstwy Actions i dodaj następującą linijkę zaraz pod linią seconds = myTime/1000;
minutes = (secs + seconds) / 60;
To pozwala obliczyć liczbę minut na podstawie znanych nam wartości: początkowej liczby sekund dodanej do liczby sekund, jakie upłynęły od uruchomienia filmu.
Kolejną linię dodamy po linii clocksecs = seconds + secs. Oto ona:
clockmins = minutes + mins;
Właśnie ustaliliśmy, jaki czas powinna wskazywać minutowa wskazówka w chwili startu.
Oczywiście musimy też nadać minutowej wskazówce właściwe położenie. A więc ostatnia linia kodu, jaką dodamy w ujęciu 2., będzie następująca:
setProperty ("minute_hand", _rotation, ((clockmins/60)*360));
Jak widzisz, kąt obrotu wskazówki minutowej obliczamy tak samo, jak kąt obrotu sekundnika; wskazówka minutowa również musi pokazać 60 kolejnych minut, wykonując 360-stopniowy obrót.
A teraz godziny
Utwórz nową warstwę o nazwie hours (godziny) i umieść ją pod warstwą ze wskazówką minutową. Narysuj gruby, krótki, czarny prostokąt i wycentruj go względem sceny. To będzie nasza wskazówka godzinowa. Zamień go w klip filmowy i poddaj takiej samej edycji, jak wskazówkę minutową i sekundową (przesuwając go do góry tak, by krzyżyk oznaczający środek sceny symbolu znalazł się u dołu). Gdy wrócisz do edycji głównego filmu, nadaj klonowi nazwę hour_hand.
Podobnie jak poprzednio, dla wskazówki godzinowej również tworzymy obiekt „datowy” w pierwszym ujęciu warstwy Actions:
myHrs = new Date ();
hrs = myHrs.getHours();
W drugim ujęciu kluczowym warstwy Actions pod linią minutes = (secs+seconds) / 60; umieścimy następującą linię kodu:
hours = (minutes +mins) / 60;
Po linii clockmins = minutes + mins dodajemy:
clockhours = hours+hrs;
Ostania linia, jaką dodamy u samego dołu kodu, jest następująca:
setProperty (”hour_hand", _rotation, ((clockhours/12)*360));
Zwróć uwagę, że w tej akcji SetProperty użyliśmy innego wyrażenia; zamiast dzielić przez 60, dzielimy przez 12. To dlatego, że wskazówka godzinowa, robiąc pełny obrót pokazuje 12 godzin; po przekroczeniu dwunastki, liczba godzin jest resetowana do jedności.
Film jest ukończony i gotów do eksportu.
Uwaga autora. Gotowy plik FLA z zegarem oraz jeszcze jeden podobny film Jake'a (z cyfrowym wyświetlaczem, który oprócz godziny podaje też datę) znajdziesz na dołączonym CD-ROM-ie w katalogu ch26.
<drobny druk początek>
Jake Smith jest członkiem cieszącego się estymą Subnetu i znanym projektantem. Na liście jego zleceniodawców znaleźć można www.kelloggs.co.uk, www.foxkids.co.uk, www.wotsits.co.uk oraz www.capri-sun.co.uk. Wierny syn Liverpoolu ukończył szkołę w roku, w którym najbardziej znaczącym filmem był „hm, czy ja wiem, chyba Wayne's World...”. Jake posiada imponującą kolekcję automatów do gier, a gry wideo są jego głównym hobby. Ponadto lubi: „muzykę, filmy kung-fu, projektowanie, no i Bacardi...”. Zagadnięty o swój pierwszy kontakt z Flashem, Jake odpowiada: „Wiele lat temu wpadła mi w oko witryna Foxa zrobiona dla The Simpsons za pomocą FutureSplash. Była dość prosta: tylko rollovery i nieskomplikowane animacje, ale sam pomysł wektorowej animacji i wynikające z tego zmniejszenie rozmiarów plików rzuciły mnie na kolana”.
<drobny druk koniec>
<szara ramka koniec>
Podsumowanie
Ruch, jaki obserwujemy w świecie rzeczywistym, rzadko bywa prostoliniowy. Zakrzywienie toru poruszającego się obiektu to sposób na uzyskanie bardziej realistycznej animacji.
Ruch obiektu możesz opisać matematycznie w użytym przez Ciebie algorytmie, definiując takie jego parametry jak: kształt trajektorii, punkt końcowy i czas trwania ruchu. Wszystkie te parametry mogą być losowo zmieniane.
Wielu doświadczonych użytkowników Flasha umieszcza skrypty nie w listwie czasowej głównego filmu, lecz w listwach czasowych klipów filmowych.
Dzięki detektorom zdarzeń typu onClipEvent() każdy klon symbolu typu Movie Clip może inicjalizować własny zestaw zmiennych i realizować własne, odrębne zadania.
Jeśli postarasz się tak projektować elementy interfejsu, by łatwo je było ponownie wykorzystać, będziesz mógł na ich bazie tworzyć zróżnicowane stylowo interfejsy w znacznie krótszym czasie.
Korzystając z obiektu „datowego”(Data Object), możesz utworzyć we Flashu prawdziwy zegar. Akcja getTimer pozwala regularnie aktualizować wyświetlany przezeń czas.
17
[Author ID1: at Thu Nov 22 13:59:00 2001
][Author ID1: at Thu Nov 22 13:59:00 2001
][Author ID1: at Thu Nov 22 13:59:00 2001
]Strona: 1
[Author ID1: at Thu Nov 22 13:59:00 2001
][Author ID1: at Thu Nov 22 13:59:00 2001
]proszę rozważyć konieczność zapisu tego fragmentu kursywą, według mnie, jest to niepotrzebne mnożenie bytów[Author ID1: at Thu Nov 22 13:59:00 2001
]
Strona: 1
DO SKŁADU: uwaga! u mnie się to wszystko mieści, ale gdyby w składzie trzeba było przerzucić część powyższej linijki dalej, to na końcu trzeba zamieścić znaczek przeniesienia ¬ o którym Autor pisał. To samo z każdą inną linią zasadniczego kodu. Natomiast komentarze można dzielić na linie dowolnie, tylko na początku każdej linii musi być // (od tłumacza)