logo2
Fizyka Chemia Matematyka strona główna   FIZYKA ZADANIA WZORY TEORIA ROZWIĽZANIA ZADAŃ   FIZYKA GIMNZAJUM ZADANIA WZORY TEORIA   CHEMIA ZADANIA WZORY TEORIA ROZWIĽZANIA ZADAŃ   MATEMATYKA WZORY ZADANIA ROZWIĽZANIA ZADAŃ   HISTORIA HISTORIA W PIGUŁCE NA TELEFON   JĘZEYK POLSKI EPOKI LITERACKIE   MODELARSTWO SZKUTNICZE SZKUTNICTWO ŻAGLOWCE HISTORYCZNE   TWORZENIE GIER 2D W DELPHI  
wędrowanie w świecie gry 2D 2d1_4.jpg

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


 
WĘDROWANIE W ŚWIECIE 2D (część 1)
wędrowanie w świecie gry 2D 2d1_1

Tematem tego artykułu jest poruszanie się bohaterów w świecie gry 2D, konkretnie chciałbym w nim przedstawić rozwiązanie pewnych problemów jakie można w takim "świecie" napotkać. Oczywiście kod programu jest w DELPHI z użyciem OMEGI

Jakie to problemy?:
a) jak wyświetlić duszka po drugiej stronie drzewa (za liśćmi) lub budynku ,
b) jak przejść przez las,
c) jak warunek a i b połączyć z testem kolizji,
d) jak uniknąć dzielenia obiektów większych niż rozmiar przyjętej siatki planszy nie tracąc spójności obiektu pod względem wykrywania kolizji i zajmowanego "klocka" pola siatki 2D,
e) jak przyśpieszyć działanie programu pomimo włączonej funkcji OmegaSprite1.Collision (sugestie).

Tu chciałbym nadmienić, że podane rozwiązania są propozycją ugryzienia problemu i na pewno nie są jedynym sposobem. Dają dobry wynik w teście na FPS i mogą być bazą (niewielka zmiana podejścia) do opracowania metody tworzenia gigantycznych światów ale o tym będzie w innym cyklu artykółow.

UWAGA
Omawiane przykłady nie są super szybkim przewijaniem światów 2D. Podstawowym tematem artykułu jest zmiana koloru duszka znajdującego się za obiektem. Program gigant2d.exe napisałem i dołączyłem do artykułu tylko po to aby nie być gołosłownym (nie uwzględniłem w nim omawianej zmiany koloru, utworzyłem w nim precyzyjniejszą metodę wykrywania przeszkody, która bazuje na pomyśle jaki opiszę w części drugiej).

Jeśli ktoś w podanych przykładach chciałby zwiększyć FPS niż przeze mnie ustalony, to powinien w komponencie OmegaScreen1 zmienić na wartość FALSE właściwość OmegaScreen1.VSYNC

wędrowanie w świecie gry 2D 2d1_2

CZEŚĆ 1- ZMIANA BARWY BOHATERA DUSZKA

Patrz rysunek A (pod głównym tematem artykułu)

Przed przystąpieniem do analizowania kodu, chciałbym zwrócić uwagę na taki problem:
obiekt umieszczony na planszy gry nie będący na przykład trawą czy też innym gruntem a mogący wytworzyć kolizję z duszkiem gracza wcale nie musi mieć rozmiary kostki pola świta 2D. Przykładowo nasz świat oprzemy na kostkach 64x32 a użyte drzewa będą mieć wymiary: 64x80, 64x90, 48x80, 38x46... A stąd już blisko do takiego problemu : powiedzmy osadzam w świecie karczmę lub inny budynek... I jak tu do niego wejść? A no prosto Tak samo jak w świecie rzeczywistym... otworzyć drzwi. Ale drzwi są (z reguły) jedne i z jednej strony!

I jest poważny problem: Jakie przyjąć rozwiązanie aby to wszystko połączyć? Pewnie wielu z nas stosuje warstwy w świecie 2D. Tylko, że są one o tych samych wymiarach co warstwa podstawowa ( ale na to też mam pomysł)... Mogą być dużo mniejsze niż taki budynek., drzewo (ale do drzew nie wchodzimy, chyba że ma dziuplę:)...Zostaje jedno uniwersalne rozwiązanie... Każda rodzina typu krzak, budynek, to osobna klasa , zaś drzwi, wejścia czy też inne przejścia, to wirtualne dziury w kolejnych warstwach...(i tak powstał temat na kolejny artykuł).

Przyjmując ten model rozwiązania należy liczyć się z tym, że nie możemy wykorzystać komponentu Omegi do tworzenia map, czyli OmegaMap

Przystępujemy do budowy szkieletu klasy przechowującej kostki świata gry i obiektów. Zaczniemy od klasy bazowej dla świata. Tu chciałbym jeszcze raz podkreślić, że nie jest to jeszcze podejście do gigantycznych światów 2D rzędu 250x250 czy też większych do 2000x2000. Jeśli mi ktoś nie wierzy, że można to ugryźć przy pomocy DELPHI ale o tym będzie w Gigantach



TKostka=class(TSprite)
protected
constructor Create(const AParent: TOmegaSprite;
const ImageListItem: TImageListItem;//indeks obrazu z listy OmegaImageList
const IdKlatka:Integer//indeks wycinanej klatki
);virtual;
procedure Draw;override;
procedure Move(const MoveCount:single);override;
end




procedure Draw; ta procedura odpowiada z sposób rysowania
a ta
procedure Move(const MoveCount:single);override; za ruch mapy świata



procedure TKostka.Move(const MoveCount:single);
begin
inherited Move(MoveCount);
x:=x+SkokX;
y:=y+SkokY;
end;



gdzie SkokX i SkokY to zmienne zdefiniowane w sekcji var głownego projektu



procedure TKostka.Draw;
begin
if ImageIndex<0 then exit;
inherited Draw;
end;



Musimy ten warunek podać if ImageIndex<0 then exit; jeśli chcemy otrzymać niewypełnione kostki warstwy bez używania dodatkowych tablic. Warto też sobie zapamiętać, ze jeśli byśmy podali do ImageIndex wartość
-1(minus jeden) to zostanie narysowany cały obraz bez wycinani klatek. A ja tu to wykorzystałem do warunku rysowania lub nie jakiegoś obiektu (bez jakiś tam dodatkowych pętli).

Na tej klasie będzie żyć każda pojedyncza kostka warstwy i dodatkowo będzie ona klasą bazową do klasy naszych krzaków i drzew. Dla osób pierwszy raz spotykających się z pojęciem warstwy w świecie 2D przedstawiam poglądowy rysunek traktujący warstwy, że tak powiem klasycznie

wędrowanie w świecie gry 2D 2d1_3

Teraz możemy zdefiniować klasę dla krzaków



TRoslina=class(TKostka)
k,
w:integer; //kolumna i wiersz zaczepienia pnia rosliny
protected
constructor Create(const AParent: TOmegaSprite;
const ImageListItem: TImageListItem;//indeks obrazu z listy OmegaImageList
const IdKlatka:Integer//indeks wycinanej klatki
);virtual;
procedure Draw;override;
procedure Move(const MoveCount:single);override;
procedure onCollision(const Sprite:Tsprite;const colX,colY:integer);override;
end;



Klasa TRoslina odziedziczy po rodzicu procedurę Move, którą w dalszej części nieznacznie zmodyfikujemy. TRoslina będzie mieć możliwość wykrywania testu kolizji z duszkiem gracza



procedure TRoslina.onCollision(const Sprite:Tsprite;const colX,colY:integer);
begin
if (Sprite is TGracz)then begin
TGracz(Sprite).fKolizja:=true;
TGracz(Sprite).fPokryty:=TGracz(Sprite).Pokryty(x,y,Image.TileWidth,Image.TileHeight);
end;
end;



Jej zadaniem jest przekazanie informacji w postaci logiczne flagi fKolizja i fPokryty do duszka gracza Na część pierwszą artykułu tyle informacji o tych klasach powinno wystarczyć. Jedynie chciałbym w tym miejscu podkreślić, że tak planujemy kod aby unikać dodatkowych pętli po utworzonych obiektach.. A może być ich znaczna ilość. Te funkcje i tak są wykonywane wystarczy spojrzeć na procedurę zegara aplikacji. Jest tam wywoływana procedura OmegaSprite1.Draw, OmegaSprite1.Move, OmegaSprite1.Collision, OmegaSprite1.Dead. Dowodem tego, że to działa jest chociażby przesuwanie mapy świata. Przecież ten kod



procedure TKostka.Move(const MoveCount:single);
begin
inherited Move(MoveCount);
x:=x+SkokX;
y:=y+SkokY;
end;



Jest tylko w tym i tylko w tym miejscu zdefiniowany. A o tym czy zmienia się wartość zmiennych SkokX lub SkokY decyduje gracz przyciskając klawisze strzałek....

Teraz bierzemy pod lupę klasę duszka Gracza



TGracz=class(TSprite)
public
fPokryty,
fKolizja:boolean;
function Pokryty(x0,y0,_width,_height:single):boolean;
procedure MoznaIsc;
protected
constructor Create(const AParent: TOmegaSprite;
const ImageListItem: TImageListItem;//indeks obrazu z listy OmegaImageList
const IdKlatka:Integer//indeks wycinanej klatki
);
procedure Move(const MoveCount:single);override;
procedure Draw;override;
end;



A poniżej kilka uwag do jej funkcjonowania



constructor TGracz.Create(const AParent: TOmegaSprite;
const ImageListItem: TImageListItem;
const IdKlatka:Integer
);
begin
inherited Create(AParent);
Image := ImageListItem;// określamy indeks obrazu duszka z listy zasobów grafiki OmegaImageList
ImageIndex :=idKlatka;// tu wycinamy konkretną klatkę
width :=Image.TileWidth;// okreslamy szerokosc i wysokosć obrazu duszka
height : =Image.TileHeight;
DoCollision :=true;// żądamy testu wykrywania kolizji
DoPixelCheck :=true;// test ma uwzględniać zmianę pikseli
end;



Tę procedurę rozbudujemy w następnych częściach artykułu



procedure TGracz.Move(const MoveCount:single);
begin
inherited Move(MoveCount);
end;



Tę również, która odpowiada ona z ruch (choć w tej postaci nic nie testuje, będzie to zrobione w następnej części)



procedure TGracz.MoznaIsc;
begin
//Sterowanie Graczem
if oisUp in Form1.OmegaInput1.keyboard.states then//idz do gory
SkokY:=SkokY+1;
if oisDown in Form1.OmegaInput1.keyboard.states then//idz w dół
SkokY:=SkokY-1;
if oisRight in Form1.OmegaInput1.keyboard.states then//idz w prawo
SkokX:=SkokX-1;
if oisLeft in Form1.OmegaInput1.keyboard.states then//idz w lewo
SkokX:=SkokX+1;
end;




Procedura poniżej zamieszczona zmienia sposób rysowania duszka gracza. Jeśli duszek znajdzie się za drzewem to zostanie wyświetlony w innej barwie. Można popróbować rożnych ustawień ostatnich pięciu parametrów to znaczy składowych R,G,B, Alpha i tej ostatniej, która może przyjąć takie wartości



bmIgnore = -1;
bmNormal = 0;
bmAdd = 1;
bmSrcAdd = 2;
bmSrcCol = 3;
bmSrcColAdd = 4;
bmMultiply = 5;
bmMultiplyAlpha = 6;
bmInvMultiply = 7;
bmInvMultiplyAlpha = 8;
bmInvert = 9;
bmSrcBright = 10;
bmDestBright = 11;
bmInvSrcBright = 12;
bmInvDestBright = 13;



Stale te zdefiniowane są w źródłach komponentu OMEGI (tu w pliku OmegaScreen.pas)



procedure TGracz.Draw;
begin
if not fKolizja then inherited Draw
else
Image.Draw(Round(x),Round(y),0,0.5,0.5,1,1,255,0,255,155,ImageIndex,0{ czyli bmNormal });
end;



A co robi to dziwactwo?



function TGracz.Pokryty(x0,y0,_width,_height:single):boolean;
begin
result:=false;
if (x+2>X0)and
(y+2>y0)and
(x-2+Image.TileWidth (y-2+Image.TileHeight then result:=true;
end;




Już odpowiadam. Sprawdza czy cały obrazek duszka został przykryty obszarem obrazu drzew czy też budynku
(rezultat jej działania wykorzystuje procedura testu kolizji klasy TRoslina). Jeśli nie, to ma się nam pokazać tylko głowa duszka lub inna część nieprzykryta bez zmiany koloru. Tak jak to pokazuje poniższy rysunek

wędrowanie w świecie gry 2D 2d1_4


Jak się przyjrzeć, to można zobaczyć, że gracz jest pod roślinką i po mimo kolizji może pod nią iść...(część 2)
Na zakończenie poniżej przedstawiam, to co powinniśmy uzyskać z kodu 1 części. (pełny w załączniku do artykułu). W części pierwszej duszek nie reaguje na kontakt z pniem drzewa... ale o tym w 2 części.

wędrowanie w świecie gry 2D 2d1_5

Koniec części 1


Nowosć

Układ okresowy pierwistków chemicznych
Układ okresowy  na telefon, tablet, smartfon
Darmowa aplikacja
Okręty- bitwa morska
okręty gra sieciowa
Darmowa aplikacja na Androida
Kalkulator jednostek fizycznych
przelicznik jednostek fizycznych  na telefon, tablet, smartfon
Darmowa aplikacja
Znaki drogowe.
znaki drogowe na telefon, tablet, smartfon
Darmowa aplikacja na Androida
Saper (512x512 pól)
Saper (512x512 pól)
Darmowa gra logiczna
The Friends of Mine
The Friends of Mine
Darmowa gra logiczna

MACHO. KAMIENNA LEGENDA
MACHO. KAMIENNA LEGENDA game for android
MACHO. STONE LEGEND
Game 2d for Android

Geometria. Wzory
geometri kalkulator wzory figur bryl

Alkomat
Alkomat na androida
Zobacz obliczenia zadań:
Zbiór zadań z fizyki K Chyla Zbiór zadań z fizyki 1 W Mroszczyk, J. Salach
Zbiór zadań z chemii zakres podstawowy K Pazdro Zbiór zadań z chemii zakres rozszerzony K Pazdro
 

epomoce.pl
Fizyka
Fizyka- zadania 1
Fizyka- zadania 2
Zadania dostęp www
Fizyka- wzory
Fizyka- teoria
Chemia
Zadania zak. roz.
Zadania zak. podst.
Zadania dostęp www
Chemia-teoria

Matematyka
Język polski
Historia (pigułka)
Historia (rozszerzona)
Fizyka- gimnazjum
Fizyka zadania- gimnazjum
Fizyka- gimnazjum teoria
Znaki drogowe
 
Matematyka wzory zadania gimnazjum
programowanie
koga
grafika2D


 


2008-2012 ©www.ePomoce.pl

FIZYKA, CHEMIA, MATEMATYKA, HISTORIA, JĘZYK POLSKI. Rozwiazania zadań z fizyki, rozwiazania zadań z chemii, zadania z fizyki, zadania z chemii