fizyka wzory prawa zadania teoria, chemia teoria zadania wzory, modelarstwo szkutnicze
 tworzenie gier 2d wędrowanie w świecie gry d41.jpg

Pobierz kod Wędrowanie w świecie 2D- część 5

 

WĘDROWANIE W ŚWIECIE 2D (część 5)

BOHATER SPOTYKA DUŻO DUŻYCH BOROSTWORÓW...


 tworzenie gier 2d wędrowanie w świecie gry d51.png


i jest to duży problem, nie ze względu na charakter spotkania (choć bohater świata 2D ma inne zdanie) ale na sposób oprogramowania natury BOROSTAWORA. Taki stwór musi się jakoś zachowywać a jego duszek powinien być oprogramowany według tych samych zasad, co duszek bohatera.

Ten artykuł porusza problem duszka stwora. Jego AI zostawiam na poziomie losowego doboru kierunku ruchu.Klasa borostwora znajduje się w pliku UnitStwor000.pas;

ZAŁOŻENIA

a) W zależności od kierunku ruchu odpowiednio dobierana jest animacja klatek
b) Duszek borostwora zmienia kolor w cieniu przeszkody na tych samych zasadach co duszek bohatera
c) Wykrywa kolizje z pniem drzewa
d) Losowo dobiera kierunek ruchu
e) Wchodzi do karczmy
f) Nie ucieka poza obszar dostępnego świta

Jak widać warunki b, c, e są wspólne z klasą duszka bohatera. Ten fakt prowadzi do wniosku, że należy utworzyć wspólna klasą - rodzica dla bohatera i borostwora i z tej klasy tworzyć "dzieci" tych duszków. Pisząc część pierwszą "Wędrówek w 2D" nie przewidywałem występowania borostworów, stąd klasę duszka borostwora utworzyłem bezpośrednio na TSprite. Choć można było wybrać i TSimpleAnimSprite, która to posiada mechanizm animacji klatek. W TSprite też da się to zrobić i to w trzech linijkach:

IDAnimacji:=IDAnimacji+dVA*Value;//dVA to predkosc animacji, Value to OmegaTimer1.DeltaSecs
if IDAnimacji >2 then IDAnimacji:=0;
ImageIndex:=Round(IDAnimacji+OfsetKlatki);




Dodatkowo osoby zainteresowane maja odpowiedź jak oprogramować duszka bohatera, aby się zmieniały jego klatki ruchu.

Tyle słowa wstępnego. Przystępujemy do pracy...

ALGORYTM DUSZKA BOROSTWORA...

Zegar gry wywołuje w takiej kolejności procedury związane z obsługa klasy bazowej duszka borostwora- TSprite

OmegaSprite1.Collision;
OmegaSprite1.Dead;// ta nas nie interesuje bo nic nie usmiercamy
OmegaSprite1.Draw;
OmegaSprite1.Move(OmegaTimer1.DeltaSecs); //czesc piąta- zamiana miejscami z OmegaSprite1.Draw;




Co prowadzi do takiego postępowania:
a) Sprawdź kolizję z innymi borostworami i duszkiem gracza
b) Rysuj duszka borostwora według tych samych procedur co bohatera ( omówiłem w poprzednich częściach)
c) Podejmij akcję związaną ze zmianą położenia

Krok a nie wykrywa kolizji z pniami tylko z duszkami obiektów żywych a to tylko dla tego, że jak przyjdzie do szukania drogi do wskazanego celu to na takim teście kolizji nic nie znajdziemy. Drogi się szuka z siatek mapy np algorytm Dijkstry

Krok b jest przeniesiony z klasy TGracz

Krok c to przebudowana procedura Move(const MoveCount:single) oraz MoznaIsc(MoveCount). Ich zadanie polega na symulowaniu decyzji związanych z ruchem borostwora. W przypadku gracza było to banalne wystarczyło odczytać jaki klawisz wciśnięto na klawiaturze. Tu na podstawie mapy wykonujemy ruch

BOROSTWÓR ZMIENIA POZYCJĘ

Chodzi o ten fragment kodu programu

procedure TStwor.MoznaIsc(const Value:single);
var
stKierunek:tkierunek;
begin

stareX:=x;
stareY:=y;
case kierunek of
kierN : kierunekN (2*Value);
kierNE : kierunekNE(2*Value);
kierE : kierunekE (2*Value);
kierSE : kierunekSE(2*Value);
kierS : kierunekS (2*Value);
kierSW : kierunekSW(2*Value);
kierW : kierunekW (2*Value);
kierNW : kierunekNW(2*Value);
end;
x:=x+Skok_X;
y:=y+Skok_Y;
JakaKostka;
x:=stareX;
y:=stareY;
if(Drogi[wd,kd]>-1)then begin
dVA:=0;
Odbicie;
Skok_X:=0;
Skok_Y:=0;
exit;//wyskocz z calej procedury
end;

IDAnimacji:=IDAnimacji+dVA*Value;//dVA to predkosc animacji
if IDAnimacji >2 then IDAnimacji:=0;
ImageIndex:=Round(IDAnimacji+OfsetKlatki);
dVA:=7;//ponownie przypisz predkość animacji bo istnieją funkcje które ją zerują
//podczas kolizji- zmniejsza to migotanie

x:=x+Skok_X;
y:=y+Skok_Y;
end;




Jest to przebudowana procedur z klasy TGracz. Idea ta sama co w analogicznej funkcji ruchu Gracz (wyjaśnienie w poprzednich częściach). Zmiana położenia odbywa się w funkcjach kierunku (osiem kierunków zgodnie z Różą Wiatrów)
kierunekN (2*Value); kierunekNE (2*Value); i tak dalej. Zastępuje to wciskanie klawiszy ruchu. Tu podkreślam, że te funkcje wyznaczają zmianę współrzędnych według tych linijek

x:=x+Skok_X;
y:=y+Skok_Y;




Linijki te nie reagują na przesuwanie wykonane przez Gracza. Duszek Gracza zawsze jest we współrzędnych środka ekranu. To świat przesuwa się względem Gracza. I my ten fakt musimy uwzględnić. A wykonujemy to
w tym miejscu kodu



procedure TStwor.Move(const MoveCount:single);
begin
//przesun bo przesunieto swiat
x:=x+SkokX;
y:=y+SkokY;
BufordV:=MoveCount;
MoznaIsc(MoveCount);
........




Dodatkowo należy omówić procedurę

Odbicie;

Procedura ta, wyznacza wolne pola z tablicy Drogi przy kolizji z pniem. Z tych pól tworzona jest tablica dynamiczna i z niej losowo je wybierane takie pole poczym zostaje ona zwolniona. Idee działania przedstawię na jednej z wewnętrznych funkcji tej procedury.
Powiedzmy, że uderzenie w pień drzewa (matematycznie w pole siatek dróg) następuje z południa. Działanie funkcji nie będę opisywać. To można prześledzić w poniższych linijkach:

function zkierunkuS:tKierunek;
const
{układ: W X E
SW S SE}
ukalKierunk:array[0..4]of tKierunek=(kierW,kierE,kierSW,kierS,kierSE);
t:array[0..4,0..1]of integer=(( 0,-1), (0, 1),
( 1,-1),( 1, 0),(1, 1));
var
//tablica mozliwych kierunków
tabKier:array of tKierunek;
_k,_w:integer;
i,r:byte;
begin
r:=1;
setLength(tabKier,r);
for i:=0 to 4 do begin
_w:=stareWD+t[i,0];if _w<0 then _w:=0;if _w>19 then _w:=19;
_k:=stareKD+t[i,1];if _w<0 then _k:=0;if _k>19 then _k:=19;
if Drogi[_w,_k]=-1 then begin
tabKier[high(tabKier)]:=ukalKierunk[i];
inc(r);
setLength(tabKier,r);
end;
end;
//wylosuj dostepny kierunek
result:=tabKier[Random(high(tabKier)+1)-1];//[Random(high(tabKier))];
tabKier:=nil;
end;



Pomysł jaki tu wykorzystałem zobrazuje tym rysunkiem:

 tworzenie gier 2d wędrowanie w świecie gry d53.png

Polu z iksem, jest zajęte przez pień drzewa, BOROSTWÓR nadchodzi z kierunku południowego (strzałka czerwona). Możliwe pola po odbiciu wskazują strzałki niebieski. Teraz wystarczy wyznaczyć wiersze i kolumny takich pól, sprawdzić czy są dostępne do wykonania ruchu, utworzyć z nich tablicę i wylosować takie pole. Proste podejście, bo szybciej jest dostać się do indeksów niż wyznaczyć to z funkcji trygonometrycznych. Szybkie wyliczenie indeksów odbywa się w pętli poprzez dodani lub odjęcie liczby 1 (jeden) względem pola przeszkody. Czy dodawać lub odejmować to najłatwiej jest to określić stałymi wartościami przechowywanym w tablicy. Podam fragment z kodu

t:array[0..4,0..1]of integer=(( 0,-1),(0, 1),( 1,-1),( 1, 0),(1, 1));




Dla ułatwienia ułożyłem ją tak jak na powyższym rysunku. Wyznaczenie możliwych pól po odbici dla pozostałych kierunków są analogiczne.

KOLIZJE Z INNYMI DUSZKAMI

Przytoczę nagłówek procedury kolizji obiektu TSprite komponentu Omegi

onCollision(const Sprite:Tsprite;const colX,colY:integer);

Procedura ta podaje bardzo ważną informację. A mianowicie zwraca współrzędne zderzenia duszka z innym duszkiem. Wynik to colX, colY. Jak ktoś uważa na lekcjach fizyki lub świeci lustrem ludziom po oczach, to wie że kąt padania i odbicia promienia światła jest taki sam i leży na jednej płaszczyźnie.
Świat 2D z natury jest płaski- jeden warunek mamy już spełniony. Kąty obliczymy w prosty sposób (proszę dosłownie nie utożsamiać naszego kąta kierunku odbicia z prawidłowym rachunkiem wynikającym z tego prawa), gdy tylko coś zauważymy.

ALGORYTM

" podziel klatkę duszka na 4 części (dwie kolumny i dwa wiersze)
" przypisz kierunki z Róży Wiatrów tym częściom według tej kolejności: NE, NW, SE, SW (proszę zauważyć ze w cale nie podałem w prawidłowej kolejności to znaczy NW,NE, SW, SE, zbieg ten należy rozumieć tak: jeżeli uderzenie nastąpiło z kierunku NW to odbicie będzie w kierunku NE)
" sprawdź w której części- polu leżą współrzędne kolizji
" na podstawie indeksu pola określ kierunek odbicia

Poglądowy rysunek:

 tworzenie gier 2d wędrowanie w świecie gry d55.png

function TStwor.KierunekPoKolizji(const colX,colY:integer):tKierunek;
var
s,h:integer;
kwadrat,K,W:byte;//kolumna i wierzs kwadratu zderzenia
begin
s:=Image.TileWidth div 2;//wyznacz polowy z serokości i wysokosci klatki
h:=Image.TileHeight div 2;
k:=colX div s;//podziel na kolumny iwiersze
w:=colY div h;
kwadrat:=k+w*2;//oblicz kawdrat zderzenia
case kwadrat of
0:result:=kierNE;//na podstwaie kawdratu zderzenia wyznacz kierunek odbicia
1:result:=kierNW;
2:result:=kierSE;
3:result:=kierSW;
end;
end;


Oczywiście nic nie stoi na przeszkodzi aby zwiększyć podział na 8 kierunków (czyli 9 pól, środkowe jakoś w tedy należy potraktować inaczej lub przypisać mu już raz przypisany kierunek)

ZABEZPIECZEINIE PRZED UCIECZKĄ BOROSTWORA Z MAPY ŚWIATA

Podam najprostszy pomysł dla tego modelu organizacji świata 2D. Proszę zauważyć, że klatki mapy to też duszki. Znamy więc współrzędne ich rogów Wystarczy więc sprawdzać czy współrzędne X i Y borostwora nie wystają poza skrajne rogi mapy świata...Jeżeli wystają w boku zachodnim to zmień kierunek na zachodni itd. A są to te linie kodu:

//najprostszy z mozliwych warunek trzymania stworów w obrebie mapy
if xthen kierunek:=kierE;
if x>Warstwa0[0,18].x then kierunek:=kierW;
if ythen kierunek:=kierS;
if y>Warstwa0[19,0].y then kierunek:=kierN;


ANIMACJA KLATEK BOROSTWORAb>

Pod uwagę wezmę jedną z ośmiu funkcji wyznaczających kierunek ruchu. Kierunek ruchu decyduje jakie klatki maja być brane pod uwagę. Jeżeli popatrzymy na plik zasobu grafiki

 tworzenie gier 2d wędrowanie w świecie gry d57.png

To zauważymy, że aby iść na wschód to trzeba dostać się do pierwszej klatki z ostatniego wiersza Czyli 9 licząc od zera.

Jak to zrobić?

Nie jest to trudne, trzeba wprowadzić pewien skok w takiej tablicy klatek tu jest to liczba 9 (aby iść na południe to była by to liczba 3) A ze potrzebujemy 3 klatek ruchu w danym kierunku (klatka 0,1,2) to odpowiednio zwiększając taki indeks plus skok wynikający z obranego kierunku mamy gotową animację po klatkach.Wynik wstawiamy do ImageIndex

function TStwor.kierunekE (const V:Single):tKierunek;
begin
Skok_X:=25*V;
ofsetKlatki:=9;
result:=kierE;
end;

IDAnimacji:=IDAnimacji+dVA*Value;//dVA to predkosc animacji
if IDAnimacji >2 then IDAnimacji:=0;
ImageIndex:=Round(IDAnimacji+OfsetKlatki);


Stosując odpowiednie ofsety klatek możemy uzyskać różne animacje np. animacja strzału, skoku itp. Ważne, aby takie klatki były zapisane w jednym obrazie. O prędkości animacji decyduje ten warunek zwiększania licznika:



IDAnimacji:=IDAnimacji+dVA*Value;


Wartość dVA ustalona jest w momencie tworzenia duszka, Value jest pobierana z zegara aplikacji gry. Podczas wykonywania kodu gry jest ona zerowana, gdy występuje zderzenie z innym duszkiem. Unika się efektu migotania wynikającego z nakładania zmieniających się klatek tych obiektów. Może również posłużyć do przyspieszani a lub opóźniania, animacji w zależności od sytuacji na przykład bieg.


Nasze dzikie BOROSTWORY tworzymy tak

//część piata- tworzenie borostworów
randomize;
for k:=0 to 49 do
begin
//losuj wspołrzedne dla borostwoara- typ 1
x:=Random(1100)+50;
y:=Random(600)+20;
//sprwdz czy w tych wspolrzednych nie ma pnia drzewa
if Drogi[y div 32,x div 32]=-1 then
TworzBorostwora(x,y,OmegaSprite1,OmegaImageList1.ImageList.Items[9],1);
//losuj wspołrzedne dla borostwoara- typ 2
x:=Random(1100)+50;
y:=Random(600)+20;
if Drogi[y div 32,x div 32]=-1 then
TworzBorostwora(x,y,OmegaSprite1,OmegaImageList1.ImageList.Items[10],1);
end;


I będzie ich w najlepszym przypadku 100 sztuk.

Nasz BOROSTWÓR umie wchodzić do karczmy. Proszę sprawdzić. Wystarczy wejść do karczmy i troszkę w niej posiedzieć a na pewno ktoś do nas przyjdzie. I cieszmy się, że nie będzie chciał na piwo.

Alkomat- wirtualny test

Alkomat- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
Olinowanie stałe- kalkulator średnic

Olinowanie stałe- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
przepis na gogfry

Przepis na gofry

zobacz
przepis na bitą śmietanę

Przepis na bitą śmietanę

zobacz