30.04.2016

Odświeżanie animacji

Obiekt animacji typu tileSeq zawiera wskaźnik do okna (poprzez wskaźnik do aktualnej ramki actualVO), w którym ma być wyświetlany, za pomocą funkcji theWindow::redrawField. Po upływie czasu przerwy między przejściami ramek, wyświetlana i odświeżana jest kolejna ramka, poprzez funkcję składową tileSeq::draw(). Indeks tej ramki ustalany jest za pomocą sumy wartości zmiennej actualFrame w aktualnej sekwencji i zmiennej nextFrame. Jeżeli zmienna nextFrame ma wartość ujemną wtedy taka animacja będzie odtwarzana wstecz.

Jeżeli aktualna ramka osiągnie indeks graniczny, dla jej sekwencji, wtedy aktualny indeks ustawiany jest na początek w tej sekwencji (w przypadku zapętlonej animacji), lub jest wstrzymywana cała animacja (poprzez wyłączenie timera dla tego obiektu).

29.04.2016

Funkcja do sprawdzania obszaru wspólnego dwóch przestrzeni dwuwymiarowych

Funkcja do obliczania obszaru wspólnego dwóch przestrzeni dwuwymiarowych, w tym projekcie, użyta jest do sprawdzenia czy wyświetlany obiekt wizualny nie wychodzi poza okno lub widzialny obszar do odświeżenia zmian (taki obszar nie musi mieć rozmiarów całego okna). W przyszłości będzie też potrzebna do detektora kolizji.

Definicja tej funkcji znajduje się w "engine2d\resources.cpp", a jej deklaracja wygląda następująco:

bool jointField( _POS f1x, _POS f1y, _SIZE f1w, _SIZE f1h, _POS f2x, _POS f2y, _POS f2w, _POS f2h, _POS& jf1x, _POS& jf1y, _SIZE& jfw, _SIZE& jfh );

Pierwsze osiem argumentów tej funkcji to pozycja i rozmiary obydwu obszarów. Kolejne argumenty referencyjne zawierają wynikowy obszar wspólny, czyli jego pozycję (jf1x, jf1y) względem pierwszego obszaru, oraz szerokość i wysokość (jfw, jfh).
 

23.04.2016

Inicjacja timera animacji

Rozpoczęcie animacji następuje po wywołaniu funkcji składowej tileSeq::start(). Funkcja ta dodaje timer, który po upływie czasu (podanego w zmiennej interval) generuje programowe zdarzenie wychwytywane w pętli głównej (każde zdarzenie jest przetwarzane według kolejki dodania). Obsługa zdarzenia animacji jest realizowana za pomocą funkcji attendProgEvents w "events_prog.cpp", która inicjuje renderowanie ramki w określonym oknie.
Implementacja timera zależy od użytej biblioteki. W przypadku tego projektu jest to SDL_AddTimer, który umieściłem w inline'owej, zuniwersalizowanej funkcji o nazwie _ADD_TIMER. Organizację kodu w aspekcie systemu operacyjnego omówiłem już we wcześniejszej notce.

22.04.2016

Dodanie animacji do macierzy sektorów

Animacja jako obiekt typu tileSeq jest zdefiniowana w "engine2d\resources.h". Zawiera kontener z ramkami typu _SURFACE, identyfikator animacji, wektor z sekwencjami, aktualnie ustawiona sekwencja, położenie i wskaźnik do aktualnie wyświetlanej ramki.
Pojedyncza sekwencja zwiera informacje - od której do której ramki odtwarzać określoną animację. Każda animacja może być wyświetlana na wiele sposobów np. od końcowej do początkowej ramki, albo np. od ramki piątej do osiemnastej i właśnie do tego służą sekwencje.
Tworzenie obiektu animacji może przebiegać na dwa sposoby. Pierwszy, poprzez konstruktor z podaną ścieżką do pliku mozaiki, z której ma zostać utworzona animacja. Drugi sposób, poprzez użycie funkcji składowej tileSeq::prepare( tileSet* t ). Przypomnę, że obiekt typu tileSet zawiera kontener z już załadowanymi powierzchniami RGBA (typu _SURFACE). Domyślnie jest tworzona jedna sekwencja: od ramki pierwszej (z indeksem 0) do ramki ostatniej.
Dodanie takiej animacji do macierzy sektorów (w celu jej przyszłego wyświetlenia), w określonym oknie, na konkretną pozycję, odbywa się za pomocą funkcji theWindow::putSeqOnMatrix.

16.04.2016

Fizyka mapy

Obecnie, każda jednostka mapy zawiera flagi informujące:
* czy przez ten sektor można przemieścić cokolwiek (nie tylko pojazd gracza);
* czy w tym sektorze znajduje się przedmiot, który można zabrać do inwentarza;
* czy w tym sektorze znajduje się obiekt, który można przemieścić;
* czy w tym sektorze znajduje się dziura;
* czy podłoga tego sektora to ruchoma platforma wodna;
* czy obiekt znajdujący sie na podłodze może eksplodować.

Początkowe stany flag są ustawiane w funkcji theLevel::buildMap przy konstruowaniu mapy z zasobów.

15.04.2016

Przydział podłogi przy budowie mapy

Format zapisu mapy (opisany wstępnie tutaj) jest dwuwymiarowy, więc funkcja odpowiedzialna za konstruowanie mapy musi sama rozpoznać, które jej elementy są położone na podłodze (czyli musi być dodana podłoga, w niższej macierzy sektorów), a które stanowią jej poziom. Część programu odpowiedzialna za tą czynność umieszczona jest w theLevel::buildMap w "level.cpp".
Przykładowa mapa "tutorial 1" z rozmieszczonymi na niej elementami, wygląda w ten sposób:
Zamiast:

10.04.2016

Przygotowanie wizualnych elementów mapy

Niektóre obiekty na mapie nie są identyczną kopią tego co znajduje się w pliku zasobów grafiki. Dlatego trzeba je najpierw odpowiednio przygotować przed umieszczeniem ich w kontenerze kafli mapy.

Np. ruchome platformy:
mogą być skierowane w różnych kierunkach, więc trzeba utworzyć ich obrócone, o odpowiednią liczbę stopni, odpowiedniki.

W przypadku przejść kierunkowych oprócz obrotu strzałki:
trzeba też nałożyć na siebie jej obraz z samym typem przejścia, np.:


 Wszystkie te sprawy są zebrane w funkcji pomocniczej prepareMainTiles w "resources_constructor.cpp".

08.04.2016

Odwzorowanie znaków ASCII z pliku mapy na identyfikatory powierzchni

Mapa planszy jest umieszczona w obiekcie typu theLevel i składa się z jednostek obszarów mapUnit.
Funkcja interpretująca dane z pliku mapy theLevel::buildMap( _CHAR* file ) sprawdza najpierw jaka jest długość pierwszej linii (z pominięciem linii konfiguracyjnej). Jeżeli rozmiar linii jest nieparzysty (a musi być, gdyż każdy obszar mapy reprezentowany jest dwoma znakami) funkcja zwraca komunikat o błędnym wprowadzeniu danych dla mapy. Podobnie jest w przypadku, gdy długość którejś następnej linii jest inna niż długość pierwszej. Ta funkcja sprawdza też czy istnieje w ogóle reprezentacja obszaru podana określonymi znakami.

Jednostka mapy zawiera informacje o tym co znajduje się w tym obszarze, jaki jest identyfikator podłogi i identyfikator obiektu, który znajduje się na tej podłodze (jeżeli jest). Czy określony obiekt można poruszyć, czy w ogóle można przemieścić coś na daną lokalizację, czy znajduje się na niej ruchoma platforma, itp.

02.04.2016

Format zapisu mapy

W katalogu "data\levels" znajdują się pliki zasobów związanych z mapami i informacjami do wyświetlenia dla gracza, przed określoną misją.
Format zapisu mapy stanowi standardowy tekst ASCII. Podstawowa jednostka obszaru mapy reprezentowana jest przez dwa znaki. Np.: "##" - ściana, ".," - podłoga, "';" - woda, ">>" - ruchoma platforma wodna, "k2" - zielony klucz, itd.
Pierwsza mapa samouczka, w pliku "data\levels\tutorial 1", wygląda następująco:

120 100 9 0 7 0 8 5 intro1
.,.,.,.,.,.,.,.,.,.,';';';';';
.,##############.,.,';';';';';
.,##.,.,.,.,.,##.,.,.,';';';';
.,##.,.,g1.,.,##.,.,.,.,';';';
.,####.,.,.,.,d2.,b2.,.,.,';';
.,##.,.,g3.,.,##.,.,.,PL.,';';
.,##############.,.,.,.,';';';
.,##.,.,.,.,.,.,.,.,.,.,';';';
.,##.,';';c#';';.,.,';';';';';
.,##.,';';k2>>';.,.,';';';';';
.,##.,';';';';';.,.,';';';';';
.,##.,.,.,.,.,.,.,.,';';';';';
.,##############.,.,';';';';';
.,.,.,.,.,.,.,.,.,.,';';';';';
.
g1....
..b2..
....g3
W pierwszej linii znajdują się podstawowe informacje odnośnie tej mapy i początkowe zasoby pojazdu: czas w sekundach, paliwo, HP, amunicja (3 typy), modyfikatory uszkodzeń, "intro1" to nazwa pliku, w którym zapisane są informacje wprowadzające (wyświetlane dla gracza przed misją).
Linie pod mapą definiują cel ukończenia misji - ułożenie odpowiednich obiektów, w odpowiedniej kombinacji (można zdefiniować wiele następujących po sobie celów).

01.04.2016

Dodawanie nowych okien do określonego ekranu

Od strony interfejsu programistycznego, dodawanie nowych okien wygląda w ten sposób (na przykładzie menu początkowego z theGame::buildMenu() w "game.cpp"):

  screens[ SCREEN_MENU ].addWindow( TITLE_POSX, TITLE_POSY, TITLE_WIDTH, TITLE_HEIGHT );
  screens[ SCREEN_MENU ].addWindow( MENU_POSX, MENU_POSY, MENU_WIDTH, MENU_HEIGHT );

  theWindow* menuW = screens[ SCREEN_MENU ].windows[ 0 ];
  theWindow* menuW1 = screens[ SCREEN_MENU ].windows[ 1 ];
  theWindow* menuW2 = screens[ SCREEN_MENU ].windows[ 2 ];


  
  menuW->addPlaneMatrix( res->bGround[ BGR_MENU ]->w, res->bGround[ BGR_MENU ]->h );
  menuW->makeBackgroundMatrix( 0, res->bGround[ BGR_MENU ] );
  menuW1->addPlaneMatrix();

  menuW2->addPlaneMatrix( res->bGround[ BGR_MENU_LIST ]->w, res->bGround[ BGR_MENU_LIST ]->h );
  menuW2->makeBackgroundMatrix( 0, res->bGround[ BGR_MENU_LIST ] );

  _SURFACE* mTitle = _CREATE_RGBA_SURFACE( TITLE_WIDTH, TITLE_HEIGHT, MENU_BG_COLOR );
  _SURFACE* txtChoose = _CREATE_RGBA_SURFACE( res->font.tiles[ 0 ]->w * strlen( MENU_CHOOSE_LEVEL_TXT ), res->font.tiles[ 0 ]->h, 0 );
  _SURFACE* txtExit = _CREATE_RGBA_SURFACE( res->font.tiles[ 0 ]->w * strlen( MENU_EXIT_TXT ), res->font.tiles[ 0 ]->h, 0 );
 
  menuW1->anchoredText = true;
  menuW1->print( &res->font, mTitle, TITLE_TEXT_POSX, TITLE_TEXT_POSY, TITLE_TEXT );
  menuW2->anchoredText = false;
  menuW2->print( &res->font, txtChoose, 0, 0, MENU_CHOOSE_LEVEL_TXT );
  menuW2->print( &res->font, txtExit, 0, 0, MENU_EXIT_TXT );

  menuW1->putOnMatrix( 0, mTitle, 0, 0 );
  menuW2->addPlaneMatrix();
  menuW2->putOnMatrix( 1, txtChoose, MENU_TXT_POSX, MENU_TXT_POSY );
  menuW2->putOnMatrix( 1, txtExit, MENU_EXIT_POSX, MENU_EXIT_POSY );

  menuW1->makeBorder( BORDER_MENU_WIDTH, BORDER_MENU_COLOR );
  menuW2->makeBorder( BORDER_MENU_WIDTH, BORDER_MENU_COLOR );

Jest też drugi sposób, z użyciem obiektów wizualnych (bez tekstu dodanego do macierzy sektorów):
//zamiast:
  menuW1->putOnMatrix( 0, mTitle, 0, 0 );
  menuW2->addPlaneMatrix();
  menuW2->putOnMatrix( 1, txtChoose, MENU_TXT_POSX, MENU_TXT_POSY );
  menuW2->putOnMatrix( 1, txtExit, MENU_EXIT_POSX, MENU_EXIT_POSY );

//można też:
  menuW1->newVObj( 0, 0, mTitle, 0, 0 );
  menuW2->newVObj( 0, 0, txtChoose, MENU_TXT_POSX, MENU_TXT_POSY );
  menuW2->newVObj( 0, 1, txtExit, MENU_EXIT_POSX, MENU_EXIT_POSY );

Ekran z identyfikatorem SCREEN_MENU zawiera 3 okna. Pierwsze okno to tło całego ekranu. Drugie okno zawiera napis tytułu gry "METOD". Trzecie okno zawiera menu właściwe (obecnie napisy "Choose level:" i "Exit"). Napis tytułu jest lekko przeźroczysty (identycznie jak tło tego okna), ponieważ jest osadzony w powierzchnię tła tego okna, które ma częściowo przeźroczysty kolor.

Funkcja składowa theWindow::makeBorder( _SIZE bw, _COLOR bc ) dodaje nowy plan z obramowaniem okna, bez żadnego wypełnienia. Pierwszy argument dla funkcji theWindow::putOnMatrix i theWindow::newVObj to indeks macierzy do której ma być dodana określona powierzchnia. Drugi argument dla funkcji theWindow::newVObj to indeks kolejności renderowania powierzchni obiektu wizualnego.