Как я могу обновить консоль наиболее эффективным способом?(игра змея с ++) - PullRequest
0 голосов
/ 17 ноября 2018

Это мой код для змеи.system("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 {
    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;
}

Ответы [ 5 ]

0 голосов
/ 19 ноября 2018

КПД system("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);
0 голосов
/ 17 ноября 2018

system("cls") фактически выполняет всю программу Windows (cmd.exe) для очистки консоли. это конечно очень не эффективно. вместо этого нам нужно просто сделать то же самое, что команда cls делает внутри cmd.exe . для очистки экрана мы можем использовать ScrollConsoleScreenBuffer - для замены содержимого буфера консольного экрана на пробелы

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;
}
0 голосов
/ 17 ноября 2018
Система

("cls") работает медленно. Кроме того, вам не нужно обновлять весь экран, так как большая его часть не меняет каждый кадр. Я вижу, вы включили windows.h, так что я полагаю, вам это нужно только для работы в Windows. Поэтому я предлагаю использовать функцию SetConsoleCursorPosition из Windows API.

Вот пример

   SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {10, 10});
   std::cout << ' ';

Этот код изменит позицию курсора на координаты (10, 10) и выведет пробел. Вы можете сделать это для каждого «пикселя», который вы хотите изменить, для каждого кадра.

0 голосов
/ 17 ноября 2018

В системах Unix curses - это классический способ реализации текстовой программы, подобной вашей:

Используя ругательства, программисты могут писать текстовые приложения без записи напрямую для какого-либо конкретного типа терминала. Проклятия библиотека в исполняющей системе отправляет правильные управляющие символы на основе типа терминала. Предоставляет абстракцию одного или нескольких окна, которые отображаются на экране терминала. Каждое окно представлено по символьной матрице. Программист устанавливает желаемый внешний вид каждого окна, затем говорит пакету curses обновить экран. Библиотека определяет минимальный набор изменений, которые необходимы для обновить дисплей и затем выполнить их с помощью терминала конкретные возможности и последовательности управления. [Википедия]

По-видимому, разрабатывается порт Windows, который называется PDCurses ; Вы можете увидеть, соответствует ли он вашим потребностям.

0 голосов
/ 17 ноября 2018

Поскольку вы используете conio.h, вы можете использовать gotoxy(x, y), чтобы перейти к координате, которую вы хотите удалить, и просто выполнить printf(" ") с пробелом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...