Читать книгу «Справочник Жаркова по проектированию и программированию искусственного интеллекта. Том 11: Программирование на Visual Basic искусственного интеллекта. Издание 2» онлайн полностью📖 — Валерия Алексеевича Жаркова — MyBook.
image

4.5. Методика добавления нового объекта в игру

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

Добавляем в проект (из отмеченной выше статьи или из Интернета) файл изображения батона хлеба bread.jpg по стандартной схеме, а именно: в меню Project выбираем Add Existing Item, в этой панели в окне “Files of type” выбираем “All Files”, в центральном окне находим и выделяем имя файла и щёлкаем кнопку Add (или дважды щёлкаем по имени файла). В панели Solution Explorer мы увидим этот файл.

Теперь этот же файл bread.jpg встраиваем в проект в виде ресурса по разработанной выше схеме, а именно: в панели Solution Explorer выделяем появившееся там имя файла, а в панели Properties (для данного файла) в свойстве Build Action (Действие при построении) вместо заданного по умолчанию значения Content (Содержание) или None выбираем значение Embedded Resource (Встроенный ресурс).

Объявляем и инициализируем объект breadImage (класса Image) для загрузки в него изображения хлеба и две текущие координаты bx и by верхнего левого угла прямоугольника, описанного вокруг хлеба, в системе координат с началом в верхнем левом углу экрана. А приведённый выше код в теле метода Form1_Paint заменяем на тот, который дан на следующем листинге.

Листинг 4.9. Метод для рисования изображения.

Текст программы опубликован в предыдущем Издании данной книги.

В режиме выполнения (Build, Build Selection; Debug, Start Without Debugging) мы видим, что на форме Form1 к перемещающемуся изображению сыра cheese.jpg добавилось изображение хлеба bread.jpg (в верхнем левом углу экрана), рис. 4.7. Однако изображения и сыра, и хлеба мерцают, что необходимо исправить методом двойной буферизации (в следующем параграфе).

4.6. Методика устранения мерцания изображения при помощи двойной буферизации

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

Чтобы устранить мерцание изображения при помощи двойной буферизации, приведённый выше код в теле метода Form1_Paint заменяем на тот, который дан на следующем листинге (с подробными комментариями).


Рис. 4.7. Подвижный сыр и неподвижный хлеб.



Рис. 4.8. Сыр закрывает хлеб.

Листинг 4.10. Метод для рисования изображения.

Текст программы опубликован в предыдущем Издании данной книги.

Если мы сейчас запустим программу на выполнение, то увидим, что мерцание уменьшилось, но не исчезло совсем. Это объясняется тем, что при выполнении метода Form1_Paint операционная система Windows сначала заполняет экран цветом фона (background color), в нашем примере белым фоном (white), и только после этого поверх фона прорисовывает встроенные в программу изображения. Поэтому необходимо сделать так, чтобы операционная система Windows не изменяла фон. Для этого воспользуемся неоднократно применяемым и в наших предыдущих книгах, и в данной книге шаблоном метода OnPaintBackground, в тело которого мы ничего не будем записывать, как показано на следующем листинге.

Листинг 4.11. Метод OnPaintBackground.

Текст программы опубликован в предыдущем Издании данной книги.

Этот метод OnPaintBackground следует записать непосредственно за методом Form1_Paint, естественно, в теле класса Form1.

Теперь в режиме выполнения (Build, Build Selection; Debug, Start Without Debugging) подвижный сыр и неподвижный хлеб уже не мерцают, и мы решили данную задачу.

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

4.7. Методика управления направлением перемещения объекта при помощи элементов управления и мыши

Теперь программа должна перемещать батон хлеба таким образом, чтобы игрок мог отбивать хлебом сыр, как ракетка отбивает мяч в теннисе. Для перемещения объекта вверх (Up), вниз (Down), влево (Left) и вправо (Right) пользователь может использовать разнообразные элементы управления и компоненты с панели инструментов Toolbox, мышь, клавиатуру, джойстик и другие устройства. Для примера, размещаем на форме четыре кнопки Button с соответствующими заголовками в свойстве Text для перемещения хлеба Вверх, Вниз, Влево и Вправо (рис. 4.9). Перед размещением кнопок, для формы Form1 в панели Properties увеличиваем её размеры Size, например, до 384; 473.

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



Рис. 4.9. Подвижный сыр и управляемый нами хлеб.



Рис. 4.10. Сыр закрывает хлеб.

Листинг 4.12. Методы для обработки событий.

Текст программы опубликован в предыдущем Издании данной книги.

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

Dim bx As Integer = 150 : Dim by As Integer = 200)

в нижней части формы.

После запуска программы (Build, Build Selection; Debug, Start Without Debugging) сыр самостоятельно (без нашего участия) перемещается по экрану, отталкиваясь от границы со звуковым сигналом.

А после нажатий мышью четырёх кнопок Вверх, Вниз, Влево и Вправо мы можем перемещать батон хлеба в соответствующих четырёх направлениях по всему экрану (рис. 4.9).

На рис. 4.9 видно, что по умолчанию на форме выделена первая кнопка Button (на этой кнопке размещён фокус (Focus) программы), и поэтому данную кнопку мы можем нажать не только мышью, но и клавишей Enter, после чего скорость перемещения сыра возрастёт пропорционально количеству нажатий. Если мы желаем, чтобы по умолчанию была выделена другая, например, вторая кнопка Button, то в каком-либо методе, например, в методе Form1_Paint следует записать известный код (button2.Focus();). В режиме выполнения мы можем перемещать фокус, например, клавишами со стрелками или Tab на любую кнопку на форме, после чего воздействовать на эту кнопку клавишей Enter.

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

Следовательно, мы решили задачу по управлению объектом (в виде батона хлеба) при помощи элементов управления, например, кнопок Button и мыши.

Аналогично, как для события MouseMove, для управления игрой можно использовать методы-обработчики других событий мыши, которые имеются в панели Properties на вкладке Events для формы Form1 (рис. 4.11).



Рис. 4.11. События для управления игрой.

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

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

Таким образом, в этой главе мы разработали такие общие методики:

добавления объекта в проект;

анимации объекта;

проектирования отскока объекта от заданной нами границы, например, экрана;

управления скоростью перемещения объекта;

добавления звукового сигнала в ключевые для игры моменты, например, в момент столкновения объекта с границей;

добавления нового объекта в игру (с использованием общего для всех объектов кода);

устранения мерцания изображения при помощи двойной буферизации;

управления направлением перемещения объекта с помощью клавиш.

Эти методики можно использовать при разработке самых разнообразных игр.

Глава 5. Методика обнаружения столкновений, программирования уничтожений летающих объектов и подсчёта очков

5.1. Определение прямоугольников, описанных вокруг объектов

Продолжаем разработку методики создания типичной и широко распространённой игры, когда в качестве летающих игровых объектов используются продукты питания, следуя следующей статье с сайта microsoft.com: Rob Miles. Games Programming with Cheese: Part Two. Так как в данной статье программы написаны на языке Visual C# и, кроме того, для устаревшей версии Visual Studio, то автору данной книги пришлось переписать все программы на язык Visual Basic и, кроме того, для современной версии Visual Studio. Общие требования к программному обеспечению для разработки этой игры приведены выше.

Также продолжаем методично и последовательно решать типичные задачи по созданию данной и всех подобных игр.

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

Прямоугольник, описанный вокруг изображения батона хлеба bread.jpg, показан на рис. 5.1.


Рис. 5.1. Прямоугольник, описанный вокруг хлеба.

Ширина полей между объектом и описанным вокруг объекта прямоугольником должна быть сведена к минимуму, чтобы объект обязательно касался прямоугольника в как можно большем количестве точек и отрезков линий. Если начало прямоугольной системы координат “x, y” находится в верхнем левом углу экрана , то координаты верхней левой точки (bx, by) и нижней правой точки (bx + batWidth, by + batHeight) однозначно определяют данный прямоугольник на экране.

В среде выполнения .NET Framework (для настольных компьютеров) известна структура Rectangle (из пространства имён System.Drawing), у которой метод-конструктор Rectangle Constructor имеет несколько перегрузок. Наиболее применяемая перегрузка метода-конструктора Rectangle Constructor (которую далее и мы будем часто применять) с параметрами (Int32, Int32, Int32, Int32) структуры Rectangle на главных (в мире программирования) языках приведена в табл. 5.1.

Таблица 5.1.

Метод-конструктор Rectangle Constructor (Int32, Int32, Int32, Int32) структуры Rectangle.

Visual Basic (Declaration)

Public Sub New ( _

x As Integer, _

y As Integer, _

width As Integer, _

height As Integer _

)

Visual Basic (Usage)

Dim x As Integer

Dim y As Integer

Dim width As Integer

Dim height As Integer

Dim instance As New Rectangle(x, y, width, height)

C#

public Rectangle (

int x,

int y,

int width,

int height

)

C++

public:

Rectangle (

int x,

int y,

int width,

int height

)

J#

public Rectangle (

int x,

int y,

int width,

int height

)

JScript

public function Rectangle (

x : int,

y : int,

width : int,

height : int

)

В этом определении метода-конструктора Rectangle Constructor параметры переводятся так:

x – координата “x” верхнего левого угла прямоугольника;

y – координата “y” верхнего левого угла прямоугольника;

width – ширина (по оси “x”) прямоугольника;

height – высота (по оси “y”) прямоугольника.

Далее в нашей программе мы сначала объявим прямоугольники, описанные вокруг объектов, как новые переменные, например, так:

'Прямоугольник, описанный вокруг первого объекта:

Dim cheeseRectangle As Rectangle

'Прямоугольник, описанный вокруг второго объекта:

Dim breadRectangle As Rectangle

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

cheeseRectangle = New Rectangle(cx, cy, _

cheeseImage.Width, cheeseImage.Height)

breadRectangle = New Rectangle(bx, by, _

breadImage.Width, breadImage.Height)

5.2. Обнаружение столкновения прямоугольников, описанных вокруг подвижных объектов


В этой структуре Rectangle (из пространства имён System.Drawing) имеются методы, которые могут обнаруживать пересечения различных перемещающихся прямоугольников. Эти методы определяют, находится ли точка одного прямоугольника внутри другого прямоугольника, и если находится, то программа определяет эту ситуацию и как столкновение этих двух прямоугольников, и как столкновение двух объектов, расположенных внутри этих прямоугольников.

Когда далее при написании программы мы поставим оператор-точку “.” после какого-либо объекта структуры Rectangle, то увидим подсказку с двумя основными методами Intersect и IntersectsWith (рис. 5.2) для обнаружения пересечения двух прямоугольников.

Рис. 5.2. Подсказка с методами Intersect и IntersectsWith.

Определение для наиболее применяемого метода IntersectsWith (который далее и мы будем часто применять) с параметром (Rectangle rect) структуры Rectangle на главных (в мире программирования) языках приведено в табл. 5.2.

Таблица 5.2.

Определение метода Rectangle.IntersectsWith структуры Rectangle.

Visual Basic (Declaration)

Public Function IntersectsWith ( _

rect As Rectangle _

) As Boolean

Visual Basic (Usage)

Dim instance As Rectangle

Dim rect As Rectangle

Dim returnValue As Boolean

returnValue = instance.IntersectsWith(rect)

C#

public bool IntersectsWith (

Rectangle rect

)

C++

public:

bool IntersectsWith (

Rectangle rect

)

J#

public boolean IntersectsWith (

Rectangle rect

)

JScript

public function IntersectsWith (

rect : Rectangle

) : Boolean

Этот метод IntersectsWith обнаруживает пересечение заданного нами первого прямоугольника со вторым прямоугольником, объявленного здесь как параметр rect.

Если метод определит, что ни одна точка одного прямоугольника не находится внутри другого прямоугольника, то метод возвращает логическое значение False.

А если метод определит, что хотя бы одна точка одного прямоугольника находится внутри другого прямоугольника, то метод IntersectsWith возвращает логическое значение True, и это значение применяется для изменения направления движения какого-либо прямоугольника на противоположное (чтобы уйти от дальнейшего пересечения), например, в таком коде:

'Проверяем столкновение объектов:

If (cheeseRectangle.IntersectsWith(breadRectangle)) Then

'Изменяем направление движения на противоположное:

goingDown = Not goingDown

'В момент столкновения подаем звуковой сигнал Beep:

Beep()

End If

5.3. Код и выполнение программы


Теперь в проекте, который мы начали разрабатывать в предыдущей главе (и продолжаем в данной главе) объявляем два прямоугольника, а приведённый выше код в теле метода Form1_Paint заменяем на тот, который дан на следующем листинге (с подробными комментариями).

Листинг 5.1. Метод для рисования изображения.

Текст программы опубликован в предыдущем Издании данной книги.

А вместо приведённого выше метода updatePositions для изменения координат записываем следующий метод, дополненный кодом для обнаружения столкновения объектов.

Листинг 5.2. Метод для изменения координат и обнаружения столкновения объектов.

Текст программы опубликован в предыдущем Издании данной книги.

В режиме выполнения (Build, Build Selection; Debug, Start Without Debugging) при помощи кнопок и мыши мы можем перемещать хлеб и этим хлебом, как ракеткой, отбивать сыр или вверх, или вниз (рис. 5.3). Напомним, что, так как угол падения сыра на хлеб равен 45 градусам, то и угол отражения сыра от хлеба (и от границ экрана) также равен 45 градусам.

5.4. Основные схемы столкновений и их реализация


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

Если мы оперируем с окружностями, описанными вокруг объектов, то возможны три основные схемы столкновений, показанные на рис. 5.4. В схемах 1 и 3 маленький круг ударяется о большой круг под углом 45 градусов и отражается под этим же углом и по этой же линии. В схеме 2 маленький круг ударяется о большой круг под углом 90 градусов и также вертикально отражается вверх.

Если же мы оперируем с прямоугольниками, описанными вокруг объектов, то возможны четыре основные схемы столкновений, показанные на рис. 5.5.



Рис. 5.3. Сыр отскочил от хлеба.



Рис. 5.4. Три схемы столкновений.Рис. 5.5. Четыре схемы столкновений.

В схемах 1 и 4 маленький прямоугольник ударяется о большой прямоугольник сбоку под углом 45 градусов и отражается под этим же углом и по этой же линии. В схемах 2 и 3 маленький прямоугольник падает на большой прямоугольник под углом 45 градусов, но отражается не по линии падения, а по линии отражения, перпендикулярной линии падения.

Для реализации более правильных схем столкновений, показанных на рис. 5.5, в нашем проекте вместо приведённого выше метода updatePositions для изменения координат записываем следующий метод, дополненный новым кодом для обнаружения столкновения объектов.

Листинг 5.3. Метод для изменения координат и обнаружения столкновения объектов.

Текст программы опубликован в предыдущем Издании данной книги.










1
...