Problem mit Snake...(erledigt)
-
aHoiHoi allerseits,
Ich bin im Moment daran, ein Snake zu programmieren. Die Aufgabe habe ich mir selbst gestellt, es sind nicht irgendwelche Hausaufgaben oder sowas
Ich habe mir die Aufgabe in kleine Probleme eingeteilt, die ich bisher alle gut lösen konnte(also ein sich selbst bewegendes Objekt in einem Feld, beeinflussung durch Tastendruck usw.) Nun bin ich daran, diese Teillösungen zusammenzuführen, leider bleib ich bei einem wichtigen Punkt stecken; Der Abfrage ob die "Schlange" (im Moment ist es nur ein einzelner Punkt, da ich es so einfacher finde, einmal die Grundlage zu schaffen) in eine Wand, oder allgemein gegen ein Hindernis fährt.Also:
Mein Spielfeld ist ein Char-Array, es ist 10 mal 10 Elemente gross. Das Element ist ein Leerschlag (also ' '). Die Umrandung des Spielfelds ist nicht im Char-Array enthalten, sie wird bei der Generierung des Spielfelds automatisch dazu "gezeichnet" Die Abfrage wäre ja nun z.B wenn die Schlange nach links kriecht: Ist die Spalte kleiner als 0 oder ist die Spalte grösser als 9. Wenn einer dieser Abfragen erfüllt wird, würde das bedeuten, dass die Schlange links oder Rechts gegen eine Wand gelaufen ist. DAs Problem ist aber, dass die Spalte nachdem die Schlange in die Wand gekrochen ist, plötzlich -5 oder so sein kann, und das Programm läuft weiter, theoretisch sollte sich das Programm dann beenden (wird später schon noch geändert, dient nur dem Test). Wenn dann eine Richtungsänderung erfolgt, (Tasten W,A,S,D) zum Beispiel nach Oben, merkt das Programm plötzlich, dass der Wert der Variablen mit der aktuellen Spalte ungültig ist und beendet sich. Wenn man nichts tut, geht dieser Wert immer weiter ins negative.Obwohl vor jedem "Kriecher" die obenstehende Abfrage durchgeführt wird. Das gleiche Problem gilt für alle Richtungen.
Die Richtung wird durch einen Buchstaben in einer Variablen bestimmt:
L = Links
R = Rechts
O = Oben
U = UntenBevor die Schlange ein Feld weiterkriecht, erfolgt eine Abfrage dieser Variablen sowie die oben beschriebene Abfrage.
Hier wäre der Code:
/* Programm: Objekt_Random_platzieren Erstellt: 11.02.09 Letzte Änderung: 12.02.09 */ #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <time.h> #include <windows.h> #define START_ZEILE 5 #define START_SPALTE 9 #define ANZAHL_ZEILEN 10 #define ANZAHL_SPALTEN 10 void zeichneSpielfeld(void); void setzeStartpunkt(void); void bewegeObjekt(void); int aktuelleZeile; int aktuelleSpalte; int abbruch = 0; int keyCode; char richtung = 'L'; char spielfeld[ANZAHL_ZEILEN][ANZAHL_SPALTEN] = { {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, }; int main(void) { setzeStartpunkt(); while(abbruch == 0) { fflush(stdin); while((!kbhit()) && (abbruch == 0)) { system("cls"); zeichneSpielfeld(); bewegeObjekt(); printf("\nZeile: %i / Spalte: %i / Richtung: %c", aktuelleZeile, aktuelleSpalte, richtung); Sleep(500); } keyCode = getch(); fflush(stdin); if (keyCode == 'w')//119) { richtung = 'O'; } else if(keyCode == 'a') { richtung = 'L'; } else if(keyCode == 's') { richtung = 'U'; } else if(keyCode == 'd') { richtung = 'R'; } } printf("\a"); } void zeichneSpielfeld(void) { int zeile; int spalte; printf(" +----------+"); for (zeile = 0; zeile < ANZAHL_ZEILEN; zeile++) { if (zeile != 0) { printf("|"); } printf("\n"); for (spalte = 0; spalte < ANZAHL_SPALTEN; spalte++) { if (spalte == 0) { printf(" |"); } printf("%c", spielfeld[zeile][spalte]); } } printf("|\n"); printf(" +----------+"); } void setzeStartpunkt(void) { spielfeld[START_ZEILE][START_SPALTE] = 'x'; aktuelleZeile = START_ZEILE; aktuelleSpalte = START_SPALTE; } void bewegeObjekt(void) { spielfeld[aktuelleZeile][aktuelleSpalte] = ' '; switch(richtung) { case 'O': { aktuelleZeile = aktuelleZeile - 1; if(aktuelleZeile < -1) { aktuelleZeile = ANZAHL_ZEILEN; } } break; case 'L': { aktuelleSpalte = aktuelleSpalte - 1; if(aktuelleSpalte < -1) { aktuelleSpalte = ANZAHL_SPALTEN; } break; } case 'U': { aktuelleZeile = aktuelleZeile + 1; if(aktuelleZeile > ANZAHL_ZEILEN) { aktuelleZeile = -1; } break; } case 'R': { aktuelleSpalte = aktuelleSpalte + 1; if(aktuelleSpalte > ANZAHL_SPALTEN) { aktuelleSpalte = -1; } break; } default: break; } spielfeld[aktuelleZeile][aktuelleSpalte] = 'x'; }
Hier noch die Abfrage einzeln:
switch(richtung) { case 'O': { aktuelleZeile = aktuelleZeile - 1; if(aktuelleZeile < -1) { aktuelleZeile = ANZAHL_ZEILEN; } } break; case 'L': { aktuelleSpalte = aktuelleSpalte - 1; if(aktuelleSpalte < -1) { aktuelleSpalte = ANZAHL_SPALTEN; } break; } case 'U': { aktuelleZeile = aktuelleZeile + 1; if(aktuelleZeile > ANZAHL_ZEILEN) { aktuelleZeile = -1; } break; } case 'R': { aktuelleSpalte = aktuelleSpalte + 1; if(aktuelleSpalte > ANZAHL_SPALTEN) { aktuelleSpalte = -1; } break; } default: break; }
Ich hoffe ihr könnt mir helfen, ich komm überhaupt nicht weiter...
Danke schon im Voraus für eure Hilfe!EDIT: Hat sich erledigt, danke @ _matze!
mfg PapaNoah
-
Wenn AnzahlSpalte negativ wird oder über ANZAHL_SPALTEN-1 hinausläuft, musst du doch auch den neuen, richtigen Wert (also 0 oder ANZAHL_SPALTEN-1) setzen! Das tust du bislang nicht...
Also irgendwie so:
if(aktuelleSpalte<0) { aktuelleSpalte=ANZAHL_SPALTEN-1; }else if(aktuelleSpalte>ANZAHL_SPALTEN-1) { aktuelleSpalte=0; }
-
Das würde dann ja bezwecken, dass die Schlange, sobald sie in eine Wand fährt, auf der anderen Seite des Feldes wieder herauskommt, verstehe ich das richtig? Ich habe gemerkt, dass ich einige überflüssige Abfragen habe, die Abfragen nach dem "oder" (||) sind unnötig, das sie nie eintreffen werden. @_matze ich habe deinen Vorschlag einmal miteingebaut, und es funktioniert nur teilweise. Wenn die Schlange horizontal kriecht, dann passiert was komisches, wenn sie in die Wand links oder rechts fährt. Zuerst wird das Objekt an zwei völlig falsche Orte gesetzt, zuerst eine Zeile oben, dann eine Zeile unten, komischerweise wird aber die Variable "Zeile" nicht verändert (???) Erst dann stimmt die Position wieder. Ich kann das Problem nicht so richtig beschreiben, am besten ihr seht's euch selbst kurz an, ich habs in dev-cpp geschrieben.
=> Hab den veränderten Code oben aktualisiert.
mfg Papa_Noah
-
PapaNoah schrieb:
Das würde dann ja bezwecken, dass die Schlange, sobald sie in eine Wand fährt, auf der anderen Seite des Feldes wieder herauskommt, verstehe ich das richtig?
Ja, klar. Funktioniert Snake nicht so? Das Snake, dass damals auf meinem Handy war, zeigte jedenfalls genau dieses Verhalten. Ich dachte, das wäre von dir gewünscht.
PapaNoah schrieb:
@_matze ich habe deinen Vorschlag einmal miteingebaut, und es funktioniert nur teilweise.
Sorry, war nicht getestet und eher nur als ein Wink in die richtige Richtung zu verstehen. Dass sich die Zeilenposition verändert, ist aber irgendwie komisch. Mal sehen, ob ich gleich noch Zeit habe, mir das kurz anzusehen (hab viel zu tun im Moment). Vielleicht macht's ja auch ein anderer in der Zwischenzeit.
-
_matze schrieb:
PapaNoah schrieb:
Das würde dann ja bezwecken, dass die Schlange, sobald sie in eine Wand fährt, auf der anderen Seite des Feldes wieder herauskommt, verstehe ich das richtig?
Ja, klar. Funktioniert Snake nicht so? Das Snake, dass damals auf meinem Handy war, zeigte jedenfalls genau dieses Verhalten. Ich dachte, das wäre von dir gewünscht.
Ja ich mag mich auch daran erinnern, dass ich 2 Versionen Snake gespielt hatte, zum einen die Version, bei welcher man durch die Wände konnte, bei der anderen konnte man das nicht. Das war eigentlich auch nicht das Problem, die Abfragen bereiteten mir Kopfzerbrechen. Sry das sollte nicht irgendwie eine bös gemeinte Frage sein, ich war mir nur nicht ganz sicher, ob dein Vorschlag auch das bewirkt.
_matze schrieb:
PapaNoah schrieb:
@_matze ich habe deinen Vorschlag einmal miteingebaut, und es funktioniert nur teilweise.
Sorry, war nicht getestet und eher nur als ein Wink in die richtige Richtung zu verstehen. Dass sich die Zeilenposition verändert, ist aber irgendwie komisch. Mal sehen, ob ich gleich noch Zeit habe, mir das kurz anzusehen (hab viel zu tun im Moment). Vielleicht macht's ja auch ein anderer in der Zwischenzeit.
Kein Problem, es hat mir auf jeden Fall mal einen Schritt weiter geholfen, da nun in etwa das passiert, was ich möchte. Wie gesagt nur diese komische Positionierung stört mich im Moment noch. Über das Aussehen des Spiels mache ich mir später Gedanken, es soll halt einfach mal funktionieren
Danke für deine Mühe, falls du die Zeit findest möchte ich dir schon im Voraus für die Hilfe danken!
mfg PapaNoah
-
Notier in bewegeObjekt im case 'L' mal Folgendes (lösche den Rest außer dem break):
aktuelleSpalte = aktuelleSpalte<=0 ? ANZAHL_SPALTEN-1 : aktuelleSpalte - 1;
Der Fehler ist, dass du einen negativen Spaltenwert zulässt. Damit gehst du im Array auch eine Zeile zurück. Der Inhalt des Arrays liegt ja tatsächlich eindimensional hintereinander im Speicher, auch wenn du es zweidimensional deklariert hast und so ansprichst.
EDIT: Und die Reihenfolge sollte
bewegeObjekt(); zeichneSpielfeld();
sein, sonst stimmt deine Zeilen-/Spalten-Angabe nicht (sie hinkt immer einen hinterher).
-
Blöde Frage, für was steht das Fragezeichen?
aktuelleSpalte = aktuelleSpalte<=0 ? ANZAHL_SPALTEN-1 : aktuelleSpalte - 1;
Muss ich das alles auf eine Zeile tun?
EDIT: Frage oben hat sich erledigt
Es funktioniert nun, herzlichen Dank!
Trotzdem nimmt es mich wunder, was diese Zeile genau macht, ich verstehe
sie nicht ganz :S...mfg PapaNoah
-
Das ist der ternäre Operator (der als einziger Operator 3 Operanden braucht). Das ist quasi ein if-ersatz (der aber nur in Ausdrücken verwendet und nicht alleine stehen kann!).
Statt
if(b) { x=1; }else { x=0; }
kannst du auch
x = b ? 1 : 0; //wenn b true ist, dann weise 1 zu, sonst 0
schreiben.
EDIT: Das Verwenden dieses Operator ist natürlich nicht immer zu empfehlen. Oft leidet die Lesbarkeit darunter. Auch, wenn man meist ein paar Zeilen damit spart.
-
Achso verstehe, das hab ich noch nie gesehen
Aber es ist logisch, hehe.
Wieder etwas neue gelernt :pVielen Dank für deine Hilfe, ich bin echt froh, dass das jetzt funktioniert!
mfg
PapaNoah
-
Gern geschehen. Du kannst ja später den fertigen Code posten, wenn du Lust hast. Ich bin gespannt, wie du das Wachsen der Schlange löst.
-
Menno... jetzt habe ich mir dein Problem auch angesehen und du bist fertig! Egal hier ist meine Lösung mit einigen Veränderungen
#define START_ZEILE 5 #define START_SPALTE 9 #define ANZAHL_ZEILEN 10 #define ANZAHL_SPALTEN 10 typedef struct { int x; int y; int dx; int dy; } snake_t; void snake_init(void); void snake_update(void); int snake_check_collision(void); void snake_move(void); void snake_draw(void); static snake_t snake; char spielfeld[ANZAHL_ZEILEN][ANZAHL_SPALTEN] = { {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, {' ',' ',' ',' ',' ',' ',' ',' ',' '}, }; int main(void) { snake_init(); while(true) { snake_update(); if(snake_check_collision()) break; snake_move(); snake_draw(); Sleep(500); } printf("\a"); system("pause"); } void snake_init(void) { snake.x = START_SPALTE; snake.y = START_ZEILE; snake.dx = -1; snake.dy = 0; } void snake_update(void) { int c; if(kbhit()) { c = getch(); if(c == 'w') { snake.dx = 0; snake.dy = -1; } else if(c == 'a') { snake.dx = -1; snake.dy = 0; } else if(c == 's') { snake.dx = 0; snake.dy = 1; } else if(c == 'd') { snake.dx = 1; snake.dy = 0; } } fflush(stdin); } int snake_check_collision(void) { if(snake.x + snake.dx < 0 || snake.x + snake.dx > ANZAHL_SPALTEN - 1|| snake.y + snake.dy < 0 || snake.y + snake.dy > ANZAHL_ZEILEN - 1) return 1; return 0; } void snake_move(void) { snake.x += snake.dx; snake.y += snake.dy; } void snake_draw(void) { int zeile, spalte; system("cls"); printf("+"); for(spalte = 0; spalte < ANZAHL_SPALTEN; spalte++) { printf("-"); } printf("+\n"); for (zeile = 0; zeile < ANZAHL_ZEILEN; zeile++) { printf("|"); for (spalte = 0; spalte < ANZAHL_SPALTEN; spalte++) { printf("%s", (snake.x == spalte && snake.y == zeile) ? "x" : " "); } printf("|\n"); } printf("+"); for(spalte = 0; spalte < ANZAHL_SPALTEN; spalte++) { printf("-"); } printf("+\n"); }
-
Werde ich machen
Ich habe bis jetzt keine Ahnung wie ich das mit der Schlange hinkriegen soll, erst mal Mittagessen
mfg PapaNoah
-
Ein kleiner Tip:
typedef struct { int x; int y; int dx; int dy; } snake_tail_t; typedef struct { int length; snake_tail_t tail[SNAKE_MAXLENGTH]; } snake_t;
Der Kopf ist snake.tail[0]. Der "Schwanz" der Schlange ist gespeicher in den restlichen Elementen von snake.tail[]. In der Funktion snake_move() muß jedes Schwanzstück jeweils die Informationen seines direkten Vorgängers übernehmen.
Viel spaß!
-
Damit hast du aber schon sehr viel verraten.
-
Ich weiß nicht, ob das die beste Lösung ist. Es ist aber am naheliegendsten. Ich hab das eben zu Ende programmiert und es ist ganz witzig. Aber die Win32-Konsole flickert so eklig. Kennt da jemand einen Trick, wie ich das verhindern kann? Ich müßte ja nur vor dem Neuzeichnen die Bildschirm-Aktualisierung der Konsole deaktiveren und nach dem Neuzeichnen wieder freigeben. Aber das geht in der Konsole nicht, oder?