C++ 如何以最有效的方式刷新控制台?(蛇游戏c+;+;)
这是我的蛇代码<代码>系统(“cls”)根本没有效率,控制台闪烁C++ 如何以最有效的方式刷新控制台?(蛇游戏c+;+;),c++,winapi,console,C++,Winapi,Console,这是我的蛇代码系统(“cls”)根本没有效率,控制台闪烁 #include <iostream> #include <string> #include <windows.h> #include <cstdlib> #include <ctime> #include <conio.h> using namespace std; bool status = false, win = false; struct Snake {
#include <iostream>
#include <string>
#include <windows.h>
#include <cstdlib>
#include <ctime>
#include <conio.h>
using namespace std;
bool status = false, win = false;
struct Snake {
int index_i;
int index_j;
};
class Game {
private:
enum eDir { UP, RIGHT, DOWN, LEFT };
eDir direction;
const int height = 25, width = 50, max_size = (height - 2)*(width - 2);
int snake_size = 1, food_x, food_y, snake_x, snake_y, score, speed;
char snake = '@', food = '*', frame = '#';
Snake *snake_body = new Snake[max_size];
public:
Game() {
snake_x = height / 2;
snake_y = width / 2;
snake_body[0].index_i = snake_x;
snake_body[0].index_j = snake_y;
PutFood();
}
~Game() {
delete[] snake_body;
}
void DrawTable() {
system("cls");
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (!i || i == height - 1 || !j || j == width - 1) {
cout << frame;
}
else if (i == food_x && j == food_y) {
cout << food;
}
else if (Check(i, j)) {
cout << snake;
}
else {
cout << " ";
}
}
cout << endl;
}
cout << "Your current score is: " << score;
}
void Control() {
if (_kbhit()) {
switch (_getch()) {
case 'w':
direction = UP;
break;
case 'a':
direction = LEFT;
break;
case 's':
direction = DOWN;
break;
case 'd':
direction = RIGHT;
break;
}
}
}
void Process() {
switch (direction) {
case UP:
snake_x--;
Move();
break;
case LEFT:
snake_y--;
Move();
break;
case DOWN:
snake_x++;
Move();
break;
case RIGHT:
snake_y++;
Move();
break;
}
}
void Move() {
/*for (int i = 0; i < snake_size; i++) { tail collision logic (if you try to reverse your move, you die). Optional.
if (snake_body[i].index_i == snake_x && snake_body[i].index_j == snake_y) {
status = true;
return;
}
}*/
snake_body[snake_size].index_i = snake_x;
snake_body[snake_size].index_j = snake_y;
if (!snake_x || snake_x == height - 1 || !snake_y || snake_y == width - 1) { // collision logic
status = true;
}
else if (snake_x == food_x && snake_y == food_y) {
snake_size++;
score++;
if (snake_size == max_size) {
win = true;
return;
}
PutFood();
}
else {
for (int index = 0; index < snake_size; index++) {
snake_body[index].index_i = snake_body[index + 1].index_i;
snake_body[index].index_j = snake_body[index + 1].index_j;
}
snake_body[snake_size].index_i = 0;
snake_body[snake_size].index_j = 0;
}
Sleep(speed);
}
void PutFood() {
srand(time(NULL));
food_x = rand() % (height - 2) + 2;
food_y = rand() % (width - 2) + 2;
}
bool Check(int i, int j) {
for (int k = 0; k < snake_size; k++) {
if (i == snake_body[k].index_i && j == snake_body[k].index_j) {
return true;
}
}
return false;
}
int getScore() {
return score;
}
void setSpeed(int s) {
speed = s;
}
};
int main() {
Game snake_game;
char exit;
string error = "Invalid choice, please choose 1-3";
int speed, choice;
cout << "Contol: WASD" << endl << "Set the difficulty level: " << endl << "1. Easy" << endl << "2. Normal" << endl << "3. Hard" << endl;
label:
cin >> choice;
try {
if (choice < 1 || choice > 3) throw error;
}
catch (char *error) {
cout << error << endl;
goto label;
}
switch (choice) {
case 1:
speed = 250;
break;
case 2:
speed = 75;
break;
case 3:
speed = 0;
break;
}
snake_game.setSpeed(speed);
while (!status && !win) {
snake_game.DrawTable();
snake_game.Control();
snake_game.Process();
}
if (status && !win) {
system("cls");
cout << "YOU LOST! Your score is: " << snake_game.getScore() << endl;
}
if (win) {
system("cls");
cout << "Congratulations! You won the game!" << endl << "Your score is: " << snake_game.getScore() << endl;
}
cin >> exit;
return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
bool状态=false,win=false;
结构蛇{
int索引_i;
int索引_j;
};
班级游戏{
私人:
枚举eDir{UP,RIGHT,DOWN,LEFT};
eDir方向;
const int height=25,width=50,max_size=(高度-2)*(宽度-2);
int snake_size=1,食物,食物,蛇,蛇,分数,速度;
char snake='@',food='*',frame='#';
蛇*蛇体=新蛇[最大尺寸];
公众:
游戏(){
snake_x=高度/2;
蛇_y=宽度/2;
snake_body[0]。索引_i=snake_x;
snake_body[0]。索引_j=snake_y;
食物();
}
~Game(){
删除[]蛇形体;
}
void DrawTable(){
系统(“cls”);
对于(int i=0;i cout当您使用conio.h时,您可以使用gotoxy(x,y)
转到要删除的坐标,只需执行带有空格的printf(“”
)。系统(“cls”)速度很慢。而且,你不需要刷新整个屏幕,因为大部分屏幕不会改变每一帧。我看到你包含了windows.h,所以我猜你只需要在windows上使用它。因此,我建议使用windows API中的函数
这里有一个例子
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {10, 10});
std::cout << ' ';
SetConsoleCursorPosition(GetStdHandle(标准输出句柄),{10,10});
在Unix系统上,std::cout是实现类似您的基于文本的程序的经典方法:
使用诅咒,程序员能够编写基于文本的应用程序
没有为任何特定的终端类型直接写入。诅咒
执行系统上的库发送正确的控制字符
基于终端类型。它提供一个或多个终端的抽象
映射到终端屏幕上的窗口。每个窗口都表示为
通过字符矩阵。程序员设置所需的外观
然后通知curses包更新屏幕。
库确定了需要进行的最小更改集
更新显示,然后使用终端的
特定功能和控制序列。[维基百科]
显然正在开发一个名为的Windows端口;您可以查看它是否满足您的需要。系统(“cls”)
实际上运行一个完整的Windows程序(cmd.exe
)清除控制台。这当然不是很有效。相反,我们需要简单地执行相同的操作,即cls命令在cmd.exe中所做的操作。对于清除屏幕,我们可以使用-将控制台屏幕缓冲区的内容替换为空格
BOOL cls()
{
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hConsoleOutput, &csbi))
{
CHAR_INFO fi = { ' ', csbi.wAttributes };
csbi.srWindow.Left = 0;
csbi.srWindow.Top = 0;
csbi.srWindow.Right = csbi.dwSize.X - 1;
csbi.srWindow.Bottom = csbi.dwSize.Y - 1;
return ScrollConsoleScreenBufferW(hConsoleOutput, &csbi.srWindow, 0, csbi.dwSize, &fi);
}
return FALSE;
}
系统(“cls”)
的效率太低。以下是清洁屏幕的类似方法:
//First get the console handle and its info.
HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsoleOut, &csbiInfo);
//Fill with ' ' in the whole console(number = X*Y).
FillConsoleOutputCharacter(hConsoleOut, ' ', csbiInfo.dwSize.X * csbiInfo.dwSize.Y, home, &dummy);
csbiInfo.dwCursorPosition.X = 0;
csbiInfo.dwCursorPosition.Y = 0;
//Set the Cursor Position to the Beginning.
SetConsoleCursorPosition(hConsoleOut, csbiInfo.dwCursorPosition);
在标准C++中,您的选项非常有限,因为语言中没有“控制台”的概念。事实上,实际上运行了一个完整的Windows程序来清除控制台。你可以使用一个单独的图形库来进行渲染,但是如果C++开始了,这可能会非常混乱。Windows提供了多种控制台处理功能,并且重新绘制屏幕,只需要擦除尾部并添加新的头。需要重新绘制整个内容。gotoxy
来自Borland编译器,希望没有人再使用它。有一种方法可以在上面提到的windows上模拟此功能。@alterigel:是的,它来自Borland。但是,显然它是可用的,它是一个选项。我看不出为什么不应该在学生项目中使用它……我认为这是使用系统调用和一个控制台库的最后一个手段,比如诅咒失败了。+ 1。我会忽略可移植性的问题,因为这个答案是使用系统特有的函数(有充分的理由)。,但是std::cout这将是非常不高效的。我已经编辑了我的答案,删除了神奇的数字和不必要的强制转换。这没有什么变化-每个std::cout混合调用Windows控制台API和C++流实现都是脆弱的。而且,std::cout
通常是缓冲的,所以你不会看到在刷新缓冲区之前对屏幕进行任何更新。这可能不是交互式游戏中需要的。