Programowanie animacjiW tym tutorialu dowiesz się co należy napisać w pliku jednostki, aby mogła ona wykorzystać narysowane przez Ciebie animacje.
Bloki animacjiJednostki w BfW posiadają mnóstwo najróżniejszych animacji począwszy od animacji ataku/obrony, przez animacje bezczynności, aż po animacje specjalne.
Każda animacja to sekwencja klatek, pojedynczych obrazków wyświetlanych jeden po drugim. Cała taka pojedyncza sekwencja nazywa się blokiem. Blok mieści się pomiędzy tagami, których nazwy określają jaka to animacja, np. dla animacji ataku będzie to:
[attack_anim] ... (animacja) ... [/attack_anim] |
a dla animacji bezczynności:
[idle_anim] ... (animacja) ... [/idle_anim] |
Wszystkie tagi określające bloki animacji zostaną omówione w dalszej części.
KlatkiKlatki animacji (ang. frames) to pojedyncze obrazki, które ułożone w odpowiedniej kolejności tworzą animację. W pliku jednostki pojedyncza klatka opisana jest tagiem [frame]. Tam umieszcza się ścieżkę do obrazka, ustawia czas wyświetlania klatki oraz wiele innych atrybutów.
[attack_anim] ... [frame] image="(ścieżka)" duration=100 (czas wyświetlania klatki na ekranie liczony w milisekundach) [/frame] ... [/attack_anim] |
Można mieć kilka klatek wyświetlanych w tym samym czasie. Przykładem tego jest animacja strzelania z łuku. Mamy tam dwa zestawy klatek - jednostkę i strzałę, które animowane są w tej samej chwili niezależnie od siebie (cały czas jest to jedna animacja ataku). Jest to możliwe dzięki zastosowaniu różnych nazw dla zestawów klatek.
Trochę skomplikowanie to brzmi, ale w rzeczywistości tak nie jest. Akurat w tym przykładzie z łukiem mamy dwa zestawy klatek: [frame][/frame] dla jednostki i [missile_frame][/missile_frame] dla strzały. Takie zestawy możemy nazywać po swojemu, ale trzeba pamiętać żeby wyglądało to według schematu [xxx_frame], gdzie xxx to nasza własna nazwa zestawu.
Timing, czyli sterowanie długością animacjiOgromną rolę w animacji odgrywa czas. W grze animacje liczone są w ms (milisekundach) - 1000 ms = 1 sekunda. Każda klatka widnieje na ekranie określoną liczbę milisekund po czym jest zastępowana przez następną i tak aż do zakończenia odtwarzania całej animacji (odpowiada za to parametr
duration - czas trwania). Blok animacji zaś dysponuje tzw. czasem startowym
start_time, który informuje grę, kiedy odtwarzać animację. Dla prostych animacji będzie on wynosił zero, ale dla animacji ataku i obrony już nie.
W animacjach ataku/obrony wszystko liczone jest względem zera, które odpowiada momentowi zadania ciosu. Wszystko przed tym (wartości ujemne) odnosić się będzie animacji, kiedy to jednostka podchodzi do drugiej aby się zamachnąć na nią mieczem, a wszystko po tym (wartości dodatnie) odpowiada tej części aniamcji, kiedy jednostka powraca na miejsce po ataku.

Na tej osi czasu pokazana jest cała animacja ataku Łucznika. Na dole mamy czas liczony przez silnik gry. Animacja zaczyna się wyświetlać 275 milisekund przed zadaniem ciosu (start_time=-275). Po wykonaniu ataku jendostka powraca na swoją pozycję i zajmuje jej to dokładnie 225 ms. Cała animacja trwa 275+225=500ms czyli pół sekundy.
Czas podany pomiędzy obrazkami to
duration - czas wyświetlania konkretnej klatki. I tak w naszym przykładzie pierwsza klatka wyświetlana jest przez 50ms, druga przez 100ms, tak samo trzecia, czwarta i piąta, 50ms trwa szósta, a siódma to normalny obrazek jednostki i nie liczy się do animacji (dałem go tylko dla pokazania całego procesu).
Tak wygląda kod tej animacji (im więcej klatek, tym bardziej skomplikowana animacja):
[attack_anim]
[filter_attack] name=short sword (animacja wyświetla się przy użyciu tylko tego ataku) [/filter_attack]
start_time=-275 (czas startowy)
[frame] duration=50 image="units/human-loyalists/bowman.png" [/frame] [frame] duration=100 image="units/human-loyalists/bowman-melee-attack-1.png" [/frame] [frame] duration=100 image="units/human-loyalists/bowman-melee-attack-2.png" [/frame]
(tutaj pojawia się warunek odnośnie trafienia, aby dobrze dobrać dźwięk)
[if] hits=yes (jednostka trafia przeciwnika) [frame] duration=100 image="units/human-loyalists/bowman-melee-attack-3.png" sound={SOUND_LIST:SWORD_SWISH} (odgłos miecza) [/frame] [/if] [else] hits=no (jednostka nie trafiła) [frame] duration=100 image="units/human-loyalists/bowman-melee-attack-3.png" sound={SOUND_LIST:MISS} (odgłos chybienia) [/frame] [/else]
(koniec warunku, ciąg dalszy animacji)
[frame] duration=100 image="units/human-loyalists/bowman-melee-attack-4.png" [/frame] [frame] duration=50 image="units/human-loyalists/bowman-melee-defend-1.png" [/frame]
[/attack_anim] |
Parametry klatkiOmówię teraz jakie najważniejsze właściwości pojedynczej klatki [frame] możemy ustawiać:
duration= (czas wyświetlania klatki w ms)
image= (ścieżka do obrazka)
image_diagonal= (ścieżka do obrazka, który ma być wyświetlany przy ataku po skosie (kierunki sw,se,nw,ne)
sound= (ścieżka do pliku dźwiękowego, jaki ma być odegrany przy tej klatce)
halo= (ścieżka do obrazka, najczęściej magicznej aury i innych efektów specjalnych, wyświetlanych nad wszystkimi innymi obrazkami)
offset= (przesunięcie, przydatne przy strzałach. Dzięki temu parametrowi można decydować o pozycji obrazka w trakcie animacji. Przyjmuje on wartość od -1.0 do 1.0, gdzie 0.0 to środek własnego heksa, 1.0 środek heksa przeciwnika, a -1.0 środek heksa za nami. Za pomocą znaku tylda ~ można rozkazać grze, aby podczas danej klatki obrazek przemieścił się na określoną pozycję, np. z 0.0 do -1.0 będzie to offset=0.0~-1.0)
alpha= (przezroczystość obrazka, przyjmuje wartości od 0.0 do 1.0 i tu też można wykorzystać tyldę)
(drobna uwaga odnośnie tyldy: znak ten znajduje się na klawiaturze pod klawiszem Esc w lewym górnym rogu. Aby go napisać, trzeba mieć wciśnięty Shift i wtedy nacisnąć tyldę. Znak ten pojawić się może dopiero po zrobieniu spacji lub wciśnięciu innego klawisza, tak więc nie panikuj jak na początku nie ujrzysz go na ekranie).
Najlepiej uczyć się programowania animacji na przykładach. Weźmy więc jednostkę Chłopa i przyjrzyjmy mu się uważnie:Nie będę wklejał całego kodu jednostki, bo jest długi. Będę omawiał tylko fragmenty związane z animacją.
Pierwszy blok animacji jaki możemy spotkać u Chłopa to animacja bezczynności [idle_anim]:

Od strony WML wygląda tak (trochę uprościłem, bo oryginał zawierał przestarzały kod i inne śmieci):
[idle_anim] start_time=0 [frame] duration=100 image="units/human-peasants/peasant-idle-1.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-2.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-3.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-4.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-5.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-6.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-7.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-4.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-2.png" [/frame] [frame] duration=100 image="units/human-peasants/peasant-idle-1.png" [/frame] [/idle_anim] |
Animacja zaczyna się w punkcie zero (czyli normalnie, bo to nie jest animacja ataku/obrony). Dlatego też start_time wynosi 0. Równie dobrze możemy to pominąć, wtedy gra automatycznie założy, że animacja rozpoczyna się w punkcie 0.
Kolejną animacją jest animacja ataku na odległość a dokładniej rzutu widłami.
[attack_anim]
[filter_attack] name=pitchfork range=ranged [/filter_attack]
start_time=-200 missile_start_time=-150
[missile_frame] duration=150 image="projectiles/pitchfork-n.png" image_diagonal="projectiles/pitchfork-ne.png" [/missile_frame]
[frame] duration=100 image="units/human-peasants/peasant-attack2.png" sound={SOUND_LIST:THROW} [/frame]
[if] hits=yes [frame] duration=50 image="units/human-peasants/peasant-ranged.png" sound=spear.ogg [/frame] [/if] [else] hits=no [frame] duration=50 image="units/human-peasants/peasant-ranged.png" [/frame] [/else]
[/attack_anim] |
Tu już jest bardziej skomplikowana sprawa, bo mamy 2 zestawy klatek - jeden dla jednostki, a drugi dla lecących w powietrzu wideł :P Ale po kolei:
[filter_attack]
name=pitchfork
range=ranged
[/filter_attack]To filtr animacji. Mówi on grze, że ta animacja ataku zostanie wyświetlona tylko wtedy, gdy jednostka użyje ataku o nazwie pitchfork (name=pitchfork) i będzie to atak na odległość.
start_time=-200
missile_start_time=-150Te dwie linijki określają czas rozpoczęcia wyświetlania animacji dla poszczególnych zestawów klatek - zwykłego i missile, czyli naszego pocisku (u nas wideł). Z missile_start_time możemy szybko wywnioskować jak długo widły będą leciały w powietrzu. W naszym przypadku 150 milisekund.
[missile_frame]
duration=150
image="projectiles/pitchfork-n.png"
image_diagonal="projectiles/pitchfork-ne.png"
[/missile_frame]To nasz pierwszy zestaw, odpowiadający animacji wideł. Jest to bardzo prosta animacja składająca się tylko z jednej klatki, która trwa 150ms i posiada dwa warianty obrazka - widły w wariancie północ-południe i widły po skosie :P Warto sprawdzić w katalogu images jak to wygląda, żeby wiedzieć o co chodzi.
Tag [missile_frame] automatycznie zawiera parametr offset ustawiony na 0.0~1.0 (obrazek wideł przemieszcza się ze środka naszego heksa na środek heksa przeciwnika), więc nie musimy tego pisać.
[frame]
duration=100
image="units/human-peasants/peasant-attack2.png"
sound={SOUND_LIST:THROW}
[/frame]
[if]
hits=yes
[frame]
duration=50
image="units/human-peasants/peasant-ranged.png"
sound=spear.ogg
[/frame]
[/if]
[else]
hits=no
[frame]
duration=50
image="units/human-peasants/peasant-ranged.png"
[/frame]
[/else]To drugi zestaw klatek. Odpowiada jednostce. W pierwszych 100ms wyświetla się obrazek "peasant-attack2.png" i emitowany jest dźwięk rzutu z makra
{SOUND_LIST:THROW}. Makro to losuje kilka różnych dźwięków rzutu i wybiera jeden, tak aby uniknąć monotonii.
Następnie mamy warunek. Jeśli jednostka trafia to emitowany jest dźwięk trafienia włócznią "spear.ogg", jeśli natomiast jednostka chybi, to nie ma żadnego dźwięku. Na tym kończy się cała animacja.
[attack_anim]
[filter_attack] name=pitchfork range=melee [/filter_attack]
start_time=-250
[frame] duration=50 image="units/human-peasants/peasant.png" [/frame]
[if] hits=yes [frame] duration=250 image="units/human-peasants/peasant-attack.png" sound=spear.ogg [/frame] [/if] [else] hits=no [frame] duration=250 image="units/human-peasants/peasant-attack.png" sound=spear-miss.ogg [/frame] [/else]
[frame] duration=100 image="units/human-peasants/peasant-attack2.png" [/frame] [frame] duration=50 image="units/human-peasants/peasant.png" [/frame]
[/attack_anim] |
Tu jest podobnie jak wyżej, tylko prościej, bo mamy jeden zestaw klatek odnoszący się tylko do jednostki. Chłop w ciągu 250ms dobiega do przeciwnika i wymierza cios. Potem w ciągu 200ms powraca na miejsce. Cała animacja trwa 450ms (50+250+100+50).
Przy animacji ataku i obrony trzeba umiejętnie określić czas rozpoczęcia, tak aby zgadzał się on z klatką animacji, odpowiadającą za wymierzenie ciosu.
W przykładzie ataku chłopa wygląda to na osi tak:

Widać tu dokładnie, gdzie przypada moment zero. Będzie miał miejsce w trakcie wyświetlania drugiej klatki. Akurat klatka ta została narysowana właśnie w taki sposób, aby odzwierciedlić cios (chłop trzyma wtedy widły w pozycji, jakby chciał kogoś nadziać). Dzięki temu mamy zachowany realizm. Czasem warto rozrysować sobie taką oś na kartce i dopiero wtedy programować czas.
Jeśli chodzi o animacje obrony, to są one podobne do animacji ataku. Jednak tutaj doradzałbym skorzystanie z uniwersalnego makra, którego używa sam chłop:
{DEFENSE_ANIM OBRAZEK_OBRONY OBRAZEK_PODSTAWOWY ODGLOS_ZRANIENIA}z uzupełnionymi wartościami:
{DEFENSE_ANIM "units/human-peasants/peasant-defend.png" "units/human-peasants/peasant.png" {SOUND_LIST:HUMAN_OLD_HIT} }Makro to jest prostym blokiem animacji obrony, składającym się z 3 klatek, z których 2 się powtarzają. Tylko środkowa klatka odpowiada za obrazek, w którym jednostka broni się. Powstaje dzięki temu dosyć prosta sekwencja, ale za to wystarczająca w większości przypadków. I ja to makro polecam każdemu. W pliku peasant.cfg znaleźć je można w linijce 22.
Uwaga odnośnie oryginalnego kodu animacji Chłopa:Jeśli zajrzysz do pliku peasant.cfg, znajdziesz zamiast parametru duration coś takiego:
begin=
end=
Jest to alternatywna metoda określania czasu wyświetlania animacji. Jest ona jednak przestarzała i niedługo zostanie usunięta z gry. Odradzam więc korzystanie z niej.
Rodzaje bloków animacjiTeraz omówię rodzaje animacji jednostek w Battle for Wesnoth i podam odpowiadające im nazwy tagów:
[leading_anim] - animacja dowodzenia (powinna zaczynać się od wartości negatywnych, np. -125. Punkt 0 odpowiada ciosowi jednostki wspieranej)
[recruit_anim] - animacja rekrutacji (wyświetla się przy rekrutowaniu oddziałów)
[standing_anim] - animacja postoju (wyświetla się cały czas, mają ją, np. nietoperze)
[idle_anim] - wyświetla się co jakiś czas (u chłopa to było podniesienie kapelusza)
[levelin_anim] - animacja wyświetlana przed awansowaniem jednostki na wyższy poziom
[levelout_anim] - animacja wyświetlana po awansie jednostki
[healing_anim] - animacja leczenia
[healed_anim] - animacja bycia leczonym :P
[poison_anim] - działa jak animacja postoju, tylko, że wyświetlana jest przy zatruciu
[movement_anim] - animacja przemieszczania się
[pre_movement_anim] - animacja przygotowania się do ruchu (np. startujący smok)
[post_movement_anim] - animacja kończenia ruchu (np. lądujący smok)
[draw_weapon_anim] - animacja przygotowania broni do walki
[sheath_weapon_anim] - animacja schowania broni po walce
[defend] - animacja obrony
[attack_anim] - animacja ataku
[death] - animacja śmierci
[victory_anim] - animacja zwycięstwa po wygranym pojedynku
[teleport_anim] - animacja teleportu (przed 0, animacja wejścia, po 0 animacja wyjścia)
[extra_anim] - animacja własna. Trzeba umieścić w niej parametr flag= i nadać mu nazwę. Potem taką animację można wywołać, np. w scenariuszu za pomocą tagu [animate_unit].
Uniwersalna rada na sam koniec:Jeśli nie wiesz jak zaprogramować konkretną animację, podejrzyj jak jest ona zrobiona u innych jednostek.
Wszelkie pytania i problemy kieruj na forum.
W następnym odcinku zajmiemy się
tworzeniem zdolności dla jednostek.