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łów
Giganty ś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
Kod:
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
Kod:
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
Kod:
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
Kod:
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 512x384 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ą
Kod:
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});
[kolor=red]SwiatProstokatnyWarstwy[2,a,b]:=TKostkaMapy.Kreacja(DaneMapy.OmegaSprite,
DAneMapy,
0,0,2,a,b,b+3{czyli z});[/kolor]
end;
end;
oraz
Kod:
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;
[kolor=red]SwiatProstokatnyWarstwy[2,a,b].ZmianaKlatki;[/kolor]
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
Kod:
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
Kod:
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.
Pobierz załącznik