Skocz do zawartości
Kowal_91

[C++] Problem przy tworzeniu mapy w grze Snake

Rekomendowane odpowiedzi

Witam,
 
Zaznaczę na początku iż dopiero jakoś od ponad miesiąca przyłożyłem się do nauki c++. Wcześniej na pierwszym roku studiów uczyliśmy się C, potem javy, więc nie jestem w tym temacie aż taki zielony. Programowanie mnie interesuje, dlatego chcę rozwijać swoje zainteresowania i kształcić się w tym kierunku.
 
Zakupiłem do tego celu książkę Jerzego Grębosza - Symfonia C++ standard. Uczę się z niej na bieżąco, razem z przykładami i próbuję pisać swoje własne programy, które przyjdą mi do głowy. Jestem osobą, która lubi uczyć się na własnych błędach, lecz problemu z którym próbuję się uporać nie mogę rozwikłać.
 
Domyślam się, że przyczyna tego jest banalna, lecz na dzień dzisiejszy nie mogę pojąć dlaczego Dołączona grafika Dlatego mam nadzieję, że mi pomożecie i wyjaśnicie dlaczego to jest źle.

 

Problem, a dokładniej rzecz mówiąc, bardziej taka ciekawostka, której nie mogę zrozumieć pojawia się podczas rysowania mapy (a dokładniej głowy węża). Ale o tym bliżej za chwilę.

 

 

Moja mapa jest tablicą dwuwymiarową obiektów typu char.

Posiadam dwie funkcje, jedna która rysuje mapę (tj. obramowanie i ustawia każdy z elementów tej tablicy jako znak ASCII o kodzie 255 (czyli spacja). Oraz druga, która powinna rysować głowę węża o podanych wartościach tej tablicy. Lecz niestety nie rysuje. Natomiast, gdy rysowanie węża uwzględnie w funkcji, która rysuje mapę to głowa węża zostaje na tej mapie narysowana.

 

Dlaczego tak się dzieje? I dlaczego głowa węża nie chce się narysować za pomocą oddzielnej funkcji?

Byłbym niezmiernie wdzięczny, gdyby ktoś mógł mi tę sytuację wytłumaczyć :)

 

Oto kod programu:

#include <iostream>#include <conio.h>#include <cstdlib>using namespace std;void rysuj_mape(char mapa[20][60]);void wstaw_weza(char mapa[20][60]);int main(){	char mapa[20][60];	rysuj_mape(mapa);	wstaw_weza(mapa);	getch();	return 0;}//////////////////// FUNKCJA RYSUJĄCA MAPĘ DO GRY ////////////////////void rysuj_mape(char mapa[20][60]){	cout << " ------------------------------------------------------------" << endl;	for(int i = 0; i < 20; i++)	{		cout << "|";	// ramka od lewej strony		for(int j = 0; j < 60; j++)		{			mapa[i][j]=255;			cout << mapa[i][j];		}		cout << "|";	// ramka od prawej strony		cout << endl;	}	cout << " ------------------------------------------------------------" << endl;}//////////////////// FUNKCJA USTAWIAJĄCA WĘŻA NA MAPIE ////////////////////void wstaw_weza(char mapa[20][60]){	mapa[10][30] = 'O';		// wstawienie węża do gry}

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Nie podałeś funkcji rysowania węża. Dołączona grafika Podaj funkcję, bo gdzieś musisz mieć minimalny błąd.

 

Funkcja ta jest zawarta w kodzie, na samym dole :) Ona ustawia (czyli rysuje) węża na mapie, a przynajmniej powinna ale tego nie robi.

//////////////////// FUNKCJA USTAWIAJĄCA WĘŻA NA MAPIE ////////////////////void wstaw_weza(char mapa[20][60]){	mapa[10][30] = 'O';		// wstawienie węża do gry}

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Spróbuj tak:

int main(){	char mapa[20][60];	wstaw_weza(mapa);	rysuj_mape(mapa);	getch();	return 0;}
1. Aktualizujesz tablicę z pozycją węża

2. Aktualizujesz ekran opierając się na zaktualizowanej tablicy

 

Nigdy na odwrót.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

 

Funkcja ta jest zawarta w kodzie, na samym dole Dołączona grafika Ona ustawia (czyli rysuje) węża na mapie, a przynajmniej powinna ale tego nie robi.

 

Zwróć uwagę, że funkcja wstaw_weza tylko aktualizuje tablicę, ale nic nie rysuje, bo przecież Twoim "rysownikiem" jest cout, którego tu nie ma. 

 

Tak jak m4r napisał, jeśli chcesz to rozwiązać w ten sposób, najpierw aktualizuj tablicę a dopiero później wywołuj coś do rysowania. Jednak ... To nie ma prawa zadziałać, ponieważ w funkcji rysuj_mape nadpisujesz wszystkie komórki wartością 255, więc Twoje 'O' przepada. Dołączona grafika

 

 

EDIT : Możesz jeszcze wstawić fragment kodu rysuj_mape , który "działa". :)

 

W tym momencie masz tak :

 

1. Rysujesz mapę z wypełnionymi komórkami na 255.

2. Aktualizujesz tablicę.

 

i nic się nie dzieje, bo nie ma prawa się dziać. W drugą stronę :

1. Aktualizujesz tablicę ([10][30]='O')

2. Rysujesz mapę, która zapełnia komórki wartością 255 (w tym Twoje 'O'). Dołączona grafika

 

W koło Macieju. Dołączona grafika 

 

Musisz pomyśleć nad innym rozwiązaniem. Dołączona grafika

Edytowane przez MaciekCi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Okej, wziąłem pod uwagę Wasze informacje i zrozumiałem na czym polegał mój błąd. Dziękuję Wam :)

Zmodyfikowałem nieco funkcję rysuj_mape, uwzględniając w niej pozycję węża. Napisałem również pozostałe funkcje, tj. poruszanie węża, oraz sprawdzanie czy wąż nie uderzył w ścianę (czy nie jest przekroczony zakres tablicy).

 

Poniżej przesyłam kod i chciałbym, abyście mi podpowiedzieli w jaki sposób mógłbym ten kod zoptymalizować, by był nieco krótszy i prostrzy. Jestem w tej dziedzinie słaby, także chętnie wysłucham wszystkich Waszych porad :) Wiem też, że używałem tam często polecenia na czyszczenie ekranu (system("cls")) i wiem też, że na pewno można co zrobić inaczej, lepiej.

#include <iostream>#include <conio.h>#include <cstdlib>using namespace std;void zeruj_mape(char mapa[20][60]);void rysuj_mape(char mapa[20][60]);void wstaw_weza(char mapa[20][60], int &x, int &y);void ruch(char mapa[20][60], int &x, int &y);bool koniec(char mapa[20][60], int &x, int &y);int main(){	char mapa[20][60];	int x = 30;	// współrzędna na osi X, czyli [j]. Przypisane są tylko początkowe wartości	int y = 10;	// współrzędna na osi Y, czyli [i]	do	{		system("cls");		zeruj_mape(mapa);		wstaw_weza(mapa, x, y);		rysuj_mape(mapa);		ruch(mapa, x, y);		koniec(mapa, x, y);	} while(!koniec(mapa, x, y));		getch();	return 0;}//////////////////// FUNKCJA ZERUJĄCA MAPĘ DO GRY ////////////////////void zeruj_mape(char mapa[20][60]){	for (int i = 0; i < 20; i++)		for (int j = 0; j < 60; j++)			mapa[i][j] = 0;}//////////////////// FUNKCJA RYSUJĄCA MAPĘ DO GRY ////////////////////void rysuj_mape(char mapa[20][60]){	cout << " ------------------------------------------------------------" << endl;	for(int i = 0; i < 20; i++)	{		cout << "|";	// ramka od lewej strony		for(int j = 0; j < 60; j++)		{			if(mapa[i][j] != 'O')				mapa[i][j]=32;			cout << mapa[i][j];		}		cout << "|";	// ramka od prawej strony		cout << endl;	}	cout << " ------------------------------------------------------------" << endl;}//////////////////// FUNKCJA USTAWIAJĄCA WĘŻA NA MAPIE ////////////////////void wstaw_weza(char mapa[20][60], int &x, int &y){	mapa[y][x] = 'O';		// wstawienie węża do gry}//////////////////// FUNKCJA PORUSZAJĄCA WĘŻA PO MAPIE ////////////////////void ruch(char mapa[20][60], int &x, int &y){	char ruch = getch();	if(ruch == 'w')	{		y--;		system("cls");		zeruj_mape(mapa);		wstaw_weza(mapa, x, y);		rysuj_mape(mapa);	}	if(ruch == 's')	{		y++;		system("cls");		zeruj_mape(mapa);		wstaw_weza(mapa, x, y);		rysuj_mape(mapa);	}	if(ruch == 'a')	{		x--;		system("cls");		zeruj_mape(mapa);		wstaw_weza(mapa, x, y);		rysuj_mape(mapa);	}	if(ruch == 'd')	{		x++;		system("cls");		zeruj_mape(mapa);		wstaw_weza(mapa, x, y);		rysuj_mape(mapa);	}}//////////////////// FUNKCJA SPRAWDZAJĄCA CZY WĄŻ UDERZYŁ W ŚCIANĘ ////////////////////bool koniec(char mapa[20][60], int &x, int &y){	bool test = false;	if(x < 0 || x > 59)	{		test = true;		system("cls");		cout << "nn KONIEC GRY!";		return test;	}	if(y < 0 || y > 19)	{		test = true;		system("cls");		cout << "nn KONIEC GRY!";		return test;	}	return test;}

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

 

Wiem też, że używałem tam często polecenia na czyszczenie ekranu (system("cls")) i wiem też, że na pewno można co zrobić inaczej, lepiej.

 

Mam już pamięć mocno zakurzoną jeśli chodzi o konsolowe zabawki, ale z tego co kojarzę, używałem funkcji write i gotoXY i zamiast clsować cały czas, używałem funkcji write, która wypisywała zupdatowaną tablicę. ;) Może Ci to w czymś pomoże... 

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Program udało mi się skrócić do takiej postaci jak w kodzie poniżej. Dodałem dodatkowo funkcję losującą i wstawiającą na mapę jabłka :)

#include <iostream>#include <conio.h>#include <cstdlib>#include <ctime>using namespace std;void zeruj_mape(char mapa[20][60]);void rysuj_mape(char mapa[20][60]);void wstaw_weza(char mapa[20][60], int &x, int &y);void ruch(char mapa[20][60], int &x, int &y);bool koniec(char mapa[20][60], int &x, int &y);void losuj_jablko(char mapa[20][60], int &x, int &y, int &jx, int &jy);int main(){	srand(time(NULL));	char mapa[20][60];	int x = 30;	// współrzędna na osi X, czyli [j]. Przypisane są tylko początkowe wartości	int y = 10;	// współrzędna na osi Y, czyli [i]	int jx = rand() % 59;	// współrzędne pierwszego jabłka	int jy = rand() % 19;	// ------------||---------------		do	{		system("cls");		zeruj_mape(mapa);		wstaw_weza(mapa, x, y);		losuj_jablko(mapa, x, y, jx, jy);		rysuj_mape(mapa);		ruch(mapa, x, y);		koniec(mapa, x, y);	} while(!koniec(mapa, x, y));		getch();	return 0;}//////////////////// FUNKCJA ZERUJĄCA MAPĘ DO GRY ////////////////////void zeruj_mape(char mapa[20][60]){	for (int i = 0; i < 20; i++)		for (int j = 0; j < 60; j++)			mapa[i][j] = 0;}//////////////////// FUNKCJA RYSUJĄCA MAPĘ DO GRY ////////////////////void rysuj_mape(char mapa[20][60]){	cout << " ------------------------------------------------------------" << endl;	for(int i = 0; i < 20; i++)	{		cout << "|";	// ramka od lewej strony		for(int j = 0; j < 60; j++)		{			if(mapa[i][j] == 0)				mapa[i][j]=32;			cout << mapa[i][j];		}		cout << "|";	// ramka od prawej strony		cout << endl;	}	cout << " ------------------------------------------------------------" << endl;}//////////////////// FUNKCJA USTAWIAJĄCA WĘŻA NA MAPIE ////////////////////void wstaw_weza(char mapa[20][60], int &x, int &y){	mapa[y][x] = 'O';		// wstawienie węża do gry}//////////////////// FUNKCJA PORUSZAJĄCA WĘŻA PO MAPIE ////////////////////void ruch(char mapa[20][60], int &x, int &y){	char ruch = getch();	if(ruch == 'w')		y--;		if(ruch == 's')		y++;	if(ruch == 'a')		x--;	if(ruch == 'd')		x++;}//////////////////// FUNKCJA SPRAWDZAJĄCA CZY WĄŻ UDERZYŁ W ŚCIANĘ ////////////////////bool koniec(char mapa[20][60], int &x, int &y){	bool test = false;	if(x < 0 || x > 59)	{		test = true;		system("cls");		cout << "nn KONIEC GRY!";		return test;	}	if(y < 0 || y > 19)	{		test = true;		system("cls");		cout << "nn KONIEC GRY!";		return test;	}	return test;}//////////////////// FUNKCJA LOSUJĄCA JABŁKO NA MAPIE ////////////////////void losuj_jablko(char mapa[20][60], int &x, int &y, int &jx, int &jy){	if(x == jx && y == jy)	{		jx = rand() % 59;		jy = rand() % 19;	}	mapa[jy][jx] = '*';}

MaciekCi, co do tych funkcji write i gotoXY nie bardzo mogę znaleźć opisy, więc też tym bardziej nie mam pojęcia jak je zastosować. Jest jeszcze jeden problem, ponieważ teraz snake jest praktycznie o zerowym poziomie trudności. Chciałbym dodać, aby wraz z czasem snake się sam poruszał w ostatnim wybranym kierunku, lecz nie mam pojęcia jak taki efekt uzyskać.

 

Może miałby ktoś jakieś porady ? :)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Ok uzyskałem ten efekt. Przerobiłem funkcję ruch, niby trochę na około ale nie ogarniam jeszcze optymalizacji ;d No ale najważniejsze, że działa :) Teraz chciałbym, aby wąż po każdym zjedzeniu jabłka rósł o jedno pole. Myślałem, żeby robić to na liście bądź na wektorach, ale nigdy jeszcze nie przerabiałem tych elementów, więc nie wiem czy się połapię. Wiem tylko że można tam łatwo dodawać element na początek i usuwać element z ostatniego miejsca, ale co i jak to już nie wiem.

Macie jakieś porady dla mnie czy to byłoby dobre rozwiązanie? Czy może jest jeszcze jakieś inne, łatwiejsze?

Poniżej przesyłam ponownie cały kod (zmianie uległa przede wszystkim funkcja ruch() )

#include <iostream>#include <conio.h>#include <cstdlib>#include <ctime>#include <Windows.h>using namespace std;void zeruj_mape(char mapa[20][60]);void rysuj_mape(char mapa[20][60]);void wstaw_weza(char mapa[20][60], int &x, int &y);void ruch(char mapa[20][60], int &x, int &y, char &ruch);bool koniec(char mapa[20][60], int &x, int &y);void losuj_jablko(char mapa[20][60], int &x, int &y, int &jx, int &jy, int &pkt);int main(){	srand(time(NULL));	char mapa[20][60];	int x = 30;	// współrzędna na osi X, czyli [j]. Przypisane są tylko początkowe wartości	int y = 10;	// współrzędna na osi Y, czyli [i]	int jx = rand() % 59;	// współrzędne pierwszego jabłka	int jy = rand() % 19;	// ------------||---------------	int punkty = 0;	char kierunek='w';		do		// główna pętla gry	{		system("cls");		zeruj_mape(mapa);		wstaw_weza(mapa, x, y);		losuj_jablko(mapa, x, y, jx, jy, punkty);		rysuj_mape(mapa);		ruch(mapa, x, y, kierunek);		koniec(mapa, x, y);	} while(!koniec(mapa, x, y));		// warunek kończący grę	cout << "nn KONIEC GRY!";	cout << "nn Twoj wynik to: " << punkty;		getch();	return 0;}//////////////////// FUNKCJA ZERUJĄCA MAPĘ DO GRY ////////////////////void zeruj_mape(char mapa[20][60]){	for (int i = 0; i < 20; i++)		for (int j = 0; j < 60; j++)			mapa[i][j] = 0;}//////////////////// FUNKCJA RYSUJĄCA MAPĘ DO GRY ////////////////////void rysuj_mape(char mapa[20][60]){	cout << " ------------------------------------------------------------" << endl;	for(int i = 0; i < 20; i++)	{		cout << "|";	// ramka od lewej strony		for(int j = 0; j < 60; j++)		{			if(mapa[i][j] == 0)				mapa[i][j]=32;			cout << mapa[i][j];		}		cout << "|";	// ramka od prawej strony		cout << endl;	}	cout << " ------------------------------------------------------------" << endl;}//////////////////// FUNKCJA USTAWIAJĄCA WĘŻA NA MAPIE ////////////////////void wstaw_weza(char mapa[20][60], int &x, int &y){	mapa[y][x] = 'O';		// wstawienie węża do gry}//////////////////// FUNKCJA PORUSZAJĄCA WĘŻA PO MAPIE ////////////////////void ruch(char mapa[20][60], int &x, int &y, char &ruch){	if(kbhit())		// sprawdza czy jest jakiś znak w buforze	{		Sleep(100);		ruch = getch();		// jeśli jest to przypisuje go do zmiennej ruch i wykonuje jedną z następujących akcji		if(ruch == 'w')			y--;			if(ruch == 's')			y++;		if(ruch == 'a')			x--;		if(ruch == 'd')			x++;	}	else	// jeśli nie ma to wykonuje ponownie ostatnio wykonany ruch	{		Sleep(100);		if(ruch == 'w')		y--;			if(ruch == 's')			y++;		if(ruch == 'a')			x--;		if(ruch == 'd')			x++;	}	}//////////////////// FUNKCJA SPRAWDZAJĄCA CZY WĄŻ UDERZYŁ W ŚCIANĘ ////////////////////bool koniec(char mapa[20][60], int &x, int &y){	bool test = false;	if(x < 0 || x > 59)	{		test = true;		system("cls");		return test;	}	if(y < 0 || y > 19)	{		test = true;		system("cls");		return test;	}	return test;}//////////////////// FUNKCJA LOSUJĄCA JABŁKO NA MAPIE ////////////////////void losuj_jablko(char mapa[20][60], int &x, int &y, int &jx, int &jy, int &pkt){	if(x == jx && y == jy)	{		jx = rand() % 59;		jy = rand() % 19;		pkt++;	}	mapa[jy][jx] = '*';}

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Czy fukcja losuj_jablko uwzględnia omijanie "pikseli" zajętych przez węża? Dołączona grafika

 

Poprawione :) Dzięki za informacje :D Dodałem pętle w momencie losowania nowego jabłka:

//////////////////// FUNKCJA LOSUJĄCA JABŁKO NA MAPIE ////////////////////void losuj_jablko(char mapa[20][60], int &x, int &y, int &jx, int &jy, int &pkt){	if(x == jx && y == jy)	{		do		{			jx = rand() % 59;			jy = rand() % 19;		} while(jx == x && jy == y);				pkt++;	}	mapa[jy][jx] = '*';}

Jak ktoś ma jeszcze jakieś uwagi, wzmianki, które warto byłoby poprawić to proszę pisać :) No i czekam nadal na odpowiedź w sprawie wydłużającego się węża po każdym zjedzeniu jabłka :>

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Dołącz do dyskusji

Możesz dodać zawartość już teraz a zarejestrować się później. Jeśli posiadasz już konto, zaloguj się aby dodać zawartość za jego pomocą.

Gość
Dodaj odpowiedź do tematu...

×   Wklejono zawartość z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Odnośnik został automatycznie osadzony.   Przywróć wyświetlanie jako odnośnik

×   Przywrócono poprzednią zawartość.   Wyczyść edytor

×   Nie możesz bezpośrednio wkleić grafiki. Dodaj lub załącz grafiki z adresu URL.

Ładowanie


×
×
  • Dodaj nową pozycję...