Bug im Snakespiel



  • Hallo liebe Experten

    Wahrscheinlich wieder einmal so eine Newbiefrage. Da ich mir den Fehler aber einfach nicht mit meinem jetzigen c++-Wissen erkären kann, hoffe ich um konstruktiven Rat von euch.

    Folgendes passiert während dem Spieldurchlauf:

    Kommt die Schlange aus der Senkrechten auf die Nahrung drauf (Oben nach Unten, Unten nach Oben) wird nicht nur wie beabsichtigt bei jeder späteren "Bewegung" der Schlange das letzte ihrer Glieder gelöscht (überschrieben mit cout<<" ";) sondern noch auf der Hoehe dieses Gliedes in der oberen Spielfeldbegrenzung das X .

    Auszug aus c_Snake.cpp:

    void c_Snake::Snake_Bewegung()
    {
    	gxy(iSnake_Pos_x[iSnake_Laenge-1], iSnake_Pos_y[iSnake_Laenge-1]); 
    	cout<<" ";	
    
    	for(int i = iSnake_Laenge-1; i > 0; i--){							   
    
          iSnake_Pos_x[ i ] = iSnake_Pos_x[ i-1 ];   //Koordinaten der Schlangenglieder				    	
    	 iSnake_Pos_y[ i ] = iSnake_Pos_y[ i-1 ];   //jeweils an sein Folgeglied uebergeben
    	};	
    }
    

    Hier die gxy( ) Funktion die mit <windows.h> arbeitet.

    void c_Snake::gxy(int x, int y)
    {
    	HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
    	COORD pos;
    	pos.X=x-1;
    	pos.Y=y-1;
    	SetConsoleCursorPosition(hCon, pos);
    }
    

    Auszug main()

    // snake_uebung.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "c_Rahmen.h"
    #include "c_Snake.h"
    
    c_Snake Snake1;	
    //+++++++++++++++++++++++++++++++++++ main ++++++++++++++++++++++++++++++++
    
    int _tmain(int argc, _TCHAR* argv[])
    {	
    
    	char antw = '0';
    	int Eingabepruefung( char antw );
    	int tempEingabe = 0;
    	long LEVEL = 200;
    
    	cout<< "Snakespiel: \n";
    	cout<< "Schlange wird mit den Tasten a,d,s,w bewegt.\n";
    	cout<< "Spielende mit der taste 'q'.\n\n";
    
    	cout<< "Weiter zum Spiel mit der Taste... 'w'\n ";
    
    	while( antw != 'w' ){
    			antw = getch();
    	};
    	system("cls");
    	antw = 0;
    	int kolli = 0;
    
    //---------------------------Aufbau Spielfeld--------------------------------
    
    	c_Rahmen Fenster( 80, 20 );
    	Fenster.draw_x();
    	Fenster.draw_y();
    
    //-----------------------------Spieldurchlauf--------------------------------
    
    	Snake1.Snake_Ausgabe();	//Ausgabe der ersten Glieder der Schlange 
    	Snake1.Food();			//Ausgabe der ersten Nahrung auf dem Spielfeld
    	while( antw != 'q' ){
    
    		int iLevel = (Snake1.get_Snake_Laenge()/10);	//Nachkommazahlen durch das Speichern in int 
    														//weglassen und so alle 10 Schlangenglieder die 
    														//Spielgeschwindigkeit um 10 erhöhen.
    		if( Snake1.get_Snake_Laenge() < 150 ){			//Ab 150 keine Verschnellerung des Spieltempos mehr
    			LEVEL = 200;			 
    			LEVEL -= iLevel*10; 
    		};
    	  //---------------------------------Score Information-------------
    		Fenster.gxy(1 , 22);
    		cout<<"Glieder der Schlange: "<<Snake1.get_Snake_Laenge();
    		Fenster.gxy(70, 22);
    		cout<<"Level :"<<iLevel;
    
    		/*Fenster.gxy(70, 23);
    		cout<<LEVEL;
    		Fenster.gxy(70, 24);
    		cout<<iLevelup<<"  "<<iTemplevel; */
    
    	  //-------------------------------------------------------------------
    		tempEingabe = 0;
    		int Nahrungsaufnahme = 0;	 
    
    	while(!tempEingabe){
    
    		if( kbhit() ){
    			antw = getch();
    		};
    
    		Sleep( LEVEL );								//Verlangsamungsfaktor
    
    		tempEingabe = Eingabepruefung(antw);		//Prueft die Richtung die gewählt 
    													//wurde und uebergibt diese an Snake
    
    		Snake1.Snake_Bewegung();					//Glieder der Schlange an Folgeglieder
    													//übergeben
    
    		switch( Snake1.get_Snake_Richtung() ){		//Kopf der Schlange in der gewählten Richtung verschieben
    
    			case Rechts:
    				Snake1.set_iSnake_Pos_x( Rechts ); 
    				break;
    			case Links:
    				Snake1.set_iSnake_Pos_x( Links ); 
    				break;
    			case Oben:
    				Snake1.set_iSnake_Pos_y( Oben ); 
    				break;
    			case Unten:
    				Snake1.set_iSnake_Pos_y( Unten );
    				break;
    			default:
    				cout<<system("PAUSE");
    				break;
    			};
    
    		Snake1.Kollisionsabfrage();
    		Snake1.Snake_Ausgabe();						 //Ausgabe der Schlange auf dem Bildschrim
    
    	}; //end while (!tempEingabe)
    	if( Snake1.Kollisionsabfrage() ){
    		antw = 'q'; //exit Spiel
    	};
    	Nahrungsaufnahme = Snake1.Snake_Essen();
    	if( Nahrungsaufnahme ){
    	Snake1.Food();
    	};
    
    	};
    
    //----------------------------------Game Over----------------------------------
    
    	Fenster.gxy( 1 , 21 );						
    	cout<<"---GAME OVER---";
    	Fenster.gxy( 1, 24 ); 
    	system("PAUSE");
    
    	return 0;
    }
    //+++++++++++++++++++++++++++++++++++ Ende main ++++++++++++++++++++++++++++++++
    
    //Definition von der Funktion Eingabepruefung --------------------
    
    int Eingabepruefung(char antw)
    {
    	RICHTUNG Richtung;							//RICHTUNG global definiert in c_Snake.h
    	switch(antw){
    		case 'd':
    			Richtung = Rechts;
    			break;
    		case 'a':
    			Richtung = Links;
    			break;
    		case 'w':
    			Richtung = Oben;
    			break;
    		case 's':
    			Richtung = Unten;
    			break;
    		case 'q':
    			return 1;
    		default:
    			return 0;
    	};
    	Snake1.set_iRichtung( Richtung );
    	return 1;
    }
    

    Danke für eure Hilfe.

    Achja ich weiss, dass man die Koordinaten auch in einem 2D Array unterbringen könnte und wohl noch sehr vieles verbesserungswürdig ist, darauf darf gerne auch eingegangen werden. Würde mich aber freuen, wenn vorallem das Bug-problem behoben werden könnte.

    Ein Kollege hat mir etwas von "Cursor" ausschalten gesagt. Wie würde sowas gehen und brächte es in meinem Fall überhaupt die Lösung?



  • Bist du dir überhaupt sicher, daß es DIESE Funktion ist, die dir deinen Spielfeldrand zerlegt? (ersetz mal den cout<<" "; testweise durch cout<<"."; und beobachte, was passiert)

    PS: Welcher Befehl übergibt eigentlich der Schlange die aktuelle Laufrichtung?



  • CStoll schrieb:

    Bist du dir überhaupt sicher, daß es DIESE Funktion ist, die dir deinen Spielfeldrand zerlegt? (ersetz mal den cout<<" "; testweise durch cout<<"."; und beobachte, was passiert)

    Ja bin ich weil, wie du selber vorschlägst, hab ich die cout<<" " mit cout<<"L" ersetzt und dann gibts statt ner Lücke en L in die Oberste Zeile.

    Ctoll schrieb:

    PS: Welcher Befehl übergibt eigentlich der Schlange die aktuelle Laufrichtung?

    Snake1.set_iRichtung( Richtung ); aus der " Eingabepruefung(char antw) //siehe Auszug main, ganz Unten.

    edit: weiter gehts dann so:

    switch( Snake1.get_Snake_Richtung() ){        //Kopf der Schlange in der gewählten Richtung verschieben 
    
                case Rechts: 
                    Snake1.set_iSnake_Pos_x( Rechts ); 
                    break; 
                case Links: 
                    Snake1.set_iSnake_Pos_x( Links ); 
                    break; 
                case Oben: 
                    Snake1.set_iSnake_Pos_y( Oben ); 
                    break; 
                case Unten: 
                    Snake1.set_iSnake_Pos_y( Unten ); 
                    break; 
                default:  
                    cout<<system("PAUSE"); 
                    break; 
                };
    
    void c_Snake::set_iSnake_Pos_x( RICHTUNG xPos ) 
    {
    
    	switch(xPos) 
    	{
    	case 1:
    		iSnake_Pos_x[ 0 ] += 1;
    		break;
    	case 2:
    		iSnake_Pos_x[ 0 ] -= 1;
    		break;
    	default:
    		cout<<"ERROR xPos ";
    	};
    }
    
    void c_Snake::set_iSnake_Pos_y( RICHTUNG yPos )
    {
    
    	switch(yPos)
    	{
    	case 3:
    		iSnake_Pos_y[ 0 ] -= 1;
    		break;
    	case 4:
    		iSnake_Pos_y[ 0 ] += 1;
    		break;
    	default:
    		cout<<"ERROR Xpos ";
    	};
    }
    


  • Debugger? Dort ist nur ein Ausgabebefehl, also sollte der nur ein Zeichen ausgeben - aber möglicherweise wird die "Snake_Bewegung()" einmal zu oft pro Durchlauf aufgerufen (wenn das immer beim Fressen passiert, könnte der Fehler in der Kollisionsabfrage() liegen).

    PS: Was für ein Datentyp sind eigentlich i_Snake_Pos_x und i_Snake_Pos_y?



  • int iSnake_Pos_x[ 255 ];									//Schlangenglied-Position x
    	int iSnake_Pos_y[ 255 ];									//Schlangenglied-Position y
    

    Beide int Array, 255 ist somit die Begrenzung der Länge für die Schlange. Diese Zahl wurde einfach einmal so festgesetzt von mir. Das ist sicher unschön, weil das Prog > 255 crashed, nur spielt wohl kaum einer so lange dieses Snake.

    Kollisionsabfrage() macht folgendes.

    int c_Snake::Kollisionsabfrage() 
    {
    //Vergleicht den Kopf auf Kollision mit dem Körper.
    	for(int i = 1; i < iSnake_Laenge; i++)	
    	{
    	 if(iSnake_Pos_x[ 0 ] == iSnake_Pos_x[ i ])
    	 {
    		if(iSnake_Pos_y[ 0 ] == iSnake_Pos_y[ i ])
    		{
    			return 1;
    		};		 
    	 };
    	};
    
        //Spielfeldrandueberschreitungen ueberpruefen, falls ja Kopf auf der
        //gegenüberliegenden Seite platzieren
    	if(iSnake_Pos_x[ 0 ] > 79)
    	{
    		iSnake_Pos_x[ 0 ] = 2;
    	};
    	if(iSnake_Pos_x[ 0 ] < 2)
    	{
    		iSnake_Pos_x[ 0 ] = 79;
    	};
    	if(iSnake_Pos_y[ 0 ] > 19)
    	{
    		iSnake_Pos_y[ 0 ] = 2;
    	}
    	if(iSnake_Pos_y[ 0 ] < 2)
    	{
    		iSnake_Pos_y[ 0 ] = 19;
    	};
    	return 0;
    
    }
    

    Zum Debuggen muss ich wohl eingestehen, dass ich nicht genau weiss, wie ich den als Hilfsmittel einsetze (das Tutorial dazu hier im Forum nur angestreift habe). Mein Compiler ist Visual C++ Express und der Debug Output nach dem Durchlaufen des Spiels ist für mich absolut nichtssagend.

    edit:
    problem gelöst hab den fehler gefunden bei der Nahrungsaufnahme wurde ein Array falsch gesetzt es war = +1 statt wie jetzt temp_y+1, wieso das aber auch auswirkungen auf case Oben: Anweisung hatte, ist mir jetzt noch schleierhaft.

    [cpp]
    case Oben:
    iSnake_Pos_y[ iSnake_Laenge-1 ] = temp_y-1;
    iSnake_Pos_x[ iSnake_Laenge-1 ] = temp_x;

    case Unten:
    iSnake_Pos_y[ iSnake_Laenge-1 ] = temp_y+1;
    iSnake_Pos_x[ iSnake_Laenge-1 ] = temp_x;
    [/cpp]

    Snake_Bewegung(int Nahrungsaufnahme) wurde jetzt mit einer if() Pruefung erweitert und wird bei einer Nahrungsaufnahme nichts mehr löschen. Damit ist gewährleistet, dass ein neues Glied falls es auf der Hoehe der Spielfeldbegrenzung erzeugt wird, nicht mehr gelöscht wird, was auch unnötig war, weil dieses "neue" Schlangenglied gar nie auf dem Bildschrim ausgegeben wurde. (Wird vor der Ausgabe ja verschoben)

    if(!Nahrungsaufnahme)
    	{
    	gxy(iSnake_Pos_x[iSnake_Laenge-1], iSnake_Pos_y[iSnake_Laenge-1]);	//Das Letze Schlangenglied löschen
    	cout<<" ";	
    	};
    

    Danke für die Hilfe und Geduld.
    Damit kann der thread hier geclosed werden.



  • Mal ein kleiner Tip am Rande: nimm statt der Arrays lieber einen deque<pair<int,int> > für deine Koordinaten-Angaben. Da bist du flexibler mit der Schlangenlänge und kannst die Schlange einfach mit push_back() verlängern und mit pop_front() weiterschieben (geht schneller als dein Kopiere).


Anmelden zum Antworten