Мультфильм «Летающая тарелка»
Богатство графических методов и свойств объектов VB позволяет делать мультфильмы разными способами. Я выберу те, что ведут к цели проще и быстрее.
Покажите своим друзьям фильм, снятый «секретной видеокамерой»: В небе над вашим домом летит летающая тарелка.
Для этого вам понадобится ввести в компьютер фотографию, на которой были бы видны ваш дом и небо. Если такой фотографии у вас нет, возьмите любой графический файл из тех, что есть на вашем диске – для тренировки сойдет.
Затем зайдите в любой графический редактор, например, в Paint, настройте очень маленький размер листа, на котором будете рисовать, и на белом фоне нарисуйте во весь этот маленький лист летающую тарелку. Сохраните ее изображение в файл в формате BMP или другом каком-нибудь, не искажающем цвета пикселей (например, для рисунков очень хорош формат PNG, он и цвета пикселей не искажает и размеры файла сильно уменьшает). Формат JPEG не подойдет, потому что он цвета пикселей чуть-чуть, почти незаметно для глаза, но искажает. Почему нельзя искажать даже чуть-чуть, скажу чуть-чуть позже. JPEG, однако, хорош для фотографий, потому что он сильно уменьшает размеры файла.
Создайте проект с формой, таймером и PictureBox. Будем писать программу, постепенно ее улучшая.
1 версия (плохая):
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.BackgroundImage = Image.FromFile("Ландшафт.jpg")
PictureBox1.Image = Image.FromFile("Тарелка.bmp")
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
PictureBox1.Left = PictureBox1.Left + 1
End Sub
Результаты работы программы вы видите на Рис. 13.9. Тарелка действительно летит по небу слева направо. Плохо то, что летит она в обрамлении большого серого и маленького белого прямоугольников. Большой серый прямоугольник – это наш PictureBox. Займемся им во 2 версии программы, а маленьким белым – в 3 версии.
Рис. 13.9
2 версия (получше). Для избавления от серого прямоугольника сделаем 3 вещи:
Избавим PictureBox от бордюра:
PictureBox1.BorderStyle = BorderStyle.None
Чтобы избавиться от серого цвета, сделаем PictureBox «прозрачным». Для этого или в режиме проектирования установим его свойству BackColor значение Transparent (в закладке Web), или напишем в коде:
PictureBox1.BackColor = Color.Transparent
Помогло. А теперь замечаем, что движение чуть притормаживает. Происходит это от того, что PictureBox слишком велик и двигать его компьютеру в нашем случае трудно. Заставим PictureBox сжаться до размеров тарелки:
PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
Вот программа:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.BackgroundImage = Image.FromFile("Ландшафт.jpg")
PictureBox1.Image = Image.FromFile("Тарелка.bmp")
PictureBox1.BorderStyle = BorderStyle.None
PictureBox1.BackColor = Color.Transparent
PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
PictureBox1.Left = PictureBox1.Left + 1
End Sub
Результаты работы программы вы видите на Рис. 13.10.
Рис. 13.10
3 версия (нормальная). Белый прямоугольник вокруг тарелки достался нам в наследство от графического редактора. Ведь сохранилась в файле не только сама тарелка, но весь прямоугольный лист, на котором ее рисовали. А лист-то был белый.
Мы имеем возможность, работая с объектом Bitmap, объявить любой цвет прозрачным. Поэтому для избавления от белого прямоугольника сделаем 3 вещи: Образуем из нашей тарелки объект Bitmap, объявим белый цвет прозрачным и присвоим Bitmap свойству Image нашего PictureBox. Вот программа:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.BackgroundImage = Image.FromFile("Ландшафт.jpg")
Dim Тарелка As New Bitmap("Тарелка.bmp")
Тарелка.MakeTransparent(Color.White)
PictureBox1.Image = Тарелка
PictureBox1.BorderStyle = BorderStyle.None
PictureBox1.BackColor = Color.Transparent
PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
PictureBox1.Left = PictureBox1.Left + 1
End Sub
Здесь я полужирным выделил три новых оператора. Использован метод MakeTransparent объекта Bitmap, который делает прозрачным цвет, указанный в качестве параметра. Удовлетворительные результаты работы программы вы видите на Рис. 13.11.
Рис. 13.11
Вопрос: Что делать, если тарелка нарисована в графическом редакторе не на белом фоне, а на другом, причем, что за цвет у фона, неизвестно? Не беда. Выберите на картинке пиксель, который наверняка имеет этот самый цвет фона и узнайте его цвет при помощи функции GetPixel, как мы это делали в 12.7.4. А затем используйте его в качестве параметра метода MakeTransparent:
Dim Цвет As Color = Тарелка.GetPixel(1, 1)
Тарелка.MakeTransparent(Цвет)
Чем плох JPEG. Сейчас становится ясно, почему нельзя было сохранять тарелку в формате JPEG. Дело в том, что этот формат платит за свою компактность некоторым искажением мельчайших деталей фотографии. При этом однородный фон вполне может превратиться в пестрый набор пикселей, имеющих очень близкие, но все-таки не абсолютно одинаковые цвета. Для глаза незаметно, но компьютер не проведешь! Он видит, что белый фон на рисунке тарелки не абсолютно белый, вернее – не везде абсолютно белый. Ведь что такое белый цвет для компьютера? Это когда все три составляющие цвета (красная, зеленая и синяя) равны 255. JPEG же может какие-нибудь из них для некоторых пикселей сделать, например, 254. И все! Компьютер отказывается считать этот цвет белым, а значит и не делает его прозрачным. Белая окантовка тарелки местами остается.
Задание 92.
Пусть тарелка, полетав туда-сюда, совершит посадку. Указание: Для этого вы можете организовать счетчик импульсов таймера и в зависимости от его величины при помощи операторов выбора выбирать в процедуре Timer1_Tick то или иное направление полета тарелки.
Пусть тарелка время от времени выстреливает вниз лазерным лучом. Указание: Для этого вы можете использовать летающий вместе с тарелкой высокий узкий PictureBox и менять его фон с прозрачного на белый и обратно или делать PictureBox иногда видимым.
Мультфильм «Человечек»
Сейчас мы с вами создадим простейший мультфильм, в котором человечек шагает среди египетских пирамид (Рис. 13.12).
Рис. 13.12
Когда мы снимаем видеокамерой идущего человека, то на разных кадрах у него разное положение ног. Все остальные части тела более-менее неподвижны. Значит, если мы будем достаточно быстро менять в поле зрения кадры с разным положением ног, то создастся иллюзия ходьбы. В нашем случае для создания приемлемой иллюзии нам достаточно смены трех кадров (см. Рис. 13.13).
Рис. 13.13
Зайдите в графический редактор Paint или какой-нибудь другой и на белом фоне нарисуйте первый кадр примерно так же, как вы рисовали летающую тарелку. Вы можете сделать человечка гораздо красивее и подробнее, чем это сделал я. Если вы отлично работаете в солидном графическом редакторе, то можете даже взять фотографию вашего знакомого, только не забудьте выкрасить белой краской все, что выходит за контуры его фигуры. Сохраните человечка под именем Кадр1. Теперь, не стирая человечка, измените ему положение ног и сохранитесь как Кадр2 (не Save, а Save as…). Аналогично создайте и сохраните Кадр3. У вас получилось три файла.
Теперь создайте проект в VB. Придайте форме картинку пирамид или чего-нибудь другого подходящего, например, улицы. Поместите на форму объект PictureBox. Создайте в коде из упомянутых 3 кадров 3 объекта Bitmap. Наша идея – придавать объекту PictureBox по очереди с достаточной скоростью картинки из этих 3 объектов. Тогда мы увидим, что человечек в PictureBox передвигает на месте ногами. Менять кадры нужно в такой последовательности: 1-2-3-2-1-2-3-2-1-2-3-2-1-2-…. Если при этом объект PictureBox будет еще и двигаться налево по форме, то и получится ходьба.
Давайте-ка для лучшего понимания мысленно разобьем эту последовательность на одинаковые участки: 1-2-3-2----1-2-3-2-----1-2-3-2- …. Длина участка получилась равной 4. Поместите на форму таймер и придайте ему интервал 100 (потом, если движение будет слишком быстрым или медленным, вы его измените). Наша задача – сделать так, чтобы при каждом выполнении процедуры таймера мы видели следующий очередной кадр из приведенной мной последовательности. Для этого я организовал переменную N и заставил ее пробегать значения 0-1-2-3-----0-1-2-3-----0-1-2-….Не путайте эту последовательность с предыдущей. Как видите, длину участка на ней я подобрал тоже равной 4. Вот программа:
Dim Кадр1 As New Bitmap("Кадр1.png")
Dim Кадр2 As New Bitmap("Кадр2.png")
Dim Кадр3 As New Bitmap("Кадр3.png")
Dim N As Integer = 0
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.BackgroundImage = Image.FromFile("Пирамиды.jpg")
Кадр1.MakeTransparent(Color.White)
Кадр2.MakeTransparent(Color.White)
Кадр3.MakeTransparent(Color.White)
PictureBox1.BorderStyle = BorderStyle.None
PictureBox1.BackColor = Color.Transparent
PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Select Case N
Case 0 : PictureBox1.Image = Кадр1
Case 1 : PictureBox1.Image = Кадр2
Case 2 : PictureBox1.Image = Кадр3
Case 3 : PictureBox1.Image = Кадр2
End Select
N = N + 1 'Увеличиваем N на 1
If N = 4 Then N = 0 'После 3 должен идти 0, а не 4
PictureBox1.Left = PictureBox1.Left - 10
End Sub
Пару операторов
N = N + 1 'Увеличиваем N на 1
If N = 4 Then N = 0 'После 3 должен идти 0, а не 4
можно заменить одним изящным оператором
N = (N + 1) Mod 4
Задание 93.
Добавьте в проект три кнопки. По одной человечек идет направо, по другой налево, по третьей останавливается. Подсказка: Рисовать новые кадры в графическом редакторе не надо.
Задание 94.
«Улыбка». Для тех, кто умеет рисовать. Попросите у своей знакомой ее фотографию, где она снята с серьезным выражением лица. Введите фото в компьютер. Сделайте в Paint, а лучше в PhotoShop еще два-три кадра этого фото, аккуратно понемножку приподнимая уголки губ на изображении. Подумайте, в какой последовательности нужно показывать кадры, чтобы улыбка постепенно возникала и исчезала.
Анимация
Анимация означает придание неподвижному предмету движения. Еще одно значение слова анимация – мультфильм.
Фокус у элементов управления
Что такое фокус? Создайте проект. Поместите на форму сначала два текстовых поля, а затем две кнопки. Не наоборот. Запустите проект. Щелкните по одной кнопке, по другой, по одному полю, по другому. Ничего, конечно, не происходит, но обратите внимание вот на что. Если последний раз вы щелкнули по кнопке, то она имеет несколько другой, чем у других кнопок, «бывший нажатый» вид. Это от того, что рамочка вокруг нее стала чуть потолще. А если последний раз вы щелкнули по текстовому полю, то именно в нем мигает курсор, а в других текстовых полях не мигает. VB как бы показывает вам, каким из элементов управления вы пользовались последним. Можно сказать, что из множества разнородных объектов на форме один какой-то выделяется, то есть находится в фокусе. Говорят, что в этом случае объект обладает фокусом.
На вашей форме 4 объекта. В фокусе всегда только один из них.
События приобретения и потери фокуса. У объектов, способных иметь фокус, имеются два события: Enter, которое возникает в момент приобретения объектом фокуса, и Leave, которое возникает в момент потери фокуса.
Запишите в окно кода такие процедуры (если вы забыли, как получать заготовки процедур, перечитайте 3.11):
Private Sub Button1_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Enter
Debug.WriteLine("Кнопка 1 получила фокус")
End Sub
Private Sub Button2_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Enter
Debug.WriteLine("Кнопка 2 получила фокус")
End Sub
Private Sub TextBox1_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Enter
Debug.WriteLine("Текстовое поле 1 получило фокус")
End Sub
Private Sub Button1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Leave
Debug.WriteLine("Кнопка 1 утратила фокус")
End Sub
Private Sub Button2_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Leave
Debug.WriteLine("Кнопка 2 утратила фокус")
End Sub
Private Sub TextBox1_Leave( ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Leave
Debug.WriteLine("Текстовое поле 1 утратило фокус")
End Sub
Я не стал писать процедуры для второго текстового поля.
Запустите проект. Обратите внимание, что первое текстовое поле уже в фокусе, о чем в окне Output вы уже имеете сообщение от вашей программы:
Текстовое поле 1 получило фокус
Щелкните по второму текстовому полю. В окне Output появляется:
Текстовое поле 1 утратило фокус
Щелкните по второй кнопке. В окне Output появляется:
Кнопка 2 получила фокус
Щелкните по первому текстовому полю. В окне Output появляется:
Кнопка 2 утратила фокус
Текстовое поле 1 получило фокус
Именно в таком порядке, а не наоборот. Пощелкайте по объектам. Понаблюдайте, что появляется при этом в окне Output и в какой последовательности.
Придаем фокус в коде. Фокус можно переводить на объект и программным путем. Дополним наш проект процедурой:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TextBox1.Focus()
Button2.Focus()
End Sub
Запустите проект. Щелкните по первой кнопке. Фокус, вопреки ожиданиям, переместился не на нее, а на вторую кнопку. А в окне Output появляется:
Текстовое поле 1 утратило фокус
Кнопка 1 получила фокус
Кнопка 1 утратила фокус
Текстовое поле 1 получило фокус
Текстовое поле 1 утратило фокус
Кнопка 2 получила фокус
Произошло это благодаря методу Focus. Дело его простое – переводить фокус на своего хозяина. Как видите, один щелчок по кнопке может приводить сразу к нескольким событиям, следующим друг за другом в строго определенном порядке.
Не все объекты, получившие фокус, проявляют это зримо. Например, метка. Если фокус на ней, мы этого никак не заметим.
Если сломалась мышь. Фокус нужен хотя бы для того, чтобы компьютер в каждый момент времени знал, какой из элементов управления должен реагировать на клавиатуру. Если вы набираете на клавиатуре текст, то вводиться он будет только в то текстовое поле, которое обладает фокусом. Если фокус находится на кнопке, то вы можете нажимать на нее не только мышкой, но и клавишей Enter.
Переводить фокус с одного объекта на другой вы можете вручную клавишей Tab. При этом фокус останавливается только на тех объектах, для которых это имеет смысл. Так, метка упомянутым способом фокуса не получит. Порядок пробегания определяется численным значением свойства TabIndex каждого элемента управления. Его вы можете видеть в окне свойств. Значение 0 получает элемент управления, первым появившийся на форме при проектировании, 1 – второй и т.д. Фокус переходит от объекта к объекту в порядке возрастания этого значения. Так, в предыдущем примере, создавая проект, я на пустой форме первым разместил текстовое поле 1, затем – текстовое поле 2. Поэтому при запуске проекта в фокусе сразу же оказалось текстовое поле 1, а после нажатия Tab – текстовое поле 2.
Вы можете изменить этот порядок, изменив некоторым объектам значения свойства TabIndex. С несколько большим комфортом вы сможете изменить их, зайдя во View ® Tab Order и пощелкав мышкой по элементам управления. Только не забудьте потом выйти тем же путем: View ® Tab Order.
Вы можете запретить фокусу останавливаться на объекте при нажатии Tab, установив в False его свойство TabStop.
Между кнопками фокус можно перемещать и клавишами перемещения курсора.
Основные события, связанные с мышью
Создадим проект с кнопкой. Зайдем в окно кода и заглянем в события, связанные с формой. Среди них отыщем события, относящиеся к мыши. Нас интересуют такие: Click (щелчок), DoubleClick (двойной щелчок), MouseDown (нажали клавишу мыши), MouseUp (отпустили клавишу мыши), MouseEnter (мышь появилась над формой), MouseLeave (мышь покинула форму), MouseMove (сдвинули мышь).
Проверим работу этих событий, для чего введем такой код:
Private Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Click
Debug.WriteLine("Сработало событие Click")
End Sub
Private Sub Form1_DoubleClick(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.DoubleClick
Debug.WriteLine("Сработало событие DoubleClick")
End Sub
Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) _
Handles MyBase.MouseDown
Debug.WriteLine("Сработало событие MouseDown")
End Sub
Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseUp
Debug.WriteLine("Сработало событие MouseUp")
End Sub
Private Sub Form1_MouseEnter(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.MouseEnter
Debug.WriteLine("Сработало событие MouseEnter")
End Sub
Private Sub Form1_MouseLeave(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.MouseLeave
Debug.WriteLine("Сработало событие MouseLeave")
End Sub
Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) _
Handles MyBase.MouseMove
Debug.WriteLine("Сработало событие MouseMove")
End Sub
Запустите проект. Сделайте окно Output повыше размером. Перемещая мышь и щелкая разными ее кнопками, а также перемещая мышь при нажатых кнопках, наблюдайте в окне Output порядок наступления событий. Обратите внимание, что вне формы и над кнопкой события не срабатывают. Вот необходимые пояснения:
Подробности событий мыши. Класс MouseEventArgs
Мы с вами бесчисленное количество раз писали процедуру
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
и ни разу не заглядывали внутрь скобок. А видим мы там два параметра процедуры: один с именем sender, другой – e. Первый имеет тип Object и обозначает объект, с которым случилось событие, в нашем случае – Button1. Второй имеет тип EventArgs. Это некий общий тип для второго параметра и на нем мы останавливаться не будем. Рассмотрим лучше заголовки процедур для событий MouseDown, MouseUp и MouseMove. Например:
Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseDown
События эти похожи и параметры у всех трех одинаковые. Мы уже сейчас можем извлечь пользу из параметра e, являющегося объектом класса MouseEventArgs. Польза от него та, что он содержит информацию о событии, например, какая кнопка мыши была нажата и какие у мыши в этот момент были координаты. Информация эта находится в нескольких свойствах объекта MouseEventArgs. Приведу те, что нас интересуют:
Свойства | Смысл | Тип значения | |||
X, Y | Координаты острия курсора мыши в момент события | Integer | |||
Button | Какая именно из кнопок мыши была нажата | Перечисление MouseButtons со значениями: Left (левая), Right (правая), Middle (средняя), None (никакая) и пара значений для пятикнопочной мыши. | |||
Clicks | Равняется 0 (если не было щелчка), 1 (если был одинарный щелчок) или 2 (если был двойной щелчок). | Integer |
Кроме этого класс MouseEventArgs поддерживает работу с колесом мыши.
Для того, чтобы понять и проверить смысл этих свойств, прочтите (чтобы понять) и запустите (чтобы проверить) такую программу:
Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseDown
Debug.WriteLine(e.X & " " & e.Y) 'Печатаем координаты события
Debug.WriteLine(e.Button.ToString) 'Печатаем английское название кнопки
If e.Button = MouseButtons.Left Then Debug.WriteLine("Нажата левая кнопка мыши")
If e.Button = MouseButtons.Right Then Debug.WriteLine("Нажата правая кнопка мыши")
If e.Button = MouseButtons.Middle Then Debug.WriteLine("Нажата средняя кнопка мыши")
End Sub
Private Sub Form1_MouseUp( ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseUp
Debug.WriteLine(e.X & " " & e.Y) 'Печатаем координаты события
Debug.WriteLine(e.Button.ToString) 'Печатаем английское название кнопки
If e.Button = MouseButtons.Left Then Debug.WriteLine("Отпущена левая кнопка мыши")
If e.Button = MouseButtons.Right Then Debug.WriteLine("Отпущена правая кнопка мыши")
If e.Button = MouseButtons.Middle Then Debug.WriteLine("Отпущена средняя кнопка мыши")
End Sub
Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseMove
Debug.WriteLine(e.X & " " & e.Y) 'Печатаем координаты события
Debug.WriteLine(e.Button.ToString) 'Печатаем английское название кнопки
If e.Button = MouseButtons.Left Then Debug.WriteLine("Удерживается нажатой левая кнопка мыши")
If e.Button = MouseButtons.Right Then Debug.WriteLine("Удерживается нажатой правая кнопка мыши")
If e.Button = MouseButtons.Middle Then Debug.WriteLine("Удерживается нажатой средняя кнопка мыши")
If e.Button = MouseButtons.None Then Debug.WriteLine("Не нажата ни одна кнопка мыши")
End Sub
Пояснения: Здесь я использовал свойственный объектам метод ToString, который возвращает в виде вразумительной строки имя или значение объекта.
Пощелкайте в разных местах формы всеми тремя кнопками мыши. Повозите мышь над формой, удерживая нажатой ту или иную кнопку. В окне Output вы будете наблюдать последовательность сообщений примерно такого вида:
264 24
None
Не нажата ни одна кнопка мыши
264 24
Left
Нажата левая кнопка мыши
337 83
Left
Удерживается нажатой левая кнопка мыши
337 83
Left
Отпущена левая кнопка мыши
341 83
None
Не нажата ни одна кнопка мыши
Пояснения здесь излишни.
Итоги. В тонкости я, конечно, не вникал, но полученной информации о мыши вполне достаточно для решения реальных задач.
Две задачи: Глаз-ватерпас и Мышка-карандаш
Глаз-ватерпас. Создадим программу на определение точности глаза и руки: Поместим на форму кнопку. При нажатии кнопки возникает и тут же исчезает в случайном месте формы маленькая окружность. Вы должны поточнее щелкнуть мышкой там, где она мелькнула (как можно ближе к ее центру). После щелчка происходит вот что: становится видимой исчезнувшая окружность, а на месте, где вы щелкнули, тоже возникает окружность (раза в два поменьше той первой, чтобы вы на глаз могли их различить). Это позволяет вам наглядно определить их близость. Кроме этого, компьютер сообщает вам, на каком точно расстоянии от центра исчезнувшей окружности было острие мышиного курсора во время щелчка.
Здесь подойдет событие MouseDown, так как оно сообщает координаты мыши во время щелчка. Вот программа для поставленной задачи:
Dim X_кружка As Integer
Dim Y_кружка As Integer
Dim Граф As Graphics = Me.CreateGraphics
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer
Randomize()
X_кружка = 200 * Rnd() 'Определяем координаты кружка (центра кружка)
Y_кружка = 200 * Rnd()
Граф.DrawEllipse(Pens.Black, X_кружка - 10, Y_кружка - 10, 20, 20) 'Чертим кружок
For i = 1 To 3000000 : Next 'Пауза, чтобы мы успели заметить кружок
Граф.Clear(Me.BackColor) 'Очищаем форму от кружка
End Sub
Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseDown
Граф.DrawEllipse(Pens.Black, X_кружка - 10, Y_кружка - 10, 20, 20) 'Кружок снова возникает
Граф.DrawEllipse(Pens.Black, e.X - 5, e.Y - 5, 10, 10) 'Чертим кружочек на месте щелчка
Dim Расстояние_до_кружка As Double = Math.Sqrt((e.X - X_кружка) ^ 2 + (e.Y - Y_кружка) ^ 2)
MsgBox("Промах на " & Math.Round(Расстояние_до_кружка) & " пикс")
Граф.Clear(Me.BackColor) 'Очищаем форму от кружков
End Sub
Пояснение того, как вычислялось расстояние ( для тех, кто знает теорему Пифагора): Мысленно соедините отрезком прямой центр первой окружности и точку щелчка. Проведите из концов отрезка вертикальные и горизонтальные линии. Вы легко увидите прямоугольный треугольник. Искомым расстоянием будет гипотенуза этого прямоугольного треугольника. Нетрудно заметить, что горизонтальный катет равен e.X - X_кружка, а вертикальный равен e.Y - Y_кружка (знак я не учитываю). Теорема Пифагора гласит, что квадрат гипотенузы равен сумме квадратов катетов. Отсюда, гипотенуза равна корню квадратному из суммы квадратов катетов (каковая формула и записана в программе).
Вы можете превратить эту программу в игру. Создайте сумматор расстояний, чтобы игрок видел свой накапливающийся суммарный результат. Предоставьте игроку возможность сделать, скажем, ровно 10 щелчков, после чего игра заканчивается. Цель – набрать наименьшую сумму в конце игры.
Мышка-карандаш. Вот программа, превращающая мышку в карандаш:
Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseMove
Dim Граф As Graphics = Me.CreateGraphics
Граф.FillEllipse(Brushes.Black, e.X, e.Y, 5, 5)
End Sub
Запустите ее и медленно ведите мышкой по форме. За мышкой остается нарисованный след из множества черных кружочков диаметра 5. Кружочки рисуются так близко друг к другу, что образуют непрерывную линию. Если мышку передвигать побыстрее, то след будет прерывистым, потому что, хоть событие MouseMove наступает и часто, но все же не бесконечно часто. В 20.9.2 мы решим проблему непрерывности, хотя, собственно говоря, вы и сейчас можете это сделать.
Задание 96.
Сделайте так, чтобы мышь-карандаш рисовала только при нажатой левой клавише, что более привычно для всех пользователей компьютера. Сделайте так, чтобы при щелчке по правой клавише толщина линии возрастала на 1. Если сможете, сделайте так, чтобы при двойном щелчке по правой клавише толщина линии убывала на 1.
Работа с мышью
В 3.11 мы познакомились с некоторыми событиями, возникающими при работе с мышью. Пришла пора расширить и углубить наше знакомство.
Событие KeyPress. Класс KeyPressEventArgs. Структура Char
Создайте проект из одной формы, без единого элемента управления. Зайдите в окно кода и выберите для формы событие KeyPress. В появившуюся заготовку процедуры запишите следующий код:
Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs) Handles MyBase.KeyPress
Debug.Write(e.KeyChar)
End Sub
Запустите проект, пощелкайте по клавишам клавиатуры и понаблюдайте за результатами в окне Output. Каждый раз, как вы нажимаете на алфавитно-цифровую клавишу (то есть набираете на клавиатуре букву, цифру, знак препинания или другой символ), набранный символ появляется в окне Output. Попробуйте ввести заглавные буквы, русские буквы. Все получается:
qwertyQWERTY12345,.!фывапроФЫВАПРО()"№;%:?*
Более того, нажатие клавиш пробела, ввода и табуляции вызывает привычный эффект. Впечатление такое, что окно Output превратилось в окно текстового редактора.
Нажатие функциональных (F1 – F12) и многих управляющих клавиш не вызывает события KeyPress. (Управляющие клавиши – это те, что сосредоточены в основном в левом конце клавиатуры и в правой ее части левее дополнительной цифровой клавиатуры.)
Из программы видно, что символы, выводимые в окно Output, являются значениями свойства KeyChar объекта e, принадлежащего классу KeyPressEventArgs. Строго говоря, значение свойства KeyChar есть структура Char, обладающая рядом полезных методов, действие некоторых из которых видно из следующей программы:
Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
Handles MyBase.KeyPress
Dim Символ As Char = e.KeyChar
Debug.WriteLine("Набран символ " & Символ)
Debug.WriteLine("Это цифра? " & Char.IsDigit(Символ))
Debug.WriteLine("Это буква? " & Char.IsLetter(Символ))
Debug.WriteLine("Правда ли, что это буква или цифра? " & Char.IsLetterOrDigit(Символ))
Debug.WriteLine("Это строчная буква? " & Char.IsLower(Символ))
Debug.WriteLine("Это заглавная буква? " & Char.IsUpper(Символ))
Debug.WriteLine("Это знак препинания? " & Char.IsPunctuation(Символ))
Debug.WriteLine("Это пробел? " & Char.IsSeparator(Символ))
Debug.WriteLine("Превратить в строчную " & Char.ToLower(Символ))
Debug.WriteLine("Превратить в заглавную " & Char.ToUpper(Символ))
End Sub
Вот как отреагирует эта программа на ввод заглавной русской буквы «Ж»:
Набран символ Ж
Это цифра? False
Это буква? True
Правда ли, что это буква или цифра? True
Это строчная буква? False
Это заглавная буква? True
Это знак препинания? False
Это пробел? False
Превратить в строчную ж
Превратить в заглавную Ж
Анализировать вводимые с клавиатуры символы для управления компьютером можно, например, такими операторами:
If Символ = ":" Then Debug.WriteLine("Набрано двоеточие")
If Char.IsDigit(Символ) Then Debug.WriteLine("Набрана цифра")
События KeyDown и KeyUp. Класс KeyEventArgs
KeyDown и KeyUp. Создайте проект из одной формы, без элементов управления. Введите такой код:
Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs) Handles MyBase.KeyPress
Debug.WriteLine("Сработал KeyPress")
End Sub
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles MyBase.KeyDown
Debug.WriteLine("Сработал KeyDown")
End Sub
Private Sub Form1_KeyUp(ByVal sender As Object, ByVal e As KeyEventArgs) Handles MyBase.KeyUp
Debug.WriteLine("Сработал KeyUp")
End Sub
Запустите проект, пощелкайте по клавишам клавиатуры и понаблюдайте за результатами в окне Output. Удерживайте нажатыми разные клавиши и тоже наблюдайте.
Выводы: При нажатии почти любой клавиши клавиатуры срабатывает событие KeyDown. Если при этом нажатие приводит к вводу символа, то сразу за ним срабатывает событие KeyPress. Если вы удерживаете клавишу нажатой, эта парочка срабатывает многократно и часто. При отпускании клавиши однократно срабатывает событие KeyUp. Я не рассматриваю ситуацию, когда одновременно удерживаются нажатыми две и более клавиш.
KeyCode. Какие именно клавиши клавиатуры были нажаты или отпущены, вам подскажет свойство KeyCode объекта e, принадлежащего классу KeyEventArgs. Введите такой код:
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e KeyEventArgs) Handles MyBase.KeyDown
Debug.WriteLine(e.KeyCode.ToString)
End Sub
Пояснения: Здесь я использовал свойственный объектам метод ToString, который возвращает в виде вразумительной строки имя или значение объекта.
Запустите проект, пройдитесь по всем клавишам клавиатуры (кроме тех, конечно, что выключают компьютер). Вот что вы увидите:
Печать в окне Output | Какая клавиша была нажата | ||
D
L F1 Tab Enter Left Space D1 D2 Escape ControlKey ShiftKey Menu | D
L F1 Tab Enter Стрелка влево Пробел Цифра 1 Цифра 2 Esc Ctrl Shift Alt |
И так далее. Пройдитесь по клавишам дополнительной цифровой клавиатуры в правой части клавиатуры при включенном и выключенном индикаторе NumLock.
Свойства Control, Alt, Shift. VB позволяет определить, какие из трех клавиш Ctrl, Alt, Shift удерживались нажатыми в момент срабатывания события KeyDown или KeyUp. Дополните процедуру:
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) _
Handles MyBase.KeyDown
Debug.WriteLine(e.KeyCode.ToString & " " & e.Control
& " " & e.Alt & " " & e.Shift)
End Sub
Здесь я использовал свойства Control, Alt, Shift объекта e. Их смысл будет ясен из распечатки в окне Output. Запустите проект, нажмите и удерживайте нажатыми клавиши Alt и Shift (но не Ctrl), после чего щелкните по клавише W. Вот что вы увидите последней напечатанной строкой в окне Output:
W False True True
Все сказанное относится и к событию KeyUp.
Анализировать нажатия клавиш для управления компьютером можно, например, такими операторами:
If e.KeyCode = Keys.F2 Then Debug.WriteLine("Нажата клавиша F2")
If e.Shift Then Debug.WriteLine("Нажата клавиша Shift")
If e.Control And e.KeyCode
= Keys.Right Then Debug.WriteLine("Нажата стрелка направо при нажатой кл Ctrl")
Перечисление Keys включает в себя коды клавиш клавиатуры.
События клавиатуры у элементов управления. События, связанные с клавиатурой, имеются у многих элементов управления. Поместим на форму, к примеру, пару кнопок и текстовое поле. Вдобавок к процедуре Form1_KeyDown напишем три процедуры:
Private Sub Button1_KeyDown …
Private Sub Button2_KeyDown …
Private Sub TextBox1_KeyDown …
Запустим программу и щелкнем по какой-нибудь клавише. Какая из четырех процедур сработает? Та, чей элемент управления находится в фокусе. А поскольку один какой-нибудь элемент на форме всегда находится в фокусе, до процедуры Form1_KeyDown дело никак не доходит. Это не всегда бывает удобно. И против этого в VB есть специальный прием: свойство формы KeyPreview устанавливается в True. Это означает приказ компьютеру при нажатии на клавишу сначала вызывать событие формы, а уж потом другого объекта. Так, если в фокусе находится кнопка 2, то при нажатии на клавишу клавиатуры сначала выполняется процедура Form1_KeyDown, а сразу за ней – Button2_KeyDown. Сказанное касается большинства клавиш и элементов управления, но не всех.
«Телефонный номер». Разберем пример использования событий клавиатуры. Пусть на форме имеется текстовое поле, которое мы предназначили для ввода телефонных номеров. Сделаем текстовое поле умным – запретим ему воспринимать символы за исключением цифр, пробелов, черточек и точек. Если пользователь попытается ввести неподходящий символ, должно появляться сообщение об ошибке, а сам символ в поле появляться не должен. После окончания ввода можно нажать клавишу Enter, пусть при этом выводится сообщение «Ввод закончен».
Вот код:
Private Sub TextBox1_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs) _
Handles TextBox1.KeyPress
Dim С As Char = e.KeyChar 'Вводимый символ
If Char.IsDigit(С) Or Char.IsSeparator(С) Or С = "-" Or С = "." Then Exit Sub
Beep()
MsgBox("В телефонном номере можно употреблять только цифры, пробелы, черточки и точки!")
e.Handled
= True
End Sub
Private Sub TextBox1_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles TextBox1.KeyDown
If e.KeyCode = Keys.Enter Then MsgBox("Ввод закончен")
End Sub
Пояснения: Используются события KeyPress и KeyDown текстового поля. Событие KeyDown используется для обработки нажатия на клавишу Enter. Оператор If в первой процедуре предназначен для того, чтобы не создавать проблем цифрам, пробелам, черточкам и точкам. Ведь выход из процедуры (Exit Sub) и есть выход без проблем. Если символ не принадлежит к упомянутым, то выхода из процедуры не происходит и выполняются последние три строки процедуры: раздается сигнал, затем появляется сообщение, а напоследок оператор e.Handled = True заставляет компьютер считать, что неосуществленная стандартная обработка события (в частности – появление символа в текстовом поле) якобы уже произошла, а значит и символу незачем появляться в текстовом поле.
Задание 97.
«Управляемый объект». На клавиатуре имеются четыре клавиши со стрелками для управления курсором. Поместите на форму какой-нибудь элемент управления. При щелчке по любой клавише управления курсором элемент управления должен двигаться с постоянной скоростью в указанном на клавише направлении.
Задание 98.
«Светофор». Нарисуйте светофор: прямоугольник и три круга. При наборе на клавиатуре символа R светофор должен загораться красным светом, G – зеленым, Y - желтым.
Задание 99.
«Зенитка». Вверху справа налево медленно движется вражеский самолет. В подходящий момент вы нажатием любой клавиши запускаете снизу вверх зенитный снаряд. При попадании компьютер выдает соответствующее сообщение.
Не шали! Поставьте при помощи событий клавиатуры преграду тем шалунам, кто в нашем калькуляторе хочет ввести с клавиатуры в текстовое поле для результата любые символы. А потом вспомните, что это можно было сделать, просто превратив текстовое поле в метку или придав текстовому полю свойство ReadOnly.
Работа с клавиатурой
Познакомимся с событиями, связанными с клавиатурой. Их три: KeyDown (клавиша нажата), KeyUp (клавиша отпущена) и KeyPress.(ввели с клавиатуры символ).
Постановка задачи
Поставим задачу сделать игру, где наш миниатюрный гоночный автомобиль будет под управлением мыши или клавиатуры нестись от старта до финиша.
На Рис. 14.1 вы видите внешний вид игры (о красоте я не заботился, красоту вы всегда сможете добавить по вкусу):
Рис. 14.1
Процесс игры таков. После загрузки проекта вы нажимаете на кнопку Начинаем сначала. На форме появляется белое квадратное поле для гонки со случайно расположенными газонами. Машина стоит на старте. Нажатием на клавишу пробела вы даете газ и машина со старта набирает скорость. Ваша цель – любым путем побыстрее добраться до финиша. На белом асфальте вы можете газовать до предельной скорости (я выбрал ее значение равным 15). Если же ненароком попадете на газон, то на газоне ваша скорость будет очень мала (я выбрал 1). Поэтому имеет смысл по газонам ездить пореже. Направление движения может быть только горизонтальное и вертикальное, наискосок машина не движется. Выбор направления (руль) – это клавиши со стрелками. Тормоз – клавиша Ctrl. В ограждение въезжать нельзя – катастрофа. Когда приедете на финиш, посмотрите на счетчик времени и увидите ваш результат. Снова нажимайте на кнопку Начинаем сначала. Теперь расположение газонов будет другим. Сажайте за клавиатуру приятеля и смотрите, не покажет ли он время лучше, чем у вас. Можете посмотреть, кто из вас покажет лучшее время из 10 заездов.
Не удивляйтесь, что скорость на спидометре не равна пути, деленному на время. Ведь это мгновенная скорость (то есть настоящая скорость в данное мгновение), а не средняя скорость автомобиля (см. Физика, 9 класс).
Все эти правила я старался сделать как можно проще, чтобы не усложнять проект. Разобравшись в проекте, вы всегда сможете дописать процедуры, делающие процесс гонки более для вас привлекательным.
Для проекта я выбрал вариант игры с одним автомобилем. Я вам намекну, как модифицировать проект, чтобы получилась игра с двумя автомобилями. Однако помните, что для грамотного создания игры с несколькими автомобилями вам нужно будет подняться на новый уровень программирования – научиться создавать собственные классы объектов.
Делим проект на части
Создадим новый проект. Прежде чем что-либо с ним делать, мысленно разделим задачу, как положено, на части (по возможности, конечно). Мы раньше уже делили задачу на части, когда создавали будильник. Сейчас вам очень полезно перечитать тот материал. И проглядеть ту программу.
Можно заметить, что в нашем проекте нет таких независимых частей, как в будильнике. Тем не менее, удобно разделить создание проекта на 3 последовательные части:
1. Рисование всех элементов поля в результате нажатия на кнопку Начинаем сначала. Этим будет заниматься одна группа процедур.
2. Затем управление машиной во время гонки. Этим будет заниматься другая группа процедур. Мы полностью задействуем идею использования таймера так, как я ее изложил в 13.5.11. На каждом импульсе таймера автомобиль должен будет проделывать весь цикл своего функционирования.
3. И наконец – определение поведения машины на газоне, на финише и при столкновении с препятствием. Сюда же я отношу организацию счетчиков на пульте управления (время, скорость, путь) и выдачу сообщений.
Первая часть – рисуем поле для гонки
Что рисовать.
1. Сначала рисуем сам квадрат поля
2. Затем старт и финиш
3. Затем газоны
Код первой части. Разместите на форме кнопку. Дайте ей имя Кнопка_начинай_сначала. Приведу работающую версию первой части программы, а вслед за ней – пояснения.
Const Отступ As Short = 20 'Имеется в виду отступ поля от края формы слева и сверху
Const Размер_поля As Short = 500 'Поле - квадрат, это сторона квадрата
Const Высота_формы As Short = Размер_поля + 4 * Отступ 'Оставляем отступ снизу
Const Ширина_формы As Short = Размер_поля + 10 * Отступ 'Оставляем справа место для кнопок и меток
Const Размер_старта As Short = 40 'Старт - квадрат, это сторона квадрата
Const Размер_финиша As Short = 40 'Финиш - квадрат, это сторона квадрата
Dim X_старта, Y_старта, X_финиша, Y_финиша As Short 'Координаты старта и финиша
'Создаем пустую поверхность величиной с форму:
Dim Картинка As New Bitmap(Ширина_формы, Высота_формы)
Dim Гр As Graphics = Graphics.FromImage(Картинка) 'Создаем над ней объект класса Graphics
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Width = Ширина_формы
Me.Height = Высота_формы
Randomize() 'Нам придется рисовать случайные газоны
End Sub
Private Sub Кнопка_начинай_сначала_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Кнопка_начинай_сначала.Click
Гр.Clear(Color.LightGray) 'Стираем светло-серым цветом предыдущую картинку, если была
Рисуем_поле()
Рисуем_старт()
Рисуем_финиш()
Рисуем_газоны()
Me.BackgroundImage = Картинка 'Придаем полю картинку, нарисованную в памяти
Будем действовать, как сказано в 12.4. Организуем в памяти «пустой лист для рисования». Для этого предназначены строки 8-10.
Загружаем форму. Содержимое процедуры Form1_Load ясно без пояснений. В эту процедуру программисты всегда вставляют то, что должно быть сделано один раз за все время выполнения проекта, причем в самом начале. После загрузки формы мы ничего на ней не видим, кроме кнопки. Эта процедура у нас еще будет расти.
Кнопка_начинай_сначала. Процедура Кнопка_начинай_сначала_Click начинается с того, что мы заполняем картинку в памяти светло-серым цветом. Это цвет ограждения в наших гонках. Дальше идет обращение к процедуре Рисуем_поле, которая просто рисует на этом сером фоне белый квадрат поля для гонки. Дальше идет обращение к трем процедурам рисования старта, финиша и газонов. Картинка в памяти готова. Предпоследняя строка процедуры делает эту картинку значением свойства формы BackgroundImage, то есть она становится «фоном» этой формы. Я мог поступить по-другому и перенести эту картинку на поверхность формы методом DrawImage класса Graphics, но в этом случае мне не удалось бы привычными средствами сделать PictureBox с изображением машины прозрачным, ведь так называемая «прозрачность» работает только по отношению к BackgroundImage. К тому же я предпочел не заботиться о перерисовке. Запустив проект, растяните форму и вы увидите, что картинка покрывает форму мозаикой. Последняя строка процедуры «проявляет» новый «фон» на форме, перерисовав ее оператором Me.Refresh().
Рисуем поле, старт и финиш. Содержимое соответствующих процедур очевидно. В формулах, определяющих координаты старта и финиша, вы легко разберетесь самостоятельно.
Рисуем газоны. Здесь в цикле рисуется 30 газонов, в каждой итерации по одному. Вы видите, что некоторые константы и переменные я объявил внутри процедуры. Так поступают, когда знают, что их значения нигде, кроме как в этой процедуре, не нужны. Значения чисел и вид формул для X_газона и Y_газона я выбрал так, чтобы газоны получались не слишком большие и не накладывались ни на ограждения, ни на старт с финишем. К тому же слишком большие газоны сольются в один большой газон и между ними нельзя будет проехать. Вид формул не принципиален, и если вам не хочется разбираться в них, можете их просто списать или придумать свои.
Результат. После ввода всего вышеприведенного кода у вас при каждом нажатии на кнопку Начинаем сначала должны рисоваться поле, старт, финиш и новая конфигурация газонов.
Вторая часть – управляем машиной
Если с первой частью все было относительно просто, то про вторую стоит поговорить подробнее. Фактически, нам нужно будет создавать автомобиль, как раньше мы создавали будильник. Не имея программистского опыта, мы попытаемся использовать житейский опыт касательно того, как автомобиль устроен. Причем применительно к задачам проекта. Так, цвет сиденья в этом смысле нам не очень важен. А важно нам управлять скоростью и направлением движения (этим в обычном автомобиле занимаются руль и педали газа и тормоза). А еще важно, чтобы автомобиль чувствовал, «куда он въехал» (газон, ограждение, финиш) и вел себя соответственно (этим мы займемся в третьей части).
Для начала поместим на форму маленький PictureBox. Это ваша машина. Так и назовем его – Машина. Поставим задачу управлять им с клавиатуры как написано в задании на игру:
Направление движения может быть только горизонтальным и вертикальным, наискосок машина не движется. Выбор направления – это клавиши со стрелками. Тормоз – клавиша Ctrl. Газ – клавиша пробела. Щелчок по клавише газа или тормоза увеличивает или уменьшает скорость на какое-то значение.
Загляните в Задание 112. Там мы уже решали задачу движения объекта по форме. Если вы ее не решили, то решите или на худой конец загляните в ответ и разберитесь в нем.
Код второй части. Приведу код, который заведует управлением машиной. Этим кодом нужно дополнить первую часть программы, чтобы получилась работающая версия проекта. Вслед за кодом я привожу пояснения. Из процедур, входящих в первую часть, я здесь перепишу только две: Form1_Load и Кнопка_начинай_сначала_Click.
Dim Цвет_фона As Color = Color.White 'Цвет фона при рисовании машины
Dim Исходная_машина As New Bitmap("Машина.BMP") 'Создаем исходное изображение машины
'Определяем 4 рабочих изображения машины:
Dim Машина_налево, Машина_вверх, Машина_направо, Машина_вниз As Bitmap
Dim x, y As Short 'Горизонтальная и вертикальная координаты автомобиля
Dim Шаг As Short 'Шаг численно равен перемещению автомобиля по форме на каждом такте таймера
Dim Газ As Boolean = False 'Нажимаем ли мы на газ
Dim Тормоз As Boolean = False 'Нажимаем ли мы на тормоз
Enum типРуль 'Куда едем
вверх
влево
вниз
вправо
End Enum
Dim Руль As типРуль
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'……………здесь расположены уже знакомые нам предыдущие строки процедуры……………
Создаем_изображения_машины()
Машина.BackColor = Color.Transparent
Me.KeyPreview = True 'Чтобы машина отзывалась на клавиатуру
txtВремя.ReadOnly = True
End Sub
Private Sub Кнопка_начинай_сначала_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Кнопка_начинай_сначала.Click
'……………здесь расположены уже знакомые нам предыдущие строки процедуры……………
Ставим_машину_на_старт()
txtВремя.Focus()
End Sub
Sub Создаем_изображения_машины()
Создаем_изображение(Машина_налево, RotateFlipType.RotateNoneFlipNone)
Создаем_изображение(Машина_вверх, RotateFlipType.Rotate90FlipNone)
Создаем_изображение(Машина_направо, RotateFlipType.Rotate180FlipNone)
Создаем_изображение(Машина_вниз, RotateFlipType.Rotate270FlipNone)
End Sub
Sub Создаем_изображение(ByRef Автомобиль As Bitmap, ByVal Поворот As RotateFlipType)
Автомобиль = New Bitmap(Исходная_машина) 'Порождаем новый Bitmap из исходного
Автомобиль.RotateFlip(Поворот) 'Поворачиваем его
Автомобиль.MakeTransparent(Цвет_фона) 'Делаем фон автомобиля прозрачным
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Изменяем_скорость()
Выбираем_куда_ехать_и_делаем_шаг()
Машина.Left = x : Машина.Top = y 'Передвигаем PictureBox
End Sub
Sub Ставим_машину_на_старт()
x = X_старта : y = Y_старта ' Координаты машины приравниваются координатам точки старта
Шаг = 0 'На старте стоим, а не едем
Руль = типРуль.влево 'Это чтобы машина знала, что когда стартуем, нужно ехать влево
Машина.Image = Машина_налево 'Ориентируем машину налево
End Sub
Sub Изменяем_скорость()
If Газ Then Шаг = Шаг + 1
If Тормоз Then
Шаг = Шаг – 2 'Потому, что тормоз действует быстрее газа
'В результате быстрого торможения скорость может стать отрицательной, что и предотвращается:
If Шаг < 0 Then Шаг = 0
End If
'Чтобы во время набора скорости и торможения приходилось без перерыва жать на педаль:
Газ = False : Тормоз = False
End Sub
Sub Выбираем_куда_ехать_и_делаем_шаг()
Select Case Руль
Case типРуль.вверх : Машина.Image = Машина_вверх : y = y - Шаг
Case типРуль.вниз : Машина.Image = Машина_вниз : y = y + Шаг
Case типРуль.влево : Машина.Image = Машина_налево : x = x - Шаг
Case типРуль.вправо : Машина.Image = Машина_направо : x = x + Шаг
End Select
End Sub
'Обработка события - нажатия клавиши на клавиатуре для управления автомобилем:
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) _
Handles MyBase.KeyDown
Select Case e.KeyCode
Case Keys.Left : Руль = типРуль.влево
Case Keys.Right : Руль = типРуль.вправо
Case Keys.Up : Руль = типРуль.вверх
Case Keys.Down : Руль = типРуль.вниз
Case Keys.Space : Газ = True
Case Keys.ControlKey : Тормоз = True
End Select
End Sub
Создаем изображения машины. Пустой PictureBox передвигать по форме неинтересно, поэтому рисуем в каком-нибудь графическом редакторе маленькую машинку (вид сверху) точно так же, как мы рисовали летающую тарелку для мультфильма. Сохраняем ее.
Во время гонок машина может ехать в 4 направлениях: влево, вправо, вверх, вниз. Значит, для придания естественности игре (чтобы машина не ехала «боком»), мы должны иметь 4 ориентации машины, между которыми будем в нужные моменты переключаться. Поэтому нам нужны 4 одинаковых изображения машины, повернутые в разные стороны. Для этого вовсе не нужно рисовать 4 рисунка и сохранять 4 файла (хотя и это можно, конечно). Вспомним, что метод RotateFlip умеет поворачивать картинки под прямыми углами. Его и используем.
Обращение к процедуре Создаем_изображения_машины мы находим, естественно, в процедуре Form1_Load, так как создать эти изображения достаточно один раз. Здесь же мы видим, как следующая строка делает прозрачным фон элемента управления Машина. Тело процедуры Создаем_изображения_машины, как видите, состоит из 4 операторов, каждый из которых создает одно из 4 изображений. Каждый из этих операторов есть в свою очередь обращение к процедуре Создаем_изображение. Рассмотрим ее получше.
В верхней части окна кода мы видим строки:
Dim Исходная_машина As New Bitmap("Машина.BMP") 'Создаем исходное изображение машины
'Определяем 4 рабочих изображения машины:
Dim Машина_налево, Машина_вверх, Машина_направо, Машина_вниз As Bitmap
Первая из них на основе нарисованной нами машины создает объект Исходная_машина типа Bitmap, который послужит исходным материалом для упомянутых 4 изображений. Каждое из них в дальнейшем получится поворотом картинки Исходная_машина на нужный угол. Тут же объявляются (но не создаются) объекты типа Bitmap для этих 4 изображений.
У процедуры Создаем_изображение два параметра: Первый из них (Автомобиль) как раз и является тем изображением, которое создает процедура. Он имеет тип Bitmap и обозначен словом ByRef, а не ByVal, так как в процессе работы изображение и создается и меняется. Второй параметр (Поворот) имеет тип перечисления RotateFlipType, которое как раз и используется при повороте картинок. Когда я рисовал машину, я нарисовал ее глядящей влево, отсюда становятся понятны значения второго параметра в обращении к процедурам.
Для формирования требуемого изображения машины нужно проделать 3 вещи:
Создать его из исходной картинки машины
Повернуть на нужный угол
Сделать фон прозрачным
Именно эти 3 вещи делают 3 оператора процедуры. Взгляните на них. Если вы не привыкли еще к параметрам объектного типа или вообще не понимаете текста этих операторов, я «перепишу» их по-другому. Например, для случая выполнения 2-го оператора процедуры Создаем_изображения_машины я просто для наглядности подставлю в них вместо параметров их значения:
Машина_вверх = New Bitmap(Исходная_машина) 'Порождаем новый Bitmap из исходного
Машина_вверх.RotateFlip(RotateFlipType.Rotate90FlipNone) 'Поворачиваем его
Машина_вверх.MakeTransparent(Цвет_фона) 'Делаем фон автомобиля прозрачным
Управляем машиной. Поместим на форму таймер. Настроим его интервал в пределах от 10 до 100 (по вкусу) На каждом импульсе таймера автомобиль должен будет проделать весь цикл своего функционирования. Из этого цикла нам для нормального передвижения машины достаточно пока озаботиться тремя вещами. Автомобиль должен:
Изменить или не изменить скорость в соответствии с приказами клавиатуры.
Изменить или не изменить направление движения в соответствии с приказами клавиатуры
Сделать очередной шаг в нужном направлении.
Эти три вещи как раз и выполняются операторами, которые мы видим в процедуре Timer1_Tick. Разберемся в них, но сначала заглянем в верхнюю части окна кода, где мы видим объявления переменных x, y, Шаг, Газ, Тормоз и Руль. Зачем нам нужны последние три переменные? Нельзя ли попроще: управлять движением объектов с клавиатуры безо всяких переменных. Попытаться можно, и программа поначалу получится короче. Но с ростом сложности проекта будут расти неудобства. Например, машина не будет знать направления своего движения, а без этого трудно будет запрограммировать отскок от ограждения.
С учетом вышесказанного проглядите процедуру Form1_KeyDown. Она проста и в комментариях не нуждается.
События, связанные с клавиатурой, имеются у многих элементов управления. Почему мы выбрали события формы? Выбор какого-нибудь элемента управления был бы для нашей игры неудобен. Если мы для программирования реакции автомобиля на нажатия клавиш выберем, например, процедуру Private Sub Button1_KeyDown, то во время гонки мы не сможем щелкать по другим кнопкам, кроме Button1, так как иначе Button1 выйдет из фокуса и автомобиль перестанет реагировать на клавиши.
Изменяем скорость. Теперь посмотрим, как регулируется скорость. Для этого заглянем в процедуру Изменяем_скорость. Действие ее полностью определяется значением переменных Газ и Тормоз. Если это газ, то на данном такте таймера шаг, а значит и скорость, возрастет на 1. Если тормоз – упадет на 2 (потому что тормоз обычно действует сильнее газа). Отрицательный шаг в результате торможения означал бы задний ход, что неестественно, поэтому в программе это предотвращается.
Как видите, значения переменных Газ и Тормоз в конце процедуры принудительно приравниваются False. Это значит, что щелчок по клавише газа или тормоза приводит только к однократному увеличению или уменьшению скорости. Ведь на следующем тике таймера переменные Газ и Тормоз будут иметь значение False, а значит процедура не приведут к изменению шага. Значит на следующем такте таймера скорость не изменится. Чтобы она изменилась, нам нужно еще раз нажать на клавишу газа или тормоза. Обычно поступают по-другому – просто удерживают клавишу нажатой, при этом событие KeyDown возникает несколько раз в секунду и скорость меняется достаточно быстро. Это соответствует механике реального автомобиля – чтобы набирать скорость, нужно непрерывно и усиленно нажимать на педаль газа, а чтобы тормозить – тормоза.
Выбираем куда ехать и делаем шаг. Именно эта процедура задает ориентацию машины и направление движения. Загляните в нее. Ее дело – чувствовать одно из 4 значений переменной Руль и в соответствии с этим значением делать две вещи: поворачивать машину в нужном направлении и менять в этом направлении ее координату. Само же изображение автомобиля на экране прыгнет на указанную координату мгновением позже, на последней строке процедуры Timer1_Tick.
В этой процедуре каждая строка оператора Select Case присваивает свойству Image нашего элемента управления Машина значение одного из 4 изображений машины, а именно как раз того, которого требует нажатие клавиши со стрелкой на клавиатуре. При этом в выбранном направлении изменяется и координата x или y. В результате мы видим, что автомобиль глядит в ту же сторону, куда он едет.
Последняя строка процедуры Timer1_Tick перемещает элемент управления Машина на экране в соответствии с вычисленными координатами x и y.
Ставим машину на старт. Когда мы нажимаем кнопку Начинаем сначала, машина, где бы она ни была и что бы ни делала, должна прыгнуть на старт и замереть, глядя влево. Именно это поведение обеспечивают все пять операторов процедуры Ставим_машину_на_старт. И именно поэтому обращение к этой процедуре включено в процедуру Кнопка_начинай_сначала_Click.
Чтобы машина реагировала на клавиатуру. Я уже говорил ранее, что поскольку один какой-нибудь элемент управления на форме всегда находится в фокусе, до процедуры Form1_KeyDown дело не дойдет. Это значит, что наша машина не будет реагировать на клавиши. Для борьбы с этим в процедуре Form1_Load свойство формы KeyPreview установлено в True. Это означает приказ компьютеру при нажатии на клавишу вызывать событие формы, а уж потом другого объекта. Но этого недостаточно. И вот почему. При запуске проекта фокус автоматически устанавливается на кнопку «Начинаем сначала». Первое нажатие во время гонки на клавишу со стрелкой приводит не к выбору направления машиной, а к перескакиванию фокуса с кнопки на другой объект формы. Если же вы вместо этого нажимаете на пробел, то машина дает газ, но радоваться тоже рано. Кнопки воспринимают пробел и Enter, как приказ на нажатие, и поэтому кнопка «Начинаем сначала» тут же сама собой нажимается и получается, что немедленно после старта газоны перерисовываются, а машина снова прыгает на старт.
Чтобы прекратить это безобразие, поместим на форму текстовое поле. Позже оно нам пригодиться для отображения счетчика времени, а сейчас у нас другая забота: увести от вредной кнопки фокус куда-нибудь подальше. Текстовое поле ведет себя смирно, назовем его txtВремя и включим в процедуру оператор, уводящий фокус с кнопки на это поле:
txtВремя.Focus()
Помогло. При нажатии клавиш со стрелками фокус не покидает текстовое поле. Но осталась одна шероховатость. Если в текстовом поле есть текст, то при нажатии клавиш со стрелками текстовый курсор будет слоняться по текстовому полю влево-вправо, а при нажатии пробела – вставлять в текст пробел. Чтобы от этого избавиться и чтобы предотвратить в дальнейшем возможность нечаянно с клавиатуры испортить показания счетчика времени, помещаем в процедуру Form1_Load оператор
txtВремя.ReadOnly = True
Это означает, что содержимое текстового поля можно только читать, но вручную не менять. Изменения возможны только программным путем.
Теперь все нормально.
Результат. После ввода всего вышеприведенного кода у вас при каждом нажатии на кнопку Начинаем сначала должны рисоваться поле, старт, финиш и новая конфигурация газонов, машина должна нормально вставать на старт, а при нажатии нужных клавиш должна нормально ускоряться, тормозить и ездить по всей форме во всех направлениях, не разбирая пока, где асфальт, где преграды и все остальное.
Третья часть – Поведение машины, организация счетчиков и пр.
Я уже говорил, что на каждом импульсе таймера автомобиль должен проделать весь цикл своего функционирования. Из этого цикла нам осталось рассмотреть два дела. Автомобиль должен:
Определить, где он находится (асфальт, газон, ограждение, финиш) и действовать соответственно
Изменить нужным образом показания приборов на пульте управления
Код третьей части. Приведу последнюю порцию кода в нашем проекте. Этим кодом нужно дополнить первые две части программы, чтобы получилась работающая версия проекта.
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.
Исправьте недоработки проекта.
Гонки двух автомобилей
Если вы хотите играть вместе с другом, вам нужны гонки двух машин. Несмотря на то, что ничего нового вам придумывать не придется, размер программы вырастет. Вам придется иметь по два экземпляра переменных, описывающих поведение каждой машины. Например, вместо переменной Руль вам придется иметь две переменные – Руль1 и Руль2. Чтобы не писать по два экземпляра процедур, задающих поведение каждой машины, вам нужно будет снабдить эти процедуры параметрами. Процедуру Form1_KeyDown вам придется дополнить новыми клавишами для управления второй машиной. При этом нужно учесть, что удерживать клавиши нажатыми будет теперь нельзя без усложнения программы. А если у вас гонки 10 автомобилей? Проблемы нарастают.
VB конечно же предлагает универсальное решение этой проблемы. Он, как и все серьезные и мощные языки программирования, является объектно-ориентированным языком. В частности, имеется в виду, что он позволяет программисту создавать собственные классы объектов. Научившись создавать собственные классы, вы сможете создать класс «автомобиль» и пользоваться каким угодно числом экземпляров этого класса. Программу поведения автомобиля вы пишете один раз и только для одного автомобиля. Остальные автомобили будут автоматически пользоваться ей же. Как все это делается, описывается в Глава 22. , но вам пока рано читать тот материал.
Задания на проекты
Нельзя научиться кататься на велосипеде, только следя за тем, как катаются другие. Нужно самому сесть на велосипед. Вы не научитесь создавать проекты, только разбирая их создание по книжке. Нужно создать свой собственный проект. В Задание 97 вы уже создавали небольшой проект «Шахматные часы». Пришла пора создать проект посложнее и побольше размером. Я предложу вам на выбор один из двух.
Торпедная атака. Начинается все красивой заставкой с названием игры. По щелчку мыши заставка пропадает. Компьютер спрашивает ваше имя, чтобы во время игры оно горело красивыми яркими буквами в углу экрана. В меню вы можете посмотреть правила игры, сведения об авторе, выбрать игру со звуком или без. Но вот игра началась. На экране морской пейзаж (нарисованный вами или взятый готовым). Вы – капитан подводной лодки и смотрите в перископ. Вдали экрана по горизонтали плывет вражеский корабль. У нижнего края экрана находится ваш торпедный аппарат. Вы целитесь, с клавиатуры или мышкой поворачивая торпедный аппарат. В подходящий момент времени вы нажимаете клавишу – и торпеда плывет вдаль, уменьшаясь по мере удаления. Если вы попали, то экран озаряется вспышкой от взрыва, на мгновение виден и сам взрыв, раздается коротенькая торжествующая мелодия, на экране – коротенький поздравительный текст, счетчик подбитых кораблей на экране увеличивается на 1. Если не попали, то зрительные и звуковые эффекты, сами понимаете – совсем другие. В любом случае уменьшается на 1 счетчик оставшихся торпед. Затем плывет следующий корабль. И так далее. Когда торпеды у вас кончатся (скажем, их было 10), игра заканчивается. Программа анализирует ваши успехи и в зависимости от них выдает на экран текст, скажем «Мазила!», если вы не попали ни разу из 10, или «Профи!», если вы попали 8 раз. Затем спрашивает, будете ли вы играть еще.
Помощь: Как компьютер определит, попала торпеда в корабль или не попала? Нужно в тот момент, когда торпеда доплывет до линии движения корабля, сравнить горизонтальные координаты корабля и торпеды, и если они достаточно близки, считать, что попала.
Улучшение. Если у всех кораблей будет одинаковая скорость, то попадать будет слишком просто, а значит и играть неинтересно. Сделайте скорость кораблей случайной в разумных пределах. Причем не нужно менять скорость одного и того же корабля в процессе движения. Пусть она остается постоянной, а то все будет зависеть не от вашего мастерства, а от везения. Различаются скорости только разных кораблей.
Пусть игрок в меню сможет выбирать из нескольких уровней трудности. Трудность удобнее всего увеличивать, уменьшая размеры корабля, то есть требуемую величину близости координат корабля и торпеды при определении попадания.
Теннис с компьютером. На экране теннисный корт, вид сверху (см. Рис. 14.2).
Рис. 14.2
Слева – большая ракетка компьютера, справа – маленькая ваша. Сверху и снизу – края корта. Мяч летает по корту, отскакивая от его краев по закону отражения (что такое «по закону отражения», объяснено в Задание 102). Ракетка компьютера совершает равномерные, медленные, бессмысленные возвратно-поступательные движения вверх-вниз, отскакивая от краев корта. Движением вашей ракетки управляете вы. Или с клавиатуры, или мышкой. Вы можете двигать ракетку вверх или вниз с постоянной скоростью, или останавливать ее. Влево-вправо ракетки не движутся. Наткнувшись на ракетки, мяч отскакивает. Если ракетка неподвижна, то мяч отскакивает по закону отражения. Если ракетка движется, то на направление отскока мяча немного влияет направление движения ракетки. Цель – не пропустить мяч за пределы корта. Если мяч улетит направо – очко проиграли вы, налево – проиграл компьютер.
Красивая заставка, меню, счет партий, подведение итогов, зрительные и звуковые эффекты должны быть не хуже, чем у «Торпедной атаки».
Помощь: Как компьютер определит, попал мяч в ракетку или не попал? Нужно в тот момент, когда мяч долетит до линии движения ракетки, сравнить вертикальную координату мяча с вертикальными координатами верхнего и нижнего краев ракетки, и если мяч находится между краями ракетки, считать, что попал.
Улучшение. Пусть игрок сможет в меню выбирать из нескольких уровней трудности. Трудность удобнее всего увеличивать, увеличивая скорость мяча.
Может возникнуть не совсем естественная ситуация, когда скорость мяча в результате ударов о ракетки приобретет слишком большую вертикальную составляющую. Тогда мяч будет в основном биться между верхним и нижним краями корта, лишь изредка попадая на ракетки. Возможно, имеет смысл вертикальную составляющую скорости ограничить.
Чтобы избежать бесконечной нудной перекидки мяча, можно при отскоке от движущейся ракетки увеличивать горизонтальную составляющую скорости на очень маленькое случайное число. Тогда скорость мяча от удара к удару будет постепенно возрастать, и скучно не будет.
Другие проекты. Вам совсем не обязательно выполнять именно одно из двух предложенных мною заданий. Если вам хочется создать проект на другую тему – на здоровье! Но помните, задуманный вами проект, легкий на вид, на деле может оказаться слишком сложным для выполнения и вам придется его бросить. Или наоборот – слишком простым, и вы не получите от его выполнения большой пользы.
_______________ _ _________________
Мы с вами закончили первый из двух циклов знакомства с Visual Basic.NET. Если вам удался последний проект и он работает не хуже, чем указано в задании (пусть даже и без звука), если вы при этом умело применяли переменные величины и давали им длинные имена, если у вас в проекте много маленьких (а не мало больших) процедур, то все в порядке: у вас должна появиться уверенность, что теперь вы можете создавать проекты любого размера и любой сложности, а значит цель этого цикла достигнута. Я вас поздравляю – вам присваивается звание «Программист-любитель III ранга»!
Проект – Гонки (игра)
Мы с вами создали уже два правильно написанных больших проекта: «Парк под луной» и «Будильник». И этого, конечно, мало. У нас нет опыта создания проектов с движением объектов по форме, да к тому же под управлением мыши и клавиатуры. Поэтому я решил создать с вами такой проект (строк на 150). На этот раз это будет игра.
Переменные с индексами
В основе массивов лежит понятие индекса. В математике широко применяются так называемые индексированные переменные. На бумаге они записываются так:
x1 x2 b8 yi yi-6 z i j z i+1 j
а читаются соответственно так: икс первое, икс второе, бэ восьмое, игрек итое, игрек и минус шестое, зет итое житое, зет и плюс первое житое. Все эти маленькие подстрочные цифры и выражения называются индексами. Поскольку в алфавите VB нет подстрочных букв и цифр, то те же индексированные переменные в VB приходится обозначать так:
X(1) X(2) B(8) Y(i) Y(i-6) Z(i,j) Z(i+1, j)
Числа Фибоначчи. Зачем математикам нужны индексированные переменные? Ну, их удобно применять хотя бы при операциях над числовыми рядами. Числовой ряд – это просто несколько чисел, выстроенных по порядку одно за другим. Чисел в ряду может быть много и даже бесконечно много.
Возьмем, например, бесконечный ряд чисел Фибоначчи:
1 1 2 3 5 8 13 21 34 .....
Попробуйте догадаться, по какому закону образуются эти числа. Если вы сами не догадались, то я подскажу: каждое из чисел, начиная с третьего, является суммой двух предыдущих. А теперь попробуем записать это утверждение с помощью языка математики. Для этого обозначим каждое из чисел Фибоначчи индексированной переменной таким образом:
Первое число Фибоначчи обозначим так: f(1),
Второе число Фибоначчи обозначим так: f(2)
и т.д.
Тогда можно записать, что
f(1)=1 f(2)=1 f(3)=2 f(4)=3 f(5)=5 f(6)=8 ......
Очевидно, что
f(3)=f(1)+f(2),
f(4)=f(2)+f(3),
f(5)=f(3)+f(4)
и т.д.
Как математически одной формулой записать тот факт, что каждое
из чисел является суммой двух предыдущих? Математики в индексном виде записывают это так:
f(i)=f(i-2)+f(i-1).
Для пояснения этой формулы подставим вместо i любое число, например, 6. Тогда получится:
f(6)=f(6-2)+f(6-1)
или упрощая:
f(6)=f(4)+f(5),
что соответствует определению чисел Фибоначчи.
Какое бы i, большее 2, мы не подставляли, получается правильное равенство. Значит, формула верна сама по себе.
Задание 101.
Запишите на бумаге в индексном виде, как получается из предыдущего числа ряда последующее:
1) 14 18 22 26 .....
2) 6 12 24 48 ....
3) 3 5 9 17 33 65 ....
Еще примеры. Вот еще примеры, когда математики предпочитают использовать индексы. Пусть мы на протяжении года каждый день раз в сутки измеряли температуру за окном. Тогда вполне естественно обозначить через t(1) температуру первого дня года, t(2) – второго, ..... , t(365) – последнего. Пусть 35 спортсменов прыгали в высоту. Тогда через h(1) можно обозначить высоту, взятую первым прыгуном, h(2) – вторым и т.д.
Пока мы только немного привыкли к индексам, но никакой выгоды от них не получили. Выгода – в следующем разделе.
Основы работы с одномерными массивами
Пример. Рассмотрим на примере простой задачи, как VB управляется с массивами. Предположим, в зоопарке живут три удава. Известна длина каждого удава в сантиметрах (500, 400 и 600). Какая длина получится у трех удавов, вытянутых в линию?
Обозначим длину первого удава – dlina(1), второго – dlina(2), третьего – dlina(3). Прикажем VB отвести под эту индексированную переменную массив ячеек в памяти. Делается это так:
Dim dlina (3) As Integer
Здесь 3 - верхняя граница индекса. В целом эту строку можно перевести так: Отвести в памяти под переменную dlina ряд ячеек типа Integer, пронумерованных от 0 до
3.
Почему от 0? Нам не нужно от 0! Нам нужно от 1! – Ну, это уже ваши проблемы – отвечает VB – я могу только от 0! – Ну, что ж – отвечаем мы – в конце концов, пусть будет от 0. В конце концов, это означает всего лишь то, что в памяти будет отведена лишняя ячейка для значения dlina(0). А мы ее просто не будем использовать, и все! И никаких значений туда не будем записывать. И никаких проблем.
Вот программа полностью:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim dlina(3) As Integer
Dim summa As Integer
dlina(1) = 500
dlina(2) = 400
dlina(3) = 600
'В этот момент в трех ячейках памяти уже находятся числа
'и с ними можно выполнять арифметические действия
summa = dlina(1) + dlina(2) + dlina(3)
Debug.WriteLine(summa)
End Sub
А теперь запустите отладочный пошаговый режим выполнения программы и загляните в окне Locals в значения dlina(1), dlina(2), dlina(3), summa. Заметьте на будущее, что желтая полоса не перескакивает через оператор
Dim dlina(3) As Integer
и плюсик в окне Locals у переменной dlina появляется только после его выполнения. Жмите на плюсик – и перед вами значения всех 4 элементов массива.
Инициализация массива. В предыдущем примере мы задавали значения элементам массива простым присвоением. Можно сделать это короче:
Мощь одномерных массивов
Внутри скобок мы можем писать не только числа, но и переменные, и выражения. От этого программа сразу станет с непривычки менее понятной, однако приобретет необыкновенную мощь.
Привыкаем к переменным и выражениям в качестве индексов. Сначала мы должны привыкнуть к переменным и выражениям в качестве индексов. Вот три эквивалентных варианта программы, распечатывающей длины удавов. Вы должны без компьютера убедиться, что все три варианта распечатают одно и то же.
Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button6.Click
Dim dlina() As Integer = {0, 500, 400, 600}
Debug.WriteLine(dlina(1))
Debug.WriteLine(dlina(2))
Debug.WriteLine(dlina(3))
End Sub
Private Sub Button7_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button7.Click
Dim dlina() As Integer = {0, 500, 400, 600}
Dim i As Integer
i = 1
Debug.WriteLine(dlina(i))
i = 2
Debug.WriteLine(dlina(i))
i = i + 1
Debug.WriteLine(dlina(i))
End Sub
Private Sub Button8_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button8.Click
Dim dlina() As Integer = {0, 500, 400, 600}
Dim i As Integer = 1
Dim k As Integer = 10
Debug.WriteLine(dlina(i))
Debug.WriteLine(dlina(k - 8))
i = 2
k = 1
Debug.WriteLine(dlina(k + i))
End Sub
Мощи здесь пока не видно, увидите чуть позже.
Вопрос: Продолжаем привыкать. Не подходя к компьютеру, ответьте, что напечатает следующая процедура:
Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
Dim a(100), i As Integer
i = 2 : a(3) = 10 : a(i) = 100 : a(i + 6) = a(i) + a(5 - i) : i = 0 : Debug.WriteLine(a(i + 3) + a(2) + a(i + 8))
End Sub
Ответ: 220
Хорошо бы вы получили правильный ответ самостоятельно. Если не смогли получить, значит вы не понимаете чего-то важного или просто невнимательны. В этом случае читайте пояснение:
Первая ступенька: i = 2 ® a(3) = 10 ® a(2) = 100 ® a(2 + 6) = a(2) + a(5 - 2) ® i = 0 ® Debug.WriteLine( a(0 + 3) + a(2) + a(0 + 8))
Вторая ступенька: i = 2 ® a(3) = 10 ® a(2) = 100 ® a(8) = a(2) + a(3) ® i = 0 ® Debug.WriteLine( a(3) + a(2) + a(8))
Третья ступенька: i = 2 ® a(3) = 10 ® a(2) = 100 ® a(8) = 110 ® i = 0 ® Debug.WriteLine( 10 + 100 + 110)
Цикл – мощное оружие при работе с массивами. Теперь решим задачу про суммарную длину удавов в предположении, что удавов не 3, а 1000:
Private Sub Button9_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button9.Click
Dim Число_удавов As Integer = 1000
'Верхняя граница индекса может быть выражена переменной величиной:
Dim dlina(Число_удавов) As Integer
Dim summa, i As Integer
'Вводим с клавиатуры длины тысячи удавов, хоть это и утомительно и никто так не делает.
'Здесь на первом выполнении цикла i=1 и поэтому компьютер вводит число в ячейку dlina(1),
'на втором - i=2 и поэтому компьютер вводит число в ячейку dlina(2) и т.д.
For i = 1 To Число_удавов
dlina(i) = InputBox("Введите длину " & i & "-го удава")
Next
'Определяем суммарную длину тысячи удавов:
summa = 0
For i = 1 To Число_удавов
summa = summa + dlina(i)
Next
Debug.WriteLine(summa)
End Sub
Отлаживая эту программу, возьмите, конечно, вместо числа 1000 какое-нибудь маленькое число.
Пример. Продолжаем привыкать к индексам. Решим еще одну задачу. Дан ряд из 10 произвольных чисел: a(1), a(2), ... , a(10). Подсчитать и напечатать суммы каждой из восьми троек стоящих рядом чисел.
первая тройка: a(1)+a(2)+a(3)
вторая тройка: a(2)+a(3)+a(4)
третья тройка: a(3)+a(4)+a(5)
......
восьмая тройка: a(8)+a(9)+a(10)
Вот программа:
Private Sub Button10_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button10.Click
Dim a() As Integer = {0, 5, 3, 4, 0, 20, 10, 23, 2, 9, 1000}
Dim i As Integer
For i = 1 To 8
Debug.WriteLine(a(i) + a(i + 1) + a(i + 2))
Next
End Sub
Вот что она напечатает:
12
7
24
30
53
35
34
1011
Следующие два задания очень важны, так как это ваши первые задания по массивам.
Задание 102.
Напишите с использованием массива программу вычисления среднегодовой температуры. Подсчитайте также количество теплых дней в году (когда температура выше 20 градусов). Узнайте, каким по порядку идет самый жаркий день. (Для отладки в компьютере годом можно считать неделю.)
Задание 103.
Вычислить и распечатать первые 139 чисел Фибоначчи.
Одномерные массивы
Одна из типичных задач программирования формулируется примерно так. Имеется большое количество данных, например, тех же температур или высот. С этими данными компьютер должен что-нибудь сделать, например, вычислить среднегодовую температуру, количество морозных дней, максимальную взятую высоту и т.п. Раньше мы уже вычисляли подобные вещи, и при этом данные вводили в компьютер с клавиатуры одно за другим. При этом всегда получалось, что они вводятся в одну и ту же ячейку памяти (см. Глава 10. ). Однако, программистская практика показывает, что удобно, а часто и необходимо иметь данные в оперативной памяти сразу все, а не по очереди. Тогда для задачи, скажем, про температуру нам понадобится 365 ячеек. Эти 365 ячеек мы и назовем массивом. Итак, массивом можно назвать ряд ячеек памяти, отведенных для хранения значений индексированной переменной. На вопрос о том, как большое количество значений оказывается в памяти, отвечу, что обычно они вводятся из файла (19.2).
Двумерные массивы
Поясним суть двумерных массивов на простом примере. Пусть на целом ряде метеостанций, расположенных в разных точках земного шара, в течение многих дней измеряли температуру воздуха. Показания термометров свели в таблицу. Ограничимся для экономии места тремя станциями и четырьмя днями.
1-й день | 2-й день | 3-й день | 4-й день | ||||||
Метеостанция 1 | -8 | -14 | -19 | -18 | |||||
Метеостанция 2 | 25 | 28 | 26 | 20 | |||||
Метеостанция 3 | 11 | 18 | 20 | 25 |
Требуется (в порядке возрастания трудности):
1) Распечатать температуру на 2-й метеостанции за 4?й день и на 3-й метеостанции за 1?й день.
2) Распечатать показания термометров всех метеостанций за 2?й день
3) Определить среднюю температуру на 3-й метеостанции
4) Распечатать всю таблицу
5) Распечатать, в какие дни и на каких метеостанциях температура была в диапазоне 24-26 градусов тепла
Для этого обозначим показания термометров при помощи индексированной переменной с двумя индексами по следующей схеме:
t(1,1) | t(1,2) | t(1,3) | t(1,4) | ||||
t(2,1) | t(2,2) | t(2,3) | t(2,4) | ||||
t(3,1) | t(3,2) | t(3,3) | t(3,4) |
Первый индекс в скобках обозначает номер строки (метеостанция), второй – номер столбца (день) прямоугольной таблицы. Такую таблицу математики называют матрицей.
В памяти отводим массив из ячеек типа Integer под значения индексированной переменной t. Будем называть его двумерным массивом:
Dim t (3, 4) As Integer
Аналогично одномерному массиву, первый индекс здесь будет меняться не от 1 до 3, а от 0 до 3, а второй – от 0 до 4. Таким образом в памяти будет отведен следующий массив ячеек:
t(0,0) | t(0,1) | t(0,2) | t(0,3) | t(0,4) | |||||
t(1,0) | t(1,1) | t(1,2) | t(1,3) | t(1,4) | |||||
t(2,0) | t(2,1) | t(2,2) | t(2,3) | t(2,4) | |||||
t(3,0) | t(3,1) | t(3,2) | t(3,3) | t(3,4) |
Мы просто не будем пользоваться верхней строкой и левым столбцом.
Программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t(3, 4) As Integer
Dim i, j, s As Integer
' Зададим значения элементов массива примитивным присваиванием:
t(1, 1) = -8 : t(1, 2) = -14 : t(1, 3) = -19 : t(1, 4) = -18
t(2, 1) = 25 : t(2, 2) = 28 : t(2, 3) = 26 : t(2, 4) = 20
t(3, 1) = 11 : t(3, 2) = 18 : t(3, 3) = 20 : t(3, 4) = 25
'Выполняем 1 пункт задания:
Debug.WriteLine(t(2, 4) & " " & t(3, 1))
'А теперь распечатаем второй столбец массива (2 пункт задания):
Debug.Write("Второй столбец: ")
For i = 1 To 3 : Debug.Write(t(i, 2) & " ") : Next
Debug.WriteLine("") 'Переводим строку
'Определим среднее значение элементов третьей строки (3 пункт задания):
i = 3
s = 0
For j = 1 To 4 : s = s + t(i, j) : Next
Debug.WriteLine("Средняя температура равна " & s / 4)
'Распечатаем всю таблицу (4 пункт задания):
For i = 1 To 3
For j = 1 To 4
Debug.Write(t(i, j) & " ")
Next j
Debug.WriteLine("") 'Переводим строку
Next i
'Распечатаем станции и дни с температурой 24-26 градусов (5 пункт задания):
For i = 1 To 3
For j = 1 To 4
If t(i, j) >= 24 And t(i, j) <= 26 Then Debug.WriteLine("Станция " & i & " день " & j)
Next j
Next i
End Sub
Вот что напечатает эта программа:
20 11
Второй столбец: -14 28 18
Средняя температура равна 18,5
-8 -14 -19 -18
25 28 26 20
11 18 20 25
Станция 2 день 1
Станция 2 день 3
Станция 3 день 4
Инициализация двумерного массива. Вместо фрагмента, объявляющего массив и задающего его значения присваиванием:
Dim t(3, 4) As Integer
t(1, 1) = -8 : t(1, 2) = -14 : t(1, 3) = -19 : t(1, 4) = -18
t(2, 1) = 25 : t(2, 2) = 28 : t(2, 3) = 26 : t(2, 4) = 20
t(3, 1) = 11 : t(3, 2) = 18 : t(3, 3) = 20 : t(3, 4) = 25
можно использовать более короткую запись:
Dim t ( , ) As Integer = { {99, 99, 99, 99, 99} , {99, -8, -14, -19, -18} , {99, 25, 28, 26, 20} , {99, 11, 18, 20, 25} }
Разберемся в ней. Верхние границы индексов не указываются, но запятая в круглых скобках остается, чтобы было понятно, что массив двумерный, а не одномерный. Чисел в фигурных скобках нужно столько, чтобы заполнить столбцы 0,1,2,3,4 и строки 0,1,2,3. Каждую строку берем в свои фигурные скобки и отделяем от других строк запятой. Весь массив снаружи «обнимается» дополнительной парой фигурных скобок. Ненужные нам нулевую строку и нулевой столбец я заполнил числами 99, а мог бы и любыми другими, так как в программе мы их не используем.
Задание 104.
Вычислить разницу между максимальной и минимальной температурой во всей таблице.
Какие бывают массивы
Массивы бывают не только числовые, но и строковые и типа Date и многие прочие. Например:
Dim s(50) As String
Это означает, что в каждой из 51 ячеек должно находиться не число, а произвольная строка. А объявление
Dim DT(10) As Date
означает, что в каждой из 11 ячеек должна находиться дата.
Пример. Вот элементарный пример использования строкового массива:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim s(50) As String
s(21) = "Привет"
s(22) = s(21) + " всем!!!"
Debug.WriteLine(s(22))
Debug.WriteLine(Len(s(21)))
End Sub
Вот что напечатает эта программа:
Привет всем!!!
6
Пример. Вот пример работы с массивами других типов:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim b(30, 6) As Boolean
Dim DT(10) As Date
b(2, 3) = False
b(5, 0) = Not b(2, 3)
Debug.WriteLine (b(5, 0))
DT(2) = #1/15/2003 11:59:42 PM#
DT(0) = DT(2).AddDays(10)
If b(5, 0) Then Debug.WriteLine (DT(0))
End Sub
Вот что напечатает эта программа:
True
25.01.2003 23:59:42
Еще пример:
Enum типРуль
вверх
влево
вниз
вправо
End Enum
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim Руль(300) As типРуль
Руль(200) = типРуль.влево
Руль(220) = Руль(200) + 1
Debug.WriteLine(Руль(220))
End Sub
Вот что напечатает эта программа:
вниз
Бывают массивы, состоящие из структур, объектов, но о них мы поговорим позже.
Многомерные массивы. Массивы могут быть одномерные, двумерные, трехмерные, четырехмерные и т.д.:
Dim a (10) As Integer | -одномерный массив | 11 ячеек | |||
Dim a (10, 5) As Integer | -двумерный массив | 66 ячеек (11*6) | |||
Dim a (9, 4, 1) As Integer | -трехмерный массив | 100 ячеек (10*5*2) | |||
Dim a (9, 4, 1, 2) As Integer | -четырехмерный массив | 300 ячеек (10*5*2*3) |
Использование массивов при программировании игр
Идеология. Есть ли польза от массивов при программировании игр? Вопрос праздный. Массивы необходимы и для шахмат, и для шашек, и для морского боя, и для крестиков-ноликов, и для многих других игр, в особенности для тех, где игра проходит на прямоугольном поле, расчерченном на квадраты. Возьмем для примера игру против компьютера в крестики-нолики на поле размером 3 на 3. Компьютеру приходится здесь рисовать на экране большие клетки, а в них – нолики (кружочки) после ваших ходов и крестики (пересекающиеся косые линии) после своих. Но этого умения недостаточно. Компьютеру ведь еще надо соображать, куда ставить крестики. А для этого нужно как минимум знать, где уже стоят крестики и нолики. А откуда он это знает? Если знание об этом хранится только на экране, то это очень неудобно, так как анализировать информацию о пикселях экрана трудно. Гораздо разумнее заранее организовать массив Dim a (3, 3) As Integer и записывать туда в нужные места нолики после ходов человека и, скажем, единички после ходов компьютера. Сразу же после записи в элемент массива нуля или единицы программа должна рисовать в соответствующем месте экрана кружок или крестик. Мыслить компьютер мог бы при помощи примерно таких операторов –
If a(1,1)=0 And a(1,2)=0 Then a(1,3)=1
Это очевидный защитный ход компьютера – на два кружочка в ряду он ставит в тот же ряд крестик.
Итак, сделаем вывод, что массив в памяти компьютера и поле для игры на экране в любой момент времени соответствуют друг другу, но компьютеру удобнее глядеть не на экран, а в память.
Проиллюстрируем идею использования массивов в играх подробнее, на специально придуманном примитивном примере (типа морского боя, но гораздо проще).
Задание на создание игры: Играют друг против друга два человека на квадратном поле размером 2 на 2:
Компьютер в игре участвует только как судья, а не как игрок.
Правила: Сначала первый игрок тайком от второго сообщает компьютеру, в каких двух клетках находятся его одноклеточные корабли (например, в правой верхней и правой нижней). Затем второй сообщает компьютеру, в какие клетки он производит два выстрела (тоже, конечно, наугад – например, в левую верхнюю и правую нижнюю). Если подбиты оба корабля – он выиграл, если подбит один – ничья, если ни одного – выиграл первый игрок.
Сначала запрограммируем эту игру без графики, а потом с графикой.
Поле боя должно быть показано на экране только один раз – после двух выстрелов, причем, если без графики, то в виде распечатки из 4 букв:
м к
о х
Здесь я использовал такие обозначения:
о - корабля здесь нет и сюда не стреляли
к - неподбитый корабль
х - подбитый корабль
м - мимо (стреляли и промахнулись)
Других вариантов быть не может. Вы видите, что приведенная распечатка отражает результат расстановки кораблей и выстрелов, описанных мной в качестве примера.
Вот программа без графики:
Dim a(2, 2) As String 'Поле боя
Dim i, j, Подбито As Integer
'Главная процедура:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
a(1, 1) = "о" : a(1, 2) = "о" 'Поначалу на поле боя кораблей нет
a(2, 1) = "о" : a(2, 2) = "о"
Устанавливаем_корабль(1)
Устанавливаем_корабль(2)
Подбито = 0 'Пока не стреляли
Выстрел(1)
Выстрел(2)
Показываем_поле_боя()
Debug.WriteLine(Подбито) 'Показываем исход битвы - количество подбитых кораблей
End Sub
Sub Устанавливаем_корабль(ByVal Номер_корабля As Integer)
i = InputBox("Первый игрок, назовите номер строки для корабля " & Номер_корабля)
j = InputBox("Первый игрок, назовите номер столбца для корабля " & Номер_корабля)
a(i, j) = "к"
End Sub
Sub Выстрел(ByVal Номер_выстрела As Integer)
i = InputBox("Второй игрок, назовите номер строки для выстрела " & Номер_выстрела)
j = InputBox("Второй игрок, назовите номер столбца для выстрела " & Номер_выстрела)
If a(i, j) = "к" Then 'Если попал, то
a(i, j) = "х" 'ставим крестик
Подбито = Подбито + 1 'и увеличиваем счетчик подбитых кораблей
ElseIf a(i, j) = "о" Then 'иначе если промахнулся, то
a(i, j) = "м" 'ставим м
End If
End Sub
Sub Показываем_поле_боя()
For i = 1 To 2
For j = 1 To 2
Debug.Write(a(i, j))
Next j
Debug.WriteLine("")
Next i
End Sub
Пояснения: Вы видите, что в качестве массива, представляющего поле для игры, я выбрал строковый массив
Dim a(2, 2) As String 'Поле боя
Теперь прочтите главную процедуру. Убедитесь, что она правильно отражает основной порядок действий в процессе игры. Затем разберитесь в процедурах Устанавливаем_корабль и Выстрел. Они с параметрами. Наконец, разберитесь в процедуре Показываем_поле_боя.
Программа не объявляет итогов боя, а всего лишь печатает количество подбитых кораблей. Определение и объявление победителя оставляю вам.
Обратите внимание, что игра мгновенно переделывается из игры на поле 2 на 2 в игру на поле, скажем, 30 на 30, простой заменой числа 2 в тексте программы на число 30. Этой возможностью мы наслаждаемся только благодаря использованию массива! В этом случае, конечно, придется в цикле записать во все клеточки поля букву «о». А если мы хотим при этом иметь больше двух кораблей и двух выстрелов, нам придется в главной процедуре обратиться в цикле к процедуре Устанавливаем_корабль и в цикле к процедуре Выстрел, что очень просто.
Если бы мы пренебрегли секретностью, то могли бы показывать поле боя после каждого хода игроков. Для этого достаточно в конец процедур Устанавливаем_корабль и Выстрел включить строку
Показываем_поле_боя()
Программа с графикой. Придумаем для простоты такую графику. Поле состоит из 4 цветных квадратов. Вот возможные цвета:
Голубой квадрат - корабля здесь нет и сюда не стреляли
Серый квадрат - неподбитый корабль
Красный квадрат - подбитый корабль
Зеленый квадрат - мимо (стреляли и промахнулись)
Замечательно, что от добавлении графики программа абсолютно не изменится за исключением единственной процедуры Показываем_поле_боя. Причем и в ней-то вся структура цикла останется неизменной. По большому счету выкинем только строку
Debug.WriteLine("")
как нужную только для текстового вывода, а строку, печатающую очередную букву из четырех:
Debug.Write(a(i, j))
заменим фрагментом, рисующим очередной квадрат из четырех. Вот новая процедура Показываем_поле_боя:
Sub Показываем_поле_боя()
Dim Размер As Integer = 100 'Размер квадрата
Dim Гр As Graphics = Me.CreateGraphics
Dim Кисть_для_воды As New SolidBrush(Color.LightBlue)
Dim Кисть_для_корабля As New SolidBrush(Color.Gray)
Dim Кисть_для_попадания As New SolidBrush(Color.Red)
Dim Кисть_для_промаха As New SolidBrush(Color.Green)
Dim Кисть As SolidBrush 'Текущая кисть для квадрата
Гр.Clear(Color.White) 'Стираем поле, нарисованное после предыдущего хода
For i = 1 To 2
For j = 1 To 2
Select Case a(i, j) 'Выбираем кисть для очередного квадрата
Case "о" : Кисть = Кисть_для_воды
Case "к" : Кисть = Кисть_для_корабля
Case "х" : Кисть = Кисть_для_попадания
Case "м" : Кисть = Кисть_для_промаха
End Select 'Рисуем очередной квадрат:
Гр.FillRectangle(Кисть, Размер * j, Размер * i, Размер, Размер)
Next j
Next i
End Sub
Пояснения: Предположим, наша процедура работает после каждого хода игроков. Мы могли бы написать ее так, чтобы после каждого очередного хода (поставили корабль или выстрелили) компьютер перерисовывал только тот квадрат, о котором шла речь. Остальные ведь остались неизменными – чего их перерисовывать? В этом случае компьютеру пришлось бы «меньше трудиться». Но я пошел по более простому и универсальному пути – после каждого хода все поле со всеми квадратами стирается и рисуется заново согласно содержимому массива a.
Кстати, строка
Гр.Clear(Color.White) 'Стираем поле, нарисованное после предыдущего хода
в нашем случае излишня. Ведь мы все равно заново перерисовываем все квадраты поля, поэтому предварительно стирать их не имеет смысла.
Крестики-нолики 3х3 – советы. В принципе вы уже готовы к программированию игры против компьютера в обычные крестики-нолики 3х3. Всю техническую сторону дела мы прошли. Остается логика, то есть объяснение компьютеру, куда ставить крестики. И вот с логикой-то у нас будет проблема. Вы скажете: Какая проблема? – ведь клеточек всего 9 штук! Один из возможных операторов уже написан:
If a(1,1)=0 And a(1,2)=0 Then a(1,3)=1
Напишу еще пару десятков подобных операторов – и дело с концом! – А пару сотен не хотите?! Вы только попробуйте перебрать все возможные варианты расстановки крестиков, ноликов и пустых клеток! Их вообще несколько тысяч. В этом случае, чтобы сократить программу, нужно применять в качестве индексов переменные величины и ломать голову над тем, какие писать процедуры, ветвления и циклы.
Поэтому любителям игр рекомендую для тренировки запрограммировать крестики-нолики не против компьютера, а как игру человека с человеком, где компьютер – лишь судья. Если получится, вот тогда можно замахнуться и на большее. Но и здесь идите постепенно. Рекомендую написать большую процедуру для правильной простановки крестика в произвольном одномерном
массиве из трех клеток. У этой процедуры будет три параметра – по числу клеток. Затем заметьте, что в реальной игре 3х3 вас интересует только 8 рядов: 3 по горизонтали, 3 по вертикали и 2 по диагонали. Значит у вас будет 8 обращений к этой процедуре. Дальше думайте сами. Все это совсем не просто.
Массивы как объекты
Оказывается, массив – это объект. Объект класса Array пространства имен System. Как?! – скажете вы, – мы до сих пор прекрасно работали с массивами и, как говорится, «ни сном, ни духом»! Мы нигде не писали New, не пользовались свойствами и методами массивов. – Что ж, верно, многим программистам вполне можно работать с массивами и не подозревать, что это объекты. Авторы VB замаскировали этот факт (как мне кажется), чтобы не пугать программистов, переходящих с Visual Basic 6.0 на VB. Массивы-объекты рождаются в вашей программе «нечувствительно» для вас безо всякого New.
И все же, вот как можно создать массив при помощи New:
Dim a() As Integer = New
Integer() {8, 1, 4, 3}
Нам будут полезны некоторые свойства и методы массивов (см. процедуру):
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim a() As Integer = {80, 60, 50, 90, 40, 20, 50, 70}
Dim t(,) As Integer = {{99, 99, 99, 99, 99}, {99, -8, -14, -19, -18}, {99, 25, 28, 26, 20}, {99, 11, 18, 20, 25}}
Debug.WriteLine(a.Length) 'Длина массива a (число элементов) = 8
Debug.WriteLine(t.Length) 'Длина массива t (число элементов) = 20
Debug.WriteLine(t.GetUpperBound(0)) 'Число строк (макс. индекс первого измерения) - 1 = 3
Debug.WriteLine(t.GetUpperBound(1)) 'Число столбцов (макс. индекс второго измерения) - 1 = 4
'Ищется первое вхождение числа 50 в одномерный массив a и находится его индекс (2):
Debug.WriteLine(Array.IndexOf(a, 50))
'Ищется последнее вхождение числа 50 в одномерный массив a и находится его индекс (6):
Debug.WriteLine(Array.LastIndexOf(a, 50))
Debug.WriteLine(Array.IndexOf(a, 55)) 'Ищется число 55 в массиве a и не находится (-1)
Array.Reverse(a) 'Все элементы массива a меняют порядок на обратный = {70, 50, 20, 40, 90, 50, 60, 80}
Array.Sort(a) 'Все элементы массива a сортируются по возрастанию = {20, 40, 50, 50, 60, 70, 80, 90}
Array.Clear(a, 4, 3) 'Обнуляется 3 элемента массива a, начиная с индекса 4= {20, 40, 50, 50, 0, 0, 0, 90}
End Sub
Из приведенных методов некоторые имеют несколько вариантов, которые я здесь не привожу.
Замечание. Учитывая, что массив – это объект, я призываю вас до поры не присваивать массив массиву целиком, без индексов, например, вот так:
Dim a() As Integer = {8, 1, 5, 2}
Dim b() As Integer
b = a
Присвоение, конечно, состоится, но совсем не такое, как вы ждали. Оно может привести к неожиданным для начинающих последствиям. Вот к каким, например. Продолжу фрагмент:
a(2) = 99
Debug.WriteLine(b(2))
Напечатается 99, а не 5, потому что массив – это объект. Почему? Расскажу позднее, в 27.2.
Массивы как параметры
До этого момента параметр процедуры или функции был для нас каким-то одним данным: это или одно число, или одна строка, или один объект. Но параметр может быть и массивом.
Задача: Имеется два массива, по три числа в каждом. Напечатать сумму элементов каждого массива. Использовать функцию sum, единственным параметром которой является суммируемый массив.
Программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a() As Integer = {4, 10, 20}
Dim b() As Integer = {100, 40, 50}
Debug.WriteLine(sum(a))
Debug.WriteLine(sum(b))
End Sub
Function sum(ByVal c() As Integer) As Integer
sum = c(0) + c(1) + c(2)
End Function
Выполните программу в пошаговом режиме, глядя в окно Locals. Понаблюдайте, как массив c принимает в себя значения элементов массивов a и b.
Задание 105.
В школе два класса. В каждом – два-три десятка учеников. Каждый ученик получил отметку на экзамене по физике. Определить, какой из двух классов учится ровнее (будем считать, что ровнее учится тот класс, в котором разница между самой высокой и самой низкой отметкой меньше).
Указание: Создать функции Минимум(c), Максимум(c) и Разница(c).
Задание 106.
На двух метеостанциях (A и B) в течение года измерялась температура. Соответственно созданы два массива чисел длиной 365. Затем оказалось, что на обеих станциях термометры были испорчены: на станции A термометр все время показывал температуру на 2 градуса выше настоящей, а на станции B – на 3 градуса ниже. Написать процедуру с двумя параметрами, которая исправляет один произвольный массив и с ее помощью исправить оба массива. Один параметр процедуры – величина поправки, другой – массив температур.
Массивы элементов управления
Задача: Выстроить вдоль нижней кромки формы на равных расстояниях друг от друга 40 элементов управления PictureBox – улыбающихся рожиц, так, как это сделано на Рис. 15.1.
Рис. 15.1
При нажатии кнопки эти рожицы должны прыгнуть вверх, так, как на Рис. 15.2.
Рис. 15.2
Трудности: По-старинке нам нужно вручную поместить на форму 40 элементов управления PictureBox и загрузить в них картинку рожицы, что само по себе утомительно. Расположить их вручную на равных расстояниях без помощи VS тоже нелегко. VB дает возможность изящно преодолеть все эти трудности программным путем.
Первый способ – использование коллекции. Поместите вручную на форму 40 элементов PictureBox. Поскольку все они автоматически входят в коллекцию элементов управления формы (загляните вперед, в 16.2), вы можете запрограммировать их правильное расположение и прыжки. Но вручную тащить на форму или копировать 40 элементов не хочется. Поэтому изберем второй способ.
Второй способ – использование массива. В 6.1.2 мы не тащили вручную на форму элемент управления, а создавали его программным путем. Перечитайте тот раздел. Здесь мы поступим совершенно аналогично, только создадим не один элемент управления, а массив из 40 элементов управления. Вот программа:
Dim Рожица(40) As PictureBox
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim i As Integer
For i = 1 To 40
Рожица(i) = New PictureBox 'Создаем очередной объект
Рожица(i).SizeMode = PictureBoxSizeMode.AutoSize
Рожица(i).Image = Image.FromFile("FACE02.ICO")
'Помещаем на свои места все объекты массива:
Рожица(i).Top = Me.Height - 50
Рожица(i).Left = 20 * i
Me.Controls.Add(Рожица(i)) 'Добавляем новую рожицу в коллекцию
Next
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer
For i = 1 To 40
Рожица(i).Top = Рожица(i).Top - i 'Прыжок всех объектов
Next
End Sub
Пояснения: Как обычно, первой выполняется процедура Form1_Load, которая все создает и расставляет. Оператор
Рожица(i).SizeMode = PictureBoxSizeMode.AutoSize
понадобился для того, чтобы каждый PictureBox уменьшился до размеров рожицы.
Затем процедура Button1_Click обеспечивает прыжок всех элементов управления, каждого на свою высоту.
Как только элемент управления создан, вы можете как угодно задавать и менять его свойства, обращаясь к нему по индексу. Например, оператор
Рожица(38).Visible = False
делает невидимой 38-ю рожицу.
Обратите внимание, что пока я для простоты создаю элементы управления без возможности обработки свойственных им событий. Так, на созданную кнопку нажимать бесполезно. Как обрабатывать события элементов управления, созданных в коде, я написал в 22.13.
Индукция. Рекурсия
Понятие рекурсии – замечательное, сложное и необходимое понятие для программиста. В первый раз, однако, вы можете прочесть этот раздел «по диагонали», уяснив только общую идею и не вникая в объяснения. В дальнейшем, при необходимости, вы вернетесь к нему.
Здесь мне никуда не уйти от классического примера о факториале. Факториалом целого положительного числа N называется произведение всех целых чисел от 1 до N. Например, факториал пяти равен 1*2*3*4*5, то есть 120. Обратите внимание, что факториал единицы по определению считается равным 1.
Все понятно. Однако, существует еще один, совершенно ужасный способ определения, что такое факториал. Этот способ определения называется индуктивным. Вот он:
«Факториал единицы равен 1. Факториал любого целого положительного числа N, большего единицы, равен числу N, умноженному на факториал числа N-1.»
Если вам уже все ясно, значит вы – профессор математики. Для обычных смертных поясню. Возьмем какое-нибудь конкретное N, например, 100. Тогда ужасное определение будет звучать проще: Факториал числа 100 равен числу 100, умноженному на факториал числа 99.
Вроде, все правильно. Ну и что из того? Какая нам выгода, что правильно? Как нам из этого правильного определения узнать, чему равен какой-нибудь конкретный факториал, скажем, факториал трех? Для того, чтобы узнать это, будем рассуждать также совершенно чудовищным образом:
Смотрю в определение: Факториал трех равен 3 умножить на факториал двух. Не знаю, чему равен факториал двух. Поэтому спускаюсь на ступеньку ниже.
Смотрю в определение: Факториал двух равен 2 умножить на факториал единицы. Не знаю, сколько это. Спускаюсь еще на ступеньку.
Смотрю в определение: Факториал единицы равен 1. Вот, наконец-то – впервые узнал конкретное число. Значит можно подниматься обратно.
Поднимаюсь на одну ступеньку. Я на этой ступеньке уже бывал. Вспоминаю: Факториал двух равен 2 умножить на факториал единицы, то есть на 1, полученную нами ступенькой ниже. Получается 2. Хорошо.
Поднимаюсь еще на ступеньку. Я на этой ступеньке тоже бывал. Вспоминаю: Факториал трех равен 3 умножить на факториал двух, то есть на 2, полученную нами ступенькой ниже. Получается 6. Задача решена!
Рассуждая таким образом, можно вычислить факториал любого числа. Этот способ рассуждения называется рекурсивным. Ну как он вам?
:
Какое отношение все это имеет к компьютерам? Дело в том, что рекурсивный способ рассуждений реализован во многих языках программирования, в том числе – и в VB. Значит, этим языкам должен быть понятен и индуктивный способ написания программ.
Обозначим кратко факториал числа N, как Factorial(N), и снова повторим наш индуктивный способ определения:
Если N=1, то Factorial(N) = 1.
Если N>1, то Factorial(N) вычисляется умножением N на Factorial(N-1).
В соответствии с этим определением, ни минуты не сомневаясь, напишем на VB функцию Factorial для вычисления факториала:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Debug.WriteLine(Factorial(3))
End Sub
Function Factorial(ByVal N As Integer) As Long
If N = 1 Then Factorial = 1
If N > 1 Then Factorial = N * Factorial(N - 1)
End Function
Обратите внимание, как удивительно соответствует код процедуры последнему варианту нашего индуктивного способа определения. Это практически дословный перевод определения с русского на английский язык. Единственное маленькое несоответствие – это употребление слева от знака равенства слова Factorial вместо Factorial(N). Но это требование грамматики, не меняющее смысла.
Что самое удивительное – функция работает! Несмотря на то, что в программе нигде не употребляется оператор цикла. А как же получается повторение? Вся соль программы в том, что функция Factorial вместо цикла включает в себя вызов самой себя – Factorial(N-1).
Что же происходит в компьютере во время выполнения программы? Механизм происходящего необычен, но в точности соответствует нашему путешествию по ступенькам. «Мыслей много, дела мало J». Запустите проект в отладочном режиме и обязательно понаблюдайте за окнами Locals и Call Stack:
Все начинается с того, что мы щелкаем по кнопке и VB пробует выполнить строку Debug.WriteLine(Factorial(3)). Для этого он вызывает функцию Factorial. Выполнение функции начинается с того, что в памяти отводится место для всех параметров функции и ее локальных переменных (в нашем случае это единственный параметр N). Затем число 3 подставляется на место параметра N, то есть в память в ячейку N посылается 3. Затем выполняется тело функции. Так как 3>1, то VB пытается выполнить умножение 3 * Factorial(3-1) и сталкивается с необходимостью знать значение функции Factorial(2), для чего вызывает ее, то есть отправляется ее выполнять, недовыполнив Factorial(3), но предварительно запомнив, куда возвращаться.
Спускаюсь на ступеньку ниже, выполнять Factorial(2). В другом месте памяти отводится место для N. Это уже другое N, чем в Factorial(3), путать их нельзя! В эту ячейку N посылается 2 (а в той продолжает сидеть 3). Затем выполняется тело функции. Пусть вас не смущает, что VB второй раз выполняет тело функции, не закончив его выполнять в первый раз. Так как 2>1, VB пытается выполнить умножение 2 * Factorial(2-1) и сталкивается с необходимостью знать значение функции Factorial(1), для чего, недовыполнив Factorial(2),вызывает ее. Итак, уже два недовыполненных обращения к одной и той же функции ждут своего часа на довыполнение.
Спускаюсь еще на ступеньку, выполнять Factorial(1). В другом месте памяти отводится место еще для одного N. В эту ячейку N посылается 1. Затем выполняется тело функции. Так как 1=1, то VB вычисляет Factorial=1. Вот – впервые конкретное число. Затем VB пытается выполнить следующую строку if N>1 then Factorial = N * Factorial(N-1). Поскольку нельзя сказать, что 1>1, то строка не выполняется и выполнение тела функции закончено. Значит можно подниматься.
Поднимаюсь на одну ступеньку. VB возвращается внутрь тела функции (той, где N=2), имея в кармане в качестве результата единицу, и успешно выполняет умножение – Factorial = 2*1 = 2.
Поднимаюсь еще на ступеньку. VB возвращается внутрь тела функции (той, где N=3), имея в кармане в качестве результата двойку, и успешно выполняет умножение – Factorial = 3*2 = 6. Задача решена.
Все произошло так, как если бы в окне кода было не одно объявление функции Factorial , а три. Из главной процедуры мы обратились к первому, из него – ко второму, а из того – к третьему. Выполнив третье, компьютер вернулся во второе, выполнив второе – в первое, выполнив первое – в главную процедуру.
Здесь мы рассмотрели рекурсию на примере функции. Однако обращаться к самой себе может с тем же успехом и процедура. Итак, рекурсией
в программировании называется вызов метода из тела самого метода.
Пути рекурсии не всегда бывают такими короткими. Часто функция А вызывает, например, процедуру В, которая вызывает процедуру С, которая в свою очередь вызывает функцию А. Круг замкнулся – рекурсия есть!
Чем хорош рекурсивный стиль программирования? В нашей программе о факториале мы как бы и не программировали вовсе, а просто объяснили компьютеру, что такое факториал. Как бы перешли на новый уровень общения с компьютером: вместо программирования – постановка задачи.
Чем плох рекурсивный стиль программирования? Если мы для решения той же задачи напишем программу не с рекурсией, а с обычным циклом, то такая программа будет выполняться быстрее и потребует меньше памяти.
Как преимущества, так и недостатки рекурсии вы поймете на примере выполнения следующего задания:
Задание 107.
Напишите рекурсивную функцию fib для вычисления чисел Фибоначчи. В любом случае загляните в ответ.
Оглядимся вокруг. Итак, мы изучили 5 способов заставить компьютер многократно выполнять код:
Операторы Do …. Loop
Операторы For
Устаревший оператор Goto
Использование таймера
Рекурсия
Простая сортировка
Задача: Задан массив из 100 произвольных положительных чисел. Отсортировать его по возрастанию.
Идея решения: Отсутствует. Тогда поговорим об идее поиска идеи. Если мы не можем придумать, как запрограммировать задачу, нужно подробно представить себе, в каком порядке мы решали бы ее вручную, без компьютера. Как бы мы сами сортировали 100 чисел, записанных на бумаге? Мы сделали бы вот что. Запаслись карандашом, ластиком и другим, пустым листом бумаги из 100 клеток. Затем нашли бы в исходном массиве максимальное число и записали его в самую правую клетку, а в исходном массиве на его месте записали бы число, меньшее самого маленького в массиве (в нашем случае подойдет 0). Затем нашли бы в изменившемся исходном массиве новое максимальное число и записали его на второе справа место, а на его место в исходном массиве – 0. И так далее.
Вот программа, воплощающая эту идею для 10 чисел:
'Вспомогательная функция для поиска максимума в массиве m размера N+1. Она выдает значение
'максимального элемента (maximum) и заодно мы узнаем номер этого элемента (Nomer_max):
Function maximum(ByVal m() As Integer, ByVal N As Integer, ByRef Nomer_max As Integer) As Integer
Dim i, max As Integer
max = m(0) : Nomer_max = 0 'max - "временный" максимум
For i = 1 To N
If max < m(i) Then
max = m(i)
Nomer_max = i
End If
maximum = max
Next
End Function
'Основная процедура сортировки исходного массива mass_ish размера N+1 в результирующий - mass_rez:
Sub sortirovka(ByVal mass_ish() As Integer, ByVal N As Integer, ByVal mass_rez() As Integer)
Dim i, Nom_max As Integer
For i = 0 To N
mass_rez(N - i) = maximum(mass_ish, N, Nom_max) 'Пишем "в правую клетку"
mass_ish(Nom_max) = 0 'Ноль - на старое место
Next
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim massiv_ishodn() As Integer = {41, 8, 17, 82, 20, 2, 30, 12, 6, 9} 'Это исходный массив
Dim N As Integer = massiv_ishodn.Length - 1 'Это его размер без 1
Dim massiv_rezult(N) As Integer 'Это наш пустой лист бумаги
sortirovka(massiv_ishodn, N, massiv_rezult) 'Сортируем массив
Dim i As Integer
For i = 0 To N
Debug.WriteLine(massiv_rezult(i)) 'Распечатываем отсортированный массив
Next
End Sub
Примечания: В программе вам полезно разобраться самому. Обратите внимание, что в заголовке функции в одном месте вместо ByVal употребляется ByRef. Разберитесь, почему. Обратите внимание, что функция maximum, кроме того, что сама имеет значение максимального элемента массива, выдает еще и порядковый номер максимального элемента – Nomer_max. Это, как я уже говорил, называется побочным эффектом функции.
Метод пузырька
Методов сортировки много. Приведенный метод – самый примитивный. Мало того, что нам пришлось расходовать память на второй массив, нам для выполнения сортировки массива из 100 элементов понадобилось бы около 100*100=10000 операций сравнения элементов массива между собой.
Существуют методы гораздо более эффективные. Приведу один из них – метод пузырька. Представьте себе тонкую вертикальную трубку с водой. Запустим снизу пузырек воздуха. Он поднимется до самого верха и остановится. Затем пустим еще один. Он поднимется наверх и остановится сразу же под первым. Затем запустим третий и так далее все сто пузырьков.
А теперь представим, что это не трубка, а наш исходный массив, а вместо пузырьков поднимаются максимальные элементы, сначала самый максимальный, потом - следующий по величине и т.д.
Вот алгоритм: Сравним первый элемент массива со вторым, и если второй больше, то это хорошо, так как они расположены в порядке возрастания. Мы ничего не делаем. А вот если первый больше, то меняем местами первый и второй элементы. В этом вся соль метода. Затем повторяем это со вторым и третьим элементами – если третий больше, то ничего не делаем, а если второй больше, то меняем местами второй и третий элементы. Затем повторяем все это с третьим и четвертым элементами и так далее. Где-то по пути мы встретим максимальный элемент, и он, согласно нашей механике постоянно меняясь местами с вышестоящими соседями, как пузырек, поднимется у нас до самого верха.
Теперь, когда мы знаем, что элемент номер 100 у нас самый большой, нам предстоит решить задачу сортировки для массива из остальных 99 элементов. Метод тот же. Запускаем второй пузырек и так далее.
Метод пузырька не требует второго массива, да и сравнений здесь в два раза меньше.
Вот программа:
'Сортировка массива mass размером N+1:
Sub puziryok(ByVal mass() As Integer, ByVal N As Integer)
Dim i, c, m As Integer
For m = N To 1 Step -1 'Всего пузырьков - 9
For i = 0 To m - 1 'i увеличивается - пузырек ползет вверх
If mass(i) > mass(i + 1) Then 'Стоит ли обмениваться значениями
c = mass(i) 'Три оператора для обмена значений
mass(i) = mass(i + 1) 'двух элементов с помощью
mass(i + 1) = c 'транзитного элемента c
End If
Next i
Next m
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim massiv() As Integer = {41, 8, 17, 82, 20, 2, 30, 12, 6, 9} 'Это массив
Dim N As Integer = massiv.Length - 1 'Это его размер без 1
puziryok(massiv, N) 'Сортируем массив
Dim i As Integer
For i = 0 To N
Debug.WriteLine(massiv(i)) 'Распечатываем отсортированный массив
Next
End Sub
В заключение скажу, что существуют методы гораздо более эффективные, чем даже метод пузырька.
Задание 108.
Задача для ученого-диетолога: Определить связь между ростом и весом боксеров-профессионалов. Для этого диетолог измерил рост и вес нескольких сотен боксеров и записал результаты измерений в длинную таблицу:
Фамилия |
Рост |
Вес |
Иванов |
173 |
71 |
Петров |
182 |
93 |
Сидоров |
169 |
62 |
Николаев |
175 |
70 |
……………. |
………………. |
…………….. |
Для более наглядного представления о характере связи между ростом и весом необходимо упорядочить эту таблицу по весу:
Фамилия |
Рост |
Вес |
Сидоров |
169 |
62 |
Николаев |
175 |
70 |
Иванов |
173 |
71 |
Петров |
182 |
93 |
Для проведения упорядочивания приглашается программист, то есть вы. Считайте, что вам заданы три исходных массива.
Сортировка
Здесь вы не узнаете ничего нового о собственно языке VB. Будем совершенствовать универсальную технику программирования.
Пусть имеется ряд чисел: 8 2 5 4. Под сортировкой понимают их упорядочивание по возрастанию (2 4 5 8) или убыванию (8 5 4 2). Сортировать можно и строки (как слова в словаре).
Сортировка – очень распространенная вещь в самых разных программах, в частности – в системах управления базами данных. Несмотря на то, что класс Array имеет специальный метод для сортировки массива, этого явно недостаточно. Вы должны уметь сортировать сами.
Тип Object
Переменные типа Object. Типы данных, которые мы проходили до этого, строгие. Если уж мы написали
Dim a As Integer
то переменная a не может быть ни строкой, ни дробным числом и ничем другим неположенным, она может быть только целым числом, и точка! Но есть и «добрые» типы данных. Самый добрый тип – это Object. Мы уже говорили о нем в 11.5.1. Там переменная типа Object принимала значения различных элементов управления. Рассмотрим теперь такую процедуру:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a As Object
a = Button1
a = 77
a = "кот"
End Sub
Как видите, здесь переменная типа Object принимает по-очереди значения таких разных вещей, как элемент управления, целое число и строка. И ничего плохого не происходит, VB не жалуется. Тип Object – это тип-хамелеон. Он не возражает, если объявленная им переменная будет иметь значение любого типа.
Выполните проект в пошаговом режиме, наблюдая за значением переменной в окне Locals.
Вместо
Dim a As Object
разрешается писать
Dim a
В этом случае VB все равно будет считать, что переменная a объявлена, как Object.
Зачем нужны строгие типы? Я уже говорил ранее, что они во многих случаях не позволяют программисту и компьютеру делать глупости и получать недостоверные результаты.
А зачем нужны добрые типы? Какой толк от типа Object? – Вот какой. Иногда бывает так, что заботясь о безопасности и объявляя тип каждой переменной, мы наталкиваемся во время выполнения программы на сообщения об ошибке вроде Type mismatch (несовпадение типов) или на другие подобные сообщения, о которых я говорил, например, в 11.5.3. Это значит, что выполняя операции над данными разных типов и преобразовывая один в другой, VB не захотел преступать строгие рамки безопасности и потерпел крах. Не всегда у начинающего программиста хватает знаний, чтобы разобраться в причинах краха. В этом случае, если вы не прочь немножко рискнуть, объявите данные типом Object. Он, хоть и допускает значения любого типа, все-таки присматривает, чтобы выполнение программы при этом проходило по возможности гладко и без неприятностей для программиста. Теперь вероятность сообщений об ошибке снизится, хотя несколько увеличится вероятность получения недостоверного результата.
Массивы типа Object. Массив – это набор однотипных элементов. Если вы объявили массив некоторого типа, то он обязан состоять из элементов только этого типа. Например, каждый из десяти элементов массива, объявленного как
Dim a(9) As Integer
обязан быть целым числом. Ни один из них не имеет права быть строкой или дробным числом.
Имеется целый ряд задач, где хотелось бы, чтобы элементы в наборе имели разный тип. Этого можно достичь хотя бы при помощи массива, если объявить массив, как вы уже догадались, типом Object:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a(3) As Object
a(1) = 32
a(2) = "кошка"
a(3) = #8:56:00 AM#
a(3) = "поварешка"
End Sub
Выполните проект в пошаговом режиме, наблюдая за значениями элементов массива в окне Locals.
Вместо
Dim a(3) As Object
разрешается писать
Dim a(3)
И все же, когда дело касается желания иметь набор из элементов разных типов, программисты не хотят использовать массивы типа Object. Они предпочитают так называемые коллекции и структуры. О них сейчас и пойдет речь.
Создание коллекции, методы коллекции
Я рассмотрю распространенный случай, когда в коллекцию входят только элементы управления.
Задача: На вашей форме имеется несколько десятков кнопок, меток и текстовых полей, которые делают свое дело, скажем, помогают обрабатывать банковскую информацию. Глядя на них, можно сказать кто принес деньги, сколько принес, в долларах или рублях, сколько денег у него стало, сколько всего в банке денег и т.д. Вам нужно, чтобы иногда, в некоторые моменты выполнения программы, такие-то и такие-то кнопки, метки и текстовые поля из самых разных мест формы (всего, скажем, дюжина объектов) меняли цвет. Ну, скажем, для того, чтобы привлечь к себе ваше внимание. А в какие-нибудь другие моменты эта же самая дюжина должна делать что-нибудь другое, например, все кнопки из этой дюжины должны быть деактивированы, чтобы на них случайно не нажали. И так далее. В общем, эта дюжина иногда должна вести себя по-другому, чем остальные элементы управления.
Решение: Создадим проект. Для простоты разместим на форме только шесть элементов управления: Label1, TextBox1, Button1, Label2, TextBox2, Button2. Пусть в нашу «дюжину» входят из них только Label2, TextBox2, Button2.
Мы могли бы поместить нашу «дюжину» в рамку (GroupBox). Но рамка не обладает такой функциональностью, как коллекция. К тому же она требует пространственной отграниченности входящих в нее элементов управления от остальных.
Программным путем создадим из наших объектов коллекцию. Для этого сначала придумаем ей имя Моя_коллекция. Коллекция – это объект, экземпляр класса Collection, и создается она соответствующим привычным образом:.
Dim Моя_коллекция As New Collection 'Объявляем и создаем коллекцию
Коллекция объявлена, но пока она пуста, в ней нет ни одного элемента. Теперь будем по очереди добавлять объекты в коллекцию. Смотрим программу и пояснения к ней:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Моя_коллекция As New Collection 'Объявляем и создаем коллекцию
Моя_коллекция.Add(Label2) 'Добавляем в коллекцию 1-й элемент
Моя_коллекция.Add(TextBox2, "Текстик") 'Добавляем в коллекцию 2-й элемент
Моя_коллекция.Add(Button2, "Кнопочка") 'Добавляем в коллекцию 3-й элемент
Debug.WriteLine(Моя_коллекция.Count) 'Печатается 3 - число элементов в коллекции
Debug.WriteLine(Моя_коллекция(2)) 'Печатаются сведения об объекте TextBox2
Debug.WriteLine(Моя_коллекция(1).Text) 'Печатается "Label2"
Моя_коллекция(3).Height = 80 'Задается высота кнопки Button2
Моя_коллекция("Текстик").Width = 100 'Задается ширина поля TextBox2
Dim i As Integer
For i = 2 To 3 'ДЛЯ 2 и 3 объекта
Debug.WriteLine(Моя_коллекция(i).Top) 'распечатывается вертикальная координата
Next
Dim Мой_объект As Control
For Each
Мой_объект In Моя_коллекция 'ДЛЯ КАЖДОГО объекта В
коллекции:
Мой_объект.BackColor = Color.Yellow 'задается желтый цвет
Next
For Each Мой_объект In Моя_коллекция 'ДЛЯ КАЖДОГО объекта В коллекции:
If TypeName(Мой_объект) = "Button" Then 'если имя типа объекта - Button, то
Мой_объект.Enabled = False 'деактивировать ее
End If
Next
Моя_коллекция.Remove(2) 'Удаляем из коллекции 2-й элемент
Моя_коллекция.Remove("Кнопочка") 'Удаляем из коллекции кнопку
End Sub
Пояснения: У всех коллекций, как экземпляров класса Collection, есть несколько методов, три из них (Add, Remove, Count) я сейчас поясню.
При помощи метода Add мы добавляем в коллекцию элементы. При этом обязательно указывается имя элемента. Также удобно через запятую указать произвольную строку – ключ элемента, по которому можно к нему при желании обращаться, что мы и сделали в двух местах ("Текстик" и "Кнопочка"). Элементы, добавляемые в коллекцию, получают номера в том порядке, в котором их добавляли, начиная с 1 (а не с 0). Обращаться к отдельным элементам коллекции можно по полученному номеру (индексу) прямо как к элементу массива, а можно и по ключу, там, где он задан.
Как видите, здесь для добавления каждого элемента мы использовали отдельный оператор, хотя есть ситуации, когда можно это делать и в цикле.
Метод Count просто сообщает число элементов в коллекции.
Строки процедуры с 6 по 13 иллюстрируют обращение к элементам коллекции по индексу или по ключу, в том числе в цикле.
Чтобы удалить отдельный элемент из коллекции (не с формы), используется метод Remove с указанием индекса или ключа элемента.
Очевидно, что работа с коллекциями напоминает работу с одномерными массивами, в чем-то побогаче ее, а в чем-то и менее удобна.
В следующем подразделе мы продолжим пояснения нашего примера.
Оператор цикла For Each
Работа For Each с коллекциями. Для коллекций удобно применять специальную разновидность оператора цикла – For Each. Для этого необходимо придумать имя переменной цикла, значение которой будет пробегать все элементы коллекции. Мы придумали имя Мой_объект. Надо объявить переменную цикла так, чтобы ее тип подходил для всех элементов коллекции. Поскольку в нашей коллекции одни только элементы управления, мы объявили так:
Dim Мой_объект As Control
хотя могли и так:
Dim Мой_объект As Object
Ведь тип Object всеяден. Но чем более конкретный тип мы указываем, тем удобнее работать.
Когда цикл выполняется в первый раз, Мой_объект «равняется» одному элементу коллекции, во второй раз – другому и т.д., пока элементы не будут исчерпаны. В остальном синтаксис и порядок выполнения оператора For Each такой же, как и у привычного нам For. К нему также можно применять оператор Exit For.
Мы использовали функцию TypeName, чтобы выбрать из всех элементов коллекции элементы только данного типа. Пояснять ее я не буду, используйте дальше по аналогии.
Работа For Each с массивами. Оператор For Each никто не запрещал использовать и при работе с массивами. Вот пример:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a() As Integer = {10, 11, 12, 13, 14}
Dim Элемент As Integer
For Each Элемент In a
Debug.WriteLine(Элемент)
Next
End Sub
Напечатает эта процедура вот что:
10
11
12
13
14
Коллекции, принадлежащие контейнерам
Мы знакомы с такими объектами, как форма, рамка, панель, вкладка. Каждый из них может включать в себя другие элементы управления. Поэтому такие объекты называются контейнерами. У контейнера есть собственная коллекция, в которую автоматически заносятся элементы управления, входящие в контейнер. Поэтому специально заботиться о добавлении их в контейнер не нужно. Коллекция формы называется Controls. Коллекция рамки GroupBox1 является ее свойством Controls, поэтому к ней можно обращаться так: GroupBox1.Controls. К коллекции панели Panel1 можно обращаться так: Panel1.Controls и т.д. Пример:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Объект As Control
For Each Объект In Controls
Debug.WriteLine(Объект.Text)
Next
For Each Объект In Panel1.Controls
Debug.WriteLine(Объект.Text)
Next
End Sub
Во вкладках элементы управления принадлежат страницам вкладки. У каждой страницы – своя коллекция элементов управления. Пусть во вкладке TabControl1 имеется три страницы: TabPage1, TabPage2 и TabPage3. Тогда фрагмент
For Each Объект In TabPage2.Controls
Debug.WriteLine(Объект)
Next
распечатает элементы управления, находящиеся на 2-й странице. Сами страницы составляют коллекцию, принадлежащую вкладке. Поэтому фрагмент
For Each Объект In TabControl1.Controls
Debug.WriteLine(Объект.Name)
Next
распечатает имена всех страниц вкладки:
TabPage1
TabPage2
TabPage3
Получается коллекция в коллекции. Матрешка. Поэтому сработает такой оператор:
Debug.WriteLine(TabControl1.Controls(1).Controls(2))
Здесь распечатывается элемент управления, стоящий под номером 2 в коллекции элементов управления, принадлежащей некой странице. А страница эта стоит под номером 1 в коллекции страниц, принадлежащей вкладке TabControl1.
Коллекции
Коллекция представляет собой набор элементов одного или разных типов. Обычно коллекции составляют из элементов управления, других объектов. Коллекция и сама является объектом (а почему бы и нет? – «коробки в коробке»). Коллекции всеядны, они позволяют иметь в одном наборе и кнопку, и метку, и строку, и переменную величину типа Integer.
Структуры
Со структурами мы с вами знакомы слабо. Несколько слов я сказал о них в 12.1. А именно, я сказал, что структура – это один из видов объектов (в широком смысле), который наряду с классами, модулями и перечислениями входит в состав пространств имен. У структуры, также как и у класса, могут быть свойства и методы. Нам известны структуры Точка, Прямоугольник и др.
В Паскале структурам соответствуют записи, в Visual Basic 6.0 структурам соответствует пользовательский тип данных, в Си структуры тоже называются структурами.
Все знакомые нам структуры достались нам готовыми, входящими в библиотеку классов .NET Framework Сейчас же мы займемся созданием собственных структур. Для нас структура будет пока не вместилищем методов и свойств, как мы привыкли, а всего лишь местом для хранения набора разнотипных данных. Вы скажете, что для этого мы уже пользовались коллекцией. Но у коллекции и у структуры немножко разные области применения. В дальнейшем вы почувствуете разницу между ними.
Рассмотрим пример.
Задание. Вы хотите занести в компьютер информацию о ваших любимых компьютерных играх (хотя бы для того, чтобы затем как-то ее анализировать, например, определить, какая игра занимает больше места на диске). Для простоты ограничим информацию об игре тремя элементами:
Название игры
Сколько места игра занимает на диске (в мегабайтах)
Хорошая или плохая графика у игры (ваша оценка)
Также для экономии ограничимся двумя играми. Ваша программа должна будет занести в память информацию об обеих играх, а затем выполнить три задания:
Распечатать название первой игры
Определить, сколько места займут на диске обе игры вместе
Ответить, хороша или плоха графика у второй игры
Ваши действия. Для начала нужно создать структуру для размещения информации об игре. Нам уже приходилось конструировать перечисления (см. 13.3). Там мы сначала создавали тип
перечисления, а затем объявляли этим типом переменные. Здесь действуем аналогично – придумываем сначала имя новому типу структуры (типИгра), причем придумываем также имена и типы всем элементам информации об игре, которые мы хотим хранить в структуре, после чего пишем:
Structure типИгра
Dim Название As String
Dim Объем As Integer
Dim Графика_хорошая As Boolean
End Structure
Слово Structure как раз и обозначает структуру.
Тип определен. Теперь VB знает, что входит в информацию об игре и сколько места в памяти она займет. Можно объявлять переменные, то есть отводить место в памяти:
Dim Игра1, Игра2 As типИгра 'Отводим в памяти место под информацию о двух играх
Вот полный текст программы:
'Создаем тип структуры. Его нельзя объявлять в процедуре:
Structure типИгра
Dim Название As String
Dim Объем As Integer
Dim Графика_хорошая As Boolean
End Structure
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Игра1, Игра2 As типИгра 'Отводим в памяти место под информацию о двух играх
'Заносим в память компьютера информацию об играх:
Игра1.Название = "StarC" : Игра2.Название = "Heroes III"
Игра1.Объем = 90 : Игра2.Объем = 200
Игра1.Графика_хорошая = False : Игра2.Графика_хорошая = True
Debug.WriteLine(Игра1.Название) 'Выполняем 1 задание
Debug.WriteLine(Игра1.Объем + Игра2.Объем) 'Выполняем 2 задание
'Выполняем 3 задание:
If Игра2.Графика_хорошая Then Debug.WriteLine("Хорошая графика") _
Else Debug.WriteLine("Плохая графика")
End Sub
Обратите внимание, что имя элемента структуры отделяется от имени переменной точкой. Нам это привычно по работе со структурами Размер, Прямоугольник и др., когда имя такого элемента структуры, как свойство, тоже отделялось от имени переменной точкой.
Массивы и структуры отличаются от простых типов данных, таких как Integer или String. Если переменная простого типа содержит в каждый момент времени только одно значение (число, строку, дату и т.п.), то массив или структура в каждый момент времени содержат по нескольку значений. Поэтому такие типы называют составными (composite).
Можно писать операторы такого вида:
Игра2 = Игра1 'Игре 2 присваиваются все элементы 1-й игры
В этом случае всем элементам игры 2 присваиваются значения соответствующих элементов игры 1.
Базы данных. Выстроенную подобным образом в памяти информацию о чем-либо часто называют базой данных. Всю информацию об одной переменной (в нашем случае об одной игре) называют записью в этой базе данных. Программу, которая извлекает информацию из базы данных, сортирует записи и производит другую обработку информации в базе данных, называют системой управления базой данных. В нашем примере роль системы управления базой данных играет процедура Button1_Click.
Базы данных являются настолько распространенным средством хранения информации, что в VB есть специальные мощные инструменты для работы с ними. Мы рассмотрим их в Глава 24. .
Массивы структур: Вы хотите создать базу данных о 30 играх. Для этого достаточно вместо очень длинного объявления
Dim Игра1, Игра2, …………… Игра30 As типИгра
объявить массив:
Dim Игра(30) As типИгра
Теперь можно использовать операторы такого вида:
Игра(16).Название = "KU4" 'название 16-й игры
Игра(25).Объем = 400 'объем 25-й игры
Debug.WriteLine(Игра(8).Графика_хорошая)
Игра(29) = Игра(12) '29-й игре присваиваются все элементы 12-й игры
Структура в структуре: Элементы структуры могут иметь не только простой тип, но и составной, в том числе быть массивом, структурой или объектом.
Пусть мы хотим иметь более подробную, чем в предыдущем примере, информацию о графике. Для этого организуем отдельную структуру:
Structure типГрафика
Dim Хорошая As Boolean
Dim Число_цветов As Integer
Dim Максимальное_разрешение As String
End Structure
А поскольку информация о графике является составной частью информации об игре, вставим переменную созданного типа в качестве элемента в структуру игры:
Structure типИгра
Dim Название As String
Dim Объем As Integer
Dim Графика As типГрафика
End Structure
Теперь мы можем писать такие операторы:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Игра(30) As типИгра
Игра(8).Графика.Хорошая = True
Игра(14).Графика.Число_цветов = 65000000
Игра(29).Графика.Максимальное_разрешение = "1280х960"
Debug.WriteLine(Игра(14).Графика.Число_цветов)
End Sub
Заключение. Вы увидели, что составные элементы структуры сами могут включать в себя структуры. Они могут вкладываться друг в друга, как матрешки, до бесконечности. Так можно создать иерархии данных практически в любой области знаний. Примеры же о массивах и объектах в качестве элементов структур я не буду рассматривать, так как они кажутся мне несколько сложноватыми для начинающих.
Задание 109.
Создайте базу данных о себе, ближайших родственниках или друзьях. О каждом должно быть известно:
Имя
Дата рождения
Цвет глаз
Массивы не используйте. Программа должна:
Распечатать ваш возраст и цвет глаз
Ответить на вопрос – правда ли, что ваш дядя старше тети.
Задание 110.
Создайте базу данных о своих книжках. О каждой книжке должно быть известно:
Название
Автор
Дата издания
Число страниц
Обязательно используйте массив не меньше, чем из 5 структур. Программа должна:
Вычислить средний возраст ваших книжек
Определить, каких книжек больше – толстых или тонких
Ответить на вопрос – есть ли у вас разные книги одного автора (это нелегко)
Оператор With
Когда мы используем длинные имена структур или объектов или глубоко вложенные друг в друга конструкции, обращение к элементам таких структур или объектов занимает в окне кода довольно много места. Например:
Игра(29).Графика.Хорошая = True
Игра(29).Графика.Число_цветов = 65000000
Игра(29).Графика.Максимальное_разрешение = "1280х960"
Оператор With позволяет сократить запись. Вот фрагмент, равносильный предыдущему:
With Игра(29).Графика
. Хорошая = True
. Число_цветов = 65000000
. Максимальное_разрешение = "1280х960"
End With
Как видите, оператор With не производит никаких действий. Он просто позволяет перенести в свой заголовок одинаковую левую часть из всех обращений к элементам объекта или структуры, за счет чего эти обращения могут быть записаны в укороченном виде.
Алфавитный указатель
"................................................................... 154
#.................................................................. 362
&......................................................... 172, 492
*................................................................... 154
/.................................................................... 154
^................................................................... 154
+.................................................................. 492
<.................................................................. 201
<=................................................................ 201
<>................................................................ 201
=.................................................................. 201
>.................................................................. 201
>=................................................................ 201
A
A.................................................................. 357
Abort........................................................... 751
AboutBox..................................................... 87
Abs................................................................ 59
Access........................................................ 668
ActiveMdiChild......................................... 746
Add........................................... 178, 458, 484
Add project.................................................. 39
Add Reference......................................... 712
AddArc....................................................... 468
AddDays.................................................... 363
AddEllipse................................................. 468
AddHandler...................................... 639, 642
AddressOf................................................. 639
AddSeconds............................................. 365
AddTicks.................................................... 365
AddYears................................................... 364
ADO.NET................................................... 668
Alias............................................................ 718
Align........................................................... 118
Alt................................................................ 414
Anchor.......................................................... 71
And............................................................. 211
AND............................................................ 698
Appearance............................ 474, 477, 542
Append............................................. 502, 512
Application................................................ 717
AppWinStyle............................................. 719
Array........................................................... 445
Asc.............................................................. 495
ASCII символы......................................... 495
AscW.......................................................... 494
ASP. NET Web Application..................... 662
Assembler................................................. 759
Auto Hide................................................... 109
AutoPopDelay.......................................... 489
AutoScroll......................................... 375, 377
AutoSize.................................................... 336
B
B.................................................................. 356
BackColor.................................... 49, 69, 552
BackgroundImage............................ 76, 336
BackSpace....................................... 784, 785
Beep.................................................... 54, 718
BETWEEN................................................. 698
Binary......................................................... 512
BinaryReader............................................ 515
BinaryWriter.............................................. 515
Bitmap........................................................ 339
Bold............................................................... 66
Bold Italic..................................................... 66
Boolean..................................................... 214
BorderStyle.................................................. 70
break.......................................................... 149
Breakpoint Properties............................. 252
Breakpoints...................................... 147, 251
Bring to Front............................................ 118
Brushes...................................................... 187
Build..................................................... 94, 705
Busy............................................................ 653
Button.................................................. 32, 409
ButtonClick................................................ 543
Buttons....................................................... 540
ByRef.......................................................... 303
Byte.................................................... 157, 158
ByVal.......................................................... 303
C
Call............................................................. 275
Call Stack.................................................. 255
Cancel........................................................ 142
CapsLock.................................................. 784
Caption...................................................... 686
Catch.......................................................... 522
CBool......................................................... 163
CByte.......................................................... 162
CChar......................................................... 163
CDate......................................................... 163
CDbl........................................................... 163
CDec.......................................................... 163
CD-R........................................................... 770
CD-ROM.................................................... 770
CD-RW....................................................... 770
Ceiling........................................................ 155
Cells........................................................... 717
CenterImage............................................. 336
Changed.................................................... 650
Char................................................... 412, 494
CharacterCasing........................................ 73
Chars.......................................................... 496
CheckBox.................................................. 473
CheckBoxes.............................................. 544
Checked........................................... 474, 476
CheckedChanged................................... 475
CheckedIndices.............................. 485, 550
CheckedItems................................. 485, 550
CheckedListBox.............................. 483, 485
CheckState................................................ 474
Child........................................................... 552
Chr.............................................................. 495
Chr(10)....................................................... 508
Chr(13)....................................................... 508
ChrW.......................................................... 494
CInt............................................................. 162
Class Library............................................. 711
Class View................................................ 731
Clear....................... 190, 445, 484, 550, 697
Click..................................... 36, 89, 407, 408
Clicks.......................................................... 409
CLng........................................................... 163
Close........................................ 500, 501, 515
Close Solution................................... 38, 116
Closed.......................................................... 92
CloseFigure.............................................. 468
CLR............................................................... 18
CObj........................................................... 163
Code............................................................. 35
Collection.................................................. 457
Color....................... 329, 331, 356, 532, 558
ColorDepth................................................ 539
ColorDialog............................................... 558
Columns........................................... 544, 546
ColumnWidth............................................ 482
COM........................................................... 714
Combo простой...................................... 483
Combo раскрывающийся..................... 483
ComboBox................................................ 482
Command Window-Immediate............ 254
Comment Selection................................ 117
Common Language Runtime.................. 18
Compile..................................................... 726
Component Designer................................ 79
Connection................................................ 677
Console..................................................... 570
Console Application................................ 569
Const................................................. 310, 586
CONSTRAINT........................................... 700
Contains........................................... 484, 549
Contents........................................... 123, 724
ContextMenu............................................ 490
Continue.................................................... 149
Control..................................... 312, 414, 703
Controls............................................ 178, 459
Copy.................................. 43, 116, 519, 781
Cos............................................................. 156
Count................................................. 458, 484
COUNTER................................................. 700
CREATE TABLE....................................... 700
Created...................................................... 649
CreateDirectory........................................ 519
CreateObject............................................. 717
CShort........................................................ 162
CSng.......................................................... 163
CStr............................................................. 163
CurrentDirectory....................................... 520
CurrentPosition........................................... 87
Cursor........................................................... 71
Cut............................................... 43, 116, 781
D
DashStyle.................................................. 330
DataColumn............................................. 690
DataGrid.................................. 686, 691, 692
DataGridTableStyle................................. 693
DataSet.................................... 684, 685, 690
DataSource............................................... 686
DataTable.................................................. 690
Date................................................... 362, 364
DateAdd..................................................... 367
DateAndTime........................................... 366
DateDiff..................................................... 366
DatePart..................................................... 366
DateSerial................................................. 367
DateString................................................. 367
DateTime.......................................... 362, 363
DateTimePicker....................................... 487
DateValue................................................. 367
Day............................................................. 364
DayOfWeek............................................... 364
DayOfYear................................................. 364
DaysInMonth............................................. 365
Debug........................................................ 255
Debug.WriteLine............................... 55, 147
Decimal............................................ 157, 160
Declare...................................................... 718
Delete....................... 43, 116, 519, 782, 785
DELETE..................................................... 699
Deleted...................................................... 649
Delphi......................................................... 759
DESC......................................................... 683
design........................................................... 33
Designer...................................................... 31
Details........................................................ 544
DialogResult............................................. 531
Dim........................................... 143, 144, 586
Directory.................................................... 518
Dispose...................................................... 190
DLL............................................................. 705
Do............................................. 232, 233, 236
Do …. Loop............................................... 232
Do …. Loop Until...................................... 234
Do …. Loop While.................................... 233
Do Until …. Loop...................................... 235
Do While …. Loop.................................... 235
Dock..................................................... 71, 542
Dockable................................................... 108
DocumentComplete................................ 653
DoEvents................................................... 747
Double................... 144, 157, 159, 160, 161
DoubleClick..................................... 407, 408
DrawArc..................................................... 187
DrawBezier............................................... 466
DrawClosedCurve................................... 467
DrawCurve................................................ 466
DrawEllipse............................................... 185
DrawImage...................................... 339, 344
Drawing..................................................... 182
DrawLine.......................................... 183, 185
DrawLines................................................. 464
DrawPath................................................... 468
DrawPie..................................................... 187
DrawPolygon............................................ 465
DrawRectangle......................................... 185
DrawRectangles....................................... 464
DrawString................................................ 191
DROP TABLE............................................ 701
DropDown Combo.................................. 483
DropDownButton..................................... 541
DropDownList........................................... 482
DropDownStyle........................................ 482
Duration....................................................... 87
DVD-ROM.................................................. 770
Dynamic Help........................................... 123
E
e......................................................... 155, 637
E.................................................................. 155
Edit.............................................................. 116
Else............................................................. 198
ElseIf.......................................................... 208
Enabled........................................................ 71
EnableRaisingEvents.............................. 650
End..................................... 46, 221, 279, 280
End Function............................................. 308
End If.......................................................... 208
End Sub.............................................. 36, 271
EndCap...................................................... 331
EndsWith................................................... 496
Enter........................................................... 405
Enum.......................................................... 372
Environment.................................... 119, 520
EOF............................................................. 513
Event.......................................................... 641
EventArgs.................................................. 409
Excel........................................................... 714
Exception................................................... 522
Exists.......................................................... 519
Exit....................................................... 38, 116
Exit Do........................................................ 238
Exit For....................................................... 243
Exit Sub...................................................... 280
Exp.............................................................. 155
Expand....................................................... 552
ExpandAll.................................................. 552
F
False........................................................... 214
File..................................................... 116, 518
File System Object................................... 498
FileAttributes............................................. 519
FileClose.......................................... 510, 512
FileGet........................................................ 511
FileMode.................................................... 515
FileName............................................ 85, 528
FileOpen........................................... 510, 512
FilePut............................................... 510, 512
FileStream................................................. 514
FileSystem................................................ 498
FileSystemWatcher................................. 649
Fill............................................. 684, 687, 697
FillEllipse................................................... 187
FillPath....................................................... 468
FillPie......................................................... 187
FillPolygon....................................... 465, 467
FillRectangle............................................. 187
FillRectangles........................................... 465
Filter................................................... 529, 649
Find............................................................. 117
Fix............................................................... 155
FlatStyle....................................................... 70
Flip.............................................................. 345
Floating...................................................... 108
Floor........................................................... 155
Focus.......................................................... 406
Font............................................. 66, 333, 532
Font Style..................................................... 66
Fonts and Colors...................................... 119
FontStyle.................................................... 333
For............................................. 239, 240, 242
For Each................................................... 458
ForeColor............................................ 70, 552
Format............................................... 117, 164
FormBorderStyle........................................ 70
Friend......................................................... 586
FROM......................................................... 683
FromArgb......................................... 353, 354
FromFile.............................................. 77, 336
FromImage............................................... 348
FullPath...................................................... 650
Function..................................................... 308
G
G.................................................................. 356
Get.............................................................. 600
GetAttributes............................................. 519
GetChar..................................................... 492
GetCreationTime..................................... 519
GetCurrentDirectory................................ 520
GetDirectories........................................... 520
GetFiles...................................................... 521
GetItemChecked...................................... 486
GetLastAccessTime................................ 519
GetLastWriteTime.................................... 519
GetLogicalDrives..................................... 521
GetNodeCount......................................... 552
GetParent.................................................. 520
GetPixel..................................................... 356
GetUpperBound....................................... 445
GoTo........................................................... 226
Graphics.................................................... 182
GraphicsPath............................................ 467
GraphicsUnit............................................. 345
Grid Pane.................................................. 680
Grid Size.................................................... 121
GridColumnStyles................................... 693
GridLines................................................... 544
GroupBox.................................................. 374
H
Handled..................................................... 415
Handles............................................... 89, 637
HatchBrush...................................... 331, 332
HatchStyle................................................. 333
Height........................................... 70, 75, 342
Help.......................................... 122, 721, 727
HelpKeyword on HelpProvider1........... 727
HelpNamespace...................................... 727
HelpNavigator on HelpProvider1......... 727
HelpProvider............................................. 727
Hide..................................................... 48, 109
HideSelection.................................. 535, 550
HorizontalResolution............................... 342
Hour............................................................ 364
HScrollBar................................................. 477
HTML.......................................................... 652
HTML Help Workshop............................. 722
HTML-документ....................................... 659
Hue............................................................. 559
I
Icon............................................. 73, 339, 341
If......................................................... 198, 210
If блочный................................................. 206
If вложенные............................................ 210
If многострочный.................................... 206
If однострочный...................................... 200
Image....................... 77, 336, 339, 349, 477
Image Editor............................................. 360
ImageAlign.................................................. 77
ImageIndex............................................... 546
ImageList.......................................... 538, 540
Images....................................................... 538
ImageSize................................................. 539
Imports......................................................... 60
In................................................................. 457
IncludeSubdirectories............................. 649
Indeterminate........................................... 474
Index.................................................. 123, 726
IndexOf........................... 445, 484, 496, 543
Inflate......................................................... 326
Inherits....................................................... 606
InitialDelay................................................ 489
Input............................................................ 512
InputBox..................................................... 141
InputString................................................. 513
Insert......................................... 484, 496, 785
INSERT...................................................... 698
InStr............................................................ 493
Int................................................................ 658
Integer...................................... 143, 157, 158
IntelliSense............................................... 320
Internet....................................................... 771
Internet Explorer....................................... 652
Invalidate................................................... 472
Is......................................................... 217, 543
IsDate......................................................... 367
IsDigit......................................................... 412
IsLeapYear................................................ 365
IsLetter....................................................... 412
IsLetterOrDigit........................................... 412
IsLower...................................................... 412
IsMdiContainer......................................... 745
IsNumeric.................................................. 219
IsPunctuation............................................ 412
IsSeparator................................................ 412
IsUpper...................................................... 412
Italic............................................................... 66
Item............................................................. 692
Items......................................... 480, 483, 545
J
Java............................................................ 759
JPEG.......................................................... 401
K
KeyChar..................................................... 412
KeyCode.................................................... 413
KeyDown.......................................... 411, 413
KeyEventArgs........................................... 413
KeyPress................................................... 411
KeyPressEventArgs................................. 412
KeyPreview...................................... 414, 424
Keys............................................................ 414
KeyUp............................................... 411, 413
L
Label............................................................. 65
LabelEdit................................................... 547
LargeChange........................................... 477
LargeImageList........................................ 550
LastIndexOf...................................... 445, 496
Lcase.......................................................... 493
Leave......................................................... 405
Left....................................... 74, 75, 409, 492
Len....................................................... 60, 492
Length............................................... 445, 496
Let............................................................... 137
Lib............................................................... 718
LinearGradientBrush...................... 331, 332
LineCap..................................................... 331
LineInput.................................................... 513
LinkClicked............................................... 491
LinkLabel................................................... 490
LinkVisited................................................. 491
LISP............................................................ 759
ListBox....................................................... 480
ListView..................................................... 544
ListViewItem............................................. 545
Load.............................................................. 92
LoadFile..................................................... 536
Locals......................................................... 250
Lock Controls............................................ 118
Locked.......................................................... 70
LOF............................................................. 513
Log.............................................................. 155
Log10......................................................... 156
Logo........................................................... 759
Long.................................................. 157, 158
Loop........................................................... 233
Lowercase................................................. 117
LTrim.......................................................... 493
Luminosity................................................. 559
M
Main............................................................ 568
MainMenu.................................................... 79
MakeTransparent..................................... 401
MappingName......................................... 693
Math..................................................... 59, 155
Max............................................................. 155
MaximizeBox.............................................. 73
Maximum.................................................. 477
MaximumSize............................................. 73
MDI............................................................. 744
MdiChildren.............................................. 747
MdiParent.................................................. 746
Media Player............................................... 83
Members............................................. 55, 123
Message.................................................... 523
Metafile............................................. 339, 341
Microsoft...................................................... 54
Microsoft Chart Control........................... 565
Microsoft Multimedia Control 6.0..... 84, 88
Microsoft WebBrowser............................ 652
Mid.............................................................. 492
Middle........................................................ 409
Min.............................................................. 155
MinimizeBox............................................... 73
Minimum................................................... 477
MinimumSize.............................................. 73
Minute........................................................ 364
Mod............................................................. 154
Module....................................................... 566
Month......................................................... 364
MonthCalendar........................................ 487
MonthName.............................................. 367
MouseDown..................................... 407, 409
MouseEnter........................................ 90, 407
MouseEventArgs...................................... 409
MouseLeave............................................. 407
MouseMove............................ 407, 408, 411
MouseUp................................................... 407
Move........................................................... 519
MsgBox...................................................... 222
MsgBox........................................................ 78
MsgBoxResult........................................... 224
MsgBoxStyle............................................. 223
MultiColumn............................................. 481
Multiline........................................................ 72
MultiSelect................................................. 549
MustInherit................................................. 610
MustOverride............................................ 611
MyBase............................................... 92, 610
MyClass..................................................... 610
N
Name................................................... 64, 541
Namespace.............................................. 730
Navigate.................................................... 653
NET Framework......................................... 18
New.................. 39, 116, 178, 328, 578, 602
New Item................................................... 577
Next............................................................ 240
Nodes................................................ 551, 552
None........................................................... 410
Normal....................................................... 336
Not.............................................................. 213
Nothing............................................. 336, 740
Now............................................................ 365
NumericUpDown..................................... 488
O
Object....................................... 311, 455, 742
Object Browser......................................... 111
OldFullPath............................................... 649
OleDbCommand..................................... 697
OleDbConnection.................................... 684
OleDbDataAdapter.................................. 684
OnClick...................................................... 658
OnPaint...................................................... 472
Opacity......................................................... 73
Open........................................... 38, 116, 717
Open File..................................................... 41
Open Project............................................... 39
OpenFileDialog........................................ 527
OpenMode....................................... 510, 512
Option Explicit........................................... 145
Options....................................................... 118
Or....................................................... 212, 334
Order.......................................................... 118
ORDER BY................................................. 683
Orientation........................................ 478, 480
Output......................................... 56, 139, 512
Overloading.............................................. 612
Overridable............................................... 609
Overrides................................................... 609
P
Page Setup............................................... 116
Paint........................................................... 348
Panel.......................................................... 375
Parent......................................................... 552
Pascal........................................................ 759
PasswordChar.......................................... 221
Paste........................................... 43, 116, 781
Path............................................................ 649
PathGradientBrush.................................. 331
PC Speaker............................................... 768
Peek........................................................... 503
PeekChar.................................................. 518
Pen............................................................. 327
Pens........................................................... 183
PerformStep............................................. 480
PI................................................................. 155
PictureBox................................................. 336
Play............................................................... 85
PlayCount.................................................... 87
Point.................................................. 324, 464
PointF......................................................... 325
PRIMARY KEY........................................... 700
Print................................................... 116, 513
PrintLine.................................................... 513
Priority........................................................ 750
Private........................................................ 586
Private Const............................................ 586
Process...................................................... 720
ProgressBar.............................................. 479
Project............................................... 117, 576
Project Explorer........................................ 574
Prolog......................................................... 759
Properties Window..................................... 63
Property..................................................... 600
Protected................................................... 607
Providers................................................... 677
Public......................................................... 586
PushButton................................................ 541
Q
Query Builder............................................ 695
Quick Watch.............................................. 254
R
R.................................................................. 356
RadioButton.............................................. 476
RaiseEvent................................................ 641
Random............................................ 510, 512
Randomize................................................ 203
Rate............................................................... 87
Read........................................................... 502
ReadBoolean............................................ 518
ReadByte................................................... 515
ReadDouble.............................................. 518
ReadLine.......................................... 501, 570
ReadOnly............................ 72, 87, 415, 600
ReadString................................................ 518
ReadToEnd............................................... 507
Recent Projects........................................ 116
Rectangle.................................................. 325
RectangleF................................................ 325
Redo........................................................... 116
Reference Types...................................... 741
Refresh...................................................... 349
Region класс............................................ 469
Region свойство...................................... 469
Regular......................................................... 66
Remove................. 458, 484, 496, 548, 552
RemoveAt.................................................. 484
Rename.............................................. 43, 781
Renamed................................................... 649
RenamedEventArgs................................ 649
Replace............................................. 117, 496
Resize........................................................ 472
Resolution................................................. 341
Resume..................................................... 751
Return......................................................... 308
Reverse...................................................... 445
RichTextBox.............................................. 533
RichTextBoxStreamType........................ 537
Right.................................................. 409, 492
Rnd.................................................... 156, 203
Rotate......................................................... 345
RotateFlip.................................................. 345
RotateTransform...................................... 360
Round......................................................... 155
Rows........................................................... 690
RTrim.......................................................... 493
run................................................................. 34
Run To Cursor.......................................... 251
S
Saturation.................................................. 559
Save........................................................... 346
Save All............................................... 38, 116
SaveFile..................................................... 536
SaveFileDialog......................................... 526
ScaleTransform....................................... 358
Scroll.......................................................... 478
Scrollable.................................................. 550
ScrollBars.................................................... 72
Scrolling..................................................... 480
Search........................................................ 123
Second....................................................... 364
Seek.................................................. 512, 515
SeekOrigin................................................ 515
SELECT................................... 683, 694, 698
Select Case............................................... 216
SelectCommand...................................... 697
SelectedIndex........................................... 484
SelectedIndexChanged.......................... 484
SelectedIndices........................................ 549
SelectedItem.................................... 481, 484
SelectedItems........................................... 549
SelectedNode........................................... 552
SelectedText............................................. 536
SelectionAlignment................................. 535
SelectionBullet......................................... 535
SelectionColor.......................................... 535
SelectionFont............................................ 535
SelectionMode......................................... 486
Send to Back............................................. 118
sender........................................................ 637
Separator................................................... 541
Server Explorer........................................ 676
Set............................................................... 600
SET............................................................. 699
SetAttributes.............................................. 519
SetLastAccessTime................................. 520
SetLastWriteTime.................................... 519
SetPixel...................................................... 357
SetResolution........................................... 343
SetToolTip................................................. 489
Shadows.................................................... 610
Shared....................................................... 598
Shell........................................................... 719
Shift................................................... 414, 784
Short.................................................. 157, 158
Shortcut........................................................ 82
Show............................................................. 50
Show Grid.................................................. 121
Show Start Page...................................... 123
ShowColor................................................ 532
ShowDialog..................................... 527, 558
ShowHelp.................................................. 727
ShowHelpIndex........................................ 727
ShowInTaskbar........................................... 73
Sibling........................................................ 552
Sign............................................................ 155
Simple Combo......................................... 483
Sin............................................................... 156
Single....................................... 157, 159, 160
Size............................................... 66, 70, 325
SizeF.......................................................... 325
SizeMode.................................................. 336
Sleep.......................................................... 751
SmallChange........................................... 477
SmallImageList........................................ 544
SnapToGrid............................................... 121
SolidBrush................................................. 331
Solution...................................................... 575
Solution Explorer...................... 95, 109, 574
Sort............................................................. 445
Sorted......................................................... 484
Sorting........................................................ 550
Split.................................................... 121, 497
Splitter........................................................ 565
SQL.................................................... 684, 698
Sqrt............................................................. 155
Start...................................................... 33, 720
Start Page.................................................. 115
StartCap..................................................... 331
StartPosition................................................ 73
StartsWith.................................................. 496
Startup object............................................ 568
Static.................................................. 301, 586
StatusBar................................................... 565
Step................................................... 241, 480
Stop............................................. 87, 280, 653
Stop Debugging......................................... 34
Str................................................................ 493
StreamReader.......................................... 500
StreamWriter............................................. 499
StretchImage............................................ 336
String........................................ 172, 492, 496
Strings................................................. 60, 492
Structure.................................................... 460
Style............................................................ 541
Sub....................................................... 36, 271
SubItems................................................... 546
Substring................................................... 496
Suspend.................................................... 751
System......................................................... 54
System.IO.................................................. 499
SystemBrushes........................................ 353
SystemColors........................................... 353
SystemPens.............................................. 353
T
Tab Order.................................................. 407
TabControl................................................ 376
TabIndex.................................................... 406
TableName............................................... 693
Tables........................................................ 690
TableStyles............................................... 693
TabPage.................................................... 376
TabStop..................................................... 407
Tan.............................................................. 156
Text............................................. 64, 484, 546
TextAlign............................................. 71, 542
TextBox........................................................ 32
TextChanged.............................................. 92
TextureBrush................................... 331, 350
Then........................................................... 198
Thread........................................................ 750
ThreadPriority........................................... 751
ThreeState................................................. 474
TickFrequency.......................................... 478
Ticks........................................................... 365
TickStyle.................................................... 479
Timer.......................................................... 367
TimeSerial................................................. 367
TimeSpan.................................................. 363
TimeString................................................. 367
TimeValue................................................. 367
To....................................................... 217, 240
ToCharArray............................................. 496
Today......................................................... 365
ToggleButton............................................ 541
ToLocalTime............................................. 365
ToLongDateString................................... 365
ToLongTimeString................................... 365
ToLower..................................................... 412
Toolbar....................................................... 539
Toolbars..................................................... 124
Toolbox............................................... 32, 110
Tools........................................................... 118
ToolTip....................................................... 488
Top......................................................... 74, 75
TOP............................................................. 698
ToShortTimeString.................................. 365
ToString............................................ 365, 410
ToUniversalTime..................................... 365
ToUpper..................................................... 412
TrackBar.................................................... 478
TranslateTransform................................. 359
TransparentColor..................................... 539
TreeNode.................................................. 552
TreeView.......................................... 544, 550
Trim............................................................ 493
True............................................................ 214
Try............................................................... 522
TypeName................................................ 458
U
Ucase......................................................... 493
Unchecked................................................ 474
Uncomment Selection............................ 117
Undo........................................................... 116
Unicode-символы................................... 494
Until............................................................ 234
Update.............................................. 684, 687
UPDATE.................................................... 699
Uppercase................................................. 117
User Control Designer............................ 704
UserControl............................................... 704
V
Val........................................................ 67, 493
Value.... 477, 480, 487, 488, 601, 658, 717
Value Types.............................................. 741
ValueChanged................................ 487, 488
VALUES..................................................... 699
VB.................................................................. 15
vbNewLine................................................ 508
VBScript..................................................... 657
View................................................... 117, 544
View Code................................................. 575
View Designer.......................................... 575
Visible........................................................... 71
Visual Basic.............................................. 759
Visual Basic .NET....................................... 15
Visual C# .NET........................................... 18
Visual C++................................................. 759
Visual C++ .NET......................................... 18
Visual J# .NET............................................ 18
Visual Studio .NET........................ 18, 26, 95
Volume......................................................... 87
VS.................................................................. 26
VScrollBar................................................. 477
W
Watch......................................................... 252
WebApplication........................................ 662
Web-приложение................................... 662
Web-сервер.............................................. 651
Web-страница......................................... 654
Web-форма.............................................. 663
WeekdayName........................................ 367
WHERE...................................................... 683
While.......................................................... 234
While …End While................................... 239
Width.................................... 70, 75, 329, 342
Window...................................................... 121
Windows API............................................. 717
Windows Form Designer generated code 179
WindowState............................................... 73
With............................................................. 463
WithEvents....................................... 636, 640
Word Wrap.......................................... 94, 117
WordWrap.................................................... 72
Workbooks................................................ 717
Worksheet................................................. 717
WrapMode................................................. 351
Write................................ 502, 512, 515, 570
WriteLine................................. 500, 512, 570
WriteOnly................................................... 602
X
X........................................................... 74, 409
XML............................................................ 688
Y
Y............................................................ 74, 409
Year............................................................ 364
Z
ZoomFactor............................................... 536
А
абсолютная величина.......................... 155
абстрактные классы.............................. 610
адаптер..................................................... 685
адрес................................................ 304, 782
активный объект....................................... 62
алгоритм................................................... 756
алфавитно-цифровая клавиша......... 412
анимация.................................................. 395
арифметическое выражение.............. 138
Ассемблер................................................ 759
Б
база данных................................... 461, 666
байт.......................................... 763, 772, 773
библиотека динамической компоновки 705
библиотека классов............................... 711
библиотека классов .NET Framework 18, 714
бит..................................................... 513, 773
блок............................................................ 299
браузер..................................................... 652
булевский тип.......................................... 214
буфер обмена......................................... 788
Бэйсик.......................................................... 19
В
ввод............................................................ 498
векторная графика................................ 338
верхняя граница индекса.................... 434
вершина.................................................... 552
ветвление........................................ 197, 198
ветка.......................................................... 552
видео............................................................ 83
видеоадаптер......................................... 767
видеокарта............................................... 767
визуальное программирование......... 180
винчестер................................................. 761
вкладка...................................................... 376
вложенные циклы......................... 262, 265
возврат каретки...................................... 507
возврат управления.............................. 764
выбор................................................ 197, 198
выборка..................................................... 684
вывод......................................................... 498
выделение фрагмента......................... 787
выделенный канал................................ 771
выделенный объект................................ 62
вызов программы................................... 764
вызов процедуры................................... 272
выражение...................................... 138, 305
выход из VS................................................ 38
выход из цикла........................................ 230
Г
гарнитура................................................. 333
главное меню............................................. 79
глобальная область видимости......... 586
градиентная заливка............................ 332
грамматика............................................... 728
графика................................... 176, 324, 464
графический путь................................... 467
графический редактор VB................... 360
графическое поле.................................. 182
Д
данные............................................. 157, 763
движение.................................................. 229
двумерные массивы.............................. 437
действия арифметики.......................... 154
делегаты................................................... 643
деление.................................................... 154
Дельфи...................................................... 759
дерево....................................................... 779
десятичные дроби................................. 155
Джава......................................................... 759
диалоговое окно выбора цвета.......... 558
диалоговое окно настройки шрифта 531
диалоговые окна открытия и сохранения файла 526
дизайнер компонентов........................... 79
динамические компоненты объектов 599
дискета...................................................... 769
дисковод................................................... 769
дисплей..................................................... 766
дозапись................................................... 502
документ................................................... 777
дополнительная цифровая клавиатура 414
дорожка..................................................... 782
доступ к локальным дискам................. 659
дочерняя вершина................................ 552
дуга............................................................. 187
Е
если............................................................ 198
Ж
жесткий диск............................................ 761
З
заголовок процедуры............................ 271
загрузка............................................ 498, 764
закладка.................................................... 376
закладка alphabetic.................................. 63
закладка categorized................................ 63
закрытие проекта..................................... 38
заливка...................................................... 331
запись...................................... 461, 498, 666
запрос.............................................. 667, 673
запятая.............................................. 68, 155
затенение................................................ 587
зацикливание.......................................... 226
звук............................................................... 83
звуковая карта........................................ 768
звуковой адаптер................................... 768
зерно......................................................... 767
знак сравнения....................................... 201
значение................................................... 137
И
И.................................................................. 211
идеология .NET......................................... 18
ИЛИ............................................................ 212
имя.............................................. 64, 151, 777
иначе......................................................... 198
индекс...................................... 432, 458, 496
индексированные переменные......... 432
индикатор процесса.............................. 479
индукция................................................... 448
инициализация....................................... 294
инициализация двумерного массива 439
инициализация массива...................... 434
инкапсуляция.......................................... 591
инсталляционный пакет...................... 125
инсталляция..................................... 95, 125
инсталляция Visual Studio .NET........... 95
инструкция............................................... 760
Интернет.................................................. 651
интерфейсы............................................ 608
исключение.............................................. 521
Истина....................................................... 214
исходные данные.................................. 763
итерация.................................................. 228
К
календари................................................ 487
каталог...................................................... 777
кисть........................................................... 187
клавиатура............................................... 411
клавиши перемещения курсора........ 785
класс........................................ 181, 577, 733
ключ............................................................ 458
ключевое поле........................................ 670
ключевые слова...................................... 152
книга........................................................... 716
кнопка.......................................................... 32
кнопка возврата...................................... 788
кнопка максимизации.............................. 73
кнопка минимизации............................... 73
кнопка отмены........................................ 788
код................................................................. 35
кодовая страницы 1251....................... 505
коллекция................................................. 456
команда..................................................... 760
комментарии.............................................. 93
компакт-диски.......................................... 770
компиляция..................................... 705, 760
компонент................................................... 55
компоненты объекта............................. 123
компьютер................................................ 756
консольное приложение...................... 569
константы................................................. 310
конструктор..................................... 328, 602
конструктор запросов........................... 680
конструктор таблицы............................ 670
конструктор форм.................................... 33
конструктор элементов управления пользователя 704
контейнер................................................. 459
контекстное меню......................... 489, 775
копирование................................... 781, 788
копирование файла.............................. 518
копирование файлов и папок............... 43
корень........................................................ 552
корень квадратный................................ 155
кривая Безье........................................... 466
круг............................................................. 184
Л
лазерные диски...................................... 770
Лисп........................................................... 759
лист............................................................ 716
логарифм.................................................. 155
логические выражения......................... 214
логические операции............................ 211
логические переменные...................... 214
логический диск...................................... 782
Лого............................................................ 759
Ложь........................................................... 214
локальные переменные.............. 296, 586
локальный диск....................................... 659
ломаная.................................................... 464
М
максимум.................................................. 268
маркер......................................................... 32
маска.......................................................... 529
массив.............................................. 432, 433
массивы как объекты............................ 445
массивы как параметры....................... 446
массивы многомерные......................... 441
массивы одномерные........................... 433
массивы структур................................... 461
массивы элементов управления....... 446
мастер....................................................... 133
мастер установки................................... 125
математика.............................................. 153
математические функции.................... 155
машинный язык...................................... 757
медиаплеер............................................... 83
меню............................................................. 79
меню «Пуск»............................................ 774
метка................................................... 65, 226
метка с гиперссылкой........................... 490
метод пузырька...................................... 453
методы.................... 48, 194, 315, 316, 319
микрофон................................................. 766
минимум.................................................... 268
многозадачность.................................... 765
многоугольник......................................... 465
множественный выбор......................... 486
моделирование...................................... 394
модель....................................................... 394
модем........................................................ 771
модуль............................ 155, 567, 569, 733
модульные переменные............. 296, 586
монитор.................................................... 766
мусор......................................................... 740
мышь.......................................................... 407
Н
наследник................................................. 605
наследование......................................... 605
наследование элементов управления 643
настройка среды.................................... 118
насыщенность........................................ 559
научный формат.................................... 164
НЕ............................................................... 213
О
области видимости....................... 293, 585
обработчик события............................. 635
обращение к процедуре...................... 272
обращение к функции.................. 306, 307
объект................................................. 25, 591
объектное программирование........... 591
объектный тип........................................ 178
объекты.................................................... 176
объявление.................................... 140, 167
объявление процедуры....................... 272
объявление функции............................ 307
окно............................................................ 775
окно Output................................................. 56
окно дизайнера компонентов............. 708
окно кода..................................................... 35
окно свойств............................................... 62
округление............................................... 155
оперативная память.. 761, 762, 763, 768
оператор............................................ 24, 760
оператор безусловного перехода.... 226
оператор варианта............................... 215
оператор присваивания.... 137, 139, 146
оператор цикла...................................... 232
операционная система........................ 764
ОС............................................................... 764
остаток от деления............................... 154
открытие проекта..................................... 38
открытие файла..................................... 499
отладка........................................ 25, 95, 249
отрезок...................................................... 184
оттенок...................................................... 559
ошибки....................................... 43, 257, 521
ошибки выполнения................................ 44
ошибки компиляции................................. 46
П
память.............................................. 761, 763
панель....................................................... 375
панель задач........................................... 774
панель инструментов............ 33, 123, 539
папка.......................................................... 777
параметры............................. 185, 276, 319
пароль....................................................... 221
Паскаль....................................................... 19
перевод строки....................................... 507
перегрузка................................................ 612
передача параметров по значению. 304
передача параметров по ссылке...... 304
передача управления........................... 764
переименовывание........................ 43, 781
переключатель....................................... 476
переменная цикла................................. 240
переменные величины 137, 149, 245, 283
переменные уровня модуля............... 296
перемещение файлов и папок.... 43, 781
перемещение фрагментов.................. 788
перенос оператора................................. 93
перенос файла....................................... 518
переопределение.................................. 609
перечисления......................................... 371
перо........................................................... 183
персональный компьютер................... 760
пиксель.................................... 757, 766, 772
плавающие окна..................................... 108
платформа .NET....................................... 18
побочный эффект.................................. 309
подсказка.................................................. 488
поле.................................................. 594, 666
ползунок................................................... 478
полиморфизм.......................................... 608
полосы прокрутки......................... 477, 779
пользователь.......................................... 756
потоки............................................... 747, 749
пошаговый режим.................................. 149
преобразование типов................ 161, 501
привязанные окна................................. 108
привязка................................................... 686
приложение............................................... 25
приложение Windows.............................. 25
принтер..................................................... 768
принтер лазерный................................. 768
принтер матричный.............................. 768
принтер струйный................................. 768
принцип инкапсуляции......................... 593
пробел....................................................... 784
провайдер................................................ 651
проводник................................................. 778
программа................................ 24, 157, 756
программист............................................ 756
продукт........................................................ 25
проект.......................................................... 25
проект - создание..................................... 39
прозрачность.......................................... 354
прозрачный цвет...................................... 77
Пролог....................................................... 759
пространство имен......................... 53, 728
простые типы данных.......................... 461
процедуры......................................... 24, 270
процедуры обработки событий......... 272
процедуры пользователя.................... 271
процедуры с параметрами................. 275
процессор................................................ 761
прямоугольник...................... 184, 325, 464
путь............................................................ 782
Р
рабочий лист........................................... 717
рабочий стол........................................... 774
радиокнопка............................................ 476
разветвляющиеся программы........... 197
размер....................................................... 325
размер картинок..................................... 341
размер шрифта........................................ 66
разрешение.................................... 341, 767
разрешение картинок........................... 341
рамка......................................................... 374
растровая графика................................ 338
расширение............................................. 778
регистр верхний..................................... 784
регистр нижний....................................... 784
регистр русский...................................... 784
Редактор вершин дерева.................... 551
Редактор коллекции картинок............ 538
Редактор коллекции кнопок................ 540
Редактор коллекции подэлементов списка 546
Редактор коллекции столбцов........... 544
Редактор коллекции элементов списка 545
Редактор строковых коллекций......... 480
режим вставки......................................... 785
режим замещения................................. 785
режим прерывания................................ 148
режим проектирования.......................... 33
режим работы........................................... 34
результат................................................. 763
рекурсия................................................... 448
решение................................................... 575
родитель................................................... 605
родительская вершина........................ 552
С
сборка............................................... 112, 711
свойства............................................. 69, 600
свойства для чтения-записи............... 600
свойства текстового поля...................... 72
свойства только для записи................ 601
свойства только для чтения................ 600
свойства формы....................................... 73
свойство...................................................... 34
свойство объекта................................... 592
связанные таблицы.............................. 667
сектор........................................................ 187
сетевая карта.......................................... 770
сетка............................................................. 34
сеть глобальная..................................... 770
сеть компьютерная................................ 770
сеть локальная....................................... 770
Си.................................................................. 19
символы........................................... 494, 771
символы типа.......................................... 161
символы формата.................................. 165
синтаксис......................................... 200, 735
синтаксическая схема........................... 200
система координат................... 74, 75, 358
система управления базами данных 461, 666
системные цвета.................................... 351
системный блок...................................... 760
сканер........................................................ 766
скобки........................................................ 154
случайное число..................................... 156
случайные величины............................ 203
смысловые ошибки.................................. 43
собственный класс................................ 577
события.............................. 24, 88, 635, 641
соединение.............................................. 677
создание папок......................................... 42
создание файлов............................ 42, 518
соответствие типов............................... 315
сортировка............................................... 452
составные типы данных...................... 461
сохранение.............................................. 505
сохранение проекта................................ 37
спецификаторы формата.................... 165
список........................................................ 480
список раскрывающийся...................... 482
список с флажками................................ 483
сплайн....................................................... 466
справочная система.............................. 721
среда визуальной разработки программ. 25
ссылка.............................................. 304, 739
ссылочные типы............................ 737, 741
стандартный модуль............................. 567
стартовая страница....................... 26, 115
стартовое меню...................................... 774
статические компоненты объектов.. 598
статические переменные.................... 301
степень - возведение............................ 154
страница................................................... 376
строка состояния................................... 565
строки............................................... 172, 492
строковые выражения.......................... 173
строковые литералы............................. 173
строковые переменные....................... 171
структуры........................................ 460, 733
ступенчатая запись программы........ 209
СУБД.......................................................... 666
сумматор.................................................. 261
сценарий.................................................. 657
счетчик............................................. 258, 488
счетчик циклов........................................ 228
съемные носители информации....... 769
Т
таймер....................................................... 369
тактовая частота.................................... 761
тег............................................................... 655
текстовое поле.......................................... 32
текстовый курсор................................... 783
текстовый редактор.............................. 782
текстурная кисть.................................... 350
текущая папка......................................... 520
тело процедуры...................... 36, 272, 307
тело цикла....................................... 226, 233
тик............................................................... 365
тип.............................................................. 143
тип выражения....................................... 163
типы данных............................................ 157
точка........................................... 68, 155, 324
точки прерывания......................... 147, 251
трансляция.............................................. 760
У
удаление файлов и папок.... 43, 518, 782
удаление фрагмента............................ 788
узел............................................................ 651
указатель.................................................. 739
умножение............................................... 154
умолчание.................................................. 64
управляющие клавиши......................... 412
условие..................................................... 201
условный оператор............................... 198
установка.................................................. 125
устройства ввода................................... 765
устройства вывода....................... 765, 766
устройства компьютера....................... 765
Ф
файл................................................. 498, 777
файлы нетипизированные................. 513
файлы с последовательным доступом 499
файлы с произвольным доступом 512, 514
файлы текстовые................................... 499
файлы типизированные...................... 510
фильтр...................................................... 529
флажок...................................................... 473
флэш-память........................................... 770
фокус......................................................... 405
форма............................... 31, 566, 743, 744
форма фигурная.................................... 469
форматирование.......................... 163, 368
фото............................................................. 76
функции........................................... 301, 306
функции пользователя......................... 306
функции преобразования типов....... 162
функциональные клавиши.................. 412
Ц
цвет объекта.............................................. 69
цвет текста................................................. 70
целая часть числа.................................. 155
целое число.................................... 143, 144
целочисленное деление...................... 154
цикл............................................................ 225
циклические программы....................... 225
Ч
числа Фибоначчи.................................... 432
числовые литералы.............................. 156
чтение....................................................... 498
Ш
шаблон...................................................... 529
шаг цикла.................................................. 241
шина.......................................................... 761
шрифты.............................................. 66, 333
штрихованая кисть................................ 332
Э
экземпляры класса................................ 176
экспоненциальный формат................ 164
элемент управления пользователя. 704
элементы управления.......... 32, 473, 525
эллипс....................................................... 184
Я
Ява............................................................. 759
язык программирования...................... 758
яркость...................................................... 559
ячейка............................................... 146, 147
Специально для http://all-ebooks.com
[†] В этой главе я кое- где буду применять термины, которые еще не объяснял и буду объяснять лишь позже. Вы можете спокойно не обращать на них внимания. Они нужны только тем, кто их уже понимает.
[‡] Модулем он стал только в VB 2003. В предыдущей версии VB он был классом, но для начинающего программиста это несущественно.
Часть III. Программирование на VB – второй уровень
Если вам кажется, что вы уже все можете, то вы правы и неправы. Правы потому, что вы можете написать сколь угодно большую и сложную программу, разбив ее на разумное число процедур. Неправы потому, что ваша способность манипулировать данными разных типов еще очень ограничена. А без нее вам не поддадутся многие «умные» задачи. Например, не познакомившись с так называемыми массивами, вы не сможете запрограммировать игру в крестики-нолики или морской бой. Не освоив работу со строками, вы не сможете решить задачу о мало-мальски серьезной шифровке и дешифровке секретных сообщений. Не умея работать с файлами, вы не сможете сохраняться в созданных вами играх. Вам нужно освоить объектное программирование, познакомиться с базами данных, с работой в Интернете и с другими вещами.
Если в предыдущей части главной целью было научить вас программировать сложные проекты для работы с простыми данными, то теперь главная цель – программировать простые проекты для работы со сложными данными. В этой части я предполагаю, что предыдущую часть вы прочитали и задания в основном выполнили.
Разветвляющиеся программы
Выбор (ветвление) в VB осуществляют три оператора:
Однострочный If
Многострочный If
Оператор выбора Select Case
Их применению в программировании на VB и посвящена эта глава.
Циклические программы
Идею цикла я поясню на примере движения объектов по экрану. Возьмем игру в воздушный бой. Самолетик по экрану должен двигаться. Но в списках операторов большинства языков программирования, используемых профессиональными программистами для создания игр, нет команды движения. Здесь нужно задаться вопросом – а что такое движение? Рассмотрим иллюзию движения, возникающую на экране кинотеатра. Если вы держали в руках кинопленку фильма, изображающего, скажем, движение автомобиля, то должны были обратить внимание, что она состоит из множества неподвижных слайдов-кадров, на каждом следующем из которых автомобиль находится чуть-чуть в другом месте, чем на предыдущем. Показывая эти кадры один за другим с большой скоростью, мы создаем иллюзию движения автомобиля. Точно так же поступают с созданием иллюзии движения на экране компьютера. Запишем алгоритм движения самолетика по экрану слева направо:
1.Зададим в уме компьютера позицию самолетика в левой части экрана.
2.Нарисуем в задуманном месте самолетик.
3.Сотрем его.
4.Изменим в уме компьютера позицию самолетика на миллиметр правее.
5.Перейдем к команде 2.
Каждая из приведенных команд алгоритма легко программируется на большинстве языков. Любой компьютер, выполнив очередную команду, автоматически переходит к выполнению следующей. Так, выполнив команду 2, компьютер всегда перейдет к выполнению команды 3. Однако, если мы захотим, то можем заставить компьютер изменить этот порядок, что мы и сделали в команде 5. Из-за нее компьютер выполняет команды в таком порядке: 1-2-3-4-5-2-3-4-5-2-3-…. Напомню, что, выполнив команду 2, компьютер всегда перейдет к выполнению команды 3, независимо от того, какую команду он выполнял перед командой 2 – 1-ю или 5-ю. Таким образом, многократно выполняется последовательность команд 2-3-4-5. Такая многократно выполняемая последовательность называется циклом. Можно сказать, что цикл – это одно из средств заставить компьютер долго работать при помощи короткой программы.
В коротком промежутке времени после выполнения команды 2 самолетик будет появляться на экране и на команде 3 исчезать, но этого достаточно, чтобы человеческий глаз его заметил. Благодаря циклу самолетик будет мелькать каждый раз в новом месте, а поскольку смена «кадров» будет очень быстрой, нам будет казаться, что происходит плавное движение самолетика.
Не нужно думать, что циклы применяются лишь при движении. Они пронизывают все программирование. Ведь со 2-й по 5-ю команды мы можем писать команды любого назначения: и вычислительные, и по работе с текстом и так далее.
Отладка программы
Мы уже добрались до программ, требующих для своей отладки более разнообразных средств, чем простой пошаговый режим. С ними мы сейчас и познакомимся. Кстати, зачастую они и более удобны.
Сначала перечитайте, пожалуйста, материал про сообщения об ошибках в 1.3.8 и про пошаговый способ в 5.3.2.
Приступим. На Рис. 9.1 вы видите в окне кода в процессе отладки некую бессмысленную процедуру Button1_Click и несколько отладочных окон. Вот эта процедура:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Debug.WriteLine("Начало работы")
Dim a As Integer = 20
Dim b As Integer = 3
Do Until b > a
Debug.WriteLine(a & " " & b)
a = a - 2
b = b + 1
Loop
Debug.WriteLine("Конец работы")
End Sub
Легко видеть, что выполнившись с начала до конца, эта процедура печатает в окне Output такую информацию:
Начало работы
20 3
18 4
16 5
14 6
12 7
10 8
Конец работы
Вообразим, что нас такая печать не устроила, мы создавали процедуру для того, чтобы было напечатано
Начало работы
20 3
18 4
Конец работы
Вообразим, что мы не можем сразу понять, как изменить программу, чтобы получилось то, что нам нужно. Ясно, что в программе где-то что-то надо исправить. Чтобы разобраться в случившемся и найти ошибку, мы затеяли отладку.
Что же мы делаем? Прежде всего мы обычным способом помещаем на экран окно Output. Затем мы ставим точку прерывания на заголовке процедуры и запускаем проект. Затем нажимаем на кнопку Button1 и проект прерывается на заголовке процедуры. Ну вот, теперь мы находимся в режиме прерывания. Теперь можно отлаживать.
Многие окна, используемые в отладке, доступны или работают лишь в режиме прерывания. Поэтому, прежде, чем помещать их на экран, я и запустил, а затем прервал проект, чтобы в этом самом режиме прерывания оказаться.
Рис. 9.1
Окно Locals. Идем дальше. При помощи Debug ® Windows ® Locals мы вызываем на экран окно Locals (см. рисунок). Назначение окна Locals – показывать в режиме прерывания значения локальных переменных и других объектов выполняемой процедуры. (Локальные переменные – это переменные, объявленные внутри процедуры[†]). Столбец Name в окне Locals означает имя переменной или объекта. Столбец Value показывает нам их значение. К тому же в столбце Type вы можете видеть информацию о типе переменных и объектов.
После того, как проект прервался на заголовке процедуры, мы запускаем клавишей F11 пошаговый режим выполнения проекта (если VS настроена на профиль Visual Basic 6, то это будет клавиша F8). После нескольких нажатий на F11 окна Locals и Output принимают тот вид, что вы видите на рисунке. Правда, мы и без окна Locals в любой момент можем узнать значения a и b, просто поместив на них мышиный курсор. Однако применение окна Locals избавляет нас и от этих мизерных трудов.
Щелкните по плюсику слева от Me. Форма расскажет вам о текущих значениях своих свойств. Такие же плюсики вы видите у других объектов. Если в вашей процедуре имеются сложные переменные, например, массивы или структуры, щелкнув по плюсику рядом с ними, вы увидите значения всех их составных частей.
Если вы перейдете к выполнению другой процедуры, то в окне Locals будут видны уже именно ее локальные переменные.
Точки прерывания (Breakpoints). Когда программа большая или циклов много, жать на F11 приходится слишком часто. Но ведь и нужды-то в этом нет. Обычно вам интересно останавливаться не на каждой строке программы, а только на некоторых подозрительных, остальные можно выполнять и без остановки. VS позволяет вам задать такие строки – точки прерывания
(Breakpoints). Щелкните по вертикальной серой полосе в левой части окна кода против строки, на которой хотите прерывать выполнение программы. На полосе появится черная точка и вся строка будет выделена черным (все это описано в 5.3.2).
Щелкните также против всех нужных вам строк. Все они станут черными. Запустите проект обычным образом (кнопка Start или клавиша F5). Дойдя до первой же точки прерывания, проект перейдет в режим прерывания. Продолжайте его работу клавишей F5. Проект будет останавливаться только на точках прерывания. Убираются точки прерывания так же, как и ставятся – щелчком мыши.
«Беги до курсора» – Run To Cursor. Это еще один удобный способ остановки в нужной вам строке. Щелкните правой клавишей мыши по нужной строке и в возникшем контекстном меню выберите команду Run To Cursor. При этом происходит вот что. Щелчок правой клавишей мыши ставит текстовый курсор в нужную строку, а команда Run To Cursor запускает выполнение. Программа будет выполняться до тех пор, пока не наткнется на строку с курсором. А теперь щелкните правой клавишей мыши в другой строке и в возникшем контекстном меню снова выберите команду Run To Cursor. Программа продолжит работу с того места, где остановилась, и будет выполняться до тех пор, пока не наткнется на строку с курсором. И так далее.
Переключаемся между способами прерывания. В процессе выполнения программы вы можете достаточно свободно переключаться между разными способами прерывания. Ставьте и убирайте точки прерывания, нажимайте то F11, то F5, бегите временами до курсора. VB будет вас слушаться. Это удобно.
Если вы работаете в пошаговом режиме в какой-то процедуре, которая вызывает много других процедур, вам не интересных, то работайте не клавишей F11, а клавишей F10. При этом все другие процедуры будут проскакиваться мгновенно.
Окна Watch. Вообразим, что окно Locals не заставило нас поумнеть. Нам все кажется, что цикл должен был бы прерваться уже при значении b=5, а почему не прервался – непонятно. Чтобы разобраться, что к чему, нам хотелось бы в любой момент времени с удобством присматривать, правда или неправда, что b=5. Также нам хотелось бы в любой момент знать, правда или неправда, что b>a. То есть нам хотелось бы, чтобы вычислялись булевские выражения b=5 и b>a и результаты в виде True или False были видны в окне. Окно Locals такой возможности не дает. Более широкие возможности – у окна Watch. Вызовем его так: Debug ® Windows ® Watch ® Watch1 (можно было вызвать и другие три окна Watch). Типичный вид этого окна при отладке другой программы вы можете видеть на Рис. 9.2.
Рис. 9.2
Его вид не отличается от окна Locals. Отличие – в возможностях. В столбец Name мы можем вводить имена каких угодно (а не только локальных) переменных и объектов. А также мы можем вводить туда какие угодно выражения. В столбце Value мы видим значения этих переменных и выражений. Если вы щелкнете по плюсику слева от Button1, то кнопка расскажет вам о текущих значениях своих свойств. Если в вашем проекте имеются сложные переменные, например, массивы или структуры, введите их имена в столбец Name и, щелкнув по плюсику рядом с ними, вы увидите значения всех их составных частей.
В нашем случае я ввел в окно Watch1 оба наших выражения: b=5 и b>a. На Рис. 9.1 вы видите значения этих выражений в тот момент выполнения проекта, когда b равно 5 и a равно 16.
Настройка точек прерывания. Окно Watch помогает неплохо. Но мы устали жать на F11. Чтобы не уставать, мы ставим точку прерывания на одну из строк в цикле, скажем, на Loop, и жмем уже на F5, а не на F11. Теперь выполнение останавливается реже – один раз на итерацию (выполнение цикла). Стало легче. Но это когда итераций мало. А если их штук 500? 500 раз нажимать на F5 – тоже не подарок. В этом случае вам захочется найти более умные и быстрые средства отладки. И они есть. Рассмотрим их применительно к нашему случаю.
Хорошо бы наша программа исполнялась в своем обычном сверхбыстром режиме и проскакивала нашу точку прерывания без остановки, но каждый раз, однако, проверяя, выполняется ли некое условие, скажем, b=5. Оно по нашим подозрениям поначалу равно False, но затем в какой-то момент выполнения вроде бы станет равным True. Вот этот самый момент мы и хотим, чтобы VB поймал и тут же остановил программу.
Итак, нам нужен совсем другой способ остановки – хоть и на нужной строке, но не всегда, а только при выполнении заданного условия. Для этого точку прерывания, установленную на данной строке, нужно настроить.
Для настройки точки прерывания завершим выполнение проекта и перейдем в режим проектирования. Щелкнем правой клавишей мыши по точке прерывания и в возникшем контекстном меню выберем Breakpoint Properties. Возникнет окно настройки свойств точки прерывания (см. Рис. 9.3).
Рис. 9.3
В этом окне нас сейчас интересуют только кнопки Condition и Hit Count.
Condition. Нажав кнопку Condition, мы видим окно настройки условия для прерывания (Рис. 9.4).
Рис. 9.4
Вводим в поле наше условие (b=5). Затем – ОК. Теперь компьютер остановится на точке прерывания только тогда, когда будет выполнено условие. Этот момент остановки вы и видите на Рис. 9.1.
Под полем для условия вы видите переключатель на два положения. Нижнее положение приказывает прерваться не при выполнении условия, а при любом изменении значения выражения, записанного в поле.
Hit Count. Нажав кнопку Hit Count, мы видим окно настройки счетчика прерываний (Рис. 9.5).
Рис. 9.5
Его дело – останавливать компьютер на точке прерывания не всегда, когда позволяет условие, а еще реже. Как именно реже, определяет список из 4 способов. Важную роль играет число, которое вы вводите в поле справа. На рисунке введено число 3 и мы для определенности будем пояснять относительно него. Вот 4 способа:
break always |
Останавливай всегда, когда позволяет условие, а если условия нет – то вообще всегда. |
break when the hit count is equal to |
Останавливай на 3-й раз и больше никогда |
break when the hit count is a multiple of |
Останавливай через каждые два раза на 3-й |
break when the hit count is greater than or equal to |
Останавливай на 3-й раз и после этого всегда |
Окно Quick Watch. Кроме этих окон вы видите на Рис. 9.1 еще одно окно – Quick Watch. Оно понадобилось вот для чего. Иногда в процессе работы в режиме прерывания вам захочется узнать, чему равно в настоящий момент значение того или иного выражения из процедуры, например a - 2. Вы, конечно, можете включить это выражение в окно Watch, но быстрее поступить так: Выделите нужное выражение в окне кода, как это сделано на рисунке, затем щелкните по выделенному выражению правой клавишей мыши и в возникшем контекстном меню выберите команду Quick Watch. Окно появляется уже с нужной информацией. Если вы захотите ввести это выражение в окно Watch, щелкните кнопку Add Watch.
Окно Command Window-Immediate позволяет в режиме прерывания вычислять выражения, узнавать значения переменных и свойств объектов проекта, и, что самое пикантное, принудительно менять их.
Вызывается оно так: Debug ® Windows ® Immediate. Возникает окно с заголовком Command Window-Immediate. Не спутайте его с окном, имеющим заголовок Command Window, которое вызывается по-другому (View ® Other Windows ® Command Window) и не совсем вам подходит.
Пользуются этим окном так. Нужно в режиме прерывания вводить в него команды (вручную или скопировав из окна кода) и нажимать Enter. Тут же, в следующей строке вы будете видеть результат. Вот пример содержимого окна Command Window-Immediate (вводимые вами команды я выделил полужирным шрифтом):
?a
14
?a+10
24
?a<33
True
Debug.WriteLine(a & " " & b)
14 6
a=1000
?a
1000
? textbox1.text
"TextBox1"
textbox1.text="Привет!"
? textbox1.text
"Привет!"
textbox1.BackColor=color.LightSeaGreen
Пояснения: Знак вопроса означает запрос значения переменной, свойства или выражения. Так, команда
?a
означает запрос на значение переменной a и получает результат 14.
Команда
Debug.WriteLine(a & " " & b)
скопирована из окна кода и дала ожидаемый результат:
14 6
Команда
a=1000
принудительно меняет значение переменной a. То есть происходит прямое и неприкрытое вмешательство в святая-святых – работу программы. Но польза от этого для отладки есть. (Это еще ничего, предыдущие версии Visual Basic разрешали менять даже операторы программы в процессе выполнения. Представляете?! И тоже, кстати, не без пользы.)
Команда
? textbox1.text
узнает значение свойства объекта.
Команды
textbox1.text="Привет!"
и
textbox1.BackColor=color.LightSeaGreen
меняют свойства объекта.
Окно Command Window-Immediate не дает вам привычной свободы окна кода. Скорее оно похоже на командную строку с историей. Для навигации по окну вы можете пользоваться мышкой и на клавиатуре стрелками вверх-вниз. Нажатие клавиши Enter на любой промежуточной строке в этом окне отправляет эту строку вниз. Стрелки вверх-вниз не движут курсор, а «вспоминают» команды. Ничего, привыкнуть можно.
Окно Call Stack. Когда в программе содержится много взаимодействующих процедур, вам при отладке может быть совершенно необходимо разобраться в последовательности их выполнения. В этом случае вам поможет окно Call Stack, которое и покажет вам эту последовательность. Вызывается оно так: Debug ® Windows ® Call Stack.
Все описанные в этом разделе возможности отладки, а также некоторые другие, не описанные мной, приведены в меню Debug (отладка).
Типичные приемы программирования
Каждому программисту известны такие понятия, как счетчик, сумматор, минимум, максимум, вложенные циклы и другие подобные, составляющие основу элементарной техники программирования. Без них не обходится ни одна реальная программа, и если мы хотим идти дальше, то нам без них тоже не обойтись. В этой главе я не буду вводить новых операторов VB, а вместо этого покажу, как программировать типичные задачи, в том числе и те, что используют упомянутые понятия.
Процедуры и функции
Понятие процедур и функций точно так же, как и понятие объектов – основополагающее в современном программировании. В этой главе вы разберетесь в процедурах и функциях и научитесь писать собственные процедуры и функции.
Работа с таймером, временем, датами
Мы с вами пока не умеем управлять временем. А это нам необходимо для работы с движением, анимацией, а также для решения других полезных и интересных задач, приведенных в этой главе. Собственно, все игры с одновременным движением персонажей делаются с помощью таймера. Венец главы – создание вашего первого не учебного, а реального проекта «Будильник-секундомер». Вы ведь видели часы в правой части панели задач Windows? Если на них поставить мышь, они покажут дату. Давайте сделаем что-нибудь получше, а именно – большие, красивые часы-будильник (можно даже во весь экран), а заодно и с секундомером. Для этого нам придется сначала познакомиться с новым типом данных – типом даты и времени суток. Также в этой главе вы изучите перечисления и освоите новые элементы управления.
Работа с мышью и клавиатурой
До сих пор в режиме работы проекта мы пользовались мышкой только для того, чтобы примитивно нажимать на кнопки, а клавиатурой – только для ввода текста в текстовые поля. Однако, возможности мыши и клавиатуры в VB гораздо шире. VB позволяет делать мышью и клавиатурой все те вещи, которые мы привыкли делать ими в любых графических и текстовых редакторах, играх и других приложениях Windows. В частности, мы можем с их помощью управлять поведением и движением объектов на форме, рисовать мышью линии, рамочки, изменять традиционное действие клавиш и так далее.
Прежде чем говорить о мыши и клавиатуре, поговорим о фокусе.
Массивы, рекурсия, сортировка
Массивы – одно из главных средств хранения в памяти компьютера больших объемов информации.
Для того, чтобы понять массивы, нужно обладать некоторой культурой математического мышления. Если этот материал покажется вам трудным, не поддавайтесь искушению пропустить его. Настоящего программирования без массивов не бывает, да и большая часть дальнейшего материала без массивов не будет понятна.
Рекурсия – это вызов процедуры из тела самой процедуры. Она лежит в основе рекурсивного стиля программирования, очень интересного и плодотворного стиля для определенного круга задач.
Сортировка – это процесс упорядочивания элементов массива. Очень популярная и распространенная задача программирования.
Начнем с массивов.
Разные звери в одном ковчеге
В предыдущей главе я рассматривал массивы – данные, состоящие из многих элементов. В этой главе я продолжу рассматривать данные, состоящие из нескольких элементов. Но если массивы – это одинаковые солдатики, стоящие стройными рядами в своих строгих батальонах, то те данные, что мы рассмотрим в этой главе, больше напоминают цыганский табор.