
Полная версия
Справочник Жаркова по проектированию и программированию искусственного интеллекта. Том 4: Программирование на Visual Basic искусственного интеллекта
Dim i As Integer
For i = 0 To tomatoes.Length – 1
If (Not tomatoes(i).visible) Then
Continue For
End If
If (cheeseRectangle.IntersectsWith( _
tomatoes(i).rectangle)) Then
'В момент столкновения подаем звуковой сигнал Beep:
Beep()
' hide the tomato
tomatoes(i).visible = False
' bounce down
goingDown = True
' only destroy one at a time
'Чтобы помидоры уничтожались не по два,
'а по одному помидору: Exit For:
Exit For
End If
Next
End If
End Sub 'Конец метода updatePositions.
В режиме выполнения (Build, Build Selection; Debug, Start Without Debugging) несколько i-х помидоров появляются в верхней части экрана в качестве мишеней (рис. 5.7), которые исчезают после попадания в них летающего сыра (рис. 5.8).
Управляя при помощи кнопок Button и мыши перемещением батона хлеба, мы можем отражать сыр вверх таким образом, чтобы уничтожить как можно больше помидоров за меньшее время, набирая при этом очки.
К разработке методики подсчёта очков в игре мы и приступаем.
5.6. Методика подсчёта очков в игре
Игра отличается от любого другого приложения тем, что один или несколько игроков набирают в игре очки, и победителем считается игрок, набравший наибольшее количество очков. А после набора определённого количества очков игра может переходить на более высокие (более сложные) и интересные уровни, после прохождения которых игрок может получить приз, например, в виде изображения какого-нибудь смешного персонажа.
Методика подсчёта очков (score) в игре подразумевает наличие в программе счётчика (scorer) очков и вывода очков на экран (например, методом DrawString) в строке:
g.DrawString(messageString, messageFont, messageBrush,
messageRectangle)
Видно, что в этом методе DrawString мы дожны определить параметры в виде шрифта messageFont, кисти messageBrush и зарезервированного прятоугольника для записи очков messageRectangle, причём в этот прямоугольник летающие объекты не должны залетать. На рис. 5.9 мы получили 20 очков за 2 сбитых помидора, а на 5.10 – 50 очков за 5 сбитых помидоров.
За каждый сбитый помидор мы можем начислить игроку любое количество очков, например, 10 очков в строке:
scoreValue = scoreValue + 10
Новые очки сразу же выводятся на экран, информируя игрока.
Приступим к программной реализации методики подсчёта очков в игре в нашем базовом учебном проекте.
Сначала мы должны опустить ряд помидоров пониже, чтобы освободить место вверху для записи очков, поэтому вместо 4 записываем ординату, равную, например, 20:
Dim tomatoDrawHeight As Integer = 20
В любом месте класса Form1 добавляем новые переменные для счётчика очков.

Рис. 5.9. Получили 20 очков за 2 сбитых помидора.

Рис. 5.10. Получили 50 очков.
Листинг 5.8. Новые переменные.
' Font for score messages.
Dim messageFont As Font = Nothing
' Rectangle for score display.
Dim messageRectangle As Rectangle
' Height of the score panel.
Dim scoreHeight As Integer = 20 '= заданной выше tomatoDrawHeight.
' Brush used to draw the messages.
Dim messageBrush As SolidBrush
' The string which is drawn as the user message.
Dim messageString As String = "Очки: 0"
' Score in a game.
Dim scoreValue As Integer = 0
Приведённый выше код в теле метода Form1_Paint заменяем на тот, который дан на следующем листинге.
Листинг 5.9. Метод для рисования изображения.
Private Sub Form1_Paint(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles MyBase.Paint
'Если буфер пустой, создаём новый буфер:
If (backBuffer Is Nothing) Then
backBuffer = New Bitmap(Me.ClientSize.Width, _
Me.ClientSize.Height)
End If
'Создаём объект класса Graphics из буфера:
Using g As Graphics = Graphics.FromImage(backBuffer)
'Очищаем форму:
g.Clear(Color.White)
'Рисуем изображения объектов в буфере backBuffer:
g.DrawImage(cheeseImage, cx, cy)
g.DrawImage(breadImage, bx, by)
Dim i As Integer
For i = 0 To tomatoes.Length – 1
If (tomatoes(i).visible) Then
g.DrawImage(tomatoImage, _
tomatoes(i).rectangle.X, _
tomatoes(i).rectangle.Y)
End If
Next
'Записываем очки игрока:
g.DrawString(messageString, messageFont, _
messageBrush, messageRectangle)
End Using
'Рисуем изображение на форме Form1:
e.Graphics.DrawImage(backBuffer, 0, 0)
End Sub
Приведённый выше код в теле метода Form1_Load (для загрузки файлов изображений игровых объектов) заменяем на тот, который дан на следующем листинге.
Листинг 5.10. Метод для загрузки файлов изображений.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
'Загружаем в объекты класса System.Drawing.Image
'добавленные в проект файлы изображений заданного формата
'при помощи потока встроенного ресурса (ResourceStream):
cheeseImage = _
New Bitmap(myAssembly.GetManifestResourceStream( _
myName_of_project + "." + "cheese.JPG"))
breadImage = _
New Bitmap(myAssembly.GetManifestResourceStream( _
myName_of_project + "." + "bread.JPG"))
'Инициализируем прямоугольники, описанные вокруг объектов:
cheeseRectangle = New Rectangle(cx, cy, _
cheeseImage.Width, cheeseImage.Height)
breadRectangle = New Rectangle(bx, by, _
breadImage.Width, breadImage.Height)
'Загружаем файл изображения нового объекта:
tomatoImage = _
New Bitmap(myAssembly.GetManifestResourceStream( _
myName_of_project + "." + "tomato.gif"))
'Инициализируем массив новых объектов и прямоугольников,
'описанные вокруг этих объектов:
initialiseTomatoes()
'Размещаем новые объекты в верхней части экрана:
placeTomatoes()
'Создаём и инициализируем шрифт для записи очков:
messageFont = New Font(FontFamily.GenericSansSerif, 10, _
FontStyle.Regular)
'Резервируем прямоугольник на экране для записи очков:
messageRectangle = New Rectangle(0, 0, _
Me.ClientSize.Width, scoreHeight)
'Задаём цвет кисти для записи очков:
messageBrush = New SolidBrush(Color.Black)
'Включаем таймер:
Timer1.Enabled = True
End Sub
И наконец, вместо приведённого выше метода updatePositions записываем следующий метод, дополненный новым кодом для изменения координат, обнаружения столкновений объектов, уничтожения помидоров и подсчёта очков.
Листинг 5.11. Метод для изменения координат и обнаружения столкновения объектов.
Sub updatePositions()
If (goingRight) Then
cx += xSpeed
Else
cx -= xSpeed
End If
If ((cx + cheeseImage.Width) >= Me.ClientSize.Width) Then
goingRight = False
'В момент столкновения подаем звуковой сигнал Beep:
Beep()
End If
If (cx <= 0) Then
goingRight = True
'В момент столкновения подаем звуковой сигнал Beep:
Beep()
End If
If (goingDown) Then
cy += ySpeed
Else
cy -= ySpeed
End If
If ((cy + cheeseImage.Height) >= Me.ClientSize.Height) Then
goingDown = False
'В момент столкновения подаем звуковой сигнал Beep:
Beep()
End If
If (cy <= 0) Then
goingDown = True
'В момент столкновения подаем звуковой сигнал Beep:
Beep()
End If
'Задаём прямоугольникам координаты объектов:
cheeseRectangle.X = cx
cheeseRectangle.Y = cy
breadRectangle.X = bx
breadRectangle.Y = by
' check for collisions.
If (goingDown) Then
' only bounce if the cheese is going down
If (cheeseRectangle.IntersectsWith(breadRectangle)) Then
'В момент столкновения подаем звуковой сигнал Beep:
Beep()
' we have a collision
Dim rightIn As Boolean = breadRectangle.Contains( _
cheeseRectangle.Right, _
cheeseRectangle.Bottom)
Dim leftIn As Boolean = breadRectangle.Contains( _
cheeseRectangle.Left, _
cheeseRectangle.Bottom)
' now deal with the bounce
If (rightIn And leftIn) Then
' bounce up
goingDown = False
Else
' bounce up
goingDown = False
' now sort out horizontal bounce
If (rightIn) Then
goingRight = False
End If
If (leftIn) Then
goingRight = True
End If
End If
End If
Else
' only destroy tomatoes of the cheese is going up
Dim i As Integer
For i = 0 To tomatoes.Length – 1
If (Not tomatoes(i).visible) Then
Continue For
End If
If (cheeseRectangle.IntersectsWith( _
tomatoes(i).rectangle)) Then
'В момент столкновения подаем сигнал Beep:
Beep()
' hide the tomato
tomatoes(i).visible = False
' bounce down
goingDown = True
' update the score
scoreValue = scoreValue + 10
'Конвертируем очки scoreValue в тип String
'и выводим на экран:
messageString = "Очки: " & _
Convert.ToString(scoreValue)
' only destroy one at a time
'Чтобы помидоры уничтожались не по два,
'а по одному помидору: Exit For:
Exit For
End If
Next
End If
End Sub 'Конец метода updatePositions.
В режиме выполнения (Build, Build Selection; Debug, Start Without Debugging), управляя летающим сыром при помощи батона хлеба, кнопок Button и мыши, мы получили 20 очков за 2 сбитых помидора (рис. 5.9) и 50 очков за 5 сбитых помидоров (рис. 5.10).
Отметим, что для управления игрой в дополнение или вместо кнопок Button (чтобы не загромождать форму Form1) можно использовать также и клавиши клавиатуры по описанной далее методике.
Итак, в этой главе мы разработали методику обнаружения столкновений, программирования уничтожений летающих объектов и подсчёта очков.
Глава 6. Методология воспроизведения звуковых файлов
6.1. Основные методики звукового сопровождения приложений
Выше мы уже применяли воспроизведение звуковых файлов по упрощённому варианту и звуковой эффект в виде звукового сигнала Beep (по-русски: Бип). Напомним, что этот сигнал появляется, когда выполнение программы дойдёт до строки: Beep(). Записывая эту строку в соответствующих местах программы, мы подаем звуковой сигнал Beep в различные моменты анимации, например, в момент каждого удара объектов о границу (внутри которой перемещаются объекты) или друг о друга.
В данной главе мы дополним воспроизведение звуковых файлов по упрощённому варианту и подачу звукового сигнала Beep воспроизведением звуковых файлов по более универсальному и сложному варианту в различные моменты выполнения нашего приложения. Естественно, кое-где можно закомментировать приведённую выше строку с методом Beep и оставить проигрывание только звуковых файлов (без наложения на них сигнала Beep).
Для управления проигрыванием звуковых файлов в приложениях и играх на настольных компьютерах, ноутбуках и планшетах разработаны три основные методики:
Методика воспроизведения звуковых файлов на основе пространства имён My.
Методика воспроизведения звуковых файлов на основе встроенного ресурса.
Методика воспроизведения звуковых файлов на основе DirectX.
У каждой из этих методик есть свои области рационального применения, есть свои преимущества и недостатки, которые мы и рассмотрим сейчас.
6.2. Методика воспроизведения звуковых файлов на основе пространства имён My
Сначала для воспроизведения звуков в приложениях и играх для настольных компьютеров, ноутбуков и планшетов мы опишем наиболее популярную методику воспроизведения звуковых файлов на основе пространства имён My (без использования и технологии DirectX, и схемы встроенного (Embedded) ресурса). Приступим к программной реализации этой методики, для общности, в новом проекте.
Для создания проекта в VS щёлкаем кнопку New Project (или File, New, Project). В панели New Project в окне Project Types выбираем тип проекта Visual Basic, Windows, в окне Templates выделяем шаблон Windows Forms Application, в окне Name записываем любое имя проекта, например, Sounds2 и щёлкаем OK. Создаётся проект, появляется форма Form1 (рис. 6.1) в режиме проектирования.

Рис. 6.1.
Форма Form1 в режиме выполнения.
Проектируем (или оставляем по умолчанию) эту форму, как описано в параграфе “Методика проектирования формы”. Например, в панели Properties в свойстве Font можно оставить по умолчанию или установить новый шрифт и его размер (Size). Чтобы изменить заголовок формы, в панели Properties в свойстве Text записываем (или вставляем из буфера обмена: правый щелчок, Paste) текст.
С панели инструментов Toolbox переносим на форму, для примера, две кнопки Button (Sound 1 и Sound 2). В панели Properties в свойстве Text записываем название каждой кнопки Sound&1 и Sound&2 с оператором &, который подчёркивает следующий за ним символ текста, что позволяет нам задействовать кнопку не только мышью, но и, удерживая нажатой клавишу Alt, нажатием клавиши с подчёркнутой буквой английского алфавита или цифрой (например, Alt+1).
На форме в режиме выполнения по умолчанию выделена первая кнопка. Но если мы желаем, чтобы в режиме выполнения на форме была выделена другая кнопка (чтобы нажимать её не только мышью, но и клавишей Enter), то в панели Properties (для Form1) в свойстве AcceptButton выбираем имя этой кнопки.
По варианту 1, для добавления звукового файла drumpad-crash.wav в проект, в меню Project выбираем Add Existing Item, в панели Add Existing Item в окне Files of type устанавливаем All Files, в окне "Look in" находим (в системной папке компьютера C, WINDOWS, Media или в папке с загруженным из Интернета файлом) файл и щёлкаем кнопку Add (или дважды щёлкаем по имени файла). Этот файл мы увидим в панели Solution Explorer. Если мы дважды щёлкнем по этому файлу drumpad-crash.wav, то откроется проигрыватель Windows Media Player, и мы услышим в течение нескольких секунд звучание типа шуршания оркестровых металлических тарелок при воздействии на них металлической метелки.
Аналогично добавляем в проект второй файл drumpad-bass_drum.wav типа удара барабана.
Аналогично можно добавить в проект ещё много звуковых файлов, которые мы желаем послушать во время выполнения приложения или игры.
По второму, закомментированному далее в программе, варианту эти файлы мы не добавляем в проект, а копируем во внешнюю папку с именем, например, Sounds, запоминая путь к этой папке, начиная от локального диска, например, D.
Теперь в панели Properties (для формы Form1) на вкладке Events дважды щёлкаем по имени события Load (Загрузка).
Появляется файл Form1.vb с шаблоном метода Form1_Load, который после записи нашего кода принимает следующий вид.
Листинг 6.1. Метод для загрузки и воспроизведения звуковых файлов.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
'Загружаем звуковые файлы формата (.wav)
'по 1-му варианту непосредственно из проекта:
My.Computer.Audio.Play("..\..\drumpad-crash.wav")
'Загружаем звуковые файлы формата (.wav)
'по 2-му варианту из внешней папки:
'My.Computer.Audio.Play( _
'"D:\MyDocs\Sounds\drumpad-bass_drum.wav")
End Sub
Видно, что в строке:
My.Computer.Audio.Play("..\..\drumpad-crash.wav")
мы используем пространство имён My, свойство Computer, свойство Audio и метод Play (рис. 6.2).
Оператор “..\..\” называется relative path – путь относительно (внутри) проекта.

Рис. 6.2. Подсказка с методом Play.
В режиме выполнения (Build, Build Selection; Debug, Start Without Debugging) мы услышим соответствующее (одноразовое) воспроизведение звукового файла, который мы добавили непосредственно в проект (а не в дополнительную папку Sounds проекта).
Если в приведённый выше шаблон метода Form1_Load мы загрузим подряд два файла, как показано ниже:
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
My.Computer.Audio.Play("..\..\drumpad-crash.wav")
My.Computer.Audio.Play("..\..\drumpad-bass_drum.wav")
End Sub
то в режиме выполнения мы услышим только второй звуковой файл.
Чтобы мы услышали сначала полностью первый звуковой файл, а затем второй звуковой файл, мы должны в методе Play в качестве второго параметра использовать константу WaitToComplete из перечисления режимов AudioPlayMode, как показано в следующем коде.
Листинг 6.2. Метод для загрузки и воспроизведения звуковых файлов.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
My.Computer.Audio.Play("..\..\drumpad-crash.wav", _
AudioPlayMode.WaitToComplete)
My.Computer.Audio.Play("..\..\drumpad-bass_drum.wav")
End Sub
Подчеркнём следующее. Чтобы все игровые действия прекратились на время исполнения мелодии, мы должны в методе Play в качестве второго параметра использовать уже применённую выше константу WaitToComplete из перечисления режимов AudioPlayMode, как показано в следующем коде.
Листинг 6.3. Метод для загрузки и воспроизведения звуковых файлов.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
My.Computer.Audio.Play("..\..\drumpad-crash.wav", _
AudioPlayMode.WaitToComplete)
End Sub
Чтобы мы услышали непрерывное циклическое (Loop) воспроизведение звукового файла, мы должны в методе Play в качестве второго параметра использовать константу BackgroundLoop из перечисления режимов AudioPlayMode, как показано в следующем коде.
Листинг 6.4. Метод для загрузки и воспроизведения звуковых файлов.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
My.Computer.Audio.Play("..\..\drumpad-crash.wav", _
AudioPlayMode.BackgroundLoop)
End Sub
Чтобы остановить (Stop) воспроизведение звука, необходимо в каком либо методе, например, в обработчике щелчка кнопки записать строку:
My.Computer.Audio.Stop()
Теперь после щелчка кнопки звучание прекратится.
Если в игре применяются несколько звуковых файлов, то их целесообразно разместить в одной папке с именем, например, Sounds. Для добавления в проект этой папки, в панели Solution Explorer (рис. 6.3) выполняем правый щелчок по имени проекта, в контекстном меню выбираем Add, New Folder, в поле появившегося значка папки записываем имя папки и нажимаем клавишу Enter.

Рис. 6.3. Папка Sounds в панели Solution Explorer.
Добавляем в эту папку (например, из Интернета) первый звуковой файл по стандартной схеме, а именно: выполняем правый щелчок по имени этой папки, в контекстном меню выбираем Add, Existing Item, в панели Add Existing Item в окне “Files of type” выбираем “All Files”, в центральном окне находим и выделяем имя файла и щёлкаем кнопку Add (или дважды щёлкаем по имени файла). В панели Solution Explorer мы увидим этот файл.
Аналогично добавляем в папку Sounds нашего проекта остальные звуковые файлы.
Напомним, что добавлять в проект все файлы можно как по одному, так и все сразу (после их выделения или только одной мышью, или мышью с нажатой клавишей Shift – для выделения всех соседних файлов, или мышью с нажатой клавишей Ctrl – для выделения всех файлов в различных местах).
Теперь, чтобы мы услышали воспроизведение звукового файла из папки Sound нашего проекта, необходимо применить следующий код.
Листинг 6.5. Метод для загрузки и воспроизведения звуковых файлов.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
My.Computer.Audio.Play("..\..\Sounds\drumpad-crash.wav", _
AudioPlayMode.WaitToComplete)
End Sub
Однако чаще требуется записывать строку типа:
My.Computer.Audio.Play("..\..\drumpad-crash.wav")
не в методе Form1_Load, а в других методах, которые управляют игровыми объектами.
Для примера, покажем, как управлять звуками при помощи двух кнопок Button, которые мы разместили на форме. В режиме проектирования дважды щёлкаем первую кнопку (или в панели Properties для этой выделенной щелчком кнопки на вкладке Events дважды щёлкаем по имени события Click). Появляется файл Form1.vb с шаблоном (обработчика щелчка по этой кнопке), который после записи нашего кода принимает вид следующего метода.
Листинг 6.6. Метод для кнопки.
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Загружаем звуковые файлы формата (.wav)
'по 1-му варианту непосредственно из проекта:
My.Computer.Audio.Play("..\..\drumpad-crash.wav", _
AudioPlayMode.BackgroundLoop)
'Загружаем звуковые файлы формата (.wav)
'по 2-му варианту из внешней папки:
'My.Computer.Audio.Play( _
'"D:\MyDocs\Sounds\drumpad-bass_drum.wav")
End Sub
В режиме проектирования дважды щёлкаем вторую кнопку (или в панели Properties для этой выделенной щелчком кнопки на вкладке Events дважды щёлкаем по имени события Click). Появляется файл Form1.vb с шаблоном (обработчика щелчка по этой кнопке), который после записи нашего кода принимает вид следующего метода.
Листинг 6.7. Метод для кнопки.
Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
'Загружаем звуковые файлы формата (.wav)
'по 1-му варианту непосредственно из проекта:
My.Computer.Audio.Play("..\..\drumpad-bass_drum.wav")
'Загружаем звуковые файлы формата (.wav)
'по 2-му варианту из внешней папки:
'My.Computer.Audio.Play( _
'"D:\MyDocs\Sounds\drumpad-crash.wav")
End Sub
В режиме выполнения (Build, Build Selection; Debug, Start Without Debugging), нажимая кнопки, мы услышим соответствующее (одноразовое или циклическое) воспроизведение звуковых файлов, которые мы добавили в проект. А именно, после щелчка первой кнопки и выполнения кода:
My.Computer.Audio.Play("..\..\drumpad-crash.wav", _
AudioPlayMode.BackgroundLoop)
мы услышим непрерывное циклическое (Loop) воспроизведение звукового файла drumpad-crash.wav типа шуршания оркестровых металлических тарелок после воздействия на них металлической метелкой. А после каждого нашего щелчка второй кнопки и выполнения кода:
My.Computer.Audio.Play("..\..\drumpad-bass_drum.wav")
мы будем слышать одиночное воспроизведение звукового файла drumpad-bass_drum.wav типа удара по барабану. Нажимая вторую кнопку с различной частотой, мы будем импровизировать и создавать различную мелодию из добавленных в проект двух звуковых файлов.
Аналогично по этой методике, добавляя в проект большое количество звуковых файлов, добавляя на форму большое количество кнопок Button (или других элементов управления) и записывая в методы-обработчики щелчков по этим кнопкам различные варианты кода для воспроизведения этих звуковых файлов в различной последовательности и с различным наложением звуков, в режиме выполнения приложения, щёлкая по кнопкам, мы будем получать множество самых разнообразных мелодий.
Отметим, что для управления воспроизведением звуковых файлов в дополнение или вместо кнопок Button (чтобы не загромождать форму Form1) можно использовать и клавиши клавиатуры, и компоненты с панели инструментов Toolbox по описанной далее методике.
6.3. Методика приостановки и возобновления звуков на основе пространства имён My
Для приостановки и возобновления воспроизведения звуковых файлов во время выполнения приложения можно разработать много вариантов кода, например, воспользоваться каким-либо элементом управления или компонентом. Так как далее для задания режимов почти всех игр мы будем применять выпадающее меню типа MenuStrip, то для решения поставленной здесь задачи применим это меню. С панели инструментов Toolbox переносим на форму элемент управления MenuStrip и щёлкаем по нему (ниже формы в режиме проектирования). На форме Form1 появляются окна с надписью Type Here (Печатайте здесь), в которые записываем команды на русском языке (по второму варианту, можно записывать в панели Properties в свойстве Text), для примера, для управления двумя звуковыми файлами: Звуки, Звук 1, Звук 2, рис. 6.4. Теперь в панели Properties в свойстве Name изменяем эти русские команды на соответствующие английские: Sounds, Sound1, Sound2. Рассмотрим первую команду Звук 1.