Создаем шар. Завершаем проект 


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



ЗНАЕТЕ ЛИ ВЫ?

Создаем шар. Завершаем проект



Модуль класса clsЛовец остается прежним. Приведу в окончательном виде все остальное, а именно: модуль класса clsШар, модуль кода и модуль (окно кода) формы.

Модуль кода

Public Const Число_шаров = 10

Public Const Размер_шара = 200

Public Const Размер_ловца = 500

Public Const Дальность = 200 'Это расстояние, на котором ловец достает шар

Public Ловец As clsЛовец 'Объявляем объект Ловец класса clsЛовец

Public Шар(1 To Число_шаров) As clsШар 'Объявляем массив объектов Шар класса clsШар

Модуль формы

Public intЧисло_пойманных_шаров As Integer

 

Private Sub Form_Load() 'Эта процедура выполняется один раз при запуске проекта

Dim i As Integer

Randomize 'Шары должны разлетаться со случайной скоростью и в случайном направлении

'Настраиваем размеры изображений шара и ловца:

imgШар(1).Height = Размер_шара

imgШар(1).Width = Размер_шара

imgЛовец.Height = Размер_ловца

imgЛовец.Width = Размер_ловца

'Порождаем массив изображений шара:

For i = 2 To Число_шаров

Load imgШар(i)

imgШар(i).Visible = True

Next i

'Порождаем объект Ловец и массив объектов-шаров:

Set Ловец = New clsЛовец

For i = 1 To Число_шаров

Set Шар(i) = New clsШар

Next i

 

Начальная_установка

KeyPreview = True 'Чтобы форма реагировала на клавиатуру

txtСчетчик_времени.Locked = True 'Чтобы в процессе игры нельзя было вручную менять показания счетчика

End Sub

 

Private Sub cmd_Начинай_сначала_Click() 'Что происходит при нажатии кнопки НАЧИНАЙ СНАЧАЛА

Начальная_установка

txtСчетчик_времени.SetFocus 'Чтобы фокус ушел с кнопки, иначе первое нажатие на стрелки клавиатуры не вызывает движения ловца

End Sub

 

Private Sub Начальная_установка() 'Все объекты встают в исходную позицию и настраиваются на новую игру

Dim i As Integer

txtСчетчик_времени.Text = 0 'Обнуляем счетчик времени

intЧисло_пойманных_шаров = 0 'Обнуляем число пойманных шаров

 

Ловец.Начальная_установка 'Ловец встает в исходную позицию и настраивается на новую игру

For i = 1 To Число_шаров 'Все шары встают в исходную позицию и настраиваются на новую игру

Шар(i).Начальная_установка

Next i

End Sub

 

Private Sub Timer1_Timer() 'Главная_процедура игры, выполняется один раз на каждом импульсе таймера

Dim i As Integer

'Действуют все шары:

For i = 1 To Число_шаров

Шар(i).Действие 'Сначала объект Шар вычисляет свои координаты,

imgШар(i).Left = Шар(i).x 'Затем изображение шара сдвигается на вычисленные координаты

imgШар(i).Top = Шар(i).y

Next i

'Действует ловец:

Ловец.Действие 'Сначала объект Ловец вычисляет свои координаты,

imgЛовец.Left = Ловец.x 'Затем изображение Ловца сдвигается на вычисленные координаты

imgЛовец.Top = Ловец.y

'Действует счетчик времени на форме, увеличивая свои показания на 1:

If intЧисло_пойманных_шаров <> Число_шаров Then txtСчетчик_времени.Text = txtСчетчик_времени.Text + 1

End Sub

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

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

Select Case KeyCode

Case vbKeyUp Ловец.Руль = вверх

Case vbKeyLeft Ловец.Руль = влево

Case vbKeyDown Ловец.Руль = вниз

Case vbKeyRight: Ловец.Руль = вправо

Case vbKeyControl: Ловец.Руль = стоп

End Select

End Sub

 

Модуль clsШар

Public x As Long 'Координаты шара

Public y As Long

Private dx As Long 'Шаг шара по горизонтали и вертикали между двумя импульсами таймера

Private dy As Long

Private Макс_шаг As Long 'Максимально возможное значение шага шара

 

Private Sub Class_Initialize() 'Процедура, выполняющаяся при рождении шара

Макс_шаг = 100

End Sub

 

Public Sub Начальная_установка() 'Шар встает в исходную позицию и настраивается на новую игру

'Ставим шар на исходную позицию:

x = f.shpБортик.Left + f.shpБортик.Width * 3 / 4 'Она отстоит по горизонтали на четверть ширины поля от правого его края

y = f.shpБортик.Top + f.shpБортик.Height / 2 'Она по вертикали расположена посредине поля

'Вычисление шага:

dx = Макс_шаг * (1 - 2 * Rnd) 'Шаг по горизонтали случаен и не превосходит Макс_шаг

dy = Макс_шаг * (1 - 2 * Rnd) 'Шаг по вертикали случаен и не превосходит Макс_шаг

End Sub

 

Public Sub Действие() 'Главная_процедура шара, выполняется один раз на каждом импульсе таймера

If Поймали Then Выход_шара_из_игры 'Сначала шар определяет, не поймали ли его,

Отскакивать_или_нет 'затем, если бортик рядом, реагирует на него,

Шаг 'и наконец делает шаг

End Sub

 

Private Sub Отскакивать_или_нет() 'Реакция на бортик

If Шар_у_пола_или_потолка Then

dy = -dy 'Отскок от пола или потолка

ElseIf Шар_у_стен Then

dx = -dx 'Отскок от стен

End If

End Sub

 

Private Sub Шаг()

x = x + dx

y = y + dy

End Sub

 

Private Function Шар_у_пола_или_потолка() As Boolean

If y < f.shpБортик.Top Or y + Размер_шара > f.shpБортик.Top + f.shpБортик.Height Then

Шар_у_пола_или_потолка = True

Else

Шар_у_пола_или_потолка = False

End If

End Function

 

Private Function Шар_у_стен() As Boolean

If x < f.shpБортик.Left Or x + Размер_шара > f.shpБортик.Left + f.shpБортик.Width Then

Шар_у_стен = True

Else

Шар_у_стен = False

End If

End Function

 

Private Function Поймали() As Boolean

'ЕСЛИ расстояние по горизонтали между центрами шара и ловца меньше Дальности

'И если расстояние по вертикали между центрами шара и ловца меньше Дальности, ТО

If Abs(x - Ловец.x - ((Размер_ловца - Размер_шара) / 2)) < Дальность _

And Abs(y - Ловец.y - ((Размер_ловца - Размер_шара) / 2)) < Дальность _

Then

Поймали = True

Else

Поймали = False

End If

End Function

 

Private Sub Выход_шара_из_игры()

x = -10000: y = -10000: dx = 0: dy = 0 'Убрать шар подальше с глаз долой и чтоб не двигался

f.intЧисло_пойманных_шаров = f.intЧисло_пойманных_шаров + 1

End Sub

 

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

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

В модуле формы разберитесь самостоятельно. Я думаю, что комментариев достаточно. Поговорим подробнее о модуле шара. Совершенно аналогично модулю ловца здесь имеется два метода - Начальная_установка и Действие. Метод Действие главный, он определяет, что должен делать шар в каждое мгновение своего полета. Он должен знать, поймали его или нет, и пора ли отскакивать от бортика. Этому и посвящены первые две из трех строк процедуры Действие. Эти две строки вычисляют нужным образом dx и dy, а третья строка - Шаг - изменяет в соответствии с этими значениями координаты x и y. Все, больше ничего во время движения шара делать не нужно.

Теперь посмотрим, что происходит при нажатии на кнопку "Начинай сначала". Выполняется процедура cmd_Начинай_сначала_Click и после пары прыжков по процедурам Visual Basic передает управление процедуре Начальная_установка каждого шара. Здесь трудности у вас может вызвать вычисление dx и dy. Поскольку значение Rnd есть случайное число в диапазоне от 0 до 1, то легко видеть, что как dx, так и dy будут случайными числами в диапазоне от -100 до 100. Этого достаточно, чтобы шар полетел со случайной скоростью в случайном направлении.

Теперь насчет отскока. Возможно, тем, кто не очень силен в математике и физике, покажется удивительным, что для отскока от горизонтальной преграды достаточно выполнить оператор dy = -dy, то есть поменять вертикальную составляющую шага на ее противоположное значение. "Но это действительно так!" Аналогично, достаточно выполнить оператор dx = -dx для отскока от вертикальной преграды. Чтобы лучше понять этот факт, запустите пошаговый режим при Макс_шаг=1000 и Число_шаров=1. При этом проследите внимательно за dx и dy, x и y.

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

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

· Иногда шар, вместо того, чтобы отскочить от борта, начинает двигаться скачками вдоль него. Возьмем к примеру левый бортик. Почти наверняка это связано с неточностью обработки вещественных чисел в операторе x=x+dx, в результате чего после отскока опять выполняется условие x < f.shpБортик.Left и шарик опять выполняет оператор dx = -dx. Рискну посоветовать (сам не проверял): в момент отскока незначительно увеличьте абсоютное значение шага: dx = -(dx+0.001).

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

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

Еще об объектах

У вас могло создаться превратное впечатление, что объект - это обязательно что-то движущееся, причем в темпе, задаваемом таймером. Совсем нет. Вспомните элементы управления - список, флажок. Это все тоже объекты. Вы можете создать класс пользователя, представляющий собой набор процедур и функций для сложной обработки информации в текстовом поле. И таймера здесь никакого не нужно.

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

Форма как объект

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

Кстати, мы ведь можем во время работы проекта "штамповать" нашу форму, как "штамповали" объекты по их классу. Делается это так. Добавьте в форму нашей игры кнопку Command1 и запишите в окно кода формы такую процедуру:

Private Sub Command1_Click()

Dim f1 As New f 'Объявляется новая форма f1 как копия формы f

f1.Show 'Форма f1 загружается и показывается на экране

End Sub

Запустите проект и в один из моментов игры нажмите кнопку Command1. Вы увидите, что игра идет уже на двух столах, причем одна и та же игра. Подвигайте ловца, понажимайте на кнопки. Поразмыслите сами, какие это сулит возможности в будущем.

Свойства только для чтения

Свойства только для чтения, как я уже говорил чуть раньше, нужны для того, чтобы обезопасить наш объект от вмешательства извне. Давайте сделаем свойством только для чтения свойство x ловца. Идея здесь такая. Объявим x, как Private. Проект сразу перестает работать, так как модулю формы для смещения изображения ловца нужно знать его координаты. Придумаем нашему свойству x "псевдоним", под которым он будет виден снаружи - Xл. Вся "фишка" в том, чтобы написать в классе clsЛовец глобальную функцию с именем Xл, все тело которой состоит из единственного оператора Xл=x:

Public Function Xл() As Long

Xл = x

End Function

Теперь заменим везде снаружи обращение Ловец.x на Ловец.Xл. Поскольку обращение к переменной и функции без параметров синтаксически неотличимо, то снаружи могут сколько угодно думать, что обращаются к переменной Xл, когда на самом деле это функция. Проект работает, проверьте. А где же здесь желанная цель "только для чтения"? А она достигнута. Потому что присваивать значение функции мы можем только в ее теле, а уж никак не из других модулей.

Теперь сотрите эту функцию. Создадим ее другим, общепринятым путем. Visual Basic предлагает удобный интерфейс для создания процедур, функций, событий и свойств: Tools®Add Procedure® поставьте переключатели в положение Property (так как мы хотим создать свойство) и Public, дайте имя свойству - Xл ®OK. Перед нами появятся две заготовки:

Public Property Get Xл() As Variant

 

End Property

 

Public Property Let Xл(ByVal vNewValue As Variant)

 

End Property

Слова Property Get означают "Получи (узнай) значение свойства", а слова Property Let означают "Присвой значение свойству". Нижнюю заготовку стираем, так как она предлагает записать код, позволяющий менять значение Xл снаружи. А верхнюю можно использовать вместо старой функции, заменив для порядка Variant на Long:

Public Property Get Xл() As Long

Xл = x

End Property

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

Public Property Get Xл() As Long

Xл = x + 1000

If x > 5000 Then MsgBox ("Дальше замучаю сообщениями")

End Property

Наследование, полиморфизм

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

Вообразим, что мы создали простой класс Автомобиль, наделив его всего лишь двигателем, рулем и 4 колесами. И никаких подробностей. Полюбовавшись работой получившейся самоходной тележки, мы решили развивать проект дальше и создать еще три класса, более богатых и подробных: Амфибия, Грузовик и Автобус. У каждого из новых классов, как и у Автомобиля, есть двигатель, руль и 4 колеса, но вдобавок к ним и много подробностей, например, у Амфибии гребной винт, а у Грузовика - кузов.

Как создавать эти 3 класса? Можно так же, как мы создавали clsШар, когда мы вручную записали весь код, причем скопировав часть кода из clsЛовец. В нашем случае нам можно было бы скопировать весь код Автомобиля в каждый из трех модулей, добавив затем в них для каждого свои процедуры и функции. Но есть гораздо более удобный и "правильный" способ - это наследование. Мы просто объявляем, что новый класс Грузовик является наследником класса Автомобиль. При этом Грузовик неявно (невидимо) приобретает весь код своего родителя - Автомобиля. Ничего не записав в код Грузовика, мы уже можем пользоваться им как Автомобилем. Чтобы снабдить Грузовик дополнительными чертами, мы пишем ему новые процедуры и функции. Аналогично поступаем с Амфибией и Автобусом.

Самое интересное то, что если мы изменяем что-то в коде родителя (Автомобиль), то это изменение тут же сказывается на наследниках. Например, если мы в Автомобиле заменили двигатель внутреннего сгорания на электрический, то эта замена немедленно произойдет и в Амфибии, и в Грузовике, и в Автобусе. Это очень ценное и удобное качество.

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

В заключение должен сказать, что Visual Basic версий 6.0 и более ранних не поддерживает настоящие полиморфизм и наследование. Впервые их поддержка осуществлена в Visual Basic.NET.

 



Поделиться:


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

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