W swoim artykule chciałbym przedstawić wam sposoby rysowania grafiki izometrycznej przy użyciu pakietu Omega.
Dlaczego izometria?
Na pewno znacie izometrię z gier takich jak: Diablo, Diablo II, Transport Tycoon, AoE itd. Wbrew pozorom sama grafika izometryczna nie jest taka trudna do opanowania, a efekt jest nieporównywalnie wielki do tego ile pracy włożymy w napisanie takiego silnika. No to zaczynamy 🙂
Grafika izometryczna
Wszystko będzie się opierać o „kafelki”, które gra będzie rysować w odpowiedni sposób. Przykład kafelka:
Użyłem określenia „w odpowiedni sposób”, dlatego że przy zwykłym sposobie rysowania map 2d wyglądałoby to tak:
Nie tak to ma wyglądać, prawda? 🙂 Zauważ, iż między poszczególnymi kafelkami tworzy sie dziura o kształcie identycznym co kafelek. Możnaby podejrzewać, iż wystarczy w takie puste miejsce wstawiać klocki, wyglądałoby to tak:
Już cieplej, ale to nie jest to, co chcemy osiągnąć 🙂 Jeśli grałeś w ww. AoE lub Transport Tycoon to zauważyłeś zapewne, że krawędzie mapy nie maja kształtu zygzaka, tylko czegoś na wzór diamentu.Wygląda to tak…
…i to właśnie taki efekt chcemy otrzymać. Ale nic nie zrobi się za nas, więc włączamy Delphi i zabieramy się do pracy 🙂
Przygotowanie do pracy
Podstawową sprawą jest umieszczenie potrzebnych komponentów na formie. Tak więc tworzymy cztery komponenty (wszystkie należą do pakietu Omega), a mianowicie:
TOmegaScreen:
Name | Omsc
Fullscreen | True
TOmegaImageList:
Name | Omil
OmegaScreen | Omsc
TOmegaFont:
Name | Omfo
OmegaScreen | Omsc
TOmegaTimer:
Name | Omti
Enabled | True
Dodatkowo ustawiamy KeyPreview formy na True.
Do samego rysowania użyjemy 3 plików graficznych:
Dodajemy je do Omil i nazywamy kolejno '0′, '1′, '2′.
Napiszmy jeszcze podstawowe zdarzenia:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); begin if key=#27 then close else if key='d' then inc(pos.X, 32) else //te if'y beda umozliwiac przsuwanie mapy if key='a' then dec(pos.X, 32) else if key='s' then inc(pos.Y, 16) else if key='w' then dec(pos.Y, 16); end; |
I ustawmy zmienne globalne:
1 2 3 4 5 6 7 |
var Form1: TForm1; pos: TPoint; mapa: array [0..63,0..63] of byte; |
Kiedy już mamy wszystko ustawione, przechodzimy do dalszej pracy.
Troche teorii 😉
Teraz musimy zaimplementować odopwiednio algorytm, a wygląda on tak:
1. w zmiennej x i y trzymamy pozycje kafla (np. 1,1 2,3 itd)
2. Jak obliczyc x pola na ekranie? Będzie to wzór:
xx= (x-y)*(w/2)-posX
Gdzie:
xx- współrzędna x pola na ekranie
x- współrzędna x pola na mapie
w- szerokość (Width) kafelka (grafiki)
posX- pozycja mapy (współrzędna x)
Możesz sobie sprawdzić na tym obrazku czy mam racje :] (pamietaj, ze to zielone pole jest pukntem 0,0 na ekranie)
3. Teraz jeszcze wystarczy obliczyć y pola 🙂 Zrobimy to za pomocą tego wzoru:
yy= ((x-1)+(y-1))*(w/2)-posY
Gdzie:
yy- współrzędna y pola na ekranie
y- współrzędna y pola na mapie
h- wysokość (Height) kafelka (grafiki)
posY- pozycja mapy (współrzędna y)
Sprawdź sobie na tym obrazku:
Skoro już wiemy jak mniej więcej działa algorytm zaimplementujmy go 🙂
Piszemy silnik
W OnShow formy ustawiamy taki kod:
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 |
procedure TForm1.FormCreate(Sender: TObject); var x,y: byte; begin Omsc.Init; Omil.Init; Omfo.Init; for x:= 0 to 63 do for y:= 0 to 63 do begin randomize; mapa[x,y]:= random(3);// zakres wylosowanej liczby: 0-2 end; omti.Enabled:= true; end; |
Pierwsze trzy funkcje inicjują komponenty, bez tego nie będą one działać. Dalsza część kodu generuje losową mapę tj. wczytuje do każdej komórki liczbę z zakresu 0-2. Jednak równie dobrze może być ona wczytywana z pliku. Ostatniej linijki chyba nie muszę tłumaczyć 🙂 Teraz przejdziemy do najważniejszego- do rysowania mapy. Oto kompletna procedura:
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 |
procedure TForm1.OmtiTimer(Sender: TObject); var x,y: byte; begin omsc.BeginRender; //poczatek rysowania omsc.ClearScreen(0,0,0); //wyczysc ekran for x:= 0 to 63 do for y:= 0 to 63 do omil.ImageList.Find(inttostr(mapa[x,y])).Draw(((x-y)*32)-pos.X, (((x-1)+(y-1))*16)-pos.Y ,0); //rysuj kafelki (liczby 32 i 16) to polowa dlugosci i szerokosci kafla, zeby nie bylo watpliwosci :) omfo.BeginFont; omfo.CreateFont('arial',12,[fsBold]); //laduj czcionke omfo.Print(20,20, inttostr(omti.FPS)); //wypisz fps na ekranie omfo.EndFont; omsc.EndRender; //koniec rysowania end; |
Silnik gotowy. Teraz możesz już się cieszyć owocami pracy – niewielkim silnikiem grafiki izometrycznej 🙂 W załączniku umieszczam kod. Za jakiś czas pojawi się druga część kursu. Będziemy ustawiać na mapie obiekty i postaci. Do zobaczenia! 🙂
Autor: Kajtek