Тема: Рух символа стрілками в консолі
1. Чи можна переміщати символ без перемалювання екрану?
2. Як взагалі в С++ запрограмувати реакцію на клавіатуру? Передивилась купу посилань - всі пишуть по-різному, і ніде нічого не зрозуміло.
Ви не увійшли. Будь ласка, увійдіть або зареєструйтесь.
Ласкаво просимо вас на україномовний форум з програмування, веб-дизайну, SEO та всього пов'язаного з інтернетом та комп'ютерами.
Будемо вдячні, якщо ви поділитись посиланням на Replace.org.ua на інших ресурсах.
Для того щоб створювати теми та надсилати повідомлення вам потрібно Зареєструватись.
Український форум програмістів → C++ → Рух символа стрілками в консолі
Сторінки 1
Для відправлення відповіді ви повинні увійти або зареєструватися
1. Чи можна переміщати символ без перемалювання екрану?
2. Як взагалі в С++ запрограмувати реакцію на клавіатуру? Передивилась купу посилань - всі пишуть по-різному, і ніде нічого не зрозуміло.
2) Я так розумію ти хочеш створити консольну гру якщо так то подивись на GetAsyncKeyState()
Наприклад
#include <Windows.h>
...
if (GetAsyncKeyState(0x57))//w,ц
{
//Code
}
getch() - зупиняє консольну програму до натиснення клавіши
наприклад
#include <conio.h>
...
if(getch()=='w')
{
//code
}
1. Чи можна переміщати символ без перемалювання екрану?
Малось на увазі «перемалювати символ»? Так, можна, хоча й загальної методики для всіх систем і всіх випадків не існує.
Якщо символ, який треба замінити, лежить у тому ж рядку екрана, де й курсур, достатньо підвести курсор у потрібне місце (виводячи символ '\010', який не друкується, а лише переміщує курсор на одну позицію назад) і надрукувати на тому місці те, що нам треба. На жаль, це не працює, якщо символ лежить в іншому рядку.
Взагалі, існує така чудова річ, як ANSI ескейп-коди (які дозволяють, зокрема, рухати курсор вгору/вниз, виводячи послідовності керуючих символів). На жаль, це годиться переважно для ніксів — віндова консоль ці коди не підтримує (хоча й існують сторонні розробки, такі як ansicon, що додають цю можливість).
У часи ДОС практикувався прямий запис у відеобуфер текстового режиму (який являв собою область пам'яті з певною адресою — задавши цю адресу вказівникові як числове значення, можна було працювати з монітором як масивом, де кожна комірка містить код символа та його колір). Однак, це дуже низькорівневий підхід, і для програм під 32- та 64-бітні системи він, схоже, неактуальний...
На жаль, я не знаю, як ця проблема вирішується цивілізовано на C++ під віндою. Треба погуглити. Поки що бачу можливе вирішення в вигляді використання ansi-кодів і запуску програми через ansicon, але, очевидно, має існувати якась бібліотека, призначена спеціально для маніпуляцій з консоллю.
2. Як взагалі в С++ запрограмувати реакцію на клавіатуру?
getch()
Причому, натискання стрілок (та ряду інших спеціальних клавіш та клавішних комбінацій) сприймається цією функцією як два символи (тобто, перший раз getch() поверне символ з кодом 0 або 224 (в залежності від того, було використано числову клавіатуру чи окремо розміщені стрілки), другий раз — код, що ідентифікує стрілку. (Не впевнений, чи стосується це всіх компіляторів та систем, але gcc (MinGW) дає в мене такий результат).
1. https://msdn.microsoft.com/uk-ua/librar … s.85).aspx
2. https://msdn.microsoft.com/uk-ua/librar … s.85).aspx
Це за умови, що ви пишете під Windows (ви цього не стверджували).
Зробила таким чином:
short getKeyPressed()
{
if (GetAsyncKeyState(VK_UP) < 0)
return UP;
else if (GetAsyncKeyState(VK_DOWN) < 0)
return DOWN;
else if (GetAsyncKeyState(VK_LEFT) < 0)
return LEFT;
else if (GetAsyncKeyState(VK_RIGHT) < 0)
return RIGHT;
else if (GetAsyncKeyState(VK_ESCAPE) < 0)
return ESCAPE;
else if (GetAsyncKeyState(VK_RETURN) < 0)
return ENTER;
else
return -1;
}
int main()
{
srand(unsigned(time(0)));
for (int i = 0; i < FIELD_SIZE; i++)
for (int j = 0; j < FIELD_SIZE; j++)
game_field[i][j] = ' ';
short key;
bool exit = false, action = false;
paintGameField();
do {
// take keyboard input
key = getKeyPressed();
// do something - move_cursor, put X or O, etc.
switch (key)
{
case ESCAPE:
exit = true;
break;
case ENTER:
break;
default:
action = movePointer(key);
}
if (action)
{
system("cls");
paintGameField();
}
Sleep(100);
} while (!exit);
return 0;
}
і все, здається, працює... Але:
1) чи можна отримати відразу, яка кнопка натиснута, а не перераховувати коди?
2) оновлення екрану дуже помітно. Як це виправити?
1. Якщо хочете, то PeekConsoleInput та ReadConsoleInput. Але ваш варіант простіший, повірте.
2. По-перше, ви оновлюєте екран за допомогою
system("cls");
Щоб ви зрозуміли, що відбувається: воно створює новий процес cmd.exe (який явно більший за ваш проект), передає йому команду cls, cmd.exe обробляє цю команду, обережно і ретельно (і повільно) чистить всі екранні буфери і завершується. Це все відбувається ДУЖЕ ДОВГО - настільки, що ви встигаєте це помітити. Можете спробувати замінити це рекомендованою M$ функцією.
По-друге, ви використовуєте cout. cout працює непогано, але через те, що деякі програмісти використовують printf-и, вимушений із ними синхронізуватися, що його сповільнює; також, можливо, ви використовуєте endl, що сповільнює його ще більше (оскільки цей маніпулятор не тільки виводить новий рядок, а й вимагає виводу всього буферу негайно, а не коли захоче сама консоль). Додайте на початок рядок
std::cout.sync_with_stdio(false);
і замініть всі endl на '\n'; сподіваюся, цього вистачить.
Якщо ж ні - замініть вивід прямими зверненнями до WinAPI WriteConsoleOutputCharacter, це дозволяє писати швидко і одразу в потрібне місце екрану. Тоді і оновлювати екран не треба буде - тільки потрібні символи. Але це потребуватиме певної переробки вашої програми.
Я вам дуже дякую, але в мене виникло запитання: що нам дає ось цей рядок
const int watchedKeysSize = sizeof( watchedKeys ) / sizeof( watchedKeys[ 0 ] ) ;
і застосування отриманого значення в циклі?
Розмір масиву. Це стандартна формула кількості елементів в масиві. Якщо захочете обробляти ще якісь кнопки, треба тільки масив підредагувати і все.
Дякую всім, хто відгукнувся на мої питання. Завдяки вашим підказкам, в мене вийшло реалізувати, хай не ідеально, гру "Хрестики-нулики".
Можливо, комусь буде цікаво, а, можливо, і корисно:
Непогано!
Єдине зауваження — програма надто різко реагує на натиски стрілок. В getKeyPressed() бажано зробити невелику часову затримку, щоб курсор не перескакував через клітинку від короткого натискання.
Сторінки 1
Для відправлення відповіді ви повинні увійти або зареєструватися