[Главная страница] [Предыдыущая часть] [Следующая часть ] [Приложения к этой части]
Итак, мы продолжаем. Это вторая статья из серии и она посвящена в основном черновой работе, которую необходимо проделать, чтобы потом, когда начнется самое интересное, не отвлекаться по мелочам. Что же мы будем делать?
Прошлая часть являлась по своей сути введением и не несла никакой практической нагрузки, хотя я и научил вас создавать объекты DirectDraw и даже менять разрешение экрана. Один вопрос: ну и что вы со всем этим будете делать?!
Создание универсального модуля
В прошлой части я привел процедуру загрузки "битмапа" в буфер DD. Много полезного она вам вряд ли принесла, но по крайней мере дала понять, с чем приходится иметь дело, программируя DirectDraw. Итак. в этой части я хочу, чтобы вы создали модуль, назовем его mdlDirectX, в который будете пихать все стандартные процедуры, чтобы потом забыв о том, как они работают пользоваться ими с чистой душой. В ИНете есть уже готовые модули (libDD, например), однако, создав свой модуль, вы будете более подкованы и сможете сами писать более гибкий код.
Сперва, в наш модуль надо сбросить все определения API функций и переменные и ссылки на объекты, относящиеся к DD, затем, туда будут записаны все функции, служащие для работы, создания каких-либо обектов, установок, уничтожения и т. п. Далее, привожу начальный текст этого модуля с комментариями. Почему начальный? Просто сейчас, этот модуль будет на первом этапе работы, то-есть в него мы запишем минимальное количество функций, требуемых для работы. Затем, по мере дальнейшего изучения DirectX, вы сможете добавлять в этот модуль все новые и новые функции.
Option Explicit
' Объявления DirectDraw
' Если у вас проблемы,
' пробуйте изменять объявления с
'
IDirectDrawxxxxx на DirectDrawxxxx
Public lpDD As
IDirectDraw
Public lpDDSFront As IDirectDrawSurface
Public lpDDSBack As IDirectDrawSurface
Public lpDDSPic As IDirectDrawSurface
Public ddsd
As DDSURFACEDESC
Public ddc As DDSCAPS
Public ddck As DDCOLORKEY
Public rc As RECT
Public i As Long
'Некоторые другие переменные
Public
bEnd As Boolean ' True = App is ending
Public X As
Long, Y As Long
' API Declarations
' Win32
Const IMAGE_BITMAP = 0
Const LR_LOADFROMFILE = &H10
Const
LR_CREATEDIBSECTION = &H2000
Const SRCCOPY =
&HCC0020
Private
Type BITMAP
bmType As Long bmWidth As Long
bmHeight As Long
bmWidthBytes As
Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As
Long
End Type
' GDI32
Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As
Long, ByVal X As Long, ByVal Y As Long) As Long
Private
Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As
Long
Private Declare Function DeleteDC Lib
"gdi32" (ByVal hdc As Long) As Long
Private
Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As
Long
Private Declare Function GetObject Lib
"gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal
nCount As Long, lpObject As Any) As Long
Private
Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal
hObject As Long) As Long
' USER32
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long)
As Long
Private Declare Function LoadImage Lib
"user32" Alias "LoadImageA" (ByVal hInst As Long, ByVal lpsz
As String, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As
Long) As Long
Private Declare Function StretchBlt Lib
"gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal
nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long,
ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal
dwRop As Long) As Long
Это та самая функция, которая была дана в прошлой главе. Она служит для загрузки ваших картинок со спрайтами в буфер DirectDraw, с которого вы потом будете "переводить" спрайты на рабочий экран
' Загружаем Bitmap
на буфер DirectDraw
Public Function
CreateDDSFromBitmap(dd As
IDirectDraw, ByVal strFile As String) As
IDirectDrawSurface
Dim hbm As Long ' Handle on bitmap
Dim bm As
BITMAP ' Заголовок bitmap
Dim ddsd As DDSURFACEDESC '
Surface описание
Dim dds As IDirectDrawSurface '
Созданный surface
Dim hdcImage As Long ' Handle on
image
Dim mhdc As Long ' Handle on surface context
'Загружаем bitmap из файла. Над этой строкой можно издеваться, если хотите
получить
'bitmap из других источников
hbm = LoadImage(ByVal 0&, strFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE
Or LR_CREATEDIBSECTION)
'
Получаем информацию о bitmap
GetObject hbm, Len(bm), bm
'
Заполняем описание surface
With ddsd
.dwSize = Len(ddsd)
.dwFlags = DDSD_CAPS +
DDSD_HEIGHT + DDSD_WIDTH
.DDSCAPS.dwCaps =
DDSCAPS_OFFSCREENPLAIN
.ddpfPixelFormat.dwSize =
8
.dwWidth = bm.bmWidth
.dwHeight = bm.bmHeight
End With
' Создаем
surface
dd.CreateSurface ddsd, dds,
Nothing
' Создаем
memory device
hdcImage = CreateCompatibleDC(ByVal
0&)
'
Выбираем bitmap в этом memory device
SelectObject
hdcImage, hbm
'
Восстанавливаем surface
dds.Restore
'
Получаем surface's DC
dds.GetDC mhdc
'
Копируем из memory device на DirectDrawSurface
StretchBlt mhdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, 0, 0,
bm.bmWidth, bm.bmHeight, SRCCOPY
' Release
the surface's DC
dds.ReleaseDC mhdc
' Release
the memory device and the bitmap
DeleteDC hdcImage
DeleteObject hbm
'
Возвращаем новый surface
Set CreateDDSFromBitmap = dds
End Function
' Очистка буфера
Sub ClearBuffer(ByRef lpDDS As IDirectDrawSurface)
Dim fx As DDBLTFX
'
Заполняем описание операции blt
With fx
.dwSize = Len(fx)
.dwFillColor =
RGB(0, 0, 0)
End With
' Заполняем surface цветом
Call lpDDS.Blt(ByVal 0&, Nothing, ByVal 0&, DDBLT_WAIT Or
DDBLT_COLORFILL, fx)
End Sub
Эта процедура для полного копирования одного буфера на другой. В аргументах у нее два объекта DirectDrawSurface. Первый - источник, а второй - цель.
' Копируем весь буфер на другой
Sub CopyBuffer(ByRef lpDDSSrc As IDirectDrawSurface, ByRef
lpDDSDest As IDirectDrawSurface)
Dim ddsd As
DDSURFACEDESC
Dim rc As RECT
'
Получить описание surface для исходного surface
With
ddsd
.dwSize = Len(ddsd)
.dwFlags = DDSD_WIDTH Or DDSD_HEIGHT
End
With
Call lpDDSSrc.GetSurfaceDesc(ddsd)
' Теперь копируем весь исходный буфер на буфер назначения
rc.Left = 0
rc.Top = 0
rc.Right = ddsd.dwWidth
rc.Bottom =
ddsd.dwHeight
' BltFast the surface
Call lpDDSDest.BltFast(0, 0, lpDDSSrc, rc, DDBLTFAST_WAIT)
' Set this flag if that surface has a source key Or
DDBLTFAST_SRCCOLORKEY)
End Sub
Процедура не делает ничего другого, как ждет, когда начнется следующий цикл обновления экрана, после чего, завершается. Огромная скорость работы :)
Sub WaitForVerticalBlank()
' This waits for the veritcal blank to end
Call lpDD.WaitForVerticalBlank(DDWAITVB_BLOCKEND, 0)
End Sub
Здесь происходит инициализация DirectDraw, создаются объекты, поверхности (Surface) и меняется разрешение экрана. В параметрах стоит режим дисплея (X x Y x Color, где Color задается в битах на пиксель (bpp)). Параметру trgtForm надо присвоить указатель на вашу главную форму, на которой будет происходить действие.
Public Sub DDrawInit(ByVal X As
Long, ByVal Y As Long, ByVal Color As Long, ByVal trgtForm As Object)
'
Initialize DirectDraw
Call DirectDrawCreate(ByVal
0&, lpDD, Nothing)
Call
lpDD.SetCooperativeLevel(trgtForm.hwnd, DDSCL_EXCLUSIVE Or DDSCL_FULLSCREEN Or
DDSCL_ALLOWREBOOT)
Call lpDD.SetDisplayMode(X, Y,
Color)
'Create a front and a bitmap surfaces
With ddsd
.dwSize = Len(ddsd)
.dwFlags =
DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
.DDSCAPS.dwCaps =
DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX
.dwBackBufferCount = 1
End With
Call lpDD.CreateSurface(ddsd,
lpDDSFront, Nothing)
' Retrieve the back buffer
With ddc
.dwCaps =
DDSCAPS_BACKBUFFER
End With
Call
lpDDSFront.GetAttachedSurface(ddc, lpDDSBack)
End Sub
Устанавливается ColorKey, то есть цвет, который в ваших спрайтах будет прозрачным. Цвет задается в формате RGB тремя параметрами составляющих цвета (0-255)
Public Sub SetColorKey(ByVal R As
Integer, ByVal G As Integer, ByVal B As Integer)
' Set
the color key
With ddck
.dwColorSpaceHighValue = RGB(R, G, B)
.dwColorSpaceLowValue = .dwColorSpaceHighValue
End With
Call lpDDSPic.SetColorKey(DDCKEY_SRCBLT,
ddck)
End Sub
Эта процедура выполняет уничтожение объектов DirectX. Если она не будет вызвана перед тем, как ваша программа закроется, появится веселая надпись типа "Программа выполнила недопустимую ошибку..." и среда VisualBasic со всем его интерфейсом GUI накроется.
Public Sub DestroyDirectX()
Call lpDD.RestoreDisplayMode
Set
lpDDSPic = Nothing
Set lpDDSBack = Nothing
Set lpDDSFront = Nothing
Set lpDD =
Nothing
End Sub
Вот эти функции понадобятся нам на первых порах. Если уж очень не хотите, можете не разбираться, как они работают, а просто пользоваться ими, как кирпичиками, далее я покажу как.
Элементарнейший движок и принципы Blitting'а
Ура! Все приготовления закончены и теперь можно чего-нть сотворить. Наша первая программа будет постоянно обновлять экран, прорисовывая спрайт, для упрощения задачи пока-что один.
Законченный проект можете загрузить в самом конце этой главы, а пока я объясню, как работает основной цикл прорисовки. Сначала - вот его код:
Private Sub Form_Load()
' Это флаг окончания цикла. Когда устанавливается в true, например
после нажатия клавиши
' цикл прорисовки
прекращается
bEnd = False
' Инициализируем DD и устанавливаем разрешение
mdlDirectX.DDrawInit 640, 480, 16, Me
' Загружаем картинку на поверхность DD
Set lpDDSPic = CreateDDSFromBitmap(lpDD, App.Path &
"\fir0.bmp")
mdlDirectX.SetColorKey 0,
0, 0
' Запускаем Engine
While bEnd = False
DoEvents 'Будем жалостливы к системе
Call ClearBuffer(lpDDSBack) 'Чистим
полотно
'Указываем координаты прямоугольника в
буфере с рисунком, которые будем копировать
'Координаты заносятся в структуру RECT, которая
передается функцией BltFast
rc.Left =
1
rc.Right = rc.Left + 84
rc.Top = 1
rc.Bottom = rc.Top + 88
'Совершаем Blitting - копируем прямоугольник с буфера с
рисунком на задний буфер
'в
указанное место (0,0)
Call
lpDDSBack.BltFast(0, 0, lpDDSPic, rc, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
Call
mdlDirectX.WaitForVerticalBlank 'Подождем до начала цикла обновления
Call lpDDSFront.Flip(Nothing,
DDFLIP_WAIT) ' Переводим задний буфер на передний
Call Sleep(80) 'Задержимся на чуть-чуть. Пока
бесполезно, так-как нет анимации
Wend
Unload Me
End Sub
Как видите, главный рисующий цикл я загнал в процедуру Form_Load. Это всего-лишь фрагмент, объекты определены не здесь. Для blitting'а, то есть перевода шаблона спрайта на задний буфер использяется метод BltFast. Он достаточно быстр и легок в использовании. В качестве параметров у метода задается координаты верхнего левого угла на поверхности на которую происводится перевод, затем указывается исходный буфер, далее, передается структура RECT, определенная в Win32 TLB. В этой тсруктуре указана позиция и размеры прямоугольника со спрайтом. И наконец, передаются флаги, влияющие на работу метода.
После перевода спрайта вызвается процедура из нашего модуля WaitForVerticalBlank, а затем, происходит "переворачивание" задней поверхности на переднюю. Позднее, для эффектов анимации можно использовать процедуру Sleep(ms as integer). Она вызывает задержку на указанное количество миллисекунд.
Структура RECT определяется четырьмя значениями: Left,Top, Right и Bottom, которые соответственно являются координатами, задающими расположение прямоугольника со спрайтом на поверхности, содержащей его.
Итак, ничего сложного нет. В следующей части мы приступим к созданию анимации.
Полный проект, содержащий код примера и модуль DirectX. Загрузить
Приятного программирования, Antiloop