Как добиться того, чтобы объект в консоли двигался вниз вне зависимости от того, нажимаю я клавишу или нет
Как добиться того, чтобы объект 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;
}