ANIMACJE WARSTW- IZOMETRIA W ŚWIECIE PROSTOKĄTNYM- CZEŚĆ 3
Witam wszystkich czytających ten artykuł. Powiedzmy, że jest to część trzecia cyklu o izomerii w świecie prostokątnym, choć temat poruszany w tym artykule można zastosować w dowolnie innym modelu świta 2D. Poniższy zrzut ekranu z działającego kodu dołączonego do artykułu wprowadzi w temat.
Na obrazie nie widać animacji, ale po uruchomieniu dema i po dojściu w to miejsce świata powinieneś Czytelniku zobaczyć:
-animację wody
-animację rosnącego zboża
-animację wirnika wiatraka
-animację koła młyńskiego
Sposób realizacji animacji nie jest trudny. Przebiega on dokładnie tak samo jak animacja ruchu postaci. Kolejne klatki są przerzucane przez zegar gry. W tym tekście chciałbym zwrócić uwagę na pewien problem:
Kiedy animować?
Świat gry można podzielić na warstwy, w których mogą się znaleźć: obiekty żywe, obiekty statyczne animowane lub nieanimowane.
Każdy obiekt dla stanu gry reprezentowany jest przez układ liczb- parametrów. Dla człowieka- gracza wystarczy tylko grafika. Stąd można dokonać kolejnego podziału obiektów, który prawidłowo przemyślany będzie decydować o prędkości gry. Mam nadzieję, że poniższym rysunkiem uda mi się lepiej wprowadzić w omawiane zagadnienie.
Niebieski obszar to kafle widoczne. Jednocześnie są to warstwy utworzone na zasadach opisanych w cyklu artykułówGiganty świata 2D. Do kafli tych warstw są ładowane dane przechowywane w szarych kaflach mapy świata na zasadzie taśmociągu. Idea też jest opisana w Gigantach… Taki układ organizacji podsuwa pewien pomysł na animację. Otóż animowane klatki warstw można podzielić na animację wpływającą na zachowanie poza częścią widoczną lub nie wpływającą. To znaczy: na przykład animacja wody nie ma żadnego wpływu na to, co się dzieje poza częścią widoczną. To samo dotyczy ruchu wirnika wiatraków czy koła młyńskiego. Przykładów można mnożyć. Ale może wystąpić taka animacja, która stanem swych klatek będzie o czymś decydować i poza częścią widoczną. Może to być zboże lub inne uprawy. Chodzi mi o to, że żniwa będą w tedy, gdy zboże dojrzeje. Czyli jest to na przykład dziesiąta klatka animacji. Tak więc, życie poza częścią widoczną musi się toczyć na tych samych zasadach co w części widocznej. Jedynie rezygnuje się z wyświetlania klatek. Ze zliczania nie. Mając to na uwadze należy wprowadzić podział animowanych obiektów.
W kodzie programu utworzyłem do tego zarys klasy TCykl, zapisanej w unit UnitCyklSwiata. Rozbudowując tą klasę można obsłużyć wiele zdarzeń. Na przykład produkcja jednostek, szkolenie itp. Procedura zliczająca klatki dla obiektów dopisanych do TList tej klasy wywoływana jest w zegarze gry
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
procedure TForm1.OmegaTimer1Timer(Sender: TObject); var x:integer; begin Cykl.deltaSeconds:=OmegaTimer1.DeltaSecs; OmegaInput1.Update; if oisbutton28 in OmegaInput1.keyboard.statesclicked then OmegaScreen1.ToggleFullScreen; if not omegascreen1.CanDraw then exit; OmegaScreen1.BeginRender; OmegaScreen1.ClearScreen(0,0,0,0); if fZaladowanoPlik then begin Cykl.Animacja; ... |
Wynik zliczeń animacji jest pamiętany w wybranych kaflach świata gry. Jest to ten rekord danych
1 2 3 4 5 6 7 8 9 |
tAnimacja=record fIdObAnim:Word;//indeks odpowiadajacy pozycji typu ob. animacji w tObiektyAnimacji=(oWoda,oWoda1,... fAnimPos1, fAnimPos2 :single;//Liczniki animacji end; |
Dla elementów typu woda, wiatrak, młyn itp. Takie zliczanie odbywa się w kaflach warstwach widocznych. Takich kafli jest dużo mniej niż wszystkich kafli świata 2D. Wynik zliczeń jest zapisywany tym samym rekordzie pojedynczego kafla i przerzucany podczas przesuwania świata na tych samych zasadach co obrazy. W kodzie programu wykonuje to nowa procedura dopisana w tej części
procedure Animacja;
Procedura ta występuje w klasie TKostkaMapy oraz TKostkaGruntuMapy Jedynie należy podczas ładowania świat zdecydować, co mam być animowane. Konkretnie wybrać kafle.
Wybór kafli do animacji
Jest to ważny element struktury organizacji zapisu świata gry. Ja ze swojego rozwiązania nie jestem w pełni zadowolony. Otóż świat buduję w swoim edytorze, który ma być uniwersalnym edytorem. No i do końca nie jestem zdecydowany jak tą uniwersalność zapewnić. Tak wiec w kodzie dołączonym do artykułu dopisałem pewną stałą tablicę przechowującą dane o animowanych obiektach
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
DaneObiektu:array [0..9]of tDaneAnimacji=( (fAnimacja1:false;fAnimacja2:false) ,//oBrak (fAnimacja1:true;fdV1:2 ;L1:4 ;P1:12),//oWoda (fAnimacja2:true;fdV2:2 ;L2:4 ;P2:12),//oWoda1 (fAnimacja1:true;fdV1:45;L1:0 ;P1:11),//oZboze1 (fN:'lopatyS';fAnimacja1:true;fdV1:5 ;L1:0 ;P1:23), //wiatrakS-wirnik animowany w pierwszym obrazie, (fN:'lopatySW';fAnimacja1:true;fdV1:5 ;L1:0 ;P1:23), //wiatrakSW-wirnik animowany w pierwszym obrazie, (fN:'lopatySE';fAnimacja1:true;fdV1:5 ;L1:0 ;P1:23), //wiatrakSE-wirnik animowany w pierwszym obrazie, (fN:'koloS' ;fAnimacja2:true;fdV2:4 ;L2:0 ;P2:31),//oMlynS-koło animowane w drugim obrazie (fN:'koloSW';fAnimacja2:true;fdV2:4 ;L2:0 ;P2:31),//oMlynSW-koło animowane w drugim obrazie (fN:'koloSE';fAnimacja2:true;fdV2:4 ;L2:0 ;P2:31) //oMlynSE-koło animowane w drugim obrazie ); |
Teraz jedynie podczas ładowania gry sprawdzam czy danej klatce warstwy istnieje wybrany obiekt. Jeżeli tak to na taką klatkę ustawiam flagę animacji. Patrz kod
1 2 3 |
UstawAnimacjeWKaflu(PMapa(Lista.Items[0])^[aI]);// animacja Wody UstawAnimacjeWKaflu(PMapa(Lista.Items[1])^[aI]);//animacja Wiatrak, Młyn |
Takie podejście wydłuża czas ładowania pliku.A plik jest duży bo zawiera 512×384 kafli w trzech warstwach. Co daje 589824 kafli. Taki mały gigant. Lepszym rozwiązaniem byłoby ustalenie tego w momencie tworzenia świata. Czyli edytorze.
Animacja w pojedynczym kaflu
W tej części do kodu wprowadziłem do wszystkich warstw możliwość wyświetlania dwóch obrazów w jednym kaflu. W poprzedniej części było to zastosowane do płynnych przejść terenu. Teraz taką właściwość posiada każdy kafel. Praktycznie daje to nam aż sześć warstw rysunku. Dużo. Można, więc zrezygnować z trzeciej warstwy. Na pewno wydatnie przyspieszy to grę. Aby to sprawdzić należy wyrzucić z kodu te linie zaznaczone czerwoną czcionką
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
procedure TForm1.CzysteMapy; var AMapa:PMapa; a,b, i:LongInt; begin ….. for b:=0 to DaneMapy.YSwiataEkranu-1 do for a:=0 to DAneMapy.XSwiataEkranu-1 do begin //warstwa ZERO- nowy pomysł SwiatProstokatnyGrunt[a,b]:=TKostkaGruntuMapy.Kreacja( DAneMapy, 0,0,0,a,b); //warstwa 1 SwiatProstokatnyWarstwy[1,a,b]:=TKostkaMapy.Kreacja(DaneMapy.OmegaSprite, DAneMapy, 0,0,1,a,b,b{czyli z}); SwiatProstokatnyWarstwy[2,a,b]:=TKostkaMapy.Kreacja(DaneMapy.OmegaSprite, DAneMapy, 0,0,2,a,b,b+3{czyli z}); end; end; |
oraz
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
procedure TForm1.OdswiezEkran; var a,b:byte; begin //odswiez ekran for b:=0 to DaneMapy.YSwiataEkranu-1 do for a:=0 to DAneMapy.XSwiataEkranu-1 do begin SwiatProstokatnyGrunt[a,b].ZmianaKlatki; SwiatProstokatnyWarstwy[1,a,b].ZmianaKlatki; SwiatProstokatnyWarstwy[2,a,b].ZmianaKlatki; end; end; |
Ale nie o tym chcę tu napisać. Na stronie grafika 2D z której pobrałem grafikę klatki do animacji na przykład młyna są tak zorganizowane
Co prowadzi do takiego rozwiązania: wyświetl budynek a potem animowane klatki koła w odpowiednich współrzędnych. Cały proces animacji przebiega w tej procedurze
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
procedure TKostkaMapy.Animacja; begin with PMapa(Lista.Items[idWskMApy])^[IDKafla] do begin //animacja pierwszego obrazu if DaneObiektu[Animacja.fIdObAnim].fAnimacja1 then begin Animacja.fAnimPos1:=Animacja.fAnimPos1+Form1.OmegaTimer1.DeltaSecs; if Animacja.fAnimPos1>Form1.OmegaTimer1.DeltaSecs*DaneObiektu[Animacja.fIdObAnim].fdV1 then begin inc(IdKlatka,1); Animacja.fAnimPos1:=0; end; if IdKlatka>DaneObiektu[Animacja.fIdObAnim].P1 then IdKlatka:=DaneObiektu[Animacja.fIdObAnim].L1; end; //animacja drugiego obrazu if DaneObiektu[Animacja.fIdObAnim].fAnimacja2 then begin Animacja.fAnimPos2:=Animacja.fAnimPos2+Form1.OmegaTimer1.DeltaSecs; if Animacja.fAnimPos2>Form1.OmegaTimer1.DeltaSecs*DaneObiektu[Animacja.fIdObAnim].fdV2 then begin inc(RGBA.idKlatka2,1); Animacja.fAnimPos2:=0; end; if RGBA.idKlatka2>DaneObiektu[Animacja.fIdObAnim].P2 then RGBA.idKlatka2:=DaneObiektu[Animacja.fIdObAnim].L2; end; end; end; |
Dla warstwy gruntu jest bardzo podobnie. Jedynie nie ma tam możliwości nakładania klatek z poza obrazu innego niż rysunek wyświetlany na pierwszym obrazie. Ponieważ zakładam ze cały grunt i wszystkie jego klatki animacji można, zapisać w jednym obrazie. Tak jak poniżej
Można teraz sobie wyobrazić, że dodajemy tu klatki animacji błota, lawy, ruchomych pisaków itp. Jednym obrazem załatwiamy cały grunt świata 2D. Wyświetlanie obu obrazów w jednym kaflu wykonuje procedura
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
procedure TKostkaMapy.Draw; begin if (ImageIndex=-1)or(fIDKafla=-1)then exit; Animacja; OdswiezKlatke; Image.Draw(Round(x+left),Round(y+top),0,0.5,0.5,1,1,Red,Green,Blue,Alpha,ImageIndex,0); if (idKlatka2=-1)then exit; fOmegaImageList.ImageList.Items[idObrazka2].Draw(Round(x+left+left2),Round(y+top+top2), 0,0.5,0.5,1,1,Red,Green,Blue,Alpha,idKlatka2,0); end; |
I to by było na tyle w tej części.
Pozdrawiam Adam Lasko (oksal) Zbylitowska Góra 1 XII 2007
PS
Do prawidłowego działania przykładu wymagane są te pliki
swiat_512x384.mue2D
teren_rosliny.oil
swiatynie.oil
zamki.oil
pierwotny001.oil
wiatrak.oil
mlyn.oil
D3DX81AB.DLL
MPPSDK.DLL
Plik swiat_512x384.mue2D został zmieniony co do organizacji zapisu. Nie będzie prawidłowo czytany dla poprzednich części i na odwrót.
Autor: oksal