Ich hab ein Consolen Snake gemacht und hätte gerne Feedback zum Code und dem Spiel.


xandixandi123

Grünschnabel
ich lerne jetzt seit kurzer Zeit c++, und habe ein kleines consolen snake programmiert.
der code ist 419 Zeilen lang und hat viele globale variablen und auch hin und wieder gotos. ich weiß ein nogo aber ich bin noch anfänger :). es gibt sogar sound wenn man ein essen isst. das einzige was ich noch mehr dazu- entwickeln könnte sind levels wo die
schlange schneller wird.


bitte feedback zum code und test ob es noch bugs gibt. und was für ein spiel könnte ich als nächstes porogrammierem,
dass ähnlich schwer zu machen ist?

ich arbeite mit dev c++.

Code:
#include <cstdlib>
#include <iostream>
#include <windows.h>
#include <conio.h>

#define Key_Up 119
#define Key_Down 115
#define Key_Right 100
#define Key_Left 97

#define Leer 0
#define Kopf 1
#define Koerper 2
#define Essen 3


using namespace std;

//die Funktion, die nach Aufruf alles grün macht
void gruen()
{
     HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
     SetConsoleTextAttribute(
     hStdout,
     FOREGROUND_GREEN
     );
}

// Position das essens
int pos_essen_a = 0;
int pos_essen_b = 0;

//Level
int level = 1;

//das Koordinatensystem
int feld [21][31];

//das Koordinatensystem(fake, um das Blinken zu ermöglichen wenn man stirbt
int feld_fake [21][31];


//die Position des Kopfes
int pos_kopf_a = 9;
int pos_kopf_b = 14;

//eine Kopie der Kopf cords
int pos_kopf_a_temp;
int pos_kopf_b_temp;

//die Postitonen der Körperteile
int pos_koerper_a[501] = {0};
int pos_koerper_b[501] = {0};

//die Schlangenlänge
int schlange = 1;



// Spawnt Essen
void spawn_essen()
{
     anfang:
     srand( (unsigned)time( NULL ) );
     
     pos_essen_a = (rand()%(20));
     pos_essen_b = (rand()%(30));

     
     if(feld[pos_essen_a][pos_essen_b] != Leer)
     //hier muss es ein goto sein um die Funktion zu wiederhohlen, wenn man einfach nur nochmal die Funktion
     //spawn_essen aufruft, dann stürtzt das Programm ab
     goto anfang;
     
     feld[pos_essen_a][pos_essen_b] = Essen;
}


//die Funktion füllt das Feld mit Null (Leer)
void null_fuellen()
{
     for(int a = 0; a < 20; a++)
     {
             for(int b = 0; b < 30; b++)
             {
                     feld[a][b] = Leer;
             }
     }
}

//die Funktion zeichnet das Spielfeld
void zeichne_feld()
{
     system("cls");
     
     gruen();
     cout << "_______________________________\n";
     for(int a = 0; a < 20; a++)
     {
             cout << "|";
             for(int b = 0; b < 30; b++)
             {
                     if(a != 19)
                     {
                          if(feld[a][b] == Leer) cout << " ";
                          if(feld[a][b] == Kopf) cout << "O";
                          if(feld[a][b] == Koerper) cout << "o";
                          if(feld[a][b] == Essen) cout << "*";
                     }
                     
                     if(a == 19)
                     {
                          if(feld[a][b] == Leer) cout << "_";
                          if(feld[a][b] == Kopf) cout << "O";
                          if(feld[a][b] == Koerper) cout << "o";
                          if(feld[a][b] == Essen) cout << "*";
                     }
                          
             }
             
     if(a == 2)
     cout << "|      SCORE: " << schlange << endl;
     
     else cout << "|" << endl;
     
     }
}

void zeichne_feld_fake()
{
     system("cls");
     
     gruen();
     cout << "_______________________________\n";
     for(int a = 0; a < 20; a++)
     {
             cout << "|";
             for(int b = 0; b < 30; b++)
             {
                     if(a != 19)
                     cout << " ";
                     
                     if(a == 19)
                     {
                          cout << "_";
                     }
                          
             }
     if(a == 2)
     cout << "|      SCORE: " << schlange << endl;
     
     else cout << "|" << endl;
     }
}

//Koordinaten in Feld-Daten umwandeln
void uebergebe_pos()
{
     for(int i = 1; i <= schlange; i++)
     {
             feld[pos_koerper_a[i]][pos_koerper_b[i]] = 2;
     }
     
     
     feld[pos_essen_a][pos_essen_b] = 3;
     
     feld[pos_kopf_a][pos_kopf_b] = 1;
}

//Koordinaten Weitergeben an weitere Köorperteile
void weitergeben_pos()
{
     pos_kopf_a_temp = pos_kopf_a;
     pos_kopf_b_temp = pos_kopf_b;
     
     for(int i = schlange; i >= 2; i--)
     {
             pos_koerper_a[i] = pos_koerper_a[i-1];
             pos_koerper_b[i] = pos_koerper_b[i-1];
     }
     

     
     pos_koerper_a[1] = pos_kopf_a_temp;
     pos_koerper_b[1] = pos_kopf_b_temp;
}

//die Schlange Startet nach Rechts
int a = Key_Right;

//der letzte Tastendruck, der vor dem Aktuellem gemacht wurde
int a_2 = 0;

//Game Over
void game_over()
{
     for(int i = 0; i < 3; i++)
     {
             //das Feld blinken lassen
             zeichne_feld_fake();
             Sleep(500);
             zeichne_feld();
             Sleep(500);
     }    
}

//wenn en essen gegessen wurde wird kurz gebeept,
//diese variable is dazu gut das es dann einen kürzeren sleep gibt
int beep_ak = 0;

int main()
{
    null_fuellen();
    
    spawn_essen();
    
    //man startet in der mitte
    feld[9][14] = 1;
    feld[0][0] = 0;
    
    zeichne_feld();
    
    a = getch();
    
    neu_eingabe:
    
    //Wenn eine Taste gedrückt nehme deren Wert an
    if(kbhit())
    a = getch();
    
    if((a_2 == Key_Down) && (a == Key_Up))
    a = Key_Down;
    
    if((a_2 == Key_Up) && (a == Key_Down))
    a = Key_Up;
    
    if((a_2 == Key_Right) && (a == Key_Left))
    a = Key_Right;
    
    if((a_2 == Key_Left) && (a == Key_Right))
    a = Key_Left;
    
    a_2 = a;
    
    switch(a)
    {
             case Key_Up:
                  
                  
                  //Wenn oberer Rand dann unterer Rand
                  if(pos_kopf_a == 0)
                  {
                                pos_kopf_a = 20;
                  }
                  
                  //Wenn Schlange Schlange berührt dann Game Over
                  if(feld[pos_kopf_a - 1][pos_kopf_b] == 2)
                  {
                                     game_over();
                                     return 0;
                  }
                  
                  //Wenn Schlange Essen berührt dann ein Teil länger und ein Level höher
                  if(feld[pos_kopf_a - 1][pos_kopf_b] == 3)
                  {
                                     feld[pos_kopf_a - 1][pos_kopf_b] == 0;
                                     ++ schlange;
                                     
                                     beep_ak = 1;
                                     Beep(700,50);
                                     ++ level;
                                     spawn_essen();
                  }
                  
                  //Wenn nichts berührt, einfach ein Feld Weiter
                  pos_kopf_a -= 1;
                  break;
                  
                  //Dasselbe jetzt nochmal mit den anderen Richtungen
             case Key_Down:
                  
                  
                  if(pos_kopf_a == 19)
                  {
                                if(feld[pos_kopf_a - 19][pos_kopf_b] == 3)
                                {
                                                   pos_kopf_a = 0;
                                                   ++ schlange;
                                                   beep_ak = 1;
                                                   Beep(700,50);
                                                   ++ level;
                                }
                                
                                if(feld[pos_kopf_a - 19][pos_kopf_b] == 2)
                                {
                                                   game_over();
                                                   return 0;
                                }
                                        
                                           
                                pos_kopf_a = 0;
                                break;
                                
                                
                  }
                  
                  if(feld[pos_kopf_a + 1][pos_kopf_b] == 2)
                  {
                                     game_over();
                                     return 0;
                  }
                  
                  
                  if(feld[pos_kopf_a + 1][pos_kopf_b] == 3)
                  {
                                     feld[pos_kopf_a + 1][pos_kopf_b] == 0;
                                     ++ schlange;
                                     beep_ak = 1;
                                     Beep(700,50);
                                     ++ level;
                                     spawn_essen();
                  }
                  
                  
                  
                  pos_kopf_a += 1;
                  break;
                  
             case Key_Left:
                  if(pos_kopf_b == 0)
                  {
                                if((pos_kopf_a == 0) || (pos_kopf_a == 1) || (pos_kopf_a == 2) || (pos_kopf_a == 3) || (pos_kopf_a == 4) || (pos_kopf_a == 5) || (pos_kopf_a == 6) ||
                                   (pos_kopf_a == 7) || (pos_kopf_a == 8) || (pos_kopf_a == 9) || (pos_kopf_a == 10) || (pos_kopf_a == 11) || (pos_kopf_a == 12) || (pos_kopf_a == 13) ||
                                   (pos_kopf_a == 14) || (pos_kopf_a == 15) || (pos_kopf_a == 16) || (pos_kopf_a == 17) || (pos_kopf_a == 18) || (pos_kopf_a == 19))
                                   {
                                               pos_kopf_b = 30;
                                   }
                  }
                  
                  if(feld[pos_kopf_a][pos_kopf_b - 1] == 2)
                  {
                                                 game_over();
                                                 return 0;
                  }
                  
                  if(feld[pos_kopf_a][pos_kopf_b - 1] == 3)
                  {
                                     feld[pos_kopf_a - 1][pos_kopf_b] == 0;
                                     ++ schlange;
                                     beep_ak = 1;
                                     Beep(700,50);
                                     ++ level;
                                     spawn_essen();
                  }
                  
                  
                  pos_kopf_b -= 1;
                  break;
             case Key_Right:
                  if(pos_kopf_b == 29)
                  {
                                if((pos_kopf_a == 0) || (pos_kopf_a == 1) || (pos_kopf_a == 2) || (pos_kopf_a == 3) || (pos_kopf_a == 4) || (pos_kopf_a == 5) || (pos_kopf_a == 6) ||
                                   (pos_kopf_a == 7) || (pos_kopf_a == 8) || (pos_kopf_a == 9) || (pos_kopf_a == 10) || (pos_kopf_a == 11) || (pos_kopf_a == 12) || (pos_kopf_a == 13) ||
                                   (pos_kopf_a == 14) || (pos_kopf_a == 15) || (pos_kopf_a == 16) || (pos_kopf_a == 17) || (pos_kopf_a == 18) || (pos_kopf_a == 19))
                                   {
                                               pos_kopf_b = -1;
                                   }
                  }
                  
                  if(feld[pos_kopf_a][pos_kopf_b + 1] == 2)
                  {
                                                 game_over();
                                                 return 0;
                  }
                  
                  if(feld[pos_kopf_a][pos_kopf_b + 1] == 3)
                  {
                                     feld[pos_kopf_a + 1][pos_kopf_b] == 0;
                                     ++ schlange;
                                     beep_ak = 1;
                                     Beep(700,50);
                                     ++ level;
                                     spawn_essen();
                  }
                  
                  
                  pos_kopf_b += 1;
                  break;
                  
             default:

                     zeichne_feld();
                                          cout << "\n          << PAUSE >>";
                     a = getch();
                     goto neu_eingabe;
    }
    
    weitergeben_pos();
    null_fuellen();
    uebergebe_pos();
    zeichne_feld();

    if(beep_ak = 1)    
    Sleep(50);
    
    else Sleep(100);
    
    beep_ak = 0;
    
    
    
    goto neu_eingabe;
    
    
    

    
    

    return EXIT_SUCCESS;
}
 

sheel

I love Asm
Hi und Willkommen bei tutorials.de :)

Zuerst einmal: Bitte halte dich an die Forenregeln hier.
Siehe meine Signatur, erster Link

Spieleidee: Vllt. Space Impact?

goto etc: Ist machmal sogar sehr hilfreich...wenn es vernünftig eingesetzt wird.

Das Programm werd ich mir jetzt einmal in Ruhe anschauen und dir dazu was schreiben.

Gruß
 

sheel

I love Asm
Also...

hat nicht wirklich was mit dem Code zu tun, aber Devcpp ist veraltet.
Sollte nicht mehr unbedingt verwendet werden.

Die Einrückungen in der unteren Hälfte in {}-Blöcken sind ...
Muss man wirklich 20 Zeichen weiter rein? 4 Reichen doch auch.

zwei Befehle, zwischen denen ca. 8 leere Zeilen sind, erleichtern das Lesen nicht gerade.

Es hat sich eingebürgert, dass man define-Namen GROSS schreibt.
Um sie von Variablen auseinanderhalten zu können.

Include etc/Glob. Vars/Funktionen sollten nicht so durcheinandergemischt werden.
Zuerst Includes, dann Variablen, dann Funktionen, dann main.

Statt für Koordinaten immer zwei Variablen oder zwei Arrays zu nehmen,
könnte man eine Koordinaten-Struktur machen.

In einer 10-Zeilen-Funktion (spawn_essen) ohne Schleifen mit einem if ein goto verwenden hat den Sinn von goto verfehlt.
Ich selbst verwende zwar auch hier und da goto, aber nur,
wenn ich mir dadurch viel Code oder Performanceverlust ersparen kann.
Hier würde eine do-while-Schleife perfekt passen.

Für einzelne Zeichen (zB. |) cout verwenden ist langsam.
Kein Fehler, aber putc wäre schneller.

Dass man statt globalen Variablen besser mit sinnvoll eingesetzten Funktionsparametern arbeitet hast du ja schon selbst erkannt.

Teile aus dem main könnte man ruhig in Funktionen auslagern.
Verglichen mit dem Rest ist main überlang.

Eine Endlosschleife im Main (vor allem mit goto) ist nicht schön.
Wenn schon, dann while(1) statt if-goto.

Bei den Tasten (Pfeiltasten) muss das Erste von zwei getch auch geprüft werden.
Es kann ja sein, dass keine Pfeiltaste, sondern zwei Buchstaben gedrückt werden?

Bei den switch-Teilen im main ist vieles vom Sinn her für alle Fälle gleich.
Könnte vor dem switch einmal geschrieben werden, statt viermal unten drin.
zB. die Rand-Kontrolle und die Gameover-Kontrolle.

Und in zwei der vier switch-cases: Was zum Teufel ist das:
C++:
if((pos_kopf_a == 0) || (pos_kopf_a == 1) || (pos_kopf_a == 2) || (pos_kopf_a == 3) || (pos_kopf_a == 4) || (pos_kopf_a == 5) || (pos_kopf_a == 6) ||
(pos_kopf_a == 7) || (pos_kopf_a == 8) || (pos_kopf_a == 9) || (pos_kopf_a == 10) || (pos_kopf_a == 11) || (pos_kopf_a == 12) || (pos_kopf_a == 13) ||
(pos_kopf_a == 14) || (pos_kopf_a == 15) || (pos_kopf_a == 16) || (pos_kopf_a == 17) || (pos_kopf_a == 18) || (pos_kopf_a == 19))
Das kann man auch einfach so schreiben:
C++:
if(pos_kopf_a >= 0 && pos_kopf_a < 20)

Ausführen muss ich das Programm erst noch. Was mir da auffällt kommt noch.

Gruß
 

sheel

I love Asm
Zum Kompilieren: Du solltest dir die Warnungen auch anschauen, nicht nur die Fehler.
C++:
feld[pos_kopf_a - 1][pos_kopf_b] == 0;
Im erste case des switch im main.
Vergleichbares gibts in den anderen drei cases.
Aber da es auch so funktioniert kannst du die Zeilen auch rausnehmen.

Zu den Tasten: Ich merk grad, dass du ja nicht die Pfeiltasten, sondern asdw verwendest.
Dann kann man im Code aber ruhig 'a' usw. verwenden, statt nichtssagender Nummern.

Zum Programm:
Mit einer Pause kann man umdrehen (180 Grad) (nur wenn man noch Länge 1 hat, am Anfang).

Es wird immer alles in der Konsole neu gezeichnet.
Das flimmert.
Verhindern kann man das, in dem man mit WriteConsoleOutputCharacter gezielt an bestimmte Koordinaten schreiben kann und den Rest stehenlasst.

Nochmal zum Code: Die Beschränkung auf max. 500 Länge (wegen dem Array) solltest du beim Fressen auch überprüfen, dass es nicht zu lang wird. Oder ein größeres Array.
Auf 500 kommt man schon...So ein Old-Style-Snake macht süchtig :D

Alles in allem:
Gut gemacht :)

Lass dich von den vielen "Kritik"-Punkten nicht einschüchtern.
Ich hab dabei immer gedacht, wie ich das machen würde,
und programmiere aber schon einige Jahre länger...

Weiter so!
 
Zuletzt bearbeitet:

xandixandi123

Grünschnabel
danke für deine mühe!

ich werde den code nochmal überarbeiten, aber das mit der nicht flimmernden feld-ausgabe mit WriteConsoleOutputCharacter bekomm ich einfach nicht hin :)

kannst du mir bitte nochmal mit der überarbeiteten zeichne_feld funktion helfen?

und noch eine frage: ist wx devcpp eine gute alternative zu devcpp? mc visual studio mag ich nicht so.

beim nächsten projekt versuche ich in einem besseren stil zu schreiben.

nochmal danke!
 

sheel

I love Asm
Bite nocheinmal: Forenregeln. Erster Link in der Signatur, §15.

Zu WxDevCpp: Noch nie gehört :D
Wenn du VS nicht magst: CodeBlocks, oder Eclipse, Netbeans...
Aber ich sag ja nicht, dass du umsteigen musst, nur kannst.

WriteConsoleOutputCharacter: Was ist denn das Problem?

zeichne_feld: Wenn du sie überarbeitet hast, zeig die neue Variante doch.
 
Zuletzt bearbeitet:

Neue Beiträge

Forum-Statistiken

Themen
272.359
Beiträge
1.558.621
Mitglieder
187.832
Neuestes Mitglied
SirrDansen