Wymagane jest zapoznanie się z następującymi podrozdziałami:
OL031 - instalacja biblioteki SDL w środowisku Dev-C++
OL032 - inicjalizacja biblioteki SDL
OL033 - powierzchnie graficzne w SDL
OL034 - przygotowanie plików własnej biblioteki graficznej
OL035 - rysowanie po powierzchni graficznej
OL036 - algorytm Bresenhama rysowania odcinka
OL037 - obcinanie grafiki do prostokąta
OL038 - podstawowe algorytmy wypełniania obszarów
OL039 - algorytm Smitha
OL040 - praca w środowisku sterowanym zdarzeniamiOL041 - czcionka bitmapowa
OL042 - czcionka wektorowa
OL043 - przyciski poleceń
OL050 - macierze - podstawowe operacje na macierzach
OL051 - przekształcenia na płaszczyźnie
OL052 - algorytm wypełniania wielokątów
OL053 - rysowanie okręgów i kół
OL054 - rysowanie elips i owali
OL055 - Go Moku - część 1
UWAGA!!!
Bieżące opracowanie NIE JEST KURSEM programowania biblioteki SDL. Są to materiały przeznaczone do zajęć na kole informatycznym w I LO w Tarnowie.
Przed pracą z tym rozdziałem utwórz w środowisku Dev-C++ nowy projekt SDL i dołącz do niego pliki SDL_gfx.cpp oraz SDL_gfx.h. Jeśli nie przeczytałeś zalecanych rozdziałów, koniecznie zrób to, a wszystko stanie się jasne. W przeciwnym razie odpuść sobie również ten rozdział. Opis funkcji bibliotecznych znajdziesz w podsumowaniu SDL013.
Artykuł nie jest już rozwijany
Program planszy gry w Go Moku jest prawie gotowy. Pozostał jedynie do rozwiązania problem zapisu i odczytu stanu gry z dysku.
Ponieważ w trakcie tworzenia programu wyszły na jaw pewne niedoróbki w bibliotece GUI (to normalne, patrz MicroSoft!!!), przed uruchomieniem programu ściągnij na swój komputer uaktualnione pliki biblioteczne. Przypominam, iż zapisujemy je w katalogu C:\Dev-Cpp\include\Sdl. Są to: SDL_gui.h oraz SDL_gui.cpp.
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne // // P055B - Test odczytu myszki //---------------------------- #include <SDL/SDL_gfx.h> #include <SDL/SDL_gui.h> using namespace std; // Stałe globalne //--------------- const int SCRX = 800; // stałe określające szerokość i wysokość const int SCRY = 600; // ekranu w pikselach const int C_LOW = 0xffcc99; // kolor normalny tła const int C_HIGH = 0xffffcc; // kolor podświetlonego tła const int C_CODE = 0x10; // maska koloru tła const int T_CODE = 0xB; // maska typu pola const int P_CODE = 0x3; // maska koloru pionów const int P_BLACK = 1; // kod czarnego glacza const int P_WHITE = 2; // kod białego gracza const int P_LAST = 4; // ostatni pion const int P_WIN = 8; // pion zwycięski const int MAXBUTTON = 6; // liczba przycisków // Zmienne globalne, widoczne we wszystkich funkcjach //--------------------------------------------------- SDL_Surface * screen; gfxFont * font = gfxOpenFont("vecprop9x12.fnt"); Uint32 pfsize = 0; // rozmiar planszy w wierszach/kolumnach = 15 lub 19 Uint32 pfx, pfy; // współrzędne lewego górnego narożnika planszy Uint32 pfwh; // szerokość/wysokość planszy w pikselach Uint32 pwh; // szerokość/wysokość pola gry Uint32 pf[20][20]; // plansza gry Uint32 pl[200][2]; // kolejne ruchy graczy Uint32 plnr; // numer ruchu Uint32 plh; // liczba wierszy tekstu na liście bool playing = true; // określa, czy pole gry reaguje na ruchy graczy bool player; // określa bieżącego gracza gfxButton * btn[MAXBUTTON]; // przyciski sterujące grą gfxOption * opt; // opcje gry gfxScrollBar * sbr; // pasek przewijania listy ruchów SDL_Rect plr; // prostokąt listy ruchów // Funkcje usługowe //----------------- // Funkcja zamienia liczbę na tekst //--------------------------------- char * utoa(Uint32 x) { static char c[4]; c[0] = 48 + x / 100; x %= 100; c[1] = 48 + x / 10; x %= 10; c[2] = 48 + x; c[3] = 0; while((c[0] == '0') && c[1]) for(int i = 1; i < 4; i++) c[i-1] = c[i]; return c; } // Funkcja oblicza wszystkie wymiary nowej planszy. // ps - liczba kolumn/wierszy na planszy = 15 lub 19 //-------------------------------------------------- void CalcNewPlayfield(Uint32 ps) { pfsize = ps; pfx = 16; pfwh = screen->h - 32; pwh = pfwh / ps; if(!(pwh % 2)) pwh--; pfwh = pwh * ps; pfx = pfy = (screen->h - pfwh) >> 1; } // Funkcja rysuje tło planszy wraz // z opisem kolumn i wierszy //----------------------------------- void DrawPlayfield() { if(SDL_MUSTLOCK(screen)) if(SDL_LockSurface(screen) < 0) exit(-1); SDL_Rect r; r.x = r.y = 0; r.w = r.h = screen->h; SDL_FillRect(screen,&r,C_LOW); // rysujemy opisy kolumn na dole planszy char c[3], * p; // przechowuje tekst kolumn lub wierszy Uint32 x, y, i; x = pfx + (pwh >> 1); // pozycja x pierwszego znaku y = pfwh + pfx + ((pfx - font->h) >> 1); c[0] = 'a'; c[1] = 0; for(i = 0; i < pfsize; i++) { gfxDrawText(screen, font, c, x - (gfxTextLength(font, c) >> 1),y,0,-1); c[0]++; x += pwh; } // rysujemy opisy wierszy x = pfx >> 1; y = pfwh + pfx - (pwh >> 1) - (font->h >> 1); for(i = 1; i <= pfsize; i++) { p = utoa(i); gfxDrawText(screen, font, p, x - (gfxTextLength(font, p) >> 1), y,0,-1); y -= pwh; } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen,0,0,screen->h,screen->h); } // Funkcja rysuje pole planszy // pcol - numer kolumny // prow - numer wiersza // pcode - kod pola //---------------------------- void DrawCell(Uint32 pcol, Uint32 prow, Uint32 pcode) { if(SDL_MUSTLOCK(screen)) if(SDL_LockSurface(screen) < 0) exit(-1); SDL_Rect r; // wypełniamy tło r.x = (pcol - 1) * pwh + pfx; r.y = (pfsize - prow) * pwh + pfy; r.w = r.h = pwh; SDL_FillRect(screen,&r,(pcode & C_CODE) ? C_HIGH : C_LOW); // rysujemy linie siatki Uint32 lcx = r.x + pwh / 2; Uint32 lcy = r.y + pwh / 2; Uint32 lrx = r.x + pwh - 1; Uint32 lly = r.y + pwh - 1; Uint32 llen = 1 + (pwh >> 1); if(prow < pfsize) gfxVLine(screen, lcx, r.y, 0, llen); if(prow > 1) gfxVLine(screen, lcx, lcy, 0, llen); if(pcol < pfsize) gfxHLine(screen, lcx, lcy, 0, llen); if(pcol > 1) gfxHLine(screen, r.x, lcy, 0, llen); // w 9 polach środkowych planszy rysujemy małe koła na przecięciu linii Uint32 fs = (pfsize + 1) >> 1; if(((prow == fs - 2) || (prow == fs) || (prow == fs + 2)) && ((pcol == fs - 2) || (pcol == fs) || (pcol == fs + 2))) gfxFillCircle(screen, lcx, lcy, 2, 0); // rysujemy piona Uint32 pr = (llen * 3) / 4; if(pcode & P_CODE) { gfxFillCircle(screen, lcx, lcy, pr, (pcode & P_BLACK) ? 0x1f1f1f : 0xffffff); gfxCircle(screen, lcx, lcy, pr, 0); if(pcode & P_LAST) { Uint32 clen = 1 + (pwh >> 1); gfxHLine(screen, (r.x + lcx) >> 1, lcy, 0xff0000, clen); gfxVLine(screen, lcx, (r.y + lcy) >> 1, 0xff0000, clen); } else if(pcode & P_WIN) { pr >>= 2; if(pr < 3) pr = 3; gfxFillCircle(screen, lcx, lcy, pr, 0xff0000); } } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen,r.x,r.y,r.w,r.h); } // Funkcja odczytuje z listy numer wiersza i kolumny //-------------------------------------------------- void ReadColRow(Uint32 pos, Uint32 &col, Uint32 &row) { col = pl[pos >> 1][pos % 2] >> 8; row = pl[pos >> 1][pos % 2] & 0xff; } // Funkcja wstawia do planszy piona // wg listy pl oraz pozycji plnr //--------------------------------- void InsertMove() { Uint32 col, row; if(playing) { if(plnr) { ReadColRow(plnr - 1, col, row); pf[row][col] &= P_LAST ^ 0xff; // zerujemy bit ostatniego piona DrawCell(col, row, pf[row][col]); } ReadColRow(plnr, col, row); pf[row][col] |= (plnr % 2) ? P_WHITE : P_BLACK; pf[row][col] |= P_LAST; DrawCell(col, row, pf[row][col]); plnr++; if((plnr >> 1) > sbr->value + sbr->vprop - 1) sbr->value = (plnr >> 1) - sbr->vprop + 1; sbr->Refresh(); (* sbr->call)(sbr); // sprawdzamy, czy po wykonaniu ruchu zawodnik jest zwycięzcą Sint32 dx[] = {-1, -1, 0, 1}; Sint32 dy[] = { 0, 1, 1, 1}; Uint32 x,y,c,n,xp,yp; c = pf[row][col] & P_CODE; // kolor bieżącego gracza for(int i = 0; i < 4; i++) { x = col; y = row; while((x > 0) && (x <= pfsize) && (y > 0) && (y <= pfsize) && ((pf[y][x] & P_CODE) == c)) { xp = x; yp = y; x += dx[i]; y += dy[i]; } n = 0; x = xp; y = yp; while((x > 0) && (x <= pfsize) && (y > 0) && (y <= pfsize) && ((pf[y][x] & P_CODE) == c)) { n++; x -= dx[i]; y -= dy[i]; } if(n == 5) { playing = false; for(int j = 1; j <= 5; j++) { pf[yp][xp] &= P_CODE; pf[yp][xp] |= P_WIN; DrawCell(xp, yp, pf[yp][xp]); xp -= dx[i]; yp -= dy[i]; } break; } } } } // Funkcja inicjuje nową grę //-------------------------- void InitGame() { Uint32 size; gfxOptElement * p = opt->item; if(!p->option) p = p->next; size = p->tag; playing = true; if(pfsize != size) { CalcNewPlayfield(size); DrawPlayfield(); } for(int i = 1; i <= size; i++) for(int j = 1; j <= size; j++) { pf[i][j] = 0; DrawCell(i,j,0); } for(int i = 0; i < 181; i++) pl[i][0] = pl[i][1] = 0; plnr = 0; sbr->value = 0; sbr->Refresh(); (* sbr->call)(sbr); } // ************************************************* // *** Funkcje obsługujące kontrolki *** // ************************************************* // Funkcja cofa wykonany ruch //--------------------------- void TakeBack() { Uint32 col, row; if(!playing) { playing = true; for(int i = 1; i <= pfsize; i++) for(int j = 1; j <= pfsize; j++) if(pf[i][j] & P_WIN) { pf[i][j] &= P_WIN ^ 0xff; DrawCell(j, i, pf[i][j]); } } if(plnr) { plnr--; ReadColRow(plnr, col, row); pf[row][col] = 0; DrawCell(col, row, 0); if(plnr) { ReadColRow(plnr - 1, col, row); pf[row][col] |= P_LAST; DrawCell(col, row, pf[row][col]); } if((plnr >> 1) < sbr->value) { sbr->value = plnr >> 1; if(sbr->value >= (sbr->vprop >> 1)) sbr->value -= (sbr->vprop >> 1); else sbr->value = 0; } sbr->Refresh(); (* sbr->call)(sbr); } } // O ile jest to możliwe, funkcja przywraca cofnięty ruch //------------------------------------------------------- void Restore() { Uint32 col, row; if(pl[plnr >> 1][plnr % 2]) InsertMove(); } // Obsługa przycisków //------------------- void BtnClick(gfxGUIObject * sender) { switch(sender -> tag) { case 0 : InitGame(); break; case 1 : TakeBack(); break; case 2 : Restore(); break; case 3 : break; case 4 : break; case 5 : exit(0); } } // Funkcja wyświetla zawartość listy ruchów na podstawie paska przewijania //------------------------------------------------------------------------ void Update_pl(gfxGUIObject * sender) { if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); SDL_FillRect(screen, &plr, C_LOWER); Uint32 y = plr.y; Uint32 d = font->h + (font->h >> 1); char * txp; for(int i = 0; i < sbr->vprop; i++) { Uint32 n = i + sbr->value + 1; txp = utoa(n); Uint32 x = plr.x + font->h + gfxTextLength(font,"000") - gfxTextLength(font, txp); x = gfxDrawText(screen, font, txp, x, y, 0xff0000, -1); x = gfxDrawText(screen, font, " : ", x, y, 0x0000ff, -1); for(int j = 0; j < 2; j++) { Uint32 plmove = pl[n-1][j]; if(plmove && ((n << 1) - 2 + j < plnr)) { x += gfxDrawChar(screen, font, (plmove >> 8) + 96, x, y, j ? 0xffffff : 0, -1); gfxDrawText(screen, font, utoa(plmove & 0xff), x, y, j ? 0xffffff : 0, -1); } else x = gfxDrawText(screen, font, "...", x, y, j ? 0xffffff : 0, -1); if(!j) x = plr.x + (plr.w >> 1) + (font->h << 1); } y += d; } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, plr.x, plr.y, plr.w, plr.h); } // Funkcja ustawia panel sterujący gry //------------------------------------ void InitControls() { if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); // Najpierw wypełniamy tło pulpitu szarym kolorem SDL_Rect r; r.x = screen->h; r.y = 0; r.w = screen->w - screen->h; r.h = screen->h; SDL_FillRect(screen, &r, C_FACE); // W na środku pierwszego wiersza umieszczamy napis z tytułem gry char * t = "Plansza do gry w GO MOKU"; Uint32 x, y, d; d = font->h + (font->h >> 1); // 1,5 wiersza tekstu x = r.x + (r.w >> 1) - (gfxTextLength(font, t) >> 1); y = font->h >> 1; gfxDrawText(screen, font, t, x - 1, y - 1, C_UPPER,-1); gfxDrawText(screen, font, t, x + 1, y + 1, C_LOWER,-1); gfxDrawText(screen, font, t, x, y, C_TEXT,-1); // Teraz wypisujemy wiersz copyright t = "(C)2008 Koło Informatyczne"; x = r.x + (r.w >> 1) - (gfxTextLength(font, t) >> 1); y += d; gfxDrawText(screen, font, t, x, y, C_TEXTBG,-1); t = "I LO w Tarnowie"; x = r.x + (r.w >> 1) - (gfxTextLength(font, t) >> 1); y += d; gfxDrawText(screen, font, t, x, y, C_TEXTBG,-1); // Rysujemy poziomą linię pod napisem copyright y += d; gfxHLine(screen, r.x, y, C_UPPER, r.w); gfxHLine(screen, r.x, y + 1, C_LOWER, r.w); // Rysujemy trójwymiarową ramkę d = 8; r.x += d; r.y = y + d; r.w -= d << 1; r.h -= r.y + d; r.x--; r.y--; gfxRectangle(screen, &r, C_UPPER); r.x += 2; r.y += 2; gfxRectangle(screen, &r, C_LOWER); r.x--; r.y--; gfxRectangle(screen, &r, C_FACE); r.x += d; r.y += d; r.w -= d << 1; r.h -= d << 1; if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); // Umieszczamy kontrolki opcji rozmiaru planszy gfxOptElement * opte1, * opte2; opte2 = new gfxOptElement(19, 1, 1, false, true, "Plansza gry 19x19", NULL, NULL); opte1 = new gfxOptElement(15, 1, 1, true, true, "Plansza gry 15x15", NULL, opte2); opt = new gfxOption(screen, font, &r, opte1); // Tworzymy przyciski sterujące funkcjami gry char * btx[] = {"Nowa gra Go Moku", "Cofnij ruch", "Przywróć ruch", "Odczytaj grę z dysku", "Zapisz grę na dysk", "Zakończ Go Moku"}; r.y += opt->rect.h + 4; r.h = font->h << 1; for(int i = 0; i < MAXBUTTON; i++) { btn[i] = new gfxButton(i, true, screen, font, &r, btx[i], BtnClick); r.y += r.h + 4; } // Tworzymy kontrolkę listy ruchów for( int i = 0; i < 180; i++) pl[i][0] = pl[i][1] = 0; r.h = screen->h - r.y - d; d = font -> h + (font->h >> 1); plh = r.h / d; r.h = plh * d; r.w -= d + 2; plr = r; r.x += r.w + 2; r.w = d; sbr = new gfxScrollBar(0, true, screen, &r, 0, 181 - plh, plh, plh - 1, Update_pl); Update_pl(sbr); SDL_UpdateRect(screen,0,0,0,0); } // Na podstawie położenia kursora myszki oraz wymiarów planszy funkcja // wylicza kolumnę i wiersz wskazywane przez kursor myszki //-------------------------------------------------------------------- void ColRow(Sint32 x, Sint32 y, Sint32 &row, Sint32 &col) { x -= pfx; y -= pfy; col = (x / pwh) + 1; row = pfsize - (y / pwh); if((col < 1) || (col > pfsize) || (row < 1) || (row > pfsize)) row = col = 0; } // Obsługa zdarzeń na planszy //--------------------------- void pfDoEvents(SDL_Event * e) { Sint32 col, row, col2, row2; if(playing) { switch(e->type) { case SDL_MOUSEMOTION: ColRow((Sint32)e->motion.x, (Sint32)e->motion.y, row, col); col2 = row2 = 0; for(int i = 1; i <= pfsize; i++) { for(int j = 1; j <= pfsize; j++) if(pf[i][j] & C_CODE) { row2 = i; col2 = j; break; } if(col2) break; } if(col2) { pf[row2][col2] &= C_CODE^0xff;; if((col2 != col) || (row2 != row)) DrawCell(col2, row2, pf[row2][col2]); } if(col) { pf[row][col] |= C_CODE; DrawCell(col, row, pf[row][col]); } break; case SDL_MOUSEBUTTONDOWN: ColRow(e->button.x, e->button.y, row, col); if(col && !(pf[row][col] & P_CODE)) { row2 = plnr >> 1; col2 = plnr % 2; pl[row2][col2] = (col << 8) | row; // kasujemy ewentualne dalsze ruchy do { col2++; if(col2 == 2) { col2 = 0; row2++; } pl[row2][col2] = 0; } while(row2 < 181); InsertMove(); } break; } } } // Obsługa zdarzeń w grze //----------------------- bool DoEvents(SDL_Event * e) { pfDoEvents(e); // plansza opt->DoEvents(e); // opcje for(int i = 0; i < MAXBUTTON; i++) btn[i]->DoEvents(e); // przyciski sbr->DoEvents(e); // pasek przewijania z listą ruchów return true; } // ********************** // *** Program główny *** // ********************** int main(int argc, char * argv[]) { if(SDL_Init(SDL_INIT_VIDEO)) exit(-1); atexit(SDL_Quit); if(!(screen = SDL_SetVideoMode(SCRX, SCRY, 32, SDL_HWSURFACE | SDL_FULLSCREEN))) exit(-1); // ustalamy tryb 15 x 15 InitControls(); // Umieszczamy na planszy przyciski sterujące trybem gry InitGame(); // obsługujemy wszystkie zdarzenia w grze SDL_Event event; // Unia zawierająca struktury zdarzeń while(true) { SDL_WaitEvent(&event); // czekamy na zdarzenie if(DoEvents(&event) && (event.type == SDL_QUIT)) break; } // zamykamy czcionkę gfxCloseFont(font); return 0; }
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe