ТОР 5 статей: Методические подходы к анализу финансового состояния предприятия Проблема периодизации русской литературы ХХ века. Краткая характеристика второй половины ХХ века Характеристика шлифовальных кругов и ее маркировка Служебные части речи. Предлог. Союз. Частицы КАТЕГОРИИ:
|
Управление компьютером с клавиатуры. Функции ReadKey и KeyPressedПопробуйте запустить программу, которая долго делает свое дело, не обращая на вас внимания. Например, такую: BEGIN repeat WriteLn(‘А нам все равно!’) until 2>3 END. Вы сидите перед компьютером и ждете, когда он закончит печатать свой текст. А он никогда не закончит. Вы принимаетесь стучать по клавишам, надеясь, что это прервет бессмысленный цикл. Бесполезно. Только когда вы, удерживая нажатой клавише Ctrl, щелкнете по клавише Break, программа прервет свою работу. Пока программы работают, они не реагируют на клавиатуру, если вы об этом специально не позаботились. А чтобы позаботиться, вы должны включить в них специальные функции ReadKeyи KeyPressedиз модуля CRT. О смысле функций вообще мы поговорим в 13.2, а сейчас разберем на примерах эти две. Дополним нашу упрямую программу парой строк: USES CRT; BEGIN Repeat if KeyPressed then WriteLn(‘Хозяин нажал клавишу!’) else WriteLn(‘А нам все равно!’) until 2>3 END. Выражение “if KeyPressed then” можно перевести как “если нажата клавиша, то”. Наткнувшись на это выражение, Паскаль проверяет, была ли нажата клавиша на клавиатуре. Когда вы запустите эту программу, она будет бесконечно печатать А нам все равно! Но как только вы щелкнете по какой-нибудь клавише, программа станет бесконечно печатать Хозяин нажал клавишу! Если функция KeyPressed просто реагирует на то, была ли нажата какая-нибудь клавиша, то функция ReadKey сообщает, какая именно клавиша была нажата. Вот программа, которая бесконечно печатает текст А нам все равно! и одновременно непрерывно ждет нажатия на клавиши, и как только клавиша нажата, однократно докладывает, была ли нажата клавиша “w” или другая клавиша, после чего продолжает печатать А нам все равно! Если мы захотим, чтобы программа закончила работу, мы должны нажать на клавишу “q”. USES CRT; VAR klavisha: Char; BEGIN Repeat Delay (1000); {иначе программа печатает слишком быстро} WriteLn(‘А нам все равно!’); if KeyPressed then begin klavisha:= ReadKey; if klavisha=’w’ then WriteLn(‘Нажата клавиша w’) else WriteLn(‘Нажата другая клавиша’) end {if} until klavisha=’q’ END. Программа доберется до строки klavisha:= ReadKey только в случае, если будет нажата какая-нибудь клавиша. Функция ReadKey определяет, какой символ был на нажатой клавише, и присваивает его значение переменной, которую мы придумали - klavisha. С этого момента вы можете как хотите анализировать эту переменную и в зависимости от ее значения управлять работой компьютера. Наша программа будет бесконечно печатать А нам все равно!, а при каждом нажатии на клавишу будет однократно сообщать Нажата клавиша w или Нажата другая клавиша. Почему однократно, а не бесконечно? Грубо это можно объяснить тем, что после выполнения функции ReadKey Паскаль “забывает”, что на клавиатуре была нажата клавиша. Вы спросите, а зачем здесь вообще нужна строка if KeyPressed then? Дело в том, что если перед выполнением функции ReadKey клавишу на клавиатуре не нажать, то функция ReadKey останавливает программу и заставляет ее ждать нажатия на клавишу, а после нажатия программа продолжает работу. Если вам в вашей программе паузы не нужны, то вам придется использовать KeyPressed. Функция ReadKey напоминает процедуру ReadLn. Однако у нее есть интересное отличие: при вводе символов по процедуре ReadLn они появляются на экране, а при вводе символа по функции ReadKey - нет. Благодаря этому свойству, с помощью ReadKey можно организовать “секретный ввод” информации в компьютер - человек, стоящий у вас за спиной и видящий экран монитора, но не видящий ваших пальцев, никогда не догадается, на какие клавиши вы нажимаете. Подробнее о механизме действия ReadKey и KeyPressed см. в следующем параграфе. Задача “Пароль на программу”. Пусть вы сделали какую-нибудь интересную программу и не хотите, чтобы ее запускал кто угодно. Для этого сделаем так, чтобы программа начинала свою работу с предложения пользователю ввести пароль. Если пользователь набрал правильный пароль, то программа нормально продолжает свою работу, в противном случае прерывается. Пусть ваш пароль -typ. Тогда для решения задачи вам достаточно вставить в начало вашей программы следующий фрагмент: WriteLn(‘Введите, пожалуйста, пароль’); Simvol1:= ReadKey; Simvol2:= ReadKey; Simvol3:= ReadKey; if NOT ((Simvol1=’t’) AND (Simvol2=’y’) AND (Simvol3=’p’)) then Halt; {Продолжение программы} Вы скажете: Кто угодно перед запуском моей программы посмотрит в ее текст и сразу же увидит пароль. Совершенно верно. Чтобы текст программы не был виден, преобразуйте ее в исполнимый файл с расширением exe (см. часть IV). Задание 96 “Светофор”: Нарисуйте светофор: прямоугольник и три окружности. При нажатии нужной клавиши светофор должен загораться нужным светом. Задание 97 “Зенитка”: Вверху справа налево медленно движется вражеский самолет (эллипс). В подходящий момент вы нажатием любой клавиши запускаете снизу вверх зенитный снаряд (другой эллипс). Буфер клавиатуры При первом прочтении этот параграф можно пропустить. Компьютер работает с клавиатурой сложнее, чем нам кажется. Причина сложности вот в чем. Предположим, вы играете в игру, где с клавиатуры управляете движением самолета. Чтобы избежать попадания вражеского снаряда, вы должны бросить самолет вверх и направо, то есть очень быстро нажать клавишу и сразу затем клавишу ®. Как только вы нажали на , компьютер несколько долей секунды обрабатывает это нажатие, то есть соображает, что теперь ваш самолет нужно бросить вверх, и наконец действительно так и делает. Если вы успели нажать на клавишу ® до того, как он это сообразил, то компьютер, не имеющий буфера клавиатуры, просто не обратит внимания на это нажатие, потому что занят мыслительной работой. А это плохо, так как вы совершенно не обязаны во время игры думать о том, успевает ли компьютер за вашими пальцами. Чтобы исправить ситуацию, нужно дать компьютеру возможность в случае занятости не игнорировать нажатия на клавиши, а запоминать их, чтобы обработать, как только освободится. Для этого и служит буфер клавиатуры - место в оперативной памяти, в котором и запоминаются эти нажатия. Вы можете, пока компьютер занят, нажать на клавиши до 16 раз - и буфер клавиатуры запомнит все 16 клавиш в том порядке, в котором они нажимались. Вот как можно изобразить процесс ввода в компьютер текста «Привет недоверчивым!», когда процессор занят: На клавиши пока не нажимали:
Нажали на клавишу П:
Нажали еще на одну клавишу - р:
Нажали еще на несколько клавиш:
Нажали на клавиши в 15-й и 16-й раз:
Нажали на клавишу в 17-й раз – раздается предупреждающий писк компьютера, буква в в буфер не записывается:
Пока мы буфер клавиатуры только заполняли. А кто его опорожнит? Процессор. Процессор, выполняющий программу на Паскале, берет что-то из буфера клавиатуры только в тот момент, когда выполняет процедуру ReadLn, функцию ReadKey и еще кое-что. В остальное время он для буфера клавиатуры занят. Посмотрим, как он берет информацию из буфера, выполняя ReadKey. Пусть перед выполнением ReadKey в буфере была такая информация:
При выполнении ReadKey первая из введенных в буфер букв – П – отправляется на обработку:
Еще одно выполнение ReadKey:
Помните, что если вы нажали на какую-нибудь клавишу и не отпускаете ее, то это равносильно частому-частому нажатию на эту клавишу. Если процессор в это время не занят выполнением процедуры ReadLn или большого количества функций ReadKey, то некому выуживать информацию из буфера клавиатуры, он мгновенно переполняется и вы слышите раздраженный писк. На практике события, описанные всеми этими схемами, встречаются редко. Только неопытный пользователь будет жать на клавиши в тот момент, когда компьютер не готов воспринимать информацию. Обычно подавляющую часть времени буфер клавиатуры пуст, ни процессор, ни клавиатура с ним не работают. Раз в вечность человек в нужный момент нажимает на клавишу, в буфере появляется символ и тут же процессор при помощи ReadLn или ReadKey выуживает его оттуда и снова надолго буфер пуст.
Теперь я могу описать правила работы KeyPressed и ReadKey как надо:
Функция KeyPressedотвечает на вопрос, есть ли что-нибудь в буфере клавиатуры. В буфере клавиатуры она никаких изменений не производит.
Функция ReadKeyзабирает из буфера клавиатуры символ, который попал туда раньше других. Если буфер клавиатуры пуст, то ReadKey останавливает компьютер. Ожидание длится до тех пор, пока в буфере не появится символ (в результате нажатия на клавишу). ReadKey сразу же его оттуда забирает и компьютер продолжает работу.
Теперь вам должно быть понятно, зачем мы в одной из предыдущих циклических программ использовали KeyPressed. Без нее ReadKey просто остановила бы компьютер.
ReadLn при своей работе опустошает буфер клавиатуры. И еще. Мы знаем, что любая информация в компьютере закодирована (см. 3.5). Поэтому, хоть для простоты я везде говорил, что в буфер попадает символ, правильней было бы сказать, что в буфер попадает код символа.
Как очистить буфер клавиатуры, не останавливая компьютер: while KeyPressed do kl:=ReadKey то есть «пока в буфере не пусто, таскай оттуда по символу».
Как ждать нажатия на произвольную клавишу: repeat until KeyPressed то есть «повторяй ничегонеделанье, пока не нажмут на клавишу». Задание 98 “Управляемая точка”: Назначьте четыре клавиши. По нажатии одной из них точка по экрану перемещается на некоторый шаг вверх, другой - вниз, третьей - влево, четвертой - вправо. В 12.11 вы узнаете, как управлять компьютером при помощи клавиш управления курсором и других. Задание 99: Добавьте еще пару клавиш - одну для увеличения шага, другую - для уменьшения.
10.9. Гетерархия. Задание на игру “Торпедная атака” При создании мультфильма в 10.2 мы придерживались стиля программирования сверху-вниз, когда процедуры образовывали подчинение, иерархию: все “основные” процедуры вызывались из раздела операторов основной программы. Менее “основные” процедуры вызывались из “основных” и т.д. Однако, для многих задач, особенно связанных с моделированием сложных объектов и искусственного интеллекта, такой метод неестественен. Процедуры здесь часто получаются равноправными и вызываются не из раздела операторов основной программы, а согласно логике задачи вызывают друг друга по мере необходимости. Такая организация общения процедур называется гетерархией. Сейчас я напишу задание на создание программы для игры “Торпедная атака”. А затем предложу схему организации процедур этой программы. В учебных целях я выберу гетерархию, хотя в данном случае можно было бы обойтись и иерархией.
Задание 100: Наверху экрана слева направо плывет вражеский корабль. Внизу притаился ваш торпедный аппарат. В подходящий момент времени вы нажимаете клавишу - и торпеда плывет вверх. Если вы попали, то видна вспышка от взрыва, может быть, на мгновение виден и сам взрыв, раздается коротенькая радостная мелодия, на экране - коротенький поздравительный текст, счетчик подбитых кораблей на экране увеличивается на 1. Если не попали, то зрительные и звуковые эффекты - совсем другие. В любом случае увеличивается на 1 счетчик выпущенных торпед. Когда торпеды у вас кончатся (скажем, их было 10), игра заканчивается. Программа анализирует ваши успехи и в зависимости от них выдает на экран текст, скажем “Мазила!”, если вы не попали ни разу из 10, или “Профессионал!”, если вы попали 8 раз. Затем спрашивает, будете ли вы играть еще.
Схема программы (читайте ее не сверху вниз, а снизу вверх): Uses CRT,Graph;
VAR все переменные опишем именно здесь, а не внутри процедур
опережающие описания процедур
PROCEDURE ZAVERSHENIE_IGRI; Здесь анализируем, насколько успешно стрелял игрок, отмечаем мелодией, цветом и текстом его достижения, затем спрашиваем, будет ли игрок играть еще. Если да, то вызываем процедуру NACHALO, иначе закрываем графический режим и - Halt.
PROCEDURE NE_PORA_LI; Здесь увеличиваем счетчик торпед. Если он>10, то вызываем процедуру ZAVERSHENIE_IGRI, иначе процедуру RISUNOK.
PROCEDURE NEPOPAL; Здесь программируем все эффекты в случае промаха, после чего вызываем процедуру NE_PORA_LI.
PROCEDURE POPAL; Здесь программируем все эффекты в случае попадания, после чего вызываем процедуру NE_PORA_LI.
PROCEDURE ATAKA; Здесь плывут одновременно корабль и торпеда. Затем в зависимости от ситуации вызываются процедуры POPAL или NEPOPAL. Учтите также ситуацию, когда вы просто забыли выстрелить.
PROCEDURE KORABL; Здесь плывет корабль до выстрела, который вызывает процедуру ATAKA. PROCEDURE RISUNOK; Здесь рисуем береговую линию, указываем на экране имя игрока, счетчики торпед и подбитых кораблей. Затем вызываем процедуру KORABL.
PROCEDURE NACHALO; Здесь устанавливаем в нуль счетчики торпед и подбитых кораблей, спрашиваем имя игрока и делаем все прочее, что нужно делать один раз за всю игру в самом ее начале. Затем прямо из процедуры NACHALO вызываем процедуру RISUNOK. Begin инициализация графического режима; DirectVideo:=false; {Чтобы работал WriteLn } NACHALO {Вот такой короткий раздел операторов.} End. Помощь (читайте ее только в крайнем случае). Когда вы выстрелите, вы заметите, что корабль стал плыть медленнее. Это происходит потому, что теперь компьютер за каждый шаг корабля должен еще просчитать и изобразить на экране торпеду, а на это нужно время. Увеличьте шаг движения корабля после выстрела или уменьшите паузу Delay так, чтобы скорость осталась примерно той же. Как компьютер определит, попал или не попал? Нужно в тот момент, когда торпеда доплывет до линии движения корабля, сравнить горизонтальные координаты корабля и торпеды, и если они достаточно близки, считать, что попал.
Улучшение. Если у всех кораблей будет одинаковая скорость, то попадать будет слишком просто, а значит и играть неинтересно. Сделайте скорость кораблей случайной. Конечно, не совсем уж (скажем, в условных единицах скорости диапазон от 0 до 10 – это слишком), а в пределах разумного (скажем, от 4 до 8 – это нормально). Причем не нужно менять скорость одного и того же корабля в процессе движения. Пусть она остается постоянной, а то все будет зависеть не от мастерства, а от везения. Различаются скорости только разных кораблей. Пусть игрок сможет выбирать из нескольких уровней трудности. Трудность удобнее всего увеличивать, уменьшая размеры корабля, то есть требуемую величину близости координат корабля и торпеды при определении попадания.
Еще одно задание 101: «Графический редактор». Создайте программу, которая бы, повинуясь нажатию разных клавиш клавиатуры, рисовала бы, расширяла, сжимала, перемещала по экрану, заливала разными цветами прямоугольники (а если вам понравилось, то и эллипсы и линии и прочее). В качестве «печки, от которой танцевать», можете взять решение задания 98.
Мы с вами закончили первый из двух циклов знакомства с Паскалем. Если вам удались «Торпедная атака» или «Графический редактор» и они у вас работают не хуже, чем указано в задании, то у вас должна появиться уверенность, что теперь, умело разбивая программы на небольшие процедуры, вы можете создавать программы любой сложности, а значит цель этого цикла достигнута. Я вас поздравляю - вам присваивается звание “ Программист-любитель III ранга ”!
Часть III. Программирование на Паскале – второй уровень Если вам кажется, что вы уже все можете, то вы правы и неправы. Правы потому, что вы можете написать сколь угодно большую программу, разбив ее на разумное число процедур. Неправы потому, что ваша способность манипулировать данными в памяти компьютера еще очень ограничена. А без нее вам не поддадутся «умные» задачи. Например, не познакомившись с так называемыми массивами, вы не сможете запрограммировать игру в крестики-нолики или решить задачу о выходе из лабиринта; не освоив работу со строками, символами и файлами, вы не сможете решить задачу о мало-мальски серьезной секретной шифровке и расшифровке сообщений. Эта часть посвящена тому, чтобы · Расширить ваши знания о возможностях Паскаля · Сделать ваши знания о Паскале строгими, иначе вы не сможете исправлять в программах грамматические ошибки, а значит программы у вас работать не будут.
Начнем с наведения порядка в наших знаниях о Паскале.
Не нашли, что искали? Воспользуйтесь поиском:
|