Как добиться того, чтобы объект в консоли двигался вниз вне зависимости от того, нажимаю я клавишу или нет

Рейтинг: 2Ответов: 1Опубликовано: 29.07.2025

Как добиться того, чтобы объект o в консоли двигался по направлению вниз вне зависимости от того, нажимаю я клавишу или нет.

Понятное дело, чтобы он двигался вниз, необходимо переменную x инкрементрировать (x++ или ++x). Но дело в том, что объект спускается вниз только тогда, когда я нажимаю какую-либо клавишу с помощью функции _getche(). А в свою очередь функция _kbhit ждет, пока клавиша будет нажата, иначе она вернет 0. Например, нажимаю я 'a', объект смещается влево и уже потом смещается вниз. Вопрос: Как добиться того, чтобы он каждые пол секунды (может и быстрее , если это можно настраивать) спускался вниз, при этом я мог бы его сдвигать влево и вправо, независимо от того, двигается ли он вниз прямо сейчас или стоит.

Вот сам исходный код:

#include <iostream>
#include <vector>
#include <conio.h>
#include <windows.h>
#include <time.h>
#include <cstdlib>
#include <cstdio>
#include <utility>

const int HEIGHT{ 25 }; /* --ВЫСОТА-- */
const int WIDTH{ 50 }; /* --ШИРИНА-- */

int map[HEIGHT][WIDTH];

int x = 4;
int y = 15;

void init(void)
{
    for (int i = 0; i != HEIGHT; i++)
        for (int j = 0; j != WIDTH; j++)
            map[i][j] = ' ';
}
void draw(void)
{
    static int count = 0;
    if (count++ == 0) {
        init();
        map[x][y] = 'o';
    }

    for (int i = 0; i != HEIGHT; i++) {
        for (int j = 0; j != WIDTH; j++){
            if (i == 0 || j == 0 || j == WIDTH-1 || i == HEIGHT-1)
                map[i][j] = '#';
        }
    }
    

}

void print_map(void)
{
    for (int i = 0; i != HEIGHT; i++)
    {
        for (int j = 0; j != WIDTH; j++)
        {
            printf("%c", map[i][j]);    
        }
        std::cout << std::endl;
    }
    printf("x = %i; y = %i;", x, y);
}
//int x = 4;
//int y = 15;
void move()
{
    x++;
    map[x][y] = ' ';
    int prev_x = x, prev_y = y;
    if (_kbhit)
    {
        char input = _getche();
        switch (input)
        {
        case 'w':
        case 'W':   --x;
            break;

        case 's':
        case 'S':   ++x;
            break;

        case 'd':
        case 'D':   ++y;
            break;
        case 'a':
        case 'A':   --y;
            break;
        }
    }
    if (map[x][y] == '#') {
        x = prev_x; y = prev_y;
    }
    else
        map[x][y] = 'o';
}
void clear_screen(void)
{
    std::system("cls");
}
int main(void)
{
    while (true)
    {
        draw();
        print_map();
        move();
        clear_screen();
    }
    return 0;
}

Буду сильно благодарен, если действительно поможете!

Ответы

▲ 1Принят

Переделал, потому что посчитал, что всё таки так будет лучше

Можно использовать GetTickOut() текущего для получения времени, _kbhit() для проверки введена ли клавиша, _getch() для получения клавиши (без её вывода), и небольшую заморозку программы, чтобы процессор не страдал

Для этого я немного упростил draw():

void draw(void)
{
    // удаляем init() и сразу заполняем либо '#', либо ' ';

    for (int i = 0; i != HEIGHT; i++) {
        for (int j = 0; j != WIDTH; j++) {
            if (i == 0 || j == 0 || j == WIDTH - 1 || i == HEIGHT - 1)
                map[i][j] = '#';
            else
                map[i][j] = ' ';
        }
    }
    map[x][y] = 'o';
}

Добавил функцию move_down() (чтобы мы двигались вниз):

void move_down()
{
    if (x + 1 < HEIGHT - 1) // если мы не упали до конца
        ++x;
}

Чуть-чуть переделал move():

void move()
{
    if (_kbhit()) {
        char input = _getch();
        switch (input) {
        case 'a':
        case 'A':
            if (y - 1 > 0) --y;
            break;
        case 'd':
        case 'D':
            if (y + 1 < WIDTH - 1) ++y;
            break;
        case 's':
        case 'S':
            move_down(); // вручную ускоряем подение
            break;
        case 'w':
        case 'W':
            if (x - 1 > 0) --x;
            break;
        }
    }
}

Ну и, понятное дело, переделал main():

int main(void)
{
    const int delay_ms = 500; // Интервал падения вниз (в миллесекундах
    DWORD last_fall = GetTickCount(); // Время последнего падения

    while (true)
    {
        if (GetTickCount() - last_fall >= delay_ms) { // Если от last_fall прошло время delay_ms
            move_down();
            last_fall = GetTickCount();
        }

        move(); // проверяем и изменяем x/y если клавиша нажата
        draw();
        print_map();
        clear_screen();
        Sleep(50); // делаем небольшую заморозку программы, чтобы процессор не страдал
    }

    return 0;
}

Дополнительная рекомендация


Чтобы экран не мигал нужно исключить использование std::system("cls"), т.к. он довольно медленный

Вместо этого можно просто двигать курсор в начало и перерисовывать всё:

// замена clear_screen();
void reset_cursor()
{
    static const HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // получаем дескриптор вывода
    COORD coord = {0, 0}; // задаём координаты
    SetConsoleCursorPosition(hOut, coord); // ставим их
}

И просто в main() заменяем clear_screen() на reset_cursor()

Но курсор будет постоянно двигаться, поэтому мы можем его сделать невидимым:

// в начале main()
// убираем курсор
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // получаем дескриптор вывода
CONSOLE_CURSOR_INFO cursorInfo;

GetConsoleCursorInfo(hOut, &cursorInfo); // получаем текущие настройки курсора
cursorInfo.bVisible = FALSE; // убираем флаг видимости
SetConsoleCursorInfo(hOut, &cursorInfo); // применяем изменения
// в конце main
// ставим курсор обратно
cursorInfo.bVisible = FALSE; // обратно добовляем флаг видимости
SetConsoleCursorInfo(hOut, &cursorInfo); // применяем изменения курсора

И при выводе координат в случае если у нас текст будет не таким же длинным, то мы увидим лишние ;, поэтому при выводе надо добавить вывод двух пробелов в конце


По итогу получаем следующий код:

#include <iostream>
#include <vector>
#include <conio.h>
#include <windows.h>
#include <time.h>
#include <cstdlib>
#include <cstdio>
#include <utility>

const int HEIGHT{ 25 }; /* --ВЫСОТА-- */
const int WIDTH{ 50 }; /* --ШИРИНА-- */

int map[HEIGHT][WIDTH];

int x = 4;
int y = 15;

void draw(void)
{
    // удаляем init() и сразу заполняем либо '#', либо ' ';

    for (int i = 0; i != HEIGHT; i++) {
        for (int j = 0; j != WIDTH; j++) {
            if (i == 0 || j == 0 || j == WIDTH - 1 || i == HEIGHT - 1)
                map[i][j] = '#';
            else
                map[i][j] = ' ';
        }
    }
    map[x][y] = 'o';
}

void print_map(void)
{
    for (int i = 0; i != HEIGHT; i++)
    {
        for (int j = 0; j != WIDTH; j++)
        {
            printf("%c", map[i][j]);    
        }
        std::cout << std::endl;
    }
    printf("x = %i; y = %i;  ", x, y); // вывод этих двух пробелов
}

void move_down()
{
    if (x + 1 < HEIGHT - 1) // если мы не упали до конца
        ++x;
}

void move()
{
    if (_kbhit()) {
        char input = _getch();
        switch (input) {
        case 'a':
        case 'A':
            if (y - 1 > 0) --y;
            break;
        case 'd':
        case 'D':
            if (y + 1 < WIDTH - 1) ++y;
            break;
        case 's':
        case 'S':
            move_down(); // вручную ускоряем подение
            break;
        case 'w':
        case 'W':
            if (x - 1 > 0) --x;
            break;
        }
    }
}

// замена clear_screen();
void reset_cursor()
{
    static const HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // получаем дескриптор вывода
    COORD coord = {0, 0}; // задаём координаты
    SetConsoleCursorPosition(hOut, coord); // ставим их
}

int main(void)
{
    // убираем курсор
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // получаем дескриптор вывода
    CONSOLE_CURSOR_INFO cursorInfo;

    GetConsoleCursorInfo(hOut, &cursorInfo); // получаем текущие настройки курсора
    cursorInfo.bVisible = FALSE; // убираем флаг видимости
    SetConsoleCursorInfo(hOut, &cursorInfo); // применяем изменения


    const int delay_ms = 500; // Интервал падения вниз (в миллесекундах
    DWORD last_fall = GetTickCount(); // Время последнего падения

    while (true)
    {
        if (GetTickCount() - last_fall >= delay_ms) { // Если от last_fall прошло время delay_ms
            move_down();
            last_fall = GetTickCount();
        }

        move(); // проверяем и изменяем x/y если клавиша нажата
        draw();
        print_map();
        reset_cursor();
        Sleep(50); // делаем небольшую заморозку программы, чтобы процессор не страдал
    }

    // ставим курсор обратно
    cursorInfo.bVisible = FALSE; // обратно добовляем флаг видимости
    SetConsoleCursorInfo(hOut, &cursorInfo); // применяем изменения курсора

    return 0;
}