Witajcie
Ostatnio zrobiłem popularną grę węża :).
Pozdrawiam
Ps. Proszę o opinie i komentarze. Czy wam się podoba czy też nie :).
- snake.rar (40 KB) - ściągnięć: 21
- snake.pas (6 KB) - ściągnięć: 50
- modules.pas (2 KB) - ściągnięć: 39
Wrzuć kod na jakiegoś vcsa, ja w żadnym wypadku nie ściągnął bym nic wrzuconego w archiwum
Pascal i wcięcia na 5 spacji? Nonkonformista.
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
Działa ale ...
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ę.
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.
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.
W załączniku przygotowałem Ci przykładowy program gotowy do kompilacji w Lazarus.
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. ;)
Poprawiłem zgodnie z sugestiami :).
Do snake dodałem stronę tytułową umożliwiającą wybór długości węża na początku gry.
Nie chcę Cię dobić @Adept123, bo wiadomo że każdy się uczy itd., ale z przykrością stwierdzam, że produkujesz paskudny kod. Paskudny pod względem wizualnym (czytelność jest tragiczna), jak również pod względem algorytmicznym i struktur danych. Łamiesz wszelkie możliwe wytyczne związane z formatowaniem kodu, używasz najróżniejszych przestarzałych technik, a nawet wykorzystujesz iście starożytne własności języka.
Pracujesz na pececie z DOS-em, że używasz krótkich nazw, w dodatku tylko dużymi literami? Mamy 2020 rok, w Lazarusie możesz używać długich nazw plików, a także stosować przestrzenie nazw (kropki wewnątrz nazwy modułu), dzięki czemu łatwiej zarządzać plikami projektu. Poza tym podzieliłeś projekt na dwa moduły w sumie bez żadnej potrzeby – nieco ponad 300 linijek kodu może istnieć w jednym module.
Pierwszy problem to formatowanie kodu, co od razu odpycha:
Length
),Druga rzecz to struktura kodu:
True
/False
w instrukcjach warunkowych, co jest niespotykane/dziwne,+=
i temu podobne), który jest przecież natywny dla FPC,To tak ogólnie. Jeśli chcesz wiedzieć jak formatować kod, przeczytaj artykuł Object Pascal Style Guide – są pokazane liczne przykłady co robić i czego nie robić. Co do technicznych aspektów to cóż – kursów i tutoriali w sieci mnóstwo, więc jest co czytać.
Nie zniechęcaj się – po prostu przełknij powyższe i zastosuj się do tego, a gwarantuję, że z biegiem czasu będziesz pisał coraz lepszy i coraz ładniejszy kod, który łatwo będzie utrzymywać i łatwo analizować przez osobny postronne.
PS: jeśli publikujesz źródła, to nie wrzucaj osobnych modułów, a po prostu wszystkie pliki projektu, bez pliku wykonywalnego, binarek i kopii zapasowych (zwykle znajdują się w katalogu lib
), oraz bez pliku z zapisem sesji (rozszerzenie .lps
). Kompletne źródła projektu to plik z rozszerzeniem .lps
, wszystkie .pp
lub .pas
(zależy których używasz) oraz ew. grafiki, dźwięki, pliki konfiguracyjne i inne, bez których program nie będzie działał poprawnie.
Jak jesteś zbyt leniwy na ręczne kopiowanie przed archiwizacją, to skorzystaj z opcji w Lazarusie – w głównym menu kliknij w Project
i wybierz opcję Publish project...
. Będziesz mógł albo tylko stworzyć kopię plików projektu, albo dodatkowo spakować je do archiwum. Tylko czytaj to co jest napisanie w komunikatach podczas publikowania.
katakrowa