Snake:D



  • Hey Leutz, habe heute zum ersten Mal Snake programmiert. Mich interessiert da ich relativ neu dabei bin mit C++, inwieweit das sinvoll gewesen ist, es so zu machen wie ich. Habe ich OOP-Like programmiert oder gar nicht ich? Damit ich bei anderen Projekten es besser machen kann. Sind knapp 300 Zeilen und wer sich das nicht antun möchte; kann ich verstehen 😋
    Nochwas: Wenn es einer bei sich aufm PC ausprobieren kann, wäre das toll(kann auch die exe versenden), da bei mir das Spiel sehr langsam läuft, obwohl Sleep() nur auf 50 gesetzt ist. Vllcht hat jmd. eine Antwort drauf.
    Für jeden Tipp bin ich sehr dankbar. Aber bitte immer mit Begruendung.
    Viele Dank!

    EDIT: Es fehlt die Überprüfung wenn der Kopf in Richtung des hinteren Teils lenkt.

    //main.cpp
    #include<iostream>
    #include<conio.h>
    #include<Windows.h>
    
    #include"CField.h"
    #include"CSnake.h"
    #include"CMove.h"
    
    using namespace std;
    
    int main()
    {
      CMove game;//Instanz für das ganze Spiel
      int zaehler;//zaehler damit nach einer eingabe, der default-move nicht ausgefuehrt wird, also schlange geradeaus
      //(gibt zwei verschieden move funktionen) 
      bool exit = false; //für die schleife
      while(!exit) 
      { 
        zaehler=0;//zaehler immer am anfang =0, damit wenn keine taste gedrueckt wird der normal move in der if ausgefuehrt wird. siehe unten 
        system("cls");//zum löschen des Ausgabefensters nach jeder Bewegung
        if(kbhit())
        {      
            char c = getch(); // Muss auf keine Eingabe warten, Taste ist bereits gedrückt 
            switch(c) //nur wenn eine Taste gedrueckt wird, ansonsten bewegt sich die Schlange von selbst 
            { 
              case 'w':
                game.move('w');//die zweite move funktion, hier wird 'w' mitgegeben, damit die funktion richtig reagieren kann
                zaehler++;//damit nur einer bewegung gemacht wird und nicht in die if gegngen wird und die schlange sich nochmal bewegt bevor
                //ein neues Bild erstellt wird
                break;
              case 'a':
                game.move('a');
                zaehler++;
                break;
              case 'd':
                game.move('d');
                zaehler++;
                break;
              case 's':
                game.move('s');
                zaehler++;
                break;
              default:
                ;
            }
        }
        if(zaehler == 0)//hier der andere move der nur gerade ausgeht; zwei moves wegen des Problems, wenn keine Taste gedrueckzt wird
        {  // und die schlange ja trotzdem selbstsändig geradeaus gehne soll
          game.default_move();}
        game.draw();//brett wird gezeichnet
        // Bewege andere Spielfiguren (z.B. die Snake-Schlange) 
         // Geschwindigkeitsregler 
         Sleep(50); 
      }
    
    }
    
    //CFiel.h
    //Selbsterklärend das Feld in dem Die schlange sich bewegt
    
    #ifndef _CField
    #define _CField
    
    class CField
    {
    protected:
      char field[20][20];// das feld 
    public:
      CField();//hier wird das Feld erstellt mit den anfangsdaten für die schlange und des randes
      virtual ~CField(){};//keine fkt.
      void draw();//zeichnen
      void setCharToField(const unsigned int x_cord,const unsigned int y_cord);//für die schlange wenn sie sich bewegt 
      void deleteOldChar(const unsigned int x_cord, const unsigned int y_cord);//auch selbsterklärend; schlangen ende löschen und nicht groeßer werden lassen
    };
    
    #endif _CField
    
    //CField.cpp  ############ fkt selbsterklärend deshalb nicht erklärt
    #include"CField.h"
    #include <iostream>
    
    using namespace std;
    
    void CField::setCharToField(const unsigned int x_cord,const unsigned int y_cord)
    {
      if( (x_cord>=0 || x_cord<20) && (y_cord>=0 || y_cord<20) )
        field[x_cord][y_cord] = '*';
    }
    
    void CField::deleteOldChar(const unsigned x_cord, const unsigned y_cord)
    {
      field[x_cord][y_cord] = ' ';
    }
    
    CField::CField()
    {
      for(int i=0; i<20; i++)
      {
        field[0][i] ='#';
        field[19][i] = '#';
        field[i][0] = '#';
        field[i][19] = '#';
      }
      for(int i=1; i<19; i++)
      {
        for(int j=1; j<19; j++)
        {
          field[i][j] = ' ';
        }
        cout << "\n";
      }
    }
    
    void CField::draw()
    {
      for(int i=0; i<20; i++)
      {
        for(int j=0; j<20; j++)
        {
          cout << field[i][j];
        }
        cout << "\n";
      }
    }
    //CSnake.h
    #include <vector>
    
    #include"CField.h"
    
    #ifndef _CSnake
    #define _CSnake
    
    class CSnake : public CField//Vererbung, damit direkt auf das feld array zugegriffen werden kann und auf die wichtige fkt.
    {
    protected:
      unsigned int x_food, y_food;//coords vom essen für die schlange
      std::vector<unsigned int> snake_x;//schlange besteht aus 2 vektoren einmal für die x coords und einmal die y coords
      std::vector<unsigned int> snake_y;//
    public:
      void new_food();//für das neue essen, wenn das alte weg ist
      int snake_food_check();//checken ob schlange schon gegessen hat
      CSnake();//schlange in vektor gespeichert und ins feld getan und das erste essen gespeichert
      void putInField();//vektoren (schlange) in das feld ausgegebn 
    };
    
    #endif _CSnake
    
    //CSnake.cpp
    #include<iostream>
    #include<stdlib.h>
    #include<time.h>
    
    #include "CSnake.h"
    
    CSnake::CSnake()//schlange startet imnmer von der selben position und das essen auch
    {
      snake_x.push_back(10);
      snake_y.push_back(10);
      snake_x.push_back(10);
      snake_y.push_back(9);
      snake_x.push_back(10);
      snake_y.push_back(8);
      snake_x.push_back(10);
      snake_y.push_back(7);
      setCharToField(snake_x[0],snake_y[0]);
      setCharToField(snake_x[1],snake_y[1]);
      setCharToField(snake_x[2],snake_y[2]);
      setCharToField(snake_x[3], snake_y[3]);
      x_food = 2; y_food = 16;
      field[x_food][y_food] = 'O';
    }
    
    void CSnake::putInField()// fkt um den vektor immer ins feld auszugebene
    {
      for(unsigned int i=0; i<snake_x.size(); i++)
        {
          setCharToField(snake_x[i],snake_y[i]);
        }
    }
    
    int CSnake::snake_food_check()
    {
      if(snake_x[0] == x_food && snake_y[0] == y_food)//da der kopf nur isst wird nach jeder bewegung überprüft oder der kopf(mund) schon gegessen hat 
        return 1;//1 für die abfrage später in move.cpp
      return 0;
    }
    
    void CSnake::new_food()//hier neues essen gesetzt werden
    {
       int x=x_food; int y=y_food;//abspeichern der alten food daten, da gleich in while solange geändert wird bis was anderes rauskommt 
       srand(time(NULL));//um das essen rnd zu verteilen
       while(field[x_food][y_food] != ' ')//hier nur reinkomemn wenn auf dem feld schon was anderes steht 
       {
         x_food=x; y_food=y;//da ja x und y neue daten hattem im 2-ten bzw n-ten durchgang 
         while(x==x_food)//solange bis die x coord neu ist und nicht alt 
         {
           x_food = rand()% 18+1;//hier wird x_food neu gesetzt
         }
         while(y==y_food)//hier wird y neugesetzt
         {
           y_food = rand()% 18+1;
         }
       }
       field[x_food][y_food] = 'O';//und wenn ein neuer geigneter ort gefunden dann das essen setzen
    }
    
    //CMove.h
    #include "CSnake.h"
    #ifndef _CMove
    #define _CMove
    
    class CMove:public CSnake 
    {
    private:
      char direction;//um die letzte Taste Taste zu speicher(w,a,s,d) um die schlange automatisch in diese rihctug laufen zu lassen, wenn nichts gedrueckt wird
      int metFood;
    public:
      CMove();//nur membervariablen gesetzt
      virtual ~CMove(){};
      void move(const char input);//wenn eine taste gedrueckt
      void default_move();//schlange von alleine
    };
    
    #endif _CMove
    
    //CMove.cpp
    #include "CMove.h"
    #include<iostream>
    #include<conio.h>
    #include<Windows.h>
    
    using namespace std;
    
    CMove::CMove()
    {
      direction = ' ';
      metFood=0;
    }
    
    void CMove::move(const char input)
    {
      direction = input;//damit wenn wir in die auto move kommen, wissen in welche richtung
      metFood = snake_food_check();//checken ob gegessen
      deleteOldChar(snake_x[snake_x.size()-1], snake_y[snake_y.size()-1]);//altes zeichen immer löschen
      if(metFood == 1)//wenn gegessen
        {
          snake_x.push_back(snake_x[snake_x.size()-1]);//dann wird die Schlange verlaengert
          snake_y.push_back(snake_y[snake_y.size()-1]);
          new_food();//und neues essen gesetzt
        }
      for(unsigned int i=1; i<snake_x.size(); i++)
      {    
        snake_x[snake_x.size()-i] = snake_x[snake_x.size()-i-1];
        snake_y[snake_y.size()-i] = snake_y[snake_y.size()-i-1];   
      }//hier wird der hintere teil ohne kopf auf den nächsten teil gesetzt(schlange fortbewegung)
    
      if(direction == 'w')
      {
        if(field[snake_x[0]-1][snake_y[0]] == '*' || field[snake_x[0]-1][snake_y[0]] == '#')//kollision überprüfen
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(20000);
          exit(0);
        }
        snake_x[0] = snake_x[0]-1;//ansonsten den kopf in die richtige richtung lenken; die nächsten teile genauso
      }
      else if(direction == 's')
      {
        if(field[snake_x[0]+1][snake_y[0]] == '*' || field[snake_x[0]+1][snake_y[0]] == '#')
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(20000);
          exit(0);
        }
        snake_x[0] = snake_x[0]+1;
      }
      else if (direction == 'a')
      {
        if(field[snake_x[0]][snake_y[0]-1] == '*' || field[snake_x[0]][snake_y[0]-1] == '#')
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(200);
          exit(0);
        }
        snake_y[0] = snake_y[0]-1;
      }
      else if(direction == 'd')
      {
        if(field[snake_x[0]][snake_y[0]+1] == '*' || field[snake_x[0]][snake_y[0]+1] == '#')
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(20000);
          exit(0);
        }
        snake_y[0] = snake_y[0]+1;
      }
      putInField();//neue schlange in das feld setzen
    }
    
    void CMove::default_move()//hier damit die schlange auto läuft
    {
      metFood = snake_food_check();//gegessen?
      deleteOldChar(snake_x[snake_x.size()-1], snake_y[snake_y.size()-1]);//schlange darf nichts liegen lassen;)
      if(metFood == 1)
        {
          snake_x.push_back(snake_x[snake_x.size()-1]);
          snake_y.push_back(snake_y[snake_y.size()-1]);
          new_food();//gleiche wie oben ob
        }
      for(unsigned int i=1; i<snake_x.size(); i++)
      {
        snake_x[snake_x.size()-i] = snake_x[snake_x.size()-i-1];
        snake_y[snake_y.size()-i] = snake_y[snake_y.size()-i-1];
      }//hinterer teil bewegt
    
      if(direction == ' ')
      {//am anfang ist direction mit ' ' definiert(konstruktur), und wenn nichts gedrueckt wird dann hierrein 
        if(field[snake_x[0]][snake_y[0]+1] == '*' || field[snake_x[0]][snake_y[0]+1] == '#')//kollision
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(20000);
          exit(0);
        }
        snake_y[0] = snake_y[0]+1;
      }
      else if(direction == 'w')
      {
        if(field[snake_x[0]-1][snake_y[0]] == '*' || field[snake_x[0]-1][snake_y[0]] == '#')
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(20000);
          exit(0);
        }
        snake_x[0] = snake_x[0]-1;
      }
      else if(direction == 's')
      {
        if(field[snake_x[0]+1][snake_y[0]] == '*' || field[snake_x[0]+1][snake_y[0]-1] == '#' )
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(20000);
          exit(0);
        }
        snake_x[0] = snake_x[0]+1;
      }
      else if (direction == 'a')
      {
        if(field[snake_x[0]][snake_y[0]-1] == '*' || field[snake_x[0]][snake_y[0]-1] == '#')
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(20000);
          exit(0);
        }
        snake_y[0] = snake_y[0]-1;
      }
      else if(direction == 'd')
      {
        if(field[snake_x[0]][snake_y[0]+1] == '*' || field[snake_x[0]][snake_y[0]+1] == '#')
        {
          std::cout << "Leider Verloren" << endl;
          Sleep(20000);
          exit(0);
        }
        snake_y[0] = snake_y[0]+1;
      }
      putInField();
    }
    

  • Mod

    Du hast die "ein Wurstbrot ist ein Supermarkt"-Krankheit. Hast du Bücher von Jürgen Wolf gelesen?

    Falls dir der Begriff nichts sagt: Total vermurkstes Design durch Missbrauch von Vererbung.



  • Wie dus besser machen kannst:
    Eine Klasse map, die verwaltet die Map.
    Darauf agiert eine Klasse snake.
    Und gewöhn dir bitte dieses C davor ab, das ist einfach nur... blöd.



  • SeppJ schrieb:

    Du hast die "ein Wurstbrot ist ein Supermarkt"-Krankheit. Hast du Bücher von Jürgen Wolf gelesen?

    Falls dir der Begriff nichts sagt: Total vermurkstes Design durch Missbrauch von Vererbung.

    Ja, das dachte ich mir. Jürgen Wolf sagt mir nichts...(jetzt nachm google, ja ich habe mal mit galileo computing mir c etwas näher gebracht, für die Uni)
    Was hätte ich denn anders machen sollen. Also ich weiß da gibt es bestimmt viel. Aber durch die Vererbung schien mir das alles relativ einfach zu sein.
    Hätte ich das mit der Vererbung lieber sein lassen und einfach Schnittstellen erstellt?
    Ich will es halt besser machen, deshalb bitte etwas konkreter, wenns geht 😃
    Dankeschön!

    Wie dus besser machen kannst:
    Eine Klasse map, die verwaltet die Map.
    Darauf agiert eine Klasse snake.
    Und gewöhn dir bitte dieses C davor ab, das ist einfach nur... blöd.

    Also das mit dem C hab ich nur von der Uni, dachte es sei sinvoll...
    Wenn du sagst, verwaltet die Map, dann macht sie alles von wegen Crash oder das Essen usw.? Darauf agiert eine Klasse Snake, im Sinne von einer Vererbung?

    EDIT: Meint ich sollte es nochmal machen, aber diesmal versuchen es besser zu machen, oder lieber an ein anderes Projekt wagen?



  • Aknayirp schrieb:

    Wie dus besser machen kannst:
    Eine Klasse map, die verwaltet die Map.
    Darauf agiert eine Klasse snake.
    Und gewöhn dir bitte dieses C davor ab, das ist einfach nur... blöd.

    Also das mit dem C hab ich nur von der Uni, dachte es sei sinvoll...
    Wenn du sagst, verwaltet die Map, dann macht sie alles von wegen Crash oder das Essen usw.? Darauf agiert eine Klasse Snake, im Sinne von einer Vererbung?

    Nein, nicht mit einer Vererbung. Eine Vererbung steht für eine ist-ein Beziehung.
    Ist Snake eine Map? Nein.
    Ich geb dir mal ein denkbares Interface vor, dann verstehst du vielleicht wie ich das meine:

    class map
    {
    public:
        // Konstruktoren...
    
        void update(); // updated die Map, indem dort Essen und so hinzugefügt werden
        void draw(); // zeichnet die Map auf dem Bildschirm
    };
    
    class snake
    {
    public:
        // Konstruktoren
    
        void update(map &m); // nimmt die Map als Referenz, d.h. veränderbar
                             // setzt an den Positionen der Schlange die Tiles entsprechend, checkt Kollision, etc.
    };
    
    int main()
    {
        map m;
        snake s;
    
        while (!game_over)
        {
            m.update();
            s.update(m);
            m.draw();
        }
    }
    

    Ist natürlich noch ausbaufähig.



  • Eine Vererbung steht für eine ist-ein Beziehung. 
    Ist Snake eine Map? Nein.
    

    Jaaa richtig. So hatte ich das auch gelernt, aber hab das heute wohl verdrängt. Aber macht natürlich Sinn.
    Du hast es jetzt hier "void update(map &m)" gesetzt, damit beim Snake update auch Zugriff auf die Klasse map hast? Wenn ja, worauf hast du dann alles Zugriff?

    Vielen Dank für die Hilfe 👍
    MfG
    Aknayirp

    EDIT: Noch ne ganz andere Frage: Wenn ich Snake.exe aufm Laptop starte(hab es auch hier drauf geschrieben), dann ist das Spiel langsam und aufm Rechner viel schneller. Woran kann das liegen?



  • Auf alles public.
    Deshalb wären vielleicht noch Funktionen angebracht wie:
    get_tile_at() bzw. set_tile_at(), damit die Snake auch sich reinzeichnen, bzw. die Früchte abfragen kann.



  • Nathan schrieb:

    Auf alles public.
    Deshalb wären vielleicht noch Funktionen angebracht wie:
    get_tile_at() bzw. set_tile_at(), damit die Snake auch sich reinzeichnen, bzw. die Früchte abfragen kann.

    Alles klar, danke. Ich denke dann versuche ich mich sobald ich kann, an eine bessere Version ran. Aber bin froh es überhaupt geschafft zu haben:D
    P.S: Im letzten Post hatte ich im EDIT eine wichtige Frage gestellen, wäre toll wenn da einer was zu sagen könnte.

    MfG
    Aknayirp



  • Liegt vermutlich am system("cls"), ist scheiße lahm.
    Siehe: http://support.microsoft.com/kb/99261/de für Alternativen.



  • Nathan schrieb:

    Liegt vermutlich am system("cls"), ist scheiße lahm.
    Siehe: http://support.microsoft.com/kb/99261/de für Alternativen.

    Also auf der Seite gibt es nur eine andere Funktion die man benutzen kann, aber kann ich die einfach so einsetzen? Und was muss ich denn
    "void cls( HANDLE hConsole )" für "HANDLE hConsole" einsetzen, wenn ich die Fkt. aufrufe?

    EDIT: Hab eine andere Funktion im I-Net gefunden, mit der es gut funktioniert:
    http://www.cplusplus.com/articles/4z18T05o/#Windows

    EDIT2: Lag wirklich am system("cls")!

    EDIT3: Frage: Habe hier im Programm oft in Memberfkt, andere Memberfkt aufgerufen, ist das so üblich? Oder ist das schlechter Programmierstil?



  • Aknayirp schrieb:

    EDIT3: Frage: Habe hier im Programm oft in Memberfkt, andere Memberfkt aufgerufen, ist das so üblich? Oder ist das schlechter Programmierstil?

    Völlig ok und üblich.



  • nwp3 schrieb:

    Aknayirp schrieb:

    EDIT3: Frage: Habe hier im Programm oft in Memberfkt, andere Memberfkt aufgerufen, ist das so üblich? Oder ist das schlechter Programmierstil?

    Völlig ok und üblich.

    Cool, thx 😃


Anmelden zum Antworten