Итак, мы продолжаем. Это вторая статья из серии и она посвящена в основном
черновой работе, которую необходимо проделать, чтобы потом, когда начнется
самое интересное, не отвлекаться по мелочам. Что же мы будем делать?
Прошлая часть являлась по своей сути введением и не несла никакой практической
нагрузки, хотя я и научил вас создавать объекты DirectDraw и даже менять
разрешение экрана. Один вопрос: ну и что вы со всем этим будете делать?!
В прошлой части я привел процедуру загрузки "битмапа" в буфер DD. Много
полезного она вам вряд ли принесла, но по крайней мере дала понять, с
чем приходится иметь дело, программируя DirectDraw. Итак. в этой части
я хочу, чтобы вы создали модуль, назовем его mdlDirectDraw7, в который
будете пихать все стандартные процедуры, чтобы потом забыв о том, как
они работают пользоваться ими с чистой душой. В ИНете есть уже готовые
модули (libDD, например), однако, создав свой модуль, вы будете более
подкованы и сможете сами писать более гибкий код.
Сперва, в наш модуль надо сбросить все переменные и ссылки на объекты,
относящиеся к DD, затем, туда будут записаны все функции, служащие для
работы, создания каких-либо обектов, установок, уничтожения и т. п. Далее,
привожу начальный текст этого модуля с комментариями. Почему начальный?
Просто сейчас, этот модуль будет на первом этапе работы, то-есть в него
мы запишем минимальное количество функций, требуемых для работы. Затем,
по мере дальнейшего изучения DirectDraw и DirectX, вы сможете добавлять
в этот модуль все новые и новые функции.
Я не привожу определения функций и констант Win32 API, как и в прошлой
части, мы будем пользоваться библиотекой Win32 TLB
'======================================
' mdlDirectDraw7 - основы работы с DirectDraw7
'======================================
'Объявления объектов DirectDraw
Public dx As New DirectX7 'Объект DirectX
Public dd As DirectDraw7 'Объект DirectDraw
Public ddsPrimary As DirectDrawSurface7 'Главная поверхность
Public ddsBack As DirectDrawSurface7 'Задний буфер
Public ddsd As DDSURFACEDESC2 'Структура с описанием поверхности
Private ddsdStore As DDSURFACEDESC2 'Вспомогательная структура описания
Public rc As RECT 'Структура RECT для блиттинга
Public caps As DDSCAPS2 'Структура с аппаратными возможностями
'Некоторые другие переменные
Public Running As Boolean 'Программа все еще работает?
Следующая проведура будет вызываться самой первой в будущих программах.
Она инициализирует DirectDraw в полноэкранном режиме. То есть создает
объект DirectDraw, устанавливает режим дисплея в dispXxdispYxdispColor
'Инициализация DirectDraw в полноэкранном режиме
'============================================
Public Sub CreateDDFullscreen(srcHwnd As Long, ByVal dispX As Long,
ByVal dispY As Long, ByVal dispColor As Long)
'Создаем объекты и устанавливаем режим
Set dd = dx.DirectDrawCreate("")
Call dd.SetCooperativeLevel(srcHwnd, DDSCL_EXCLUSIVE Or DDSCL_FULLSCREEN
Or DDSCL_ALLOWREBOOT)
Call dd.SetDisplayMode(dispX, dispY, dispColor, 0, DDSDM_DEFAULT)
'Создаем flipping chain
ddsd.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
ddsd.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX
ddsd.lBackBufferCount = 1
Set ddsPrimary = dd.CreateSurface(ddsd)
'Получить BackBuffer
caps.lCaps = DDSCAPS_BACKBUFFER
Set ddsBack = ddsPrimary.GetAttachedSurface(caps)
End Sub
Если создали, надо уничтожить! Вот процедура, убивающая DirectDraw. Внимание:
оффскринные поверхности надо уничтожать отдельно!
'Уничтожить DirectDraw
'====================
Public Sub DestroyDD()
'Восстанавливаем режим
Call dd.RestoreDisplayMode
Call dd.SetCooperativeLevel(0, DDSCL_NORMAL)
'Убиваем поверхности, а ПОТОМ объект DirectDraw
Set ddsBack = Nothing
Set ddsPrimary = Nothing
Set dd = Nothing
End Sub
Следующая функция разбиралась в прошлой главе - загрузка битмапа из файла
'Создание поверхности из картинки в файле
'======================================
Function CreateDDSFromFile(ByVal FileName as String, Optional CKey as
Long=0) as DirectDrawSurface7
'Объявления
Dim dds as DirectDrawSurface7 'Временная вспомогательная поверхность
Dim ddsd as DDSURFACEDESC2 'Описание временной поверхности
Dim StorePic as stdPicture 'Временное хранилище картинки
Dim Bmp as Win32.Bitmap 'Тип BITMAP, описывающий растровое изображение
Dim hDCPicture as Long, hDCSurface as Long 'DC картинки и поверхности
Dim ddCK as DDCOLORKEY 'Для установки ключевого цвета
'Загружаем картинку и получаем объект картинки
StorePic=LoadPicture(FileName) 'Загружаем картинку из файла
'Получаем описание картинки в структуру BITMAP
Call Win32.GetObject(StorePic.Handle, Len(Bmp), Bmp)
'Получаем DC картинки
hDCPicture=Win32.CreateCompatibleDC(ByVal 0&)
Call Win32.SelectObject(hDCPicture, StorePic.Handle)
'Теперь, создаем поверхность
ddsd.lFlags = DDSD_CAPS Or DDSD_WIDTH Or DDSD_HEIGHT 'Необходимые флаги
ddsd.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN 'Поверхность оффскринная
ddsd.lHeight = bmp.bmHeight 'Высота поверхности как у картинки
ddsd.lWidth = bmp.bmWidth 'Ширина поверхности как у картинки
'Вызываем метод, создающий поверхность
Set dds=dd.CreateSurface(ddsd) 'dd - глобальный объект DirectDraw7
'Переводим картинку на поверхность
Call dds.Restore
hDCSurface = dds.GetDC 'Подготовка к прямому доступу к поверхности
Call Win32.StretchBlt(hDCSurface, 0, 0, bmp.bmWidth, bmp.bmHeight, hDCPicture,
0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY) 'Это копирует картинку в буфер
Call dds.ReleaseDC(hDCSurface) 'Конец прямого доступа к поверхности
Call Win32.DeleteDC(hDCPicture) 'Уничтожить объект картинки - больше
не нужен
'Устанавливаем ключевой цвет
ddCK.low=CKey 'Работает правильно только в 24-битном цвете
ddCK.high=ddCK.low 'Но для простых случаев пойдет
Call ddы.SetColorKey(DDCKEY_SRCBLT, ddCK)
'Возвращаем объект
Set CreateDDSFromFile=dds
Set dds=nothing
End Function
Важная вещь - очистка буфера. Обычно очищается задний буфер перед началом
сборки на нем новой сцены. Если вы не очистите буфер, а потом попробуете
"перевернуть" его на главную поверхность, получите незабываемое
впечатление об использовании видеопамяти. Попробуйте!
'Очистка заданного буфера
'========================
Public Sub ClearBuffer(ByVal dds As DirectDrawSurface7)
Dim Color As Long
Color = RGB(0, 0, 0) 'Задаем черный цвет
dds.GetSurfaceDesc ddsdStore 'Получить описание очищаемой поверхности
'Заполняем структуру RECT, так, чтобы она охватывала всю поверхность
With rc
.Top = 0
.Left = 0
.Right = ddsdStore.lWidth 'Вот зачем нам надо было описание поверхности
.Bottom = ddsdStore.lHeight 'Высота и ширина подгоняются под нее
End With
dds.BltColorFill rc, Color 'Заполняем черным цветом область, указанную
в RECT
End Sub
Понятно, что вы можете очищать не всю поверхность, а только нужные области
для улучшения быстродействия.
Ура! Все приготовления закончены и теперь можно чего-нибудь нарисовать.
Наша программа перейдет в режим экрана 800x600x16, очистит задний буфер
и нарисует на нем два круга, взятых из файла. Круги будут частично наложены
один на другой, чтобы вы поняли, что такое ColorKey и прозрачный цвет.
Для упрощения задачи, не будем сейчас делать цикл прорисовки, а просто
один раз совершим блиттинг за задний буфер.
Я подразумеваю, что вы уже подготовили модуль mdlDirectDraw7 и подключили
его к своему проекту. Также, подключите библиотеки DX7VB и Win32.
Если все готово, тогда начинаем:
'Объявления
Dim Exited As Boolean 'Флаг, чтобы мы только один раз уничтожали объекты
Dim ddsPic1 As DirectDrawSurface7 'Поверхность под картинку
Позаботимся о благополучном выходе уже сейчас. Программа закончится,
когда пользователь нажмет на любую клавишу.
Private Sub Form_KeyPress(KeyAscii As Integer)
mdlDirectDraw7.DestroyDD 'Выход
Exited = True
End
End Sub
Private Sub Form_Unload(Cancel As Integer)
If Exited = False Then mdlDirectDraw7.DestroyDD 'Во избежании ошибок
End Sub
Теперь инициализация и загрузка картинки. Это будет происходить при запуске
формы.
Private Sub Form_Load()
Call mdlDirectDraw7.CreateDDFullscreen(frmMain.hWnd, 800, 600, 16) 'Инициализация
Set ddsPic1 = mdlDirectDraw7.CreateDDSFromFile(App.Path & "\ball.bmp")
'Загрузить картинку
Exited = False
Run 'Запускаем прорисовку
End Sub
Последним вызовом в этой подпрограмме мы вызываем процедуру рисования.
Обычно именно здесь я размещаю свои "движки", то есть циклы,
постоянно обновляющие экран и вызывающие необходимые расчеты. Сейчас "движок"
мы делать не будем, потому что задача наша предельно проста - нарисовать
два шарика.
Private Sub Run()
DoEvents 'Обязательно, когда делаете цикл прорисовки
mdlDirectDraw7.ClearBuffer ddsBack 'Чистим полотно
'Заполняем структуру RECT координатами рисунка на оффскринном буфере
rc.Left = 0
rc.Top = 0
rc.Right = 100
rc.Bottom = 100
'Рисуем два шарика
Call ddsBack.BltFast(10, 10, ddsPic1, rc, DDBLTFAST_SRCCOLORKEY Or DDBLTFAST_WAIT)
Call ddsBack.BltFast(60, 60, ddsPic1, rc, DDBLTFAST_SRCCOLORKEY Or DDBLTFAST_WAIT)
'Делаем Flip
Call ddsPrimary.Flip(ddsBack, DDFLIP_WAIT)
'Пока все
End Sub
Вот и вся программа.
Для blitting'а, то есть перевода шаблона спрайта на задний буфер используется
метод BltFast. Он достаточно быстр и легок в использовании. В качестве
параметров у метода задается координаты верхнего левого угла на поверхности
на которую происводится перевод, затем указывается исходный буфер, далее,
передается структура RECT. В этой сруктуре указана позиция и размеры прямоугольника
со спрайтом. И наконец, передаются флаги, влияющие на работу метода. Флаг
DDBLTFAST_SRCCOLORKEY говорит, что используется ключевой цвет, указанный
для источника, а флаг DDBLTFAST_WAIT говорит, что надо ждать до следующего
обновления экрана лучевой трубкой.
Метод Flip применяется для главной поверхности. В качестве параметра
ему передается исходная поверхность и флаги, влияющие на работу. Флаг
DDFLIP_WAIT говорит, что нельзя совершать какие-либо действия с поверхностью
до того, как флиппинг завершится.
Итак, ничего сложного нет. В следующей части мы приступим к созданию
анимации.
|