| ||||||||||||||||
![]() | ||||||||||||||||
| ||||||||||||||||
![]() | ||||||||||||||||
| ||||||||||||||||
![]() |
Часть третья: Творим! Да уж давно бы пора! - подумают некоторые. Ну ладно, не злитесь, в этой главе мы разработаем и претворим в жизнь самую настоящую DirectX'овую игруху! Не будем очень оригинальны и сделаем PingPong; ну знаете, такая штука, где ездят две ракетки и пинают мячик стараясь чтобы противник по нему не попал. Так вот, мы постараемся сделать полностью (ну или почти) работающий ПингПонг для одного игрока против компьютера. Как подключить второго игрока я надеюсь вам будет абсолютно понятно - это останется в качестве упражнения. Кроме того, для упрощения задачи я пока не буду рассматривать вывод текстовых сообщений - это в раздел эффектов в следующую часть. А для усложнения задачи - выработаем такой алгоритм, в котором поле у нас будет представлено в виде матрицы в которой есть элементы 0 - свободно и 1 - блок. От блоков мячик соответственно отскакивает. Это сделано для того, чтобы мяч мог передвигаться независимо от расположения блоков, вам надо будет только поменять ссответствующее значение в матрице. Это уже будет нечто действительно похожее на движок и вы можете делать различные игровые поля. Итак, приступаем Что сначала? Ну с самого начала, еще даже перед тем как начать рисовать графику, надо решить один такой маленький вопросик: а какой у нас будет ПингПонг: вертикальный или горизонтальный? Я выбрал вертикальный; не спрашивайте почему, вы всегда сможете сделать другой. Разрешение экрана будет 640x480x16, а размер матрицы - 12*16, то есть одна клетка - 40*40 пикселей. Ну вот, а теперь можно рисовать графику. Процесс этот утомительный только по одной причине: кропотливость. Сначала надо выбрать размерность каждого объекта: бита будет размером 80*20, мяч - 20*20, а блок - 40*40, чтобы занимал одну клетку. Размеры мяча и биты абсолютно произвольные, но если вы захотите взять другие, учтите, что придется исправлять все нижеследующие математические расчеты. Итак,
зайдите куда-нибудь типа FreeHand8 и сотворите шедевр в стиле Малевича! Каждый
объект рисуйте по отдельности и сохраняйте в 24-разрядном файле BMP. Не
забывайте, что потребуется две разных биты. Нарисовали - вперед в Painbrush!
Выбирайте пункт меню "Вставить из файла" и скомпонуйте все спрайты на
одном рисунке. Затем подгоните размер рисунка под границы спрайтов (рисунок не
может быть больше, чем разрешение экрана, иначе он не поместится в буфере). Да!
И не забудьте, что нам потребуется "прозрачный" цвет. Я взял для этого
черный (RGB(0,0,0)), поэтому контуры на рисунках спрайтов у меня вроде как
черные, а на самом деле нет (RGB(12,12,12)). Я нарисовал вот такую картину и далее подразумевается, что координаты расположения спрайтов будут как у меня. Примечание: как всегда я где-то напортачил, поэтому размерности спрайтов у меня с погрешностями +/- 1 пиксел.
Первые аккорды кода Давным давно, еще в школе, нам преподавали бейсик (простой!). Поэтому я еще некоторое время то и дело боролся с искушением свалить все в одну кучу. Это не только непрактично, но и просто как-то неэстетично и плохо выглядит, поэтому давайте обсудим структуру программы. Заместо того, чтобы вводить кучу переменных, для описания объектов используем типы BallInf и BetInf, описывающие соответственно мячик и биту. Создадим экземпляр мячика Ball и два экземпляра биты Bet1 и Bet2 "As BetInf". Модуль mdlDirectXу нас уже есть, поэтому помимо формы frmBall код будет размещаться в модуле mdlBall, в котором будут функции и процедуры, выполняющие основные расчеты и размещение объектов на экране. В моих принципах освобождать модули кода форм от лишних процедур, не относящихся непосредственно к обработке их событий поэтому я жестоко и беспощадно БУДУ убирать все служебные процедуры в другие модули. Можете теперь в меня чем-нибудь кинуть. Программу будем писать постепенно, создавая сначала костяк, а потом наращивая все более и более завороченные фичи (не баги!). Для начала, создаем новый проект, добавляем в него нужные библиотеки того DirectX, с каким хотите работать - на данном этапе это не принципиально. Я использовал DirectX 6, благо он есть у всех. Дадим имя проекту - VBPong, переименуем главную форму в frmBall, добавим все вышеперечисленные модули и сохраним все хозяйство в одну новую папку. В нее же надо перекопировать графический файл, который мы предусмотрительно приготовили заранее. Теперь, все готово и можно приступать к написанию кода. Начнем с инициализации экрана. В модуль кода формы frmBall напишем следующие строки, которые будут управлять экраном во время работы программы и обеспечивать нормальный выход. По ходу работы я буду все пояснять. Dim bEnd as Boolean 'Когда этот флаг True - это сигнал к выходу из программы Private
Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) 'При
выходе надо уничтожать все объекты DirectX Private
Sub Form_Load()
'Следующие строки назовем "Регион 1". Сюда будут помещаться начальные
определения игры
'Теперь, создаем главный цикл прорисовки экрана ' Это место назовем "Регион 2". Здесь надо
разместить последовательность рисования всех 'Теперь, завершающие действия
витка И на этом код формы пока заканчивается. Внимательно присмотрясь к тому что будет делать программа, вы увидите, что после инициализации, она войдет в цикл, в котором будет находиться до самого своего конца. Идея такова, чтобы в этом цикле заставить программу отображать все нужные нам объекты каждый раз на заданных местах. Места же эти каждый раз перевычисляются на очередном витке цикла. Таким образом, вычисления сводятся к определению новых координат объекта, заданию структуры RECT, о которой говорилось в прошлой главе, и применению метода BltFast. Все эти действия помещаются в "Регионе 2" и будут занимать все дальнейшее время нашей работы в этой части. Теперь я думаю вам стало ясно, почему я был категорически против размещения всей программы в модуле кода формы. Почти все действия, относящиеся к DirectX мы уже сделали и теперь приступаем к математике. Добавим функциональности Переходим в модуль mdlBall. Сперва, как я уже говорил, определим структуры BallInf и BetInf, а также создадим нужные переменные. Типы выглядят так: Type BetInf
Type
BallInf 'Две биты и
мяч Инициализация закончена. Текущее положение ракетки зависит от координаты X. Y у обеих ракеток постоянная - у нижней - 460, у верхней - 0. Чтобы посчитать новое положение ракетки исходя из старого, надо прибавить к координате X приращение SpeedX. Если приращение отрицательное, то бита двигается влево и наоборот. Bet1 управляется игроком, поэтому приращение задается нажатием клавиши "влево" или "вправо". Если клавиша отпускается, то приращение равно нулю. Приращение Bet2 вычисляется компьютером. Положение мяча задается координатами X и Y. У него также существуют два осевых приращения. Карта размера 16*12 (16 - по-горизонтали, 12 - по-вертикали). Учтите, что массив начинается с нуля и сначала идут строки. Давайте напишем функцию, которая сбрасывает положения мяча и бит до начальных. Public Sub Reset() Bet2.X = 320
Ball.X = 320 Здесь используется дополнительная функция GetRandomSpeedX. Она возвращает случайное приращение по оси X, колеблющееся от -5 до 5. Вот как она описывается. Private
Function GetRandomSpeedX() As Integer Теперь, функция, которая инициализирует карту. Ее нужно вызывать только один раз, когда загружается уровень. Вы можете сами поэкспериментировать, дописыая новые уровни. Public Sub LoadMap(ByVal Level As Integer) Мы всего-лишь задали матрицу. Нам еще понадобится процедура, которая будет рисовать карту каждый виток цикла по этой матрице. Public Sub DrawMap() Теперь, процедура, которая будет рисовать биты. В параметре ей указывается какая бита будет рисоваться, 1-нижняя, 2-верхняя Public Sub DrawBet(ByVal Num As Integer) If Num = 1 Then 'Заполняем недостающие элементы структуры RECT
и рисуем биту на заднем буфере Отлично, теперь "приделаем" нашей нижней бите управление. Доведите процедуру обработки события формы frmBall_KeyDown до следующего состояния, а затем допишите обработку события frmBall_KeyUp: Private Sub Form_KeyDown(KeyCode As Integer, Shift As
Integer) Private Sub Form_KeyUp(KeyCode As
Integer, Shift As Integer) Добавим в регион 1 следующее: mdlBall.Reset А в регион 2 это: mdlBall.DrawMap Запустите программу. Вы увидите, что уровень уже нарисован и вы можете двигать свою битку в пределах стенок. Эти стенки по определению должны быть всегда, учитывайте это. Выходите из программы как и раньше, с помощью Escape. Свободный полет Осталось самое сложное - запрограммировать мячик, чтобы он летал и отскакивал от тех мест, которые в массиве помечены единичкой. Я сделал это вот каким нехитрым образом. Итак, представьте себе двухмерный мячик. В процессе полета он может находиться целиком в одном секторе (каждый сектор - 40*40, если помните, а мяч - 20*20), в двух секторах одновременно, в трех не может, а вот в четырех - запросто. Теперь представьте, что у мячика есть крайние точки, так называемые - "контрольные точки". Можно сказать, что если одна из этих точек находится в каком-то секторе, то некая часть мячика тоже находится в этом секторе. Таким образом, если контрольная точка находится в секторе, который помечен 0, то все нормально, однако, если контрольная точка попадает в сектор с 1, то он уже занят блоком и мячик должен отскочить. В какую сторону? Посмотрите на такой рисунок:
Вы видите на нем четыре контрольных точки, отмеченных красным, а также их координаты в системе координат мяча. Если мяч попал в занятый сектор точкой номер 1, то очевидно, что он стукнулся о преграду, находящуюся сверху него и должен отсочить вниз. Это легко задать формулой SpeedY=-SpeedY. Если "просигналила" точка номер два, то мяч отскакивает влево и т. д. При этом, мяч нужно "отпозиционировать" то-есть поставить просигналившую точку ровно на границу с занятым сектором, чтобы мяч не "залипал" там. Конечно, для правильного отображения четырех точек явно мало, мяч может уже быть в занятом секторе и ни одна точка не присогналит, поэтому в идеале надо рассматривать каждую точку окружности мяча, лезть в дебри тригонометрии... бррр... Я вас еще не убедил? Хорош этот способ или нет, решайте сами. По крайней мере он работает. Теперь функция, которая выдает контрольную точку, если мяч попал в занятый сектор или ноль, если все нормально. Private Function CheckPoint() As Integer Функция будет всегда выдавать только одну точку с приоритетом по часовой стрелке. Настал момент рисования мяча.
Public
Sub DrawBall()
Private
Sub MoveBall() Private Function GetDirectSpeedX() As
Integer Наконец, последняя функция - вычисление приращения по X ракетки компьютера в зависимости от позиции мяча. Все очень просто. Компьютер всегда старается, чтобы левый и правый край мяча попадал в поле его ракетки. Private Sub DoBetAI() Громкие апплодисменты! Написание модуля завершено. Теперь добавьте в регион две cтроки: mdlBall.DrawBet
2 И запустите программу (Не забудьте снять ремарку с вызова функции DoBetAI). Работает? Ура! Надеюсь, теперь вам немного стало ясно, как делаются игрушки на DirectX. Если нет - то это нормально. Просто надо упражняться, упражняться и еще раз упражняться... Не помню кто сказал... На этом части три пришел конец. В следующей части поговорим об эффектах. Приложения Пример - проект VBPong ~25кбПриятного
программирования,
[Назад][Содержание] |
|
![]() | ||||||||||||||||
| ||||||||||||||||
![]() | ||||||||||||||||
|