| ||||||||||||||||
![]() | ||||||||||||||||
| ||||||||||||||||
![]() | ||||||||||||||||
| ||||||||||||||||
![]() |
Часть третья: Творим! Да уж давно бы пора! - подумают некоторые. Ну ладно, не злитесь, в этой главе мы разработаем и претворим в жизнь самую настоящую 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". Модуль mdlDirectDraw7 у нас уже есть (ведь есть, правда?!, поэтому помимо формы frmBall код будет размещаться в модуле mdlBall, в котором будут функции и процедуры, выполняющие основные расчеты и размещение объектов на экране. В моих принципах освобождать модули кода форм от лишних процедур, не относящихся непосредственно к обработке их событий поэтому я жестоко и беспощадно БУДУ убирать все служебные процедуры в другие модули. Можете теперь в меня чем-нибудь кинуть. Программу будем писать постепенно, создавая сначала костяк, а потом наращивая все более и более завороченные фичи (не баги!). Для начала, создаем новый проект, добавляем в него нужные библиотеки (как всегда - dx7vb и win32). Дадим имя проекту - VBPong, переименуем главную форму в frmBall, добавим все вышеперечисленные модули и сохраним все хозяйство в одну новую папку. В нее же надо перекопировать графический файл, который мы предусмотрительно приготовили заранее. Теперь, все готово и можно приступать к написанию кода. Начнем с инициализации экрана. В модуль кода формы frmBall напишем следующие строки, которые будут управлять экраном во время работы программы и обеспечивать нормальный выход. По ходу работы я буду все пояснять.
И на этом код формы пока заканчивается. Внимательно присмотрясь к тому что будет делать программа, вы увидите, что после инициализации, она войдет в цикл, в котором будет находиться до самого своего конца. Идея такова, чтобы в этом цикле заставить программу отображать все нужные нам объекты каждый раз на заданных местах. Места же эти каждый раз перевычисляются на очередном витке цикла. Таким образом, вычисления сводятся к определению новых координат объекта, заданию структуры RECT, о которой говорилось в прошлой главе, и применению метода BltFast. Все эти действия помещаются в "Регионе 2" и будут занимать все дальнейшее время нашей работы в этой части. Теперь я думаю вам стало ясно, почему я был категорически против размещения всей программы в модуле кода формы. Почти все действия, относящиеся к DirectX мы уже сделали и теперь приступаем к математике.
Добавим функциональности Переходим в модуль mdlBall. Сперва, как я уже говорил, определим структуры BallInf и BetInf, а также создадим нужные переменные. Типы выглядят так:
Инициализация закончена. Текущее положение ракетки зависит от координаты X. Y у обеих ракеток постоянная - у нижней - 460, у верхней - 0. Чтобы посчитать новое положение ракетки исходя из старого, надо прибавить к координате X приращение SpeedX. Если приращение отрицательное, то бита двигается влево и наоборот. Bet1 управляется игроком, поэтому приращение задается нажатием клавиши "влево" или "вправо". Если клавиша отпускается, то приращение равно нулю. Приращение Bet2 вычисляется компьютером. Положение мяча задается координатами X и Y. У него также существуют два осевых приращения. Карта размера 16*12 (16 - по-горизонтали, 12 - по-вертикали). Учтите, что массив начинается с нуля и сначала идут строки. Давайте напишем функцию, которая сбрасывает положения мяча и бит до начальных.
Здесь используется дополнительная функция GetRandomSpeedX. Она возвращает случайное приращение по оси X, колеблющееся от -5 до 5. Вот как она описывается.
Теперь, функция, которая инициализирует карту. Ее нужно вызывать только один раз, когда загружается уровень. Вы можете сами поэкспериментировать, дописывая новые уровни.
Мы всего-лишь задали матрицу. Нам еще понадобится процедура, которая будет рисовать карту каждый виток цикла по этой матрице.
Теперь, процедура, которая будет рисовать биты. В параметре ей указывается какая бита будет рисоваться, 1-нижняя, 2-верхняя
Отлично, теперь "приделаем" нашей нижней бите управление. Доведите процедуру обработки события формы frmBall_KeyDown до следующего состояния, а затем допишите обработку события frmBall_KeyUp:
Добавим в регион 1 следующее:
А в регион 2 это:
Запустите программу. Вы увидите, что уровень уже нарисован и вы можете двигать свою битку в пределах стенок. Эти стенки по определению должны быть всегда, учитывайте это. Выходите из программы как и раньше, с помощью Escape. Свободный полет Осталось самое сложное - запрограммировать мячик, чтобы он летал и отскакивал от тех мест, которые в массиве помечены единичкой. Я сделал это вот каким нехитрым образом. Итак, представьте себе двухмерный мячик. В процессе полета он может находиться целиком в одном секторе (каждый сектор - 40*40, если помните, а мяч - 20*20), в двух секторах одновременно, в трех не может, а вот в четырех - запросто. Теперь представьте, что у мячика есть крайние точки, так называемые - "контрольные точки". Можно сказать, что если одна из этих точек находится в каком-то секторе, то некая часть мячика тоже находится в этом секторе. Таким образом, если контрольная точка находится в секторе, который помечен 0, то все нормально, однако, если контрольная точка попадает в сектор с 1, то он уже занят блоком и мячик должен отскочить. В какую сторону? Посмотрите на такой рисунок: Вы видите на нем четыре контрольных точки, отмеченных красным, а также их координаты в системе координат мяча. Если мяч попал в занятый сектор точкой номер 1, то очевидно, что он стукнулся о преграду, находящуюся сверху него и должен отсочить вниз. Это легко задать формулой SpeedY=-SpeedY. Если "просигналила" точка номер два, то мяч отскакивает влево и т. д. При этом, мяч нужно "отпозиционировать" то-есть поставить просигналившую точку ровно на границу с занятым сектором, чтобы мяч не "залипал" там. Конечно, для правильного отображения четырех точек явно мало, мяч может уже быть в занятом секторе и ни одна точка не просигналит, поэтому в идеале надо рассматривать каждую точку окружности мяча, лезть в дебри тригонометрии... бррр... Я вас еще не убедил? Хорош этот способ или нет, решайте сами. По крайней мере он работает. Теперь функция, которая выдает контрольную точку, если мяч попал в занятый сектор или ноль, если все нормально.
Функция будет всегда выдавать только одну точку с приоритетом по часовой стрелке. Настал момент рисования мяча.
Наконец, последняя функция - вычисление приращения по X ракетки компьютера в зависимости от позиции мяча. Все очень просто. Компьютер всегда старается, чтобы левый и правый край мяча попадал в поле его ракетки.
Громкие апплодисменты! Написание модуля завершено. Теперь добавьте в регион две cтроки:
И запустите программу (Не забудьте снять ремарку с вызова функции DoBetAI). Работает? Ура! Надеюсь, теперь вам немного стало ясно, как делаются игрушки на DirectX. Если нет - то это нормально. Просто надо упражняться, упражняться и еще раз упражняться... Не помню кто сказал... На этом части три пришел конец. В следующей части поговорим об эффектах. |
|
![]() | ||||||||||||||||
| ||||||||||||||||
![]() | ||||||||||||||||
|