Gra wąż

0

Witajcie

Ostatnio zrobiłem popularną grę węża :).

Pozdrawiam

Ps. Proszę o opinie i komentarze. Czy wam się podoba czy też nie :).

2

Wrzuć kod na jakiegoś vcsa, ja w żadnym wypadku nie ściągnął bym nic wrzuconego w archiwum

1

Pascal i wcięcia na 5 spacji? Nonkonformista.

1
procedure DrawingSnakeAndFood(wsp_x,wsp_y: integer);
var
   wi,wj: byte;
   point_x1,point_y1,point_x2,point_y2: integer;
   wl,wk: integer;
begin
     point_x1 := 0; point_y1 := 0;
     for wi := 1 to 10 do
        begin
             for wj := 1 to 10 do
                begin
                     point_y1 := wsp_y * (wi - 1);
                     point_y2 := point_y1 + wsp_y;
                     if wi in [2..3] then
                       begin
                            point_y2 := point_y2 - 1;
                            if wi = 3 then
                              point_y1 := point_y1 - 1
                       end
                     else
                         if wi in [4..6] then
                           begin
                                point_y1 := point_y1 - 1;
                                point_y2 := point_y2 - 1;
                                if wi = 6 then
                                  point_y2 := point_y2 - 1
                           end
                         else
                             if wi = 10 then
                               point_y1 := point_y1 - 3
                             else
                                 if wi in [7..9] then
                                   begin
                                        point_y1 := point_y1 - 2;
                                        point_y2 := point_y2 - 2;
                                        if wi = 9 then
                                          point_y2 := point_y2 - 1
                                   end;

                     point_x1 := wsp_x * (wj - 1);
                     point_x2 := point_x1 + wsp_x;

                     if wj in [6..10] then
                       begin
                            if wj = 6 then
                              point_x2 := point_x2 - 1
                            else
                                begin
                                     point_x1 := point_x1 - 1;
                                     point_x2 := point_x2 - 1
                                end
                       end;

                    if board[wi,wj] = 'X' then
                      begin
                           setcolor(4);
                           rectangle(point_x1,point_y1,point_x2,point_y2);
                           setcolor(10);
                           circle(point_x1 + (wsp_x div 2),point_y1 + (wsp_y div 2),wsp_y div 2);
                           putpixel(point_x1 + (wsp_x div 2),point_y1 + (wsp_y div 2),12)
                      end
                    else
                        if board[wi,wj] = '0' then
                          begin
                               setcolor(2);
                               rectangle(point_x1,point_y1,point_x2,point_y2);
                               setcolor(13);
                               circle(point_x1 + (wsp_x div 2),point_y1 + (wsp_y div 2),wsp_y div 2);
                               putpixel(point_x1 + (wsp_x div 2),point_y1 + (wsp_y div 2),11)
                          end
                end
        end
end;

Podoba mi się, Takie nieoczywiste

5

Działa ale ...

  1. Strasznie miga. Niepotrzebnie przerysowujesz cały ekran. Czyścisz i rysujesz na nowo. Tak można robić ale tylko gdy grafikę buforujesz.
  2. Procedura rysowania planszy jest "kosmiczna". Widać, że to efekt końcowy zgadywania tak długo aż zadziała a nie przemyśleń jak ma to działać. Wystarczyła pojedyncza pętla bez wszelkich ifów ... rysująca węża na podstawie tablicy i 1 linijka rysująca jedzenie ( wywołanie procedury rys. jedzenie ). ( przecież i tak kasujesz obraz op każdym kroku ). Co jakby plansza była 1000x1000 wykonywałbyś te operacje if'y, pypisania itp ... milion razy na każdą klatkę?

Skoro już to napisałeś i działa to teraz przemyśl cały algorytm jeszcze raz i napisz ponownie ale tym razem poprawnie. Brawo za dobre chęci ale obecna wersja niestety nie zasługuje na pochwałę od strony planowania i samego kodu.
Jeśli będziesz miał pytania to pomogę.

3

Przy rysowaniu węża istotne jest odmalowywanie tylko tych fragmentów, które uległy zmianie – dotyczy to zarówno węża, jak i interfejsu (punktów, długości itp.). W przypadku konsolowych aplikacji jest to banalne, bo skasowanie bieżącego znaku i namalowanie nowego to po prostu namalowanie nowego – stary znika. Jeśli chodzi o węża, to przemalowuje się go tylko wtedy, gdy się poruszył, a jeśli się poruszył, to zamalowujesz jego ogon i malujesz głowę w nowym miejscu.

To tak w skrócie. I popraw te wcięcia, bo kod wygląda z nimi tragicznie! W Pascalu używa się wcięć dwuznakowych, we wszystkich konstrukcjach kodu. Są wyjątki, ale póki nie potrafisz formatować zwyczajnego kodu w praktycznie w ogóle, to zostawmy temat wyjątków na kiedyś indziej.

2

Generalnie stosując pascal'ową bibliotekę graph / BGI tak należałoby zrobić ( rysowaćtylko zmiany ) bo w sumie nie ma innej możliwości - ale to z wielu powodów problematyczna metoda.. Z tego co pamiętam to w Graph.pas nie było obsługi buforowania aczkolwiek tego typu sztuczek już od lat się nie stosuje bo przekopiowanie bufora do pamięci 20 razy na sek. to czas właściwie pomijalny a metoda taka rozwiązuje wszelkie problemy z nachodzeniem się obiektów, kolorowym tłem ( np. obrazek ) i wiele innych...
Najbardziej podstawowe funkcje do rysowania wykorzystywane w WinApi to SetDIBitsToDevice + GetDC jak o nich poczytasz to będziesz miał solidną bazę do dalszych zabaw.
Funkcje wstępnie wyglądają strasznie ale w praktyce nie jest trudno.

W sumie całość sprowadza się do ( delphi / lazarus ):


procedure Rysuj();
const

  maxBufferX = 1920 ;
  maxBufferY = 1080 ;

Type

  // Twój bufor ekranu jeden typ liniowy, drugi z dostępem do poszczególnych składowych kolorów.
  //
  TScreen = array [ 0 .. maxBufferX*maxBufferY ] of Dword ; // planujemy układ kolorów RGBA
  TScreenRGBA = Array [ 0 .. maxBufferX*maxBufferY ] of record 
    R, G, B, A :byte ;
  end ;

Var

  canvasHandle : HWND ;
  _bmpinf:TbitmapInfoHeader;
  _bmpin:TBITMAPINFO;
  _rgbq:array [0..0]of TRGBQUAD;

  Screen : TScreen ;
  ScreenRGBA : TScreenRGBA absolute Screen ; // absolute rzutuje obszar pamięci na Screen  

  x, y : integer ;

begin

  //
  // Inicjujemy niezbędne struktury opisujące parametry bufora obrazu
  //

  canvasHandle := [HWND] ; // Tu przypisać HWND okna w którym rysujemy.

  _bmpinf.bisize:=sizeof(_bmpinf);
  _bmpinf.biwidth:=MaxX;
  _bmpinf.biheight:=MaxY;
  _bmpinf.biplanes:=1;
  _bmpinf.bibitcount:=32;
  _bmpinf.bicompression:=0;
  _bmpinf.bisizeimage:=0;
  _bmpinf.biXpelspermeter:=0;
  _bmpinf.biYpelspermeter:=0;
  _bmpinf.biclrused:=0;
  _bmpinf.biclrimportant:=0;
  _rgbq[0].rgbblue:=1;
  _rgbq[0].rgbgreen:=2;
  _rgbq[0].rgbred:=3;
  _rgbq[0].rgbreserved:=0;
  _bmpin.bmiheader:=_bmpinf;
  _bmpin.bmicolors[0]:=_rgbq[0];

  // 
  // Tutaj rysujesz cokolwiek w bufrze ...
  // np. wypełnienie losowymi odcieniami czerwonego
  //

  for x := 0 to maxBufferX - 1 do  
  for y := 0 to maxBufferY - 1 do
  begin
    ScreenRGBA [ x + maxBufferX * Y ].R = random(255) ;
  end;

  //
  // Wyrzucasz na ekran / do okna.
  //

  SetDIBitsToDevice( GetDc ( canvasHandle ), 0, 0 ,maxBufferX, maxBufferY, 0, 0, 0, maxBufferY, @Screen[0], _bmpin,DIB_RGB_COLORS ) ;

end;

czyli właśnie kopiowanie zawartości bufora na ekran ... Niestety wszystko rysować trzeba samemu lub użyć jakiejś biblioteki.

1

W załączniku przygotowałem Ci przykładowy program gotowy do kompilacji w Lazarus.

screenshot-20200916200129.png

0

Standardowe wykorzystanie tylnego bufora to nie jest jedyny sposób na przyspieszenie renderowania. W poważniejszych projektach bazujących na FCL i LCL (np. mój Deep Platformer lub ostatnio Richtris), można spokojnie korzystać z natywnego GDI+ i wydajność jest całkiem niezła (tym bardziej w porównaniu do Linuchów) i tam bitmapy jako bufory sprawdzają się super.

Tutaj problemem jest to, że rysowanie odbywa się bezpośrednio na docelowym płótnie (obojętne na którym). Ten problem można rozwiązać poprzez zamalowanie ”nieważnego” kafla zwykłym Rectangle i namalowanie go od nowa. Taki sposób i tak znacząco przyspieszy proces renderowania, dzięki czemu nic nie będzie migać. Oczywiście o ile gra bazuje na tabelarycznie zbudowanej planszy i wężu przesuwającym się o cały kafel (a nie o piksel/kilka pikseli w każdej klatce).

W każdym razie zawsze da się zrobić lepiej niż malować wszystko od nowa. ;)

0

Poprawiłem zgodnie z sugestiami :).

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0