Третья часть – Поведение машины, организация счетчиков и пр

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

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

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

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

Dim Шаг, Время, Путь As Integer

'Чтобы секундомер судьи запускался автоматически, когда мы стартуем:

Dim Секундомер_запущен As Boolean

Private Sub Кнопка_начинай_сначала_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _

Handles Кнопка_начинай_сначала.Click

'……………здесь расположены уже знакомые нам предыдущие строки процедуры……………

Me.KeyPreview = True 'чтобы машина слушалась руля и педалей

Секундомер_запущен = False 'Мы еще не стартовали, секундомер не пущен,

Шаг = 0 : Время = 0 : Путь = 0 'на приборах - нули

lbl_Сообщение.Text = "" 'Пока никакого сообщения нет

End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

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

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

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

Машина.Left = x : Машина.Top = y

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

End Sub

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

Dim Цвет_под_автомобилем As Color = Картинка.GetPixel(x, y)

If Цвет_под_автомобилем.ToArgb = Color.Green.ToArgb And Not Шаг < 1 _

Then Шаг = 1 'На газоне скорость мала

If Цвет_под_автомобилем.ToArgb = Color.Red.ToArgb _

Then Приехали() : lbl_Сообщение.Text = "Финиш!"

If Цвет_под_автомобилем.ToArgb = Color.LightGray.ToArgb _

Then Приехали() : lbl_Сообщение.Text = "Врезались в ограждение!"

End Sub

Sub Приехали()

Шаг = 0 'Раз приехали, надо остановиться, …

Секундомер_запущен = False 'выключить секундомер и сделать так, …

Me.KeyPreview = False 'чтобы машина НЕ слушалась руля и педалей

End Sub

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

Dim Максимальная_скорость As Short = 15 'Быстрее мотор не тянет

If Газ And Шаг < Максимальная_скорость Then Шаг = Шаг + 1

'……………здесь расположены уже знакомые нам последующие строки процедуры………

End Sub

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

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

If Секундомер_запущен Then Время = Время + 1 'Время – число импульсов таймера.

txtВремя.Text = "Время = " & Время 'Показания секундомера

lbl_Скорость.Text = "Скорость = " & Шаг 'Показания спидометра – скорость (Шаг)

Путь = Путь + Шаг 'Путь – это сумма шагов

lbl_Путь.Text = "Путь = " & Путь 'Показания спидометра - путь

End Sub

Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) _

Handles MyBase.KeyDown

Секундомер_запущен = True 'Секундомер запускается от нажатия на любую клавишу

Select Case e.KeyCode

'……………здесь расположены уже знакомые нам строки процедуры……………

End Select

End Sub

Поведение машины на газоне, на ограждении, на финише. Разместите на форме метку, в которую будут помещаться сообщения о том, что мы находимся на финише или врезались в ограждение. Назовите ее lbl_Сообщение.

Рассмотрим главную процедуру проекта – Timer1_Tick. В соответствии с двумя дополнительными делами, которые должен выполнить автомобиль, в процедуру добавились два оператора:

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

'……………

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

Поговорим о процедуре Определяем_где_мы. Ее задача – задать реакцию автомобиля на три ситуации: попадание на газон, на финиш и на ограждение. Метод, при помощи которого автомобиль определяет, где он, самый простой – GetPixel. Его мы разобрали в 12.7.4. Поскольку газон я рисовал зеленым, финиш – красным , а ограждение – светло-серым (LightGray), то метод GetPixel выдает на них разные результаты. Операторы If эти результаты анализируют и задают реакцию автомобиля. Как видите, на газон машина реагирует только установкой скорости = 1 (если она уже не была меньше 1), на два других цвета она выполняет процедуру Приехали и выдает подходящее сообщение в метку lbl_Сообщение.

Немного о грамматике. Мы не могли записать

If Цвет_под_автомобилем = Color.Green

так как VB не может устанавливать с помощью знака равенства равенство таких объектов, как цвет. Пришлось предварительно методом ToArgb преобразовывать цвета к специальному представлению ARGB, которое допускает подобное сравнение:

If Цвет_под_автомобилем.ToArgb = Color.Green.ToArgb ……

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

Me.KeyPreview = True 'чтобы машина слушалась руля и педалей

из процедуры Form1_Load в процедуру Кнопка_начинай_сначала_Click.

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

Вычислением этих трех величин и их отображением занимается процедура Отображаем_информацию. Она выполняется на каждом тике таймера. Заглянем в нее. Содержание ее очевидно. В пояснении нуждаются только моменты запуска и остановки секундомера. Когда мы нажимаем на кнопку Начинаем сначала, все три упомянутые величины устанавливаются в 0, а переменная Секундомер_запущен – в False (см. текст процедуры Кнопка_начинай_сначала_Click). Поэтому после нажатия на кнопку секундомер стоит на нуле. Как только мы нажимаем клавишу газа или какую-нибудь другую, секундомер запускается (см. текст процедуры Form1_KeyDown). Останавливается он после этого только в том случае, если машина «приехала» или мы нажали на кнопку Начинаем сначала.

Упомяну еще, что переменная Время является не временем в секундах, а количеством тактов таймера, прошедших со старта.

Максимальная скорость. При большой скорости машина делает огромные шаги от одного такта таймера к другому. А ведь цвет под собой она ощущает не непрерывно, а только на тактах таймера. Значит она может перепрыгнуть через тоненький газон, не заметив его. То же относится к финишу и к ограждению. Если шаг превысит размер финиша, то машина и его может перепрыгнуть, не заметив. Машина может перепрыгнуть через тонкое ограждение и упереться в край формы, при этом VB выдаст ошибку (кстати – почему?). Значит, нужно ограничить скорость машины. Поскольку размер финиша равен 40, а минимальная толщина ограждения у нас равна 20, то я выбрал максимальный шаг = 15. Простая механика ограничения приведена в процедуре Изменяем_скорость. А через газоны пусть перепрыгивает!

Резюме. Ну вот, кажется, и все пояснения. Организовывая переменные для хранения информации о состоянии машины и создавая много маленьких процедур с удачными длинными названиями, мы сделали программу более читабельной и открытой для усовершенствования. Попробуйте обойтись малым числом длинных процедур – и вы увидите, что программа, может быть и став покороче (а вполне может быть и нет), потеряла четкость, понятность и легкость модификации.

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

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

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

2.Координатами (х, у) машины считается ее левый верхний угол, а не центр. Это приводит к некоторой неестественной асимметрии поведения машины при поворотах и переезде с цвета на цвет. Причина этого – слишком простая строка в процедуре Timer1_Tick:

Машина.Left = x : Машина.Top = y 'Передвигаем PictureBox

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

3.Сам Бог велел добавить в проект управление машиной с помощью мыши. Например, щелчок мышью в стороне от машины или сзади вызывает поворот машины в соответствующую сторону. Щелчок спереди – газ, щелчок на правую клавишу мыши в любой точке формы – тормоз. Для этого вам нужно будет написать процедуру Form1_MouseDown, придавая в ней нужные значения переменным Руль, Газ и Тормоз аналогично тому, как это делает процедура Form1_KeyDown. Вам придется поразмыслить, как правильно сравнивать координаты машины и мыши, чтобы добиться нужного результата.

Задание 100.

Исправьте недоработки проекта.

Наши рекомендации