То, что написал Брейкмастер реализуется очередью команд как описал в одной своей статье Антон Григорьев для Королевства Дельфи. Там он делал в качестве примера статьи игру Сокобан (человечек ходит по уровням и собирает по углам ящики в целевые клетки)
Цитата
Cамой нетривиальной частью игры оказалась правильная реакция на клавиатуру. Связано это с тем, что перемещение человечка не мгновенно. Управление человечком осуществляется стрелками. Если пользователь нажал и удерживает стрелку, за время движения человечка в буфере накапливаются сообщения WM_KeyDown, связанные с автоповтором нажатия, причём накапливаются быстрее, чем извлекаются, поэтому когда пользователь отпускает клавишу, человечек продолжает двигаться и останавливается дальше, чем это было задумано. С другой стороны, время перемещения на одну клетку оказалось меньше, чем стандартная величина задержки перед первым повтором нажатия, и из-за этого при удерживании стрелки возникала неприятная пауза между первым и вторым перемещениями. Я попробовал несколько способов, которые имели другие проблемы: например, человечек не реагировал на изменение направления движения, если новая стрелка была нажата до того, как человечек закончил движение в предыдущем направлении. Или неадекватно реагировал, если нажать стрелку, а за время движения успеть отпустить её и снова нажать. В результате экспериментов я остановился на следующем алгоритме.
При получении сообщения WM_KeyDown о нажатии стрелки проверяем по его параметрам, является ли нажатие первым, или это автоповтор. Автоповторы просто игнорируем.
Если нажатие первое, помещаем в очередь сообщений специальное пользовательское сообщение WM_DoStep, передавая через его параметры информацию о том, какая из стрелок нажата. На этом обработка WM_KeyDown заканчивается.
Обработчик WM_DoStep начинает с того, что проверяет текущее состояние нужной стрелки с помощью API-функции GetAsyncKeyState. Если окажется, что пользователь уже отпустил стрелку, ничего делать не надо, сразу выходим. (При реакции на первое нажатие клавиши эта проверка явно излишняя — вряд ли пользователь сумеет так быстро её отпустить. Но зато она хорошо спасает от повторного нажатия/отпускания во время движения человечка — он всё равно остановится, если клавиша в момент принятия решения не будет нажата.)
Если проверка пройдена, осуществляем перемещение.
В конце работы WM_DoStep вновь проверяет состояние нужной стрелки, и если она до сих пор не отпущена, помещает в очередь ещё одно сообщение WM_DoStep с теми же параметрами.
Эта реализация обеспечивает наиболее комфортное, на мой взгляд, поведение человечка. Во-первых, он движется плавно, без пауз. Во-вторых, после отпускания стрелки останавливается при первой же возможности. В-третьих, если игрок захочет поменять направление движения, он может наживать другую стрелку до того, как человечек остановится.
Можно, думаю, и без сообщений такое написать, а просто очередью команд, когда нажимается клавиша, т.е. срабатывает keydown то проверяем есть ли данная команда в очереди и кладем в очередь сверху если нет команды. а в конце передвижения на конкретную клетку очищаем очередь от команды, которую только что обработали, и опять проверяем очередь. таким образом нажатая кнопка будет гнать человечка пока кнопку не отпустим, и он по инерции не побежит, так как очередь сама очистится... Как-то так )