Глава 12. Работа с мышью и клавиатурой 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Глава 12. Работа с мышью и клавиатурой



До сих пор в режиме работы проекта мы пользовались мышкой только для того, чтобы примитивно нажимать на кнопки, а клавиатурой - только для ввода текста в текстовые поля. Однако, Visual Basic позволяет мышью и клавиатурой делать все те вещи, которые мы делаем ими в любых графических и текстовых редакторах, играх и других приложениях Windows. В том числе, мы можем с их помощью управлять поведением и движением объектов на форме.

Работа с мышью

Создадим программу на определение точности руки и глаза: При нажатии кнопки возникает в случайном месте экрана и тут же исчезает маленькая окружность. Вы должны поточнее щелкнуть мышкой там, где она была. После щелчка компьютер сообщает вам, на каком расстоянии от центра окружности было острие мышиного курсора во время щелчка.

Для создания программы нам необходимо поближе познакомиться с событиями, возникающими при работе с мышью. Заглянем в "универсальный справочник" - Object Browser. Поскольку щелкать мышью мы будем над формой, то в левой части Object Browser выберем объект Form. В правой части отыщем события, связанные с мышью. Нас пока интересует пять событий: Click (щелчок), DblClick (двойной щелчок), MouseDown (нажали клавишу мыши), MouseUp (отпустили клавишу мыши), MouseMove (сдвинули мышь).

События Click и DblClick нам не подойдут, так как они ничего не говорят о координатах мыши во время щелчка. А вот MouseDown подойдет, так как координаты сообщает. А при щелчке события MouseDown и MouseUp обязательно наступают, так как любой щелчок это не что иное, как нажатие и отпускание.

События MouseDown и MouseUp

Зайдем в окно кода и выберем для объекта Form событие MouseDown (как это делать, я вас учил в 3.6). В окне кода появится следующая заготовка:

Private Sub Form_MouseDown (Button As Integer, Shift As Integer, X As Single, Y As Single)

 

End Sub

4 параметра в скобках - это 4 вещи, которые компьютер сообщает процедуре в момент события:

· Button - какая из трех кнопок мыши была нажата

· Shift - были ли при этом в нажатом состоянии служебные клавиши на клавиатуре и какие именно

· X, Y - координаты острия курсора мыши во время нажатия

Для того, чтобы понять и проверить смысл этих параметров, прочтите (чтобы понять) и запустите (чтобы проверить) такую программу:

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

 

'Определяем, какая клавиша мыши нажата:

Select Case Button

Case 1: Debug.Print "Нажата левая клавиша"

Case 2: Debug.Print "Нажата правая клавиша"

Case 4: Debug.Print "Нажата средняя клавиша"

End Select

 

'Определяем, какие из трех клавиш клавиатуры (Shift, Ctrl, Alt) были при этом в нажатом состоянии:

Select Case Shift

Case 0: Debug.Print "Не нажата ни одна клавиша Shift, Ctrl, Alt"

Case 1: Debug.Print "Нажата клавиша Shift"

Case 2: Debug.Print "Нажата клавиша Ctrl"

Case 3: Debug.Print "Нажаты клавиши Shift, Ctrl"

Case 4: Debug.Print "Нажата клавиша Alt"

Case 5: Debug.Print "Нажаты клавиши Shift, Alt"

Case 6: Debug.Print "Нажаты клавиши Ctrl, Alt"

Case 7: Debug.Print "Нажаты клавиши Shift, Ctrl, Alt"

End Select

 

'Определяем координаты острия курсора мыши во время нажатия:

Debug.Print "X="; X, "Y="; Y

 

End Sub

В численном значении Shift есть система. Посмотрите в процедуре, чему "равны" клавиши Shift, Ctrl, Alt по одиночке. 1, 2 и 4. Так вот, их совместное нажатие "равно" их сумме. Убедитесь.[†]

Поместите на форму несколько разных объектов. Обратите внимание, что при щелчке по ним процедура не срабатывает. Это естественно, у каждого объекта есть свое событие MouseDown.

 

Событие MouseUp работает аналогично.

Пример программы

Вот программа для поставленной выше задачи на точность руки и глаза:

Dim X_кружка As Integer

Dim Y_кружка As Integer

Dim Расстояние_до_кружка As Double

 

Private Sub Form_Load()

Randomize

End Sub

 

'Процедура для создания мелькнувшего кружка:

Private Sub Command1_Click()

X_кружка = 4000 * Rnd 'Определяем координаты кружка (центра кружка)

Y_кружка = 4000 * Rnd

ForeColor = vbBlack 'Чертим кружок черным цветом

Circle (X_кружка, Y_кружка), 50

For i = 1 To 3000000: Next 'Пауза, чтобы мы успели заметить кружок

ForeColor = BackColor 'Стираем кружок цветом фона

Circle (X_кружка, Y_кружка), 50

End Sub

 

'Процедура для определения расстояния от щелчка до кружка:

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

Расстояние_до_кружка = Sqr((X - X_кружка) ^ 2 + (Y - Y_кружка) ^ 2)

MsgBox ("Промах на " & Int(Расстояние_до_кружка) & " твип.")

End Sub

Пояснение того, как вычислялось расстояние (для тех, кто знает теорему Пифагора): Мысленно соедините отрезком прямой центр окружности и точку щелчка. Это будет гиппотенуза прямоугольного треугольника, катеты которого проведите вертикальной и горизонтальной линией. Нетрудно заметить, что горизонтальный катет равен X - X_кружка, а вертикальный равен Y - Y_кружка (знак я не учитываю). Гиппотенуза же равна нужному нам расстоянию. Теорема Пифагора гласит, что квадрат гиппотенузы равен сумме квадратов катетов. Отсюда, гиппотенуза равна корню квадратному из суммы квадратов катетов (каковая формула и записана в программе).

Событие MouseMove

Это событие возникает, как только мы сдвигаем мышку с места, а во время движения мыши оно возникает постоянно и многократно. Оно похоже на событие MouseDown, но есть и отличия. Запустите и проверьте эту поясняющую программу:

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

'Определяем, какие клавиши мыши удерживаются нажатыми во время движения:

Select Case Button

Case 0: Debug.Print "Не нажата ни одна клавиша мыши"

Case 1: Debug.Print "Нажата левая клавиша"

Case 2: Debug.Print "Нажата правая клавиша"

Case 3: Debug.Print "Нажаты левая, правая клавиши"

Case 4: Debug.Print "Нажата средняя клавиша"

Case 5: Debug.Print "Нажаты левая, средняя клавиши"

Case 6: Debug.Print "Нажаты правая, средняя клавиши"

Case 7: Debug.Print "Нажаты левая, правая, средняя клавиши"

End Select

 

'Определяем, какие из трех клавиш клавиатуры (Shift, Ctrl, Alt) удерживаются нажатыми во время движения:

Select Case Shift

Case 0: Debug.Print "Не нажата ни одна клавиша Shift, Ctrl, Alt"

Case 1: Debug.Print "Нажата клавиша Shift"

Case 2: Debug.Print "Нажата клавиша Ctrl"

Case 3: Debug.Print "Нажаты клавиши Shift, Ctrl"

Case 4: Debug.Print "Нажата клавиша Alt"

Case 5: Debug.Print "Нажаты клавиши Shift, Alt"

Case 6: Debug.Print "Нажаты клавиши Ctrl, Alt"

Case 7: Debug.Print "Нажаты клавиши Shift, Ctrl, Alt"

End Select

 

'Определяем, координаты острия курсора мыши во время движения:

Debug.Print "X="; X, "Y="; Y

End Sub

Если событие MouseDown сообщает о нажатии какой-то одной клавиши мыши, то MouseMove сообщает о любой комбинации мышиных клавиш. Если событие MouseDown сообщает о событии нажатия какой-то клавиши, то MouseMove сообщает о состоянии мышиных клавиш (удерживаются нажатыми или нет).

Мышь рисует

Вот программа, превращающая мышку в карандаш:

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

PSet (X, Y)

End Sub

Запустите ее и медленно ведите мышкой по форме. За мышкой остается нарисованный след.

Задание 110: Сделайте так, чтобы мышь рисовала только при нажатой левой клавише, что более привычно для всех, кто работал в графических редакторах.

Задание 111: Сделайте так, чтобы при щелчке по правой клавише толщина линии возрастала на 1.

Работа с клавиатурой

Поставим задачу сделать игру, где наш миниатюрный гоночный автомобиль будет под управлением клавиш клавиатуры нестись от старта до финиша. Для этого вам нужно познакомиться с событиями, связанными с клавиатурой. Их три: KeyDown (клавиша нажата), KeyUp (клавиша отпущена) и KeyPress. (по клавише щелкнули). Нас пока интересуют только первые два.

События KeyDown и KeyUp

Создайте проект из одной формы, без элементов управления. Зайдите в окно кода и выберите для объекта Form событие KeyDown. В появившуюся заготовку процедуры запишите сдедующий код:

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)

 

'Определяем, какая клавиша клавиатуры была нажата:

Select Case KeyCode

Case vbKeyUp: Debug.Print "Нажата стрелка вверх"

Case vbKeyDown: Debug.Print "Нажата стрелка вниз"

Case vbKeyLeft: Debug.Print "Нажата стрелка налево"

Case vbKeyRight: Debug.Print "Нажата стрелка направо"

Case vbKeyW: Debug.Print "Нажата клавиша W"

Case vbKey7: Debug.Print "Нажата клавиша 7"

Case vbKeySpace: Debug.Print "Нажата клавиша пробела"

Case vbKeyDelete: Debug.Print "Нажата клавиша Delete"

Case vbKeyF4: Debug.Print "Нажата клавиша F4"

Case vbKeyAdd: Debug.Print "Нажата клавиша +"

Case vbKeyEscape: Debug.Print "Нажата клавиша Esc"

End Select

 

'Определяем, какие из трех клавиш клавиатуры (Shift, Ctrl, Alt) при этом были в нажатом состоянии:

Select Case Shift

Case 0: Debug.Print "Не нажата ни одна клавиша Shift, Ctrl, Alt"

Case 1: Debug.Print "Нажата клавиша Shift"

Case 2: Debug.Print "Нажата клавиша Ctrl"

Case 3: Debug.Print "Нажаты клавиши Shift, Ctrl"

Case 4: Debug.Print "Нажата клавиша Alt"

Case 5: Debug.Print "Нажаты клавиши Shift, Alt"

Case 6: Debug.Print "Нажаты клавиши Ctrl, Alt"

Case 7: Debug.Print "Нажаты клавиши Shift, Ctrl, Alt"

End Select

End Sub

Два параметра в скобках заголовка процедуры - это две вещи, которые компьютер сообщает процедуре в момент события:

· KeyCode - код нажатой клавиши. На клавиатуре - сто с лишним клавиш. На каждой - по две буквы или один, два, три других символа. Компьютер различает клавиши независимо от того, какие значки на них нанесены. Так мать различает сыновей независимо от того, что написано у них на майках. У каждого сына есть имя, у каждой клавиши есть код (KeyCode). Итак, попросту говоря, компьютер сообщает процедуре, какая клавиша была нажата. Полный список кодов находится в Object Browser (класс KeyCodeConstants).

· Shift - были ли при этом в нажатом состоянии клавиши клавиатуры (Shift, Ctrl, Alt) и какие именно. В численном значении Shift та же система, что и в событии MouseDown. Совместное нажатие клавиш Shift, Ctrl, Alt "равно" сумме их нажатий по-одиночке (1, 2 и 4).

Запустите проект и проверьте, как работает программа. Обратите внимание, что при удержании клавиш в нажатом состоянии событие генерируется (создается компьютером) несколько раз в секунду. Положение несколько меняется, когда при нажатой Shift, Ctrl или Alt щелкается обычная клавиша. Впрочем, пока это неважно.

 

События, связанные с клавиатурой, имеются у многих объектов. Поместим на форму, к примеру, пару кнопок и текстовое поле. Предположим, процедуру Private Sub Form_KeyDown мы стерли, а написали три процедуры:

Private Sub Command1_KeyDown…

Private Sub Command2_KeyDown…

Private Sub Text1_KeyDown…

Запустим программу и щелкнем по какой-нибудь клавише. Какая из трех процедур сработает? Та, чей объект находится в фокусе. Для нашей игры это неудобно: если мы для программирования реакции автомобиля на нажатия клавиш выберем, например, процедуру Private Sub Command2_KeyDown, то во время гонки мы не сможем щелкать по другим кнопкам, кроме Command2, так как иначе Command2 выйдет из фокуса и автомобиль перестанет реагировать на клавиши. Не надо было стирать процедуру Private Sub Form_KeyDown, восстановим ее. Но это не помогает. По простой причине - один какой-нибудь объект на форме всегда находится в фокусе, так что до процедуры Private Sub Form_KeyDown дело никак не доходит. Против этого в Visual Basic есть специальный прием - свойство формы KeyPreview устанавливается в True. Это означает приказ компьютеру почти каждое нажатие на клавиши считать событием формы, а не другого объекта. Вдобавок к этому советую поместить в самый конец процедуры Private Sub Form_KeyDown оператор KeyCode=0. Тоже хорошо помогает (без комментариев).

Что значит "почти каждое нажатие"? Есть исключения - клавиши Enter, Esc, Tab в некоторых случаях.

 

Событие KeyUp работает аналогично.

 

Задание 112 “Светофор”: Нарисуйте светофор: прямоугольник и три круга. При нажатии на клавиатуре клавиши R светофор должен загораться красным светом, G - зеленым, Y - желтым. Здесь таймер не нужен.

Задание 113 “Зенитка”: Вверху справа налево медленно движется вражеский самолет. В подходящий момент вы нажатием любой клавиши запускаете снизу вверх зенитный снаряд. Здесь нужны два таймера.

 

12.3. Проект - Гонки (игра)

Мы с вами разобрали уже два больших проекта: "Калькулятор" и "Будильник". Но "Калькулятор" можно не считать, на нем мы просто знакомились с Visual Basic. Выходит, "Будильник" - единственный большой проект, который написан более-менее правильно. И этого, конечно, мало. Вам нужен опыт создания проектов, в частности проектов с движением объектов по форме, да к тому же под управлением мыши и клавиатуры. Поэтому я решил разобрать с вами еще один проект (строк на 180). На этот раз это будет игра.

Постановка задачи

Вот внешний вид игры (о красотах я не заботился, красоты вы всегда сможете добавить по вкусу):

 

 

Процесс игры таков. Вы нажимаете на кнопку Начинаем сначала. На форме рисуется квадратное поле для гонки со случайно расположенными газонами. Нажатием на клавишу пробела вы даете газ и машина со старта набирает скорость. Ваша цель - любым путем побыстрее добраться до финиша. На белом асфальте вы можете газовать до любой скорости. Если же ненароком попадете на газон, то на газоне ваша скорость будет очень мала (я выбрал 15). Поэтому имеет смысл по газонам ездить пореже. Направление движения может быть только горизонтальное и вертикальное, наискосок машина не движется. Выбор направления (руль) - это клавиши со стрелками. Тормоз - клавиша B (английская). Тормозить надо по той причине, что на слишком большой скорости руль отказывает (это не потому, что проект плохой, а потому что я так придумал). В ограждение врезаться нельзя - катастрофа. Когда приедете на финиш, посмотрите на счетчик времени - это ваш результат. Снова нажимаете на кнопку Начинаем сначала. Теперь расположение газонов будет другим. Сажайте за клавиатуру приятеля и смотрите, не покажет ли он время лучшее, чем ваше. Можете посмотреть, кто из вас покажет лучшее время из 10 заездов. И тому подобное.

Не удивляйтесь, что скорость на спидометре не равна пути, деленному на время. Ведь это мгновенная (то есть настоящая скорость в данное мгновение), а не средняя скорость автомобиля (см. Физика, 9 класс).

 

Все эти правила я старался сделать как можно проще, чтобы не усложнять проект. Разобравшись в проекте, вы всегда сможете дописать процедуры, делающие процесс гонки более для вас привлекательным.

Для проекта я выбрал вариант игры с одним автомобилем. Я вам намекну, как модифицировать проект, чтобы получилась игра с двумя автомобилями. Однако помните, что для грамотного создания игры с несколькими автомобилями вам нужно будет подняться на новый уровень программирования - научиться создавать собственные классы объектов.

Делим проект на части

Начнем создавать проект. Прежде всего, как положено, разделим его по возможности на части. Мы уже делили проект на части, когда создавали будильник. Сейчас вам очень полезно перечитать тот материал. И проглядеть ту программу.

Сразу же бросается в глаза, что наша задача распадается на две последовательные почти независимые части:

1. Сначала рисование поля и всех его элементов, выкатывание машины на старт, обнуление скорости, времени и пути - в общем, все то, что должно быть сделано после нажатия на кнопку Начинаем сначала, но до начала движения. Этим будет заниматься одна группа процедур.

2. Затем управление машиной и событиями во время гонки. Этим будет заниматься другая группа процедур. Мы полностью задействуем идею использования таймера, так, как я ее изложил в 11.3. На каждом импульсе таймера автомобиль должен будет проделывать весь цикл своего функционирования.

Первая часть

Поскольку в первой части нет движения, то, в общем, все равно, что именно рисовать сначала, что потом, но и здесь следование житейской логике дает некоторую экономию кода и смысла:

 

1. Сначала нарисуем большой квадрат поля (ограждение), затем

2. Рисуем старт

3. Рисуем финиш

4. Рисуем газоны

5. Ставим машину на старт и обнуляем показания приборов

 

В общем, идеология первой части ясна. Пора разбираться в полном тексте программы, приведенном ниже. Здесь вам бы очень помогло, если бы программа уже находилась в вашем компьютере. Вам было бы легко и приятно в такт моим словам нажимать клавишу F8.

Начнем сверху. Разберитесь в объявленных переменных, не вникая пока в те, что относятся к движению.

 

Разобрались? Теперь идем дальше. Первая процедура, которая выполняется в проекте, это, конечно, Form_Load. В каждом проекте в ней удобно программировать все действия и вычисления, которые нужно выполнить один раз за все время работы проекта, в самом начале. В нашем случае это в основном задание и вычисление значений переменных величин - размеров элементов поля игры. Кое-что здесь нуждается в пояснении:

WindowState - это не переменная, а свойство формы. Вы помните, что свойства формы, в отличие от свойств других объектов, можно писать, не указывая хозяина (Form1.WindowState).

ScaleLeft и ScaleTop. Это тоже свойства формы. Мы их устанавливаем, если нам не нравится, что начало системы координат находится в левом верхнем углу формы.ScaleLeft занимается смещением начала координат по горизонтали, ScaleTop - по вертикали. Примеры:

Оператор Смысл
ScaleLeft = 1000 Начало системы координат смещается налево на 1000 твипов
ScaleLeft = -500 Начало системы координат смещается направо на 500 твипов
ScaleTop = 800 Начало системы координат смещается вверх на 800 твипов
ScaleTop = -500 Начало системы координат смещается вниз на 500 твипов

В нашем проекте мы, конечно, могли бы обойтись и без этого, но многие операторы рисования у нас запишутся гораздо проще, если начало координат находится в левом верхнем углу поля игры, а не формы.

 

Все числа, которые встречаются в программе, я подобрал на опыте, исходя из размеров экрана и быстродействия своего компьютера. Вы можете эти числа изменить и посмотреть, что получится.

 

После выполнения процедуры Form_Load вы видите распахнутую на весь экран пустую форму с четырьмя текстовыми полями, тремя метками и кнопкой, ждущей нажатия. После щелчка по этой кнопке должна в полном объеме выполнится первая часть нашего проекта, так, как она описана выше. Заглянем в процедуру Кнопка_начинай_сначала_Click.

Здесь процедуры рисования ограждения, старта и финиша ясны без пояснений. Поясню процедуру Рисуем_газоны. Вы видите, что некоторые переменные я объявил внутри процедуры. Так поступают, когда знают, что значения этих переменных нигде, кроме как в этой процедуре, не нужны. Значения чисел и вид формул для X_газона и Y_газона я выбрал так, чтобы газоны не накладывались ни на ограждения, ни на старт с финишем.

Процедуру Ставим_машину_на_старт я поясню во второй части, так как она больше относится к ней.

Оператор Спидометр.SetFocus, появился вот почему. При запуске проекта фокус автоматически устанавливается на кнопку "Начинаем сначала". Кнопка в фокусе перехватывает нажатия на стрелки, в результате чего первое нажатие во время гонки на клавишу со стрелкой приводило не к выбору направления машиной, а к перескакиванию фокуса с кнопки на другой объект формы. Оператор Спидометр.SetFocus просто увел фокус с кнопки.

Вот, в общем, и все, что касается первой части. После ее выполнения мы увидим все, что нужно для старта, процедура Ставим_машину_на_старт поставит машину на старт, мотор будет заведен, вам останется только коснуться руля или педалей, чтобы началась часть вторая. Перейдем к ней.

Вторая часть

Если с первой частью все просто, то про вторую стоит поговорить подробнее. Фактически, нам нужно будет создавать автомобиль, как раньше мы создавали будильник. Не имея программистского опыта, мы попытаемся использовать житейский опыт касательно того, как автомобиль устроен. Причем применительно к задачам проекта. Так, цвет сиденья нам не очень важен в этом смысле. А важно нам управлять скоростью и направлением движения (этим в обычном автомобиле занимаются руль и педали газа и тормоза). А еще важно, чтобы автомобиль чувствовал, "куда он въехал" (газон, ограждение, финиш) и вел себя соответственно.

Итак, на каждом импульсе таймера автомобиль должен будет проделать весь цикл своего функционирования:

· Определить, где он находится (асфальт, газон, ограждение, финиш) и действовать соответственно.

· Изменить или не изменить скорость в соответствии с приказами клавиатуры.

· Изменить или не изменить в соответствии с приказами клавиатуры направление движения и сделать очередной шаг в нужном направлении.

· Изменить нужным образом показания приборов на пульте управления.

 

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

Создайте отдельный пустой проект и поместите на форму объект Image. Дайте ему имя Image_авто. Чтобы он был виден в режиме работы, придайте значение его свойству BorderStyle. Он будет слушаться клавиш со стрелками при помощи такой программы:

Dim x As Integer 'Горизонтальная координата

Dim y As Integer 'Вертикальная координата

 

Private Sub Form_Load()

x = Image_авто.Left

y = Image_авто.Top

End Sub

 

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)

Select Case KeyCode

Case vbKeyRight: x = x + 100

Case vbKeyLeft: x = x - 100

Case vbKeyDown: y = y + 100

Case vbKeyUp: y = y - 100

End Select

Image_авто.Left = x

Image_авто.Top = y

End Sub

Эта программа только иллюстрирует идею, но для проекта она не годится. Во-первых, потому, что для непрерывного движения объекта необходимо клавишу держать все время нажатой, что создает трудности при обработке нажатия в это время других клавиш. Во-вторых, сложные проекты с таймерами всегда должны помнить, куда они едут, а для этого нужна переменная величина. Есть и другие причины.

 

Создадим переменную, назовем ее Руль и сделаем перечислимой (см. объявления в тексте программы). Она выполнит предназначенную ей роль - помнить, куда мы едем. Создадим также перечислимую переменную Педаль (см. там же). Она будет хранить информацию о том, нажали ли мы педаль газа, тормоза или вообще на педаль не нажимали. Теперь разберемся с процедурой Form_KeyDown. Вы видите, что переменная Руль будет чувстврвать нажатие на стрелки только тогда, когда скорость движения автомобиля меньше пороговой. О том, что на педали пока не нажимали, сообщает компьютеру процедура Ставим_машину_на_старт. Именно поэтому после нажатия кнопки Начинаем сначала машина сама не срывается со старта, несмотря на работающий мотор. Оператор Клавиатура_сработала = True нужен для того, чтобы запустить счетчик времени

If Клавиатура_сработала Then Время = Время + 1

в процедуре Отображаем_информацию).

 

Итак, при нажатии нужных клавиш меняются значения нужных переменных. Но дела никакого не происходит. О деле чуть позже, а сейчас давайте подумаем, как сделать так, чтобы автомобиль глядел в ту же сторону, куда он едет. Есть всего 4 направления движения, значит и 4 ориентации автомобиля. Нужно всего лишь взять 4 картинки автомобиля с разной ориентацией и вовремя их менять. Откуда взять картинки? Нарисовать одну в графическом редакторе, а там ее легко поворачивать в нужном направлении и сохраняться. Сохранитесь в 4 файлах. Если вы не хотите рисовать, возьмите из папки с иконками Visual Basic, о которой я уже говорил, 4 иконки в виде стрелок, направленных в разные стороны. Вот вам и автомобиль.

Затем поместите на форму 4 объекта Image, дайте им имена Image_вверх, Image_вниз, Image_налево, Image_направо и придайте им соответствующие картинки. Чтобы эти объекты не мозолили глаза, сделайте их невидимыми. Вся механика будет заключаться в том, что когда вы нажимаете на клавиатуре стрелку вверх, должен выполняться оператор

Image_авто.Picture = Image_вверх.Picture

а когда вниз - то оператор

Image_авто.Picture = Image_вниз.Picture

И так далее.

Теперь можно разбираться в тексте процедур.

 

Мы с вами чуть выше определили четыре вещи, которые должен проделать автомобиль на каждом импульсе таймера. Посмотрите в процедуру Timer1_Timer и найдите там обращения к соответствующим процедурам. Из них нас сейчас интересует процедура Выбираем_куда_ехать_и_делаем_шаг. Именно она задает направление движения. Загляните в нее. Ее дело - чувствовать одно из 4 значений переменной Руль и запускать соответствующую из 4 процедур. Каждая из этих процедур делает две вещи: поворачивает машину в нужном направлении и меняет в этом направлении ее координату. Само же изображение автомобиля прыгнет на указанную координату чуть позже - во время выполнения процедуры Показываем_автомобиль.

Теперь посмотрим, как регулируется скорость. Прежде всего, при нажатии кнопки Начинаем сначала скорость устанавливается в ноль процедурой Ставим_машину_на_старт. В процессе гонки скорость регулируется процедурой Изменяем_скорость. Действие ее полностью определяется значением переменной Педаль. Если это газ, то на данном такте таймера скорость возрастет на 5. Если тормоз - упадет на 10 (потому что тормоз обычно действует сильнее газа). Как видите, значение переменной Педаль в конце процедуры принудительно приравнивается никакой педали. Это значит, что на следующем такте таймера скорость не изменится. Чтобы она изменилась, нам нужно еще раз нажать на клавишу газа или тормоза. Обычно поступают по-другому - просто удерживают клавишу нажатой, при этом событие KeyDown возникает несколько раз в секунду и скорость меняется достаточно быстро. Это соответствует механике реального автомобиля - чтобы набирать скорость, нужно непрерывно держать нажатой педаль газа, а чтобы тормозить - тормоза.

Процедура Отображаем_информацию заботится о том, чтобы на каждом такте в текстовых полях Спидометр, txtПуть и txtВремя были правильные цифры. В нашем проекте переменная Скорость только называется скоростью, а на самом деле это расстояние, на которое перемещается машина на одном такте таймера. Поэтому для вычисления суммарного пути, пройденного автомобилем, вполне уместен оператор Путь = Путь + Скорость. Переменная Время тоже является не временем в секундах, а количеством тактов таймера, прошедших со старта.

Теперь о процедуре Определяем_где_мы. Ее задача - задать реакцию автомобиля на три ситуации: попадание на газон, на финиш и на полосу ограждения. Метод, при помощи которого автомобиль определяет, где он, самый простой - Point. Его мы разобрали в 9.7. Поскольку газон я рисовал сиреневым (vbMagenta), финиш - синим, а ограждение - красным, то метод Point выдает на них разные результаты. Оператор Select эти результаты анализирует и задает реакцию автомобиля. Как видите, на сиреневое компьютер реагирует только установкой скорости = 15, на два других цвета он выдает сообщение в текстовое поле Сообщение, выключает мотор и устанавливает переменную Приехали, чтобы компьютер ее тут же проанализировал (см. процедуру таймера) и вышел из процедуры таймера, так как, когда приехали, делать, естественно, больше нечего.

В процедуре Ставим_машину_на_старт вы вполне сможете разобраться по комментариям самостоятельно.

 

Ну вот, кажется, и все пояснения.

Текст программы

Dim x As Integer 'Горизонтальная координата автомобиля

Dim y As Integer 'Вертикальная координата автомобиля

Dim X_старта As Integer 'Координаты старта и финиша

Dim Y_старта As Integer

Dim X_финиша As Integer

Dim Y_финиша As Integer

Dim Размер_старта As Integer 'Старт - квадрат, это сторона квадрата

Dim Размер_финиша As Integer 'Финиш - квадрат, это сторона квадрата

Dim Число_газонов As Integer 'Каждый газон - это случайный прямоугольник в случайном месте

Dim Максимальный_размер_газона As Integer

Dim Размер_поля As Integer 'Поле - квадрат, это сторона квадрата

Dim Отступ_поля_от_края_формы As Integer 'Имеется в виду отступ слева и сверху

Dim Путь As Long 'Путь автомобиля с момента старта (в твипах)

Dim Время As Integer 'Время измеряется количеством импульсов таймера с момента щелчка по кнопке начала

Dim Скорость As Integer 'Скорость - это не скорость, а расстояние. Она численно равна шагу автомобиля на каждом такте таймера

Dim Пороговая_скорость As Integer 'Скорость, выше которой не работает руль

Dim Цвет_под_автомобилем As Long 'Нужен чтобы знать, где находится автомобиль - на газоне, на финише, врезался в ограждение

 

Private Enum типРуль

вверх

влево

вниз

вправо

End Enum

Dim Руль As типРуль

 

Private Enum типПедаль

тормоз

газ

ни_та_ни_эта

End Enum

Dim Педаль As типПедаль

 

Dim Приехали As Boolean 'Приехали = True, когда приехали на финиш или врезались в ограждение

Dim Клавиатура_сработала As Boolean 'Чтобы секундомер судьи запускался автоматически, когда мы стартуем, не раньше

 

Private Sub Form_Load()

Timer1.Enabled = False 'Нечего мотору зря работать до начала гонки

Randomize 'Нам придется рисовать случайные газоны

WindowState = 2 'Окно игры распахнуто на весь экран

BackColor = vbWhite 'Асфальт белый

'Задаем и вычисляем размеры элементов поля (все числа подбирайте еа опыте):

Отступ_поля_от_края_формы = 500

ScaleLeft = -Отступ_поля_от_края_формы 'Смещаем для удобства начало системы координат в левый верхний угол поля

ScaleTop = -Отступ_поля_от_края_формы

Размер_поля = Screen.Height - 2 * Отступ_поля_от_края_формы 'Отступ от края экрана должен быть и снизу

Размер_старта = 600

Размер_финиша = 600

X_старта = Размер_поля - Размер_старта 'Чтобы старт находился у правой кромки поля

X_финиша = 0 'Чтобы финиш находился у левой кромки поля

Y_старта = Размер_поля / 2 'Чтобы старт по высоте находился посредине поля

Y_финиша = Размер_поля / 2 'Чтобы финиш по высоте находился посредине поля

Число_газонов = 30

Максимальный_размер_газона = Размер_поля / 6

Пороговая_скорость = 200

End Sub

 

Private Sub Кнопка_начинай_сначала_Click()

Клавиатура_сработала = False 'Мы еще не стартовали

Timer1.Enabled = False 'Нечего мотору зря работать до начала гонки

'Рисуем поле игры со всеми элементами:

Cls 'Перед тем, как заново рисовать, надо все стереть

Рисуем_границы_поля

Рисуем_старт

Рисуем_финиш

Рисуем_газоны

'Необходимые начальные установки:

Ставим_машину_на_старт

Приехали = False

Timer1.Enabled = True 'Заводим мотор, скоро начало гонки

Спидометр.SetFocus 'Это чтобы фокус ушел с кнопки, а то первое нажатие на стрелку действует не так, как нам надо

End Sub

 

Private Sub Рисуем_границы_поля()

DrawWidth = 10

Line (0, 0)-(Размер_поля, Размер_поля), vbRed, B

DrawWidth = 1 'Возвращаем нормальную толщину карандаша

End Sub

 

Private Sub Рисуем_старт()

Line (X_старта, Y_старта)-(X_старта + Размер_старта, Y_старта + Размер_старта), vbGreen, BF

End Sub

 

Private Sub Рисуем_финиш()

Line (X_финиша, Y_финиша)-(X_финиша + Размер_финиша, Y_финиша + Размер_финиша), vbBlue, BF

End Sub

 

Private Sub Рисуем_газоны() 'Каждый газон - это прямоугольник случайного размера в случайном месте

Dim i As Integer

Dim X_газона As Integer 'Горизонтальная координата верхнего левого угла газона

Dim Y_газона As Integer 'Вертикальная координата верхнего левого угла газона

For i = 1 To Число_газонов 'Числа в формулах подобраны на опыте:

X_газона = 2 * Размер_финиша + Размер_поля * Rnd * 2 / 3

Y_газона = Размер_финиша + (Размер_поля - Размер_финиша - Максимальный_размер_газона) * Rnd

Line (X_газона, Y_газона)-(X_газона + Максимальный_размер_газона * Rnd, Y_газона + Максимальный_размер_газона * Rnd), vbMagenta, BF

Next

End Sub

 

Private Sub Ставим_машину_на_старт()

x = X_старта: y = Y_старта 'Координаты машины приравниваются координатам точки старта

Скорость = 0: Путь = 0: Время = 0

Руль = влево 'Это чтобы машина знала, куда ехать, когда стартуем нажатием на газ

Едем_влево 'Отсюда нам нужна только ориентация машины налево, а шага не будет, так как скорость=0

Педаль = ни_та_ни_эта 'Газовать пока нельзя, а тормозить бессмысленно

Image_авто.Visible = True 'До первого старта я сделал машину невидимой, теперь пришла пора ей появиться

Показываем_автомобиль

End Sub

 

Private Sub Timer1_Timer()

Определяем_где_мы

If Приехали Then Exit Sub

Изменяем_скорость

Выбираем_куда_ехать_и_делаем_шаг

Показываем_автомобиль

Отображаем_информацию

End Sub

 

Private Sub Определяем_где_мы()

Цвет_под_автомобилем = Point(x, y)

Select Case Цвет_под_автомобилем

Case vbMagenta: Скорость = 15 'На газоне скорость мала

Case vbBlue: Сообщение.Text = "Финиш!": Timer1.Enabled = False: Приехали = True

Case vbRed: Сообщение.Text = "Врезались в ограждение!": Timer1.Enabled = False: Приехали = True

End Select

End Sub

 

Private Sub Изменяем_скорость()

Select Case Педаль

Case газ

Скорость = Скорость + 5

Case тормоз

If Скорость > 0 Then Скорость = Скорость - 10 'потому, что тормоз быстрее газа

If Скорость < 0 Then Скорость = 0 'В результате быстрого торможения скорость может стать отрицательной, что и предотвращается

End Select

Педаль = ни_та_ни_эта 'Это чтобы во время набора скорости и торможения приходилось без перерыва жать на педаль

End Sub

 

Private Sub Выбираем_куда_ехать_и_делаем_шаг()

Select Case Руль

Case вверх: Едем_вверх

Case вниз: Едем_вниз

Case влево: Едем_влево

Case вправо: Едем_вправо

End Select

End Sub

 

Private Sub Едем_вверх()

Image_авто.Picture = Image_вверх.Picture

y = y - Скорость

End Sub

 

Private Sub Едем_вниз()

Image_авто.Picture = Image_вниз.Picture

y = y + Скорость

End Sub

 

Private Sub Едем_влево()

Image_авто.Picture = Image_налево.Picture

x = x - Скорость

End Sub

 

Private Sub Едем_вправо()

Image_авто.Picture = Image_направо.Picture

x = x + Скорость

End Sub

 

Private Sub Показываем_автомобиль()

Image_авто.Left = x

Image_авто.Top = y

End Sub

 

Private Sub Отображаем_информацию()

Спидометр.Text = Скорость

Путь = Путь + Скорость 'Нужно помнить, что именем Скорость назван шаг автомобиля

txtПуть.Text = Путь

If Клавиатура_сработала Then Время = Время + 1 'Счетчик времени запускается только тогда, когда мы стартуем

txtВремя.Text = Время

End Sub

 

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)

Клавиатура_сработала = True

Select Case KeyCode

Case vbKeyUp: If Скорость < Пороговая_скорость Then Руль = вверх 'Поворот на большой скорости запрещен

Case vbKeyLeft: If Скорость < Пороговая_скорость Then Руль = влево

Case vbKeyDown: If Скорость < Пороговая_скорость Then Руль = вниз

Case vbKeyRight: If Скорость < Пороговая_скорость Then Руль = вправо

Case vbKeySpace: Педаль = газ

Case vbKeyB: Педаль = тормоз

End Select

KeyCode = 0 'Это чтобы объекты на форме не реагировали на клавиатуру

End Sub

Недоработки проекта

Замеченные мной недоработки вызваны в основном нежеланием увеличивать размер кода и заключаются в следующем:

 

· Координатами (х,у) машины считается ее левый верхний угол, а не центр. Причина этого - слишком простые операторы в процедуре Показываем_автомобиль. Это приводит к некоторой ассиметрии поведения машины при переезде с цвета на цвет. Немного повозившись, вы сможете исправить положение.

· При большой скорости машина делает огромные шаги от одного такта таймера к другому. А ведь цвет под собой она ощущает не непрерывно, а только на тактах таймера. Значит она может перепрыгнуть через тоненький газон, не заметив его. То же относится к финишу и особенно к ограждению. Сделайте их потолще, чем у меня на картинке, или ограничьте скорость.

· Сам бог велел вам добавить в проект управление машиной с помощью мыши. Например, щелчок мышью в стороне от машины вызывает поворот машины в эту сторону, удерживание нажатой левой клавиши мыши - газ, правой - тормоз. Для этого вам нужно будет написать процедуру MouseDown, придавая в ней нужные значения переменным Руль и Педаль аналогично тому, как это делает процедура KeyDown. Вам придется поразмыслить, как правильно сравнить координаты машины и мыши, чтобы добиться нужного результата.

Гонки двух автомобилей



Поделиться:


Последнее изменение этой страницы: 2016-08-12; просмотров: 402; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.141.100.120 (0.297 с.)