Konsolenspiel - Snake Race(v2.0)
-
Hallo!
Ich hab nun schon seit ca einem Monat an einem Konsolenspiel gearbeitet. Insgesamt hab ich alle paar Tage immer mal dran gearbeitet und eine neue Version herausgebracht.
Ein Freund von mir meinte, ich sollte das ganze mal im Internet veröffentlichen und das ganze ein paar "pros" zeigen, die sehr gut programmieren können. Er meinte, die würden mich auslachen, wenn sie den Code sehen xD
Also dachte ich mir, ich veröffentliche das ganze mal. Ist ja auch open-source.
Mich würde mal interessieren, was ihr so zu dem Code sagt. Ich hab das ganze prozedural gehalten, wo auch gleich meine erste Frage wäre:
Wäre alles leichter gewesen, wenn man das ganze Objektorientiert, also in einer Klasse "Level", geschrieben hätte oder ist das schon gut wie es ist? Wie findet ihr den Stil, ist alles zu kompliziert oder versteht man das was ich machen wollte? Sind meine Kommentare richtig gesetzt, zu wenig/viel?Achja, ich hab mit Absicht keinen Schwanz gemacht, es soll ja auch kein wirkliches Snake sein, der Name stammt von einem Freund, ich hatte keinen Plan, wie ich das nennen soll.
Counter-Strike sounds hab ich genommen, weil ich die gerade auf meinem Computer hatte (flog irgendwo ein 1.6 rum..), das ganze verleiht dem ja auch eine gewissene Note.Und wie lässt sich das Spiel spielen? Gibt's da irgendwelche speziellen Spielvorschläge?
Dort befindet sich einmal die fertige .exe mit dem genialen, selbst gepaintetem Icon, und der Ordner "source", wo sich der Quellcode und Code::Blocks Projektdatei befindet.
Wer also mal Zeit hat, kann sich das ganze ja mal ansehen. Entschuldigt, wenn der Code zu unübersichtlich ist, ich programmiere ja auch erst seit ca. 2 Jahren (Ich bin 14).
Aktueller Downloadlink: Version 2.0
-
Geil
-
Ähm..
Ich finds zwar schön, dass dir das Spiel gefällt, aber ein "Geil" hilft mir jetzt nicht wirklich weiterOkay, um euch ein bisschen Tipparbeit zu ersparen, bastel ich euch einfach mal einen Auswertungsbogen, dann warte ich ein paar Tage auf Antworten, addiere alles zusammen und bekomme dann ein Gesamtergebnis.
Auswertungsbogen
Zum Spiel:
Interface [1-100%]
Sounds [1-100%]
Spielspaß [1-100%]
Abwechslung [1-100%]
Schwierigkeit [1-100%]
Grafische Gestaltung [1-100%]Zum Code:
Kommentare:
Häufigkeit [1-100%]
Inhalt [1-100%]
Richtige Platzierung [1-100%]Verständlichkeit [1-100%]
Übersichtlichkeit [1-100%]
Aufteilung (Modulare Programmgestaltung) [1-100%]
Wiederholungen (unnützer Code, ersetzung durch Funktionen) [1-100%]Tipps:
- Was könnte man verbessern?
- Was könnte man dem Spiel hinzufügen?
- Andere Sounds?
Danke schonmal^^
-
Mir hat das Spiel ziemlich gut gefallen. Schön sauber und einfach gehalten, funktioniert gut :).
Zum Sourcecode: Habe mir jetzt nicht alles angesehen, nur ein paar kleine Kommentare. Und nein, ich werde keine Noten (oder Prozente) vergeben ;).
Variablen / Konstanten:
- Variablennamen sollten nicht mit _ beginnen. Das ist zwar nicht verboten, aber unschön (wie z.B. const string _modus = ...).
- Konstanten werden meistens nur mit Grossbuchstaben geschrieben. Z.B. WEISS statt weiss
- Verwende doch const statt #define um deine Konstanten zu definieren
- Ich würde dir empfehlen nur eine Sprache für Variablennamen etc. zu verwenden und nicht unbedingt zu mischen (kleines Detail am Rande)//ASCII-Zahlen für jeden Buchstaben und spezielle zeichen #define Aa 97 #define Ab 98
Das ist ziemlich unnötig: 'a' ist 97 und besser lesbar als Aa.
Wiederholungen (unnützer Code, ersetzung durch Funktionen) [1-100%]
Da gibt es noch Optimierungspotential :). Du hast z.B. relativ oft Zeilen wie:
color(rot); gotoxy(25, 15); cout << "<- Maximum erreicht!"; color(weiss);
in unterschiedlichen Variationen. Wäre es nicht angenehm wenn du eine Funktion hättest wie:
printTextAt("<- Maximum erreicht!",25,15,rot)
mit der Farbe als optionales Argument?
Anderes Beispiel: Du hast die 4 Fälle: Nach oben bewegen, nach unten bewegen, nach rechts bewegen und nach links bewegen. Dabei hast du viel Code 4 mal praktisch genau gleich. Es wäre einfacher, da Funktionen zu verwenden. Z.B. eine Funktion die aufgerufen wird, wenn der Spieler das Spiel verliert. Die würde dann die Sounds abspielen, Bildschirm leeren, "VERLOREN" anzeigen, warten, fragen ob man nochmals spielen will. Schau mal wie oft du nur diesen einzelnen Block verwendest (und ich habe mir nur einen einzigen Spielmodus angeschaut ).Das sind ein paar mehr oder weniger zufällige Dinge (nicht eine komplette Liste), die mir durch den Kopf gegangen sind, als ich deinen Code überflogen habe. Das sind alles Vorschläge, mehr als Denkanstösse gedacht, nicht als Kritik. Grundsätzlich hast du ein lauffähiges Spiel, das ist schon sehr viel :).
Ausserdem:
Hast du dich schonmal etwas mit Klassen etc. beschäftigt? Es wäre z.B. schön, dein Spielfeld durch eine Klasse zu kapseln. (Nur so als Hinweis, wenn du keine Ahnung hast was eine Klasse ist, einfach ignorieren :xmas1: )Ach ja, wenn du fragen hast, oder möchtest dass wir uns z.B. ein Codestück genauer ansehen, einfach fragen :).
Edit: Hatte den Sound am PC ausgeschalten, als ich dein Spiel gespielt habe. Kann also nichts darüber sagen.
-
Erst mal Danke, dass du dir Zeit genommen hast, den Code anzusehen.
Das mit den Prozenten, war auch nur für Leute gedacht, die schnell eine Bewertung machen wollen.^^
Hm, wieso sollte ich denn keine Unterstriche _ verwenden?
Ist #define für Konstanten nicht C? Ich hab jetzt mit Absicht const genommen.
Und das präfix-A hab ich genommen, da es ja vorkommen kann, das ich mal irgendwann char a; brauchen könnte. Das präfix A ist nur für die Übersichtlicher (soll für ASCII stehen :))Und was das mit den Wiederholungen angeht, hatte ich bereits den selben Gedanken wie du. Da es sich bei color allersings um ein Makro handelt (welches ich aus dem Internet kopiert hab, ich beschäftige mich nicht wirklich mit der winAPI) kann man das doch schlecht als Parameter an eine Funktion übergeben, oder wie soll das gehen?
Und ja, das mit dem rechts links usw. ist recht umständlich. Ich glaube ich werde für das überprüfen des jeweiligen feld-chars eine Funktion schreiben, die das ganze dann auswertet und jeweilige Sounds abspielt und Meldungen ausgibt. Da hast du wohl recht
Klar weiß ich was Klassen sind, im nachhinein ist mir aufgefallen, dass man es viel leichter hätte lösen können, wenn ich eine Klasse "Level" geschrieben hätte. Das wäre dann einmal massig Code, ließe sich dann aber sehr einfach per
Level Modus_eins(ohne_ende_werte); Modus_eins.set_sleep_x(bla); Modus_eins.start(); Level Modus_zwei; //usw.
abändern. Leider fiel mir erst etwas später auf, dass es sich um ein doch relativ umfangreiches Projekt handelt, sodass es jetzt zu viel Arbeit wäre, alles Objektorientiert umzuschreiben. Oder?
EDIT: Wie soll ich denn das Spielfeld mit einer Klasse realisieren? So in der Art:
class Feld { int X; int Y; public: void ueberpruefe(int X, int Y); usw. };
Oder wie soll ich das machen?
-
Also
Incocnito schrieb:
Hm, wieso sollte ich denn keine Unterstriche _ verwenden?
Ich zitiere mal aus Stroustrups Buch
Die C++ Programmiersprache schrieb:
Mit einem Unterstrich beginnende Namen sind für spezielle Mittel der Implementierung und der Laufzeitumgebung reserviert, daher sollten solche Namen nicht im Anwendungsprogramm benutzt werden.
Ist #define für Konstanten nicht C? Ich hab jetzt mit Absicht const genommen.
Genau, du solltest nicht #define verwenden wie in rubbish.h.
Und das präfix-A hab ich genommen, da es ja vorkommen kann, das ich mal irgendwann char a; brauchen könnte. Das präfix A ist nur für die Übersichtlicher (soll für ASCII stehen :))
Es geht mir weniger um das Präfix-A, sondern vielmehr darum dass du etwas unsinniges definierst (abgesehen von ESC / Pfeiltasten). Immer wenn du Aa brauchst, könntest du auch 'a' schreiben. Das ist lesbarer unabhängig davon ob du deine Konstante Aa oder ASCII_A nennst. Diese Konstanten sind unnötig. Du kannst z.B. auch schreiben testVariable='a'+1 (gibt 'b' bzw. 98). Du musst nicht den Zahlenwert extra abspeichern.
Da es sich bei color allersings um ein Makro handelt (...) kann man das doch schlecht als Parameter an eine Funktion übergeben, oder wie soll das gehen?
Das kannst du schon (obwohl ich, wie schon gesagt Konstanten verwenden würde). Erstens: Du musst nicht das color Makro übergeben, sondern die Farbe. Das makro ist bekannt, wenn du in dem File in dem du die Funktion implementierst dein "rubbish.h" (wirklich blöder Name :P) includierst. Laut der Hilfe zu SetConsoleTextAttribute wird die Farbe als unsigned short übergeben. Also hast du etwas wie printTextAt([...], unsigned short fgColor). In der Funktion kannst du dann color(fgColor) verwenden oder alternativ statt dem color Makro direkt den Aufruf der WinAPI-Funktion.
Hmm, ich hoffe du bist jetzt nicht verwirrter als vorher.[quote]sodass es jetzt zu viel Arbeit wäre, alles Objektorientiert umzuschreiben. Oder?[quote]
Ja, das ist sicher so. Es ist mir nur aufgefallen.Edit: Habe jetzt keine Zeit mehr dein Edit zu beantworten. Mache ich dann morgen, falls das dann noch niemand anderes gemacht hat ;).
-
Hoppla, da hast du recht, wozu dann das ganze mit Aa
Werd ich (über)morgen abändern. Und die Funktion werde ich dann auch so machen, wie du gesagt hast (keine Angst, bin nicht verwirrt, war ich vorher auch nicht ). Mir ist nur eben aufgefallen, das ich ziemlich verpeilt war und vergessen hab, dass das ganze ja als ZAHLEN gespeichert sind, ich Idiot
Erst denke ich.. hää wie soll man denn einen Text als Parameter übergeben.. Achja, das sind ja ZAHLEN xD
Ja, dann rufe ich einfach mit den jeweiligen Zahlen gleich die Funktion auf, denke ich.
Ich sitz wohl zu lange vor dem PC für heute. Morgen ist ja auch schon Weihnachten..
Jo, rubbish steht einfach nur für alles Mögliche was sich so in den Jahren angesammelt hat.. ^^Gut Unterstriche entferne ich dann auch.
Und const statt #define, merk ich mir.Danke dafür
Also, allen frohe Weihnachten :xmas2:
-
Und die Funktion werde ich dann auch so machen, wie du gesagt hast
Das waren nur Beispiele, schau den Code durch und überlege dir, was du doppelt und dreifach machst (wie wäre es z.B. mit einer Funktion erstelleSpielfeld(anzahlBrunnen)? um ein weiteres Bsp. zu nennen). Es geht ja darum, dass du merkst für was man Funktionen brauchen könnte um den Code übersichtlicher zu machen.
-
Habs mir nur kurz angesehen, vielleicht schaue ich später noch mal über den Code..
Aber nur so nebenbei.. sind die CS-Sounds eigentlich frei?
-
Keine Ahnung, die sind noch vom alten 1.6 (das ist von 2003). Aber ich glaube das stört wohl niemanden
-
Soo, falls es noch jemanden interessiert, ich arbeite jetzt wieder an dem Projekt weiter.
Ich hab die Ratschläge von lustig berücksichtigt und erstmal alle #define direktiven durch const und Funktionen ersetzt. Außerdem habe ich statt zb. 'Aj' einfach 'j' genommen, womit die Konstanten dafür nutzlos werden.
Außerdem bin ich nun dabei, für jede Modus-Datei eine Header zu schreiben, die den jeweiligen Bewegen-Funktion-Prototyp beinhaltet. Ich konnte keine allgemeine Funktion schreiben, da bei jedem Modus ja in eine andere Highscore-Datei geschrieben wird, ander Sounds verwendet werden und noch diverse andere Kleinigkeiten anders ablaufen. Die Definition dieser 'speziellen' Bewegungsfunktion schreibe ich dann gleich mit in die selbige Datei 'Modus_X.cpp', Ich habs nämlich so gelernt, dass in Headerdateien niemals Funktions-Definitionen stehen.
Ich werd das dann morgen oder so hochladen, eben wenn ich fertig bin.
Und ich werd das ganze mal bei sourceforge.net hochladen, file-upload gefällt mir nicht besonders.ps. Ich hab schon fast 70 Downloads, das hätte ich nicht erwartet
EDIT: Mir ist gerade noch etwas aufgefallen. Wenn ich eine Funktion "void bewegen(...)" schreiben will, muss ich fast 10 Variablen übergeben. Ich werde wohl eine Struktur Player oder so schreiben müssen, wo das dann gespeichert wird.
-
Ach was, mir ist es jetzt egal, wie lange das dauern wird: ich schreibe mir jetzt eine geeignete Klasse und mache das ganze Objektorientiert. Wenn mir dann etwas auffällt oder ich Fragen hab, werde ich mich hier wieder melden
-
Also endlich, ich habs dann doch noch geschafft. Snake Race ist nun Objektorientiert^^
Jetzt brauch ich natürlich wieder eure Hilfe. Ich muss natürlich wissen, was ihr von meinem Code haltet. Die Größe hat sich mal eben halbiert, und das bei gleichbleibender Leistung.
Ich hab jetzt 2 Klassen: EinmalSpiel
(Spiel.h und Spiel.cpp) undFeld
(Feld.h und Feld.cpp).
Wie hätte man es besser lösen können?
Und wie findet ihr den Codestil, hab ich was falsch gemacht?
Und dann eben die selben Fragen wie vorher auch.
Ich hab das ganze jetzt nicht auf sourceforge.net hochgeladen, sondern wieder auf file-upload.net:
Snake Race feat cs 1.8
Ich freu mich schon auf Rückmeldungen im Bezug auf den Quellcode
-
Lustiges Teil Aber mir viel was auf
Die Highscore als Klartext in einer exe halte ich eher für gewöhnungsbedürftig.
Nehm lieber sowas wie .dat und speichere die Daten binär.
Außerdem gibts da noch ein Formatierungsproblem mit dem Datum >>> 030.1.2011Es gibt einen Grafikbug bei Modus 1, nach dem das Spiel gewonnen wurde und
der Name eingegeben wurde.
Bestätigt man das weitere Spielen mit "n" so gibt es lustige Effekte. Die Schlange
beginnt trotzdem von neuem> auch bei Modus 2
Modus 3: Nach dem einsammeln des ersten Objekts ist das Spiel bereits zu Ende
Code hab ich mir allerdings nur mal grob angeschaut aber mir viel auf:
1. modus_eins bis modus_vier
sieht mir bissl nach Copy-Paste aus und abändern, was anders ist.
Hier bieten sich ebenfalls Klassen / Vererbung an. z.B. Basisklasse Modus und dann Kindklassen ModusFoo und ModusBar.2. es gibt viele identische Codesegmente, die überall auftauchen. Zentralisiere solche Sachen und nutze Funktionen.
Beispiel:system("cls"); make_rang(); gotoxy(1, 1); std::cout << "Gebe deinen Namen ein (keine Leerzeichen und max. 10 Zeichen): "; //gewinner bekannt machen std::string gewinner; std::cin >> gewinner; set_name(gewinner); if(gewinner.size() > 10) { color_cout(10, 30, GELB, "Name zu gross!"); Sleep(1000); while(kbhit()) getch(); continue;
Schreib dir hier eine Funktion enterName, dann musst du diesen Schnipsel nicht überall reinkopieren
3. Entscheide dich für eine Sprache
-Deutsch
-Englisch
-Denglisch (natürlich nicht :D)4. vielleicht an der ein oder anderen Stelle sinnvollere Namen wie get_durch() << ??? denn ich muss lange suchen, was "durch" ist
5. Bei Übergabe von Objekten an Klassen nutze Referenzen
von char Feld::get_zeichen(const Spiel para) const
nach char Feld::get_zeichen(const Spiel ¶) constMach dir vielleicht mal ein Klassendiagramm. Was brauchst du für Klassen. Was sollen diese für Daten kapseln. Was muss wo drauf zugreifen. Quasi ein Konzept, bevor du anfängst wild umzubauen.
-
Erst mal danke für die Anwort!
Highscore als Klartext in einer .exe hab ich deswegen gemacht, damit man die Highscores nicht so einfach abändern kann (sowas wie rechtsklick -> öffnen mit geht bei einer .exe ja nicht), und außerdem weiß ich auch nicht, was binär speichern bedeutet^^
Der Formatierungsfehler war ein kleiner Tippfehler, den hab ich eben behoben.
Einen Grafikfehler gibts bei mir nicht
Ich hab jetzt einfach mal ein break dabei eingefügt, und noch einige Dinge so abgeändert, dass es funktionieren sollte.Zu 3: Sowas aber auch :D:D Ich war noch beim debuggen, und weil ich nicht jedes mal 15 Punkte einsammeln wollte hab ich die Punkte gleich mal auf 14 gesetzt, damit ich sehen konnte, ob alles funktioniert. Ist jetzt wieder auf 0
Zum Code:
Naja es sind eben viele Kleinigkeiten die geändert werden, sodass es schwierig für mich war, das ganze nur einmal zu schreiben. Da ich jetzt nicht so ein Profi mit Klassen bin, ist mir das noch zu kompliziert.
Gut, die Variablennamen sind vielleicht nicht immer beste Wahl - statt y_minus hätte man auch stumpf RECHTS benutzen können, aber was solls. Darauf achte ich dann beim nächsten mal
Eigentlich hab ich immer auf die richtige Verwendung von const und Referenzen geachtet, ich kann das aber auch mal übersehen haben.
Ich hab das geupdatete nochmal hochgeladen, tritt der Grafikfehler bei dir immer noch auf?
Link: Download
-
Incocnito schrieb:
damit man die Highscores nicht so einfach abändern kann (sowas wie rechtsklick -> öffnen mit geht bei einer .exe ja nicht)
Wo hast du das denn gelesen. Öffne mal notepad und zieh des Teil per drag&drop rein
Ja, der Grafikfehler ist bei mir nun behoben ... löblich löblich.
Dann bin ich mal auf Version 2.0 gespannt
-
Ich hab nochmal ordendlich optimiert. Die main.cpp ist von 16kB auf 6kB gesunken und in jeder modus_x.cpp - datei stehen nun weniger als 200 zeilen
Ohne eure Hilfe hätt' ich das nicht geschafft.
Ich hab also nochmal einige neue Funktionen geschrieben zu so ziemlich allem, was doppelt vorkam. Wenn ihr noch irgendwas findet, was doppelt vorkommt und woraus man eine Funktion basteln könnte, wär das wirklich cool. Mal sehen, ob ihr noch irgendwas findet, es ist jetzt ja schon extrem wenig geworden ^^Ach ja, natürlich kann man leicht eine exe im Editor öffnen, ich weiß das schon. Nur wissen das eben viele Computer-Noobs nicht, und deswegen ist es wenigstens ein kleiner Schutz. Außerdem ist die Endung ja prinzipiell egal - und wenn die exe in diesem Fall das leichte rumeditieren um einige Sekunden verzögert, warum nicht^^
Ansonsten würde ich gerne wissen, wie ich das "binär" speichern sollte, und wie ich das dann wieder lesen kann.Hier der aktuelle Downloadlink: Download
-
Zu Dateien in C++ gibts auch einen FAQ Eintrag, da steht auch wie man eine Datei binär öffnet / liest / schreibt.
-
Ich hab jetzt nochmal wieder ordentlich was geändert und alles erreicht, was ich in Version 1.8 erreichen wollte.
Ich werde dann weiterhin etwaige Aktualisierungen und neue Versionen hier posten, und dann könnt ihr auch noch Kritik/Verbesserungsvorschläge zum Spiel äußern. Nachdem ich nun tagelang mit dem Code beschäftigt war, wirds Zeit wieder was neues zu adden
Den neuesten Downloadlink findet ihr dann immer auf der ersten Seite dieses Threads.
Die neueste Version mit dem umgekrempelten Menu findet ihr auch dort.
-
So, Version 1.9 ist nun fertig.
Es gibt nun Items, die einem Spielvorteile verschaffen können. Außerdem wurde das Menu komplett umgekrempelt und benutzerfreundlicher gestaltet.
Was geändert wurde steht aber auch noch mal im Programm beim ersten Start und natürlich in der Versions.log. Downloadlink auf der ersten Thread-Seite zu finden.Viel Spaß^^
-
Ich habe mal kurz über den neuen Sourcecode geschaut (naja, über einen Teil zumindest ).
Erst mal Kompliment, das sieht schon viel aufgeräumter auf.2 Dinge fallen trotzdem auf. Du hast jetzt eine Klasse geschrieben für dein Spiel. Im Moment ist das aber vom Prinzip her eher eine Struktur mit ein paar Hilfsfunktionen. Du hast für _jede_ Variable eine get und set funktion, die nichts anderes machen als den Wert der Variable ohne überprüfung zu setzen bzw. zurückzugeben.
So könntest du ja gerade so gut alle Variablen public machen und hättest dir eine Menge Schreibarbeit gespart.Ein Beispiel was z.B. verändert werden könnte.
drei.set_points(drei.get_points()+1); gotoxy(13, 55); cout << drei.get_points() << " von 15 Punkten";
Immer wenn du die Punkte veränderst willst du auch die Punkteanzeige am Bildschirm updaten. Lass das doch deine Klasse (bzw. die set Funktion) machen. Die Set Funktion würde dann immer wenn die Punkte geändert werden, diese auch an die entsprechende Position am Bildschirm schreiben. So weisst du dass die Anzeige immer mit der Punktzahl übereinstimmt.
Evt. sollte die set Funktion sogar noch mehr machen, z.B. überprüfen ob du schon gewonnen hast. Wenn ja kannst du ja true zurückgeben sonst false.
Ausserdem bietet es sich auch an eine erhoehe_Punkte Funktion zu schreiben, wenn du öfters so etwas wie oben machst.
Oderzwei.setze_richtung(c); } //if(kbhit()) if(!zwei.get_x_plus() && !zwei.get_y_plus() && !zwei.get_x_minus() && !zwei.get_y_minus()) continue;
wie wäre es, wenn setze_richtung die überprüfung gleich durchführt und das Ergebnis zurückliefert?
Das gilt nicht nur für die Spiel Klasse sondern für alle, das zieht sich durch den ganzen Code... z.B. auch wenn du den Spieler auf ein Feld setzt machst du jede Menge abfragen (ist da ein Brunnen? Spieler nicht zeichnen. Ist da eine Wand? Spieler vor der Wand zeichnen. etc.). Wäre es nicht einfacher, wenn du eine Funktion hättest die all das erledigt sobald du die Position des Spielers änderst (und dann halt zurückgibt, ob er noch lebt).Zweiter Punkt: Deine modus1, modus2, modus3, etc. files sind fast identisch. Da brauchst du eigentlich nur eine Funktion, bei dem du das Spiel je nach Modus mit unterschiedlichen Start / Gewinn Werten initialisierst.