Konsole Flackert bei Snake

whatever87

Grünschnabel
Hallo alle zusammen,

ich hab es endlich geschafft Snake zu programmieren.Es läuft auch ganz gut Kollisionen usw. funktionieren glaub ich auch alle. Ich habe Snake als Konsolenprogramm geschrieben, nun habe ich aber ein Problem. Die Konsole Flackert so dermaßen, dass man da schon fast nen Epilleptischen anfall bekommt. Hab auch schon bei google und hier im Forum gesucht woran das liegen könnte, aber nix gefunden.
Ich hoffe der Code ist gut lesbar, sind hoffentlich genug Kommentare drin. Habe mir noch nicht überlegt wie ich einige Sachen eleganter und schneller machen könnte, will erstmal das das geflackere aufhört. Für anregungen und Kritik jeglicher Art bin ich aber offen.

Code:
using namespace std;

/* Includes */
#include "stdafx.h"

/* Tasten definieren */
const int HOCH = 119;
const int LINKS = 97;
const int RUNTER = 115;
const int RECHTS = 100;
const int PAUSE = 112;
const int ESC = 27;

/* Globale Variablen */
const int MAX_SNAKE_LENGTH = 441; //23 * 23
const char SnakeBody = 219;
int SnakeLength = 5;
int Snake[MAX_SNAKE_LENGTH][2];
char Field[23][23];
int Food[1][2] = {1,1};
int RANGE_MIN = 1;	//Startwert des Zufallsgenerators
int RANGE_MAX = 22;//Endwert des Zufallsgenerators
int CollisionCount = 0;
char KeyPress = RECHTS;
char KeyPressOld = RECHTS;
bool FoodEaten = true;
unsigned long int Score = 0;
int Speed = 100;
int GameState = 0;
int PointsCount = 10;


/* Funktionsprototypen */
void FieldInit(void);
void PrintField(void);
void SnakeInit(void);
void SetFood(void);
void WriteSnakeToField(void);
void WriteFoodToField(void);
int MoveSnake(void);
char abbruch = 'j';
int temp;

int main()
{
	/* Initialisierung des zufallsgenerators mit der aktuellen zeit */
	srand( (unsigned) time(NULL));
	FieldInit();
		SnakeInit();
		SetFood();
	
	do
	{
		
		FieldInit();
		PrintField();
		GameState = MoveSnake();
		Sleep(20);
		if(GameState == 2)
		{
			cout << "Verloren! Nochmal spieln? (j/n): ";
			cin >> abbruch;
			SnakeInit();
			SetFood();
		}
	}while(abbruch == 'j');
	
}

/* Spielfeld mit Leerzeichen füllen */
void FieldInit(void)
{
	for(int y = 0; y < 23; y++)
	{
		for(int x = 0; x < 23; x++)
		{	
			/* Inneres Spielfeld */
			Field[x][y] = ' ';
			/* Spielfeldwand */
			if(x == 0)
			{
				if(y == 0)
				{
					/* Linkes oberes eckstück */
					Field[x][y] = 201;
				}
				else if(y == 22)
				{
					/* Linkes unteres eckstück */
					Field[x][y] = 200;
				}
				else if(y < 23)
				{
					/* vertikaler strich */
					Field[x][y] = 186;
				}
			}
			else if(x == 22)
			{
				if(y == 0)
				{
					/* rechtes oberes eckstück */
					Field[x][y] = 187;
				}
				else if(y == 22)
				{
					/* rechtes unteres eckstück */
					Field[x][y] = 188;
				}
				else if(y < 23)
				{
					/* vertikaler strich */
					Field[x][y] = 186;
				}
			}
			else if(y == 0)
			{
				if(x < 23)
				{
					/* horizontaler strich*/
					Field[x][y] = 205;
				}
			}
			else if(y == 22)
			{
					if(x < 23)
					{
						/* horizontaler strich */
						Field[x][y] = 205;
					}
			}
		}
	}
}

/* Spielfeld ausgeben */
void PrintField(void)
{
	WriteSnakeToField();
	WriteFoodToField();
	/* Sendet den DOS-Befehl "cls" an die Konsole
	   ist das selbe wie clrscr() in borland */	
	system("cls");
	for(int y = 0; y < 23; y++)
	{
		for(int x = 0; x < 24; x++)
		{
			
			if(x == 23)
			{
				if(y == 0)
				{
					cout << Score;
				}
			}
			else
			{
				cout << Field[x][y];
			}
		}
		cout << "\n";
	}
}

/* Startposition der Schlange festlegen, restliches array mit Leerzeichen füllen */
void SnakeInit(void)
{
	for(int iPosY = 0; iPosY < 2; iPosY++)
	{
		for(int iPosX = 0; iPosX < MAX_SNAKE_LENGTH; iPosX++)
		{
			Snake[iPosX][iPosY] = -1;
			/* -1 damit der Bug der bei Pos0,0
			   für kurze Zeit ein Körperteil erscheinen
			   lässt behoben ist 
			*/	
		}
	}
	/* Startpos der schlange, schlange hat 5LE am anfang */
	Snake[0][0] = 5;
	Snake[0][1] = 21;
	Snake[1][0] = 4;
	Snake[1][1] = 21;
	Snake[2][0] = 3;
	Snake[2][1] = 21;
	Snake[3][0] = 2;
	Snake[3][1] = 21;
	Snake[4][0] = 1;
	Snake[4][1] = 21;

}

/* Futterposition nach zufallsprinzip ermitteln */
void SetFood(void)
{
	Field[Food[0][0]][Food[0][1]] = ' '; //alte Futterposition löschen
	
	/* Mit dieser rechnung gibt der zufallsgen. nur zwischen min und max aus */
	Food[0][0] = ( ((double)rand() / (double)RAND_MAX) * RANGE_MAX + RANGE_MIN );
	Food[0][1] = ( ((double)rand() / (double)RAND_MAX) * RANGE_MAX + RANGE_MIN );
	
	/* Falls ein Poswert 0/23 sein sollte 
	 kommt trotz abfrage manchmal vor... 
	 dies ist die 1. von 2 abfragen OMG */
	if(Food[0][0] == 0)//für x
	{
		Food[0][0] = 1;
	}
	else if(Food[0][0] == 23)
	{
		Food[0][0] = 22;
	}
	else if(Food[0][1] == 0)//für y
	{
		Food[0][1] = 1;
	}
	else if(Food[0][1] == 23)
	{
		Food[0][1] = 22;
	}
}

/* Schlangenposition ins Field-Array schreiben */
void WriteSnakeToField(void)
{
	int TmpSnakePosX;
	int TmpSnakePosY;
	/* Schlangenpositionen ins Feld schreiben */
	for(int PosX = 0; PosX < SnakeLength; PosX++)
	{
		for(int PosY = 0; PosY < 2; PosY++)
		{
			if(PosY == 0)
			{
				TmpSnakePosX = Snake[PosX][PosY];
			}
			else if(PosY == 1)
			{
				TmpSnakePosY = Snake[PosX][PosY];
			}
		}
		/* an der pos. der schlange ein Körperteil ins Feld schreiben */
		Field[TmpSnakePosX][TmpSnakePosY] = SnakeBody;
	}
}

/* die Pos. des Futters ins Spielfeld schreiben */
void WriteFoodToField(void)
{
	if(FoodEaten == true)
	{
		SetFood();

		/* Falls das Feld schon belegt ist */
		while(Field[Food[0][0]][Food[0][1]] != ' ')
		{
			SetFood();
		}
	}
	/* An der Position des Futters ein 'O' schreiben */
	Field[Food[0][0]][Food[0][1]] = 'O';
	FoodEaten = false;
}

/* tasten abfragen und richtung bestimmen */
int MoveSnake(void)
{
	if(_kbhit() != 0)
	{
		KeyPress = _getch();
	}
	
	/* umdrehen ist nicht erlaubt! */
	if( (KeyPressOld == HOCH) && (KeyPress == RUNTER) )
	{
		KeyPress = HOCH;
	}
	else if( (KeyPressOld == LINKS) && (KeyPress == RECHTS) )
	{
		KeyPress = LINKS;
	}
	else if( (KeyPressOld == RUNTER) && (KeyPress == HOCH) )
	{
		KeyPress = RUNTER;
	}
	else if( (KeyPressOld == RECHTS) && (KeyPress == LINKS) )
	{
		KeyPress = RECHTS;
	}
	
	/* Bewegen der Schlange und Kollisionen Abfragen */
	switch(KeyPress)
	{
		case HOCH:
			{
				{
				int TempX;
				int TempY;
				/* Kollision oben */
				if(Snake[0][1] - 1 == 0)
				{
					CollisionCount += 1;
					if(CollisionCount == 3)
					{
						CollisionCount = 0;
						return 2;
					}
				}
				/* Kollision mit Schlange selbst */
				else if(Field[ Snake[0][0] ][ Snake[0][1] - 1 ] == SnakeBody)
				{
					CollisionCount += 1;
					if(CollisionCount == 3)
					{
						CollisionCount = 0;
						return 2;
					}
				}
				else
				{
					/* Kollision mit Futter */
					if(Field[ Snake[0][0] ][ Snake[0][1] - 1 ] == 'O')
					{
						SnakeLength += 1;
						FoodEaten = true;
						Score += PointsCount;
					}
					/* Schlange Bewegen */
					for(int i = SnakeLength - 1; i > 0; i--)
					{
						TempX = Snake[i-1][0];
						TempY = Snake[i-1][1];
						Snake[i][0] = TempX;
						Snake[i][1] = TempY;
					}
					/* Neue Position für den Kopf */
					Snake[0][1] = Snake[0][1] - 1;
					CollisionCount = 0;
					KeyPressOld = HOCH;
					return 1;
				}
			}
			break;
		case LINKS:
			{
				int TempX;
				int TempY;
				/* kollision links */
				if(Snake[0][0] - 1 == 0)
				{
					CollisionCount += 1;
					if(CollisionCount == 3)
					{
						CollisionCount = 0;
						return 2;
					}
				}
				/* Kollision mit Schlange selbst */
				else if(Field[ Snake[0][0] - 1 ][ Snake[0][1] ] == SnakeBody)
				{
					CollisionCount += 1;
					if(CollisionCount == 3)
					{
						CollisionCount = 0;
						return 2;
					}
				}
				else
				{
					/* Kollision mit Futter */
					if(Field[ Snake[0][0] - 1 ][ Snake[0][1] ] == 'O')
					{
						SnakeLength += 1;
						FoodEaten = true;
						Score += PointsCount;
					}
					/* Schlange Bewegen */
					for(int i = SnakeLength - 1; i > 0; i--)
					{
						TempX = Snake[i-1][0];
						TempY = Snake[i-1][1];
						Snake[i][0] = TempX;
						Snake[i][1] = TempY;
					}
					/* Neue Position für den Kopf */
					Snake[0][0] = Snake[0][0] - 1;
					CollisionCount = 0;
					KeyPressOld = LINKS;
					return 1;
				}
			}
			break;
		case RUNTER:
			{
				int TempX;
				int TempY;
				/* Kollision unten */
				if(Snake[0][1] + 1 == 22)
				{
					CollisionCount += 1;
					if(CollisionCount == 3)
					{
						CollisionCount = 0;
						return 2;
					}
				}
				/* Kollision mit Schlange selbst */
				else if(Field[ Snake[0][0] ][ Snake[0][1] + 1 ] == SnakeBody)
				{
					CollisionCount += 1;
					if(CollisionCount == 3)
					{
						CollisionCount = 0;
						return 2;
					}
				}
				else
				{
					/* Kollision mit Futter */
					if(Field[ Snake[0][0] ][ Snake[0][1] + 1 ] == 'O')
					{
						SnakeLength += 1;
						FoodEaten = true;
						Score += PointsCount;
					}
					/* Schlange Bewegen */
					for(int i = SnakeLength - 1; i > 0; i--)
					{
						TempX = Snake[i-1][0];
						TempY = Snake[i-1][1];
						Snake[i][0] = TempX;
						Snake[i][1] = TempY;
					}
					/* Neue Position für den Kopf */
					Snake[0][1] = Snake[0][1] + 1;
					CollisionCount = 0;
					KeyPressOld = RUNTER;
					return 1;
				}
			}
			break;
		case RECHTS:
			{
				int TempX;
				int TempY;
				/* Kollision rechts */
				if(Snake[0][0] + 1 == 22)
				{
					CollisionCount += 1;
					if(CollisionCount == 3)
					{
						CollisionCount = 0;
						return 2;
					}
				}
				/* Kollision mit Schlange selbst */
				else if(Field[ Snake[0][0] + 1 ][ Snake[0][1] ] == SnakeBody)
				{
					CollisionCount += 1;
					if(CollisionCount == 3)
					{
						CollisionCount = 0;
						return 2;
					}
				}
				else
				{
					/* Kollision mit Futter */
					if(Field[ Snake[0][0] + 1 ][ Snake[0][1] ] == 'O')
					{
						SnakeLength += 1;
						FoodEaten = true;
						Score += PointsCount;
					}
					/* Schlange Bewegen */
					for(int i = SnakeLength - 1; i > 0; i--)
					{
						TempX = Snake[i-1][0];
						TempY = Snake[i-1][1];
						Snake[i][0] = TempX;
						Snake[i][1] = TempY;
					}
					/* Neue Position für den Kopf */
					Snake[0][0] = Snake[0][0] + 1;
					CollisionCount = 0;
					KeyPressOld = RECHTS;
					return 1;
				}
			}
			break;
			}
		case ESC:
			return 0;
	}
	return 1;
}

danke im vorraus.
habe das Projekt als Zip angehängt, ist MS Visual Studio 2005 .NET

greetz whatever
 

Anhänge

  • 24440attachment.zip
    5,6 KB · Aufrufe: 49
hi

system("cls");

Da liegt das Problem. Ich hab kA wie man das macht, dass das nicht flackert. Aber ich kenns von der Textausgabe. Is nervig.
 
danke für die schnelle antwort.
des mit dem sytem("cls"); hab ich mir auch schon überlegt. habs auch mit dem borland builder probiert, da kann man den befehl clrscr(); benutzen der is um einiges schneller aber der scheiss flackert trotzdem.
würs was bringen wenn ich des mit nem buffer mach? also erst die positionsdaten in einen zwischenpuffer kopieren und dann erst in das eigentlich Field kopieren und gleich ausgeben?
hab mir des schon überlegt aber obs was bringt is halt die frage...
 
Das ist leider eine Schwäche der Windows-Konsole. Da helfen auch systemnahe Konsolenfunktionen nicht, das flackern bleibt.

Es hilft da eigentlich nur A) Fullscreen oder B) eine GDI/DirectDraw/Direct3d/OpenGL-Emulation der ASCII-Darstellung.
 
Müßte es nicht eigentlich auch ohne 'cls' gehen? Du überschreibst doch sowieso alle Positionen in der Konsole entweder mit einer Leerstelle, oder mit einem Zeichen für Schlange bzw. Futter.
 
Endurion hat gesagt.:
Das ist leider eine Schwäche der Windows-Konsole. Da helfen auch systemnahe Konsolenfunktionen nicht, das flackern bleibt.

Es hilft da eigentlich nur A) Fullscreen oder B) eine GDI/DirectDraw/Direct3d/OpenGL-Emulation der ASCII-Darstellung.
…oder C) Verwendung eines Konsolepuffers:

Code:
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

#define CONSOLE_WIDTH	80
#define CONSOLE_HEIGHT	25
#define CONSOLE_CHARS (CONSOLE_WIDTH * CONSOLE_HEIGHT)

HANDLE g_hScreenBuffer;
short g_aFrameBuffer[CONSOLE_CHARS];

void consolePresent()
{
	CHAR_INFO aCharInfo[CONSOLE_CHARS];
	int i;
	
	for (i = 0; i < CONSOLE_CHARS; ++i) {
		aCharInfo[i].Char.AsciiChar = g_aFrameBuffer[i] & 0xff;
		aCharInfo[i].Attributes = (g_aFrameBuffer[i] >> 8) & 0xff;
	}
	
	COORD max, src;
	SMALL_RECT out;
	
	max.X = CONSOLE_WIDTH;
	max.Y = CONSOLE_HEIGHT;
	
	src.X = src.Y = 0;
	
	out.Top = out.Left = 0;
	out.Right = CONSOLE_WIDTH - 1;
	out.Bottom = CONSOLE_HEIGHT - 1;
	
	WriteConsoleOutput(g_hScreenBuffer, aCharInfo, max, src, &out);
}

void consoleSetup()
{
	g_hScreenBuffer = CreateConsoleScreenBuffer(
		GENERIC_WRITE, 0, NULL,
		CONSOLE_TEXTMODE_BUFFER, NULL);
	SetConsoleActiveScreenBuffer(g_hScreenBuffer);
	
	COORD size;
	size.X = CONSOLE_WIDTH;
	size.Y = CONSOLE_HEIGHT;
	SetConsoleScreenBufferSize(g_hScreenBuffer, size);
	
	SetConsoleMode(g_hScreenBuffer, 0);
	
	CONSOLE_CURSOR_INFO cci;
	cci.bVisible = FALSE;
	cci.dwSize = 1;
	SetConsoleCursorInfo(g_hScreenBuffer, &cci);
}

int main(int argc, char** argv)
{
	consoleSetup();
	
	char *szMessage = "tutorials.de - User helfen Usern   ***   ";
	int nLen = strlen(szMessage);
	int aOffsets[CONSOLE_HEIGHT];
	short *aMessage = malloc(sizeof(short) * nLen);
	int i;

	for (i = 0; i < CONSOLE_HEIGHT; ++i) aOffsets[i] = rand() % nLen;
	for (i = 0; i < nLen; ++i) aMessage[i] = 0x0700 | szMessage[i];

	while (_kbhit() == 0) {				
		for (i = 0; i < CONSOLE_HEIGHT; ++i) {
			aOffsets[i] = (aOffsets[i] + (i%2)*2-1) % nLen;
			int nOffset = (nLen + aOffsets[i]) % nLen;
			int j;
			short *dest = g_aFrameBuffer + CONSOLE_WIDTH * i;
			for (j = 0; j < CONSOLE_WIDTH; ++j) {
				nOffset = (nOffset + 1) % nLen;
				*dest = aMessage[nOffset];
				dest++;
			}
		}

		consolePresent();

		Sleep(80);
	}
	
	free(aMessage);
	
	return 0;
}
Ja, Kommentare waren grad aus :p

Grüße,
Matthias
 
danke sehr matthias werd mich mal dran setzen. hab den code grad mal kurz überflogen und nix gepeilt *g*
ich denk mal ich muss mein code einfach da in die main irgendwo reinsetzen. wird schon irgendwie klappen.google wird mir bestimmt auch weiterhelfen können
ich sag dann bescheid wenns geklappt hat

greetz whatever

edit: @jokey2: nee ohne cls gehts net, weil dann sieht des aus wie wenn man nen Film band laufen lassen würde.
und die scrollbar wird dann immer immer kleiner. d.h. wenn ich nach oben scrolle sehe ich die voran gegangen "screens" kp. wie ich des erklärn soll ^^
 
Zuletzt bearbeitet:
edit: @jokey2: nee ohne cls gehts net, weil dann sieht des aus wie wenn man nen Film band laufen lassen würde.
und die scrollbar wird dann immer immer kleiner. d.h. wenn ich nach oben scrolle sehe ich die voran gegangen "screens" kp. wie ich des erklärn soll
Ja, das hatte ich schon gesehen, als ich es dann mal ausprobiert habe. :-(
Aber Du könntest vor der Ausgabe mit gotoxy(0,0) (anstatt cls) den Cursor in die linke, obere Ecke setzen und dann zeichnen. Dann wird alles überschrieben.
Falls Du mit Visual Studio arbeitest, da gibt es leider kein gotoxy. Aber hier gibt es eine Headerdatei zu runterladen, in der diese Funktion für Windows implementiert ist.
Du kannst es natürlich auch selber mit SetConsoleCursorPosition(...) implementieren. Aber wieso selber machen, wenn es andere schon getan haben!? ;-)
 
Zuletzt bearbeitet:
Zurück