CSDN博客

img Eastunfail

GIFs, JPGs和DirectInput Mouse在用VB做游戏的运用

发表于2001/12/30 9:23:00  1263人阅读

由于时间仓促,翻译的不是很好,希望大家能够将就看看。

 

将 GIFs, JPGs文件加载到表面
作者: W-Buffer
相对而言,不是很难,我们并不需要解码器来得到JPG的每一个字节,或任何类似于次的,相反,我们将用一个图片框来打开一个图片,然后再将其传到表面,但首先我门需要声明API函数:
Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC 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 dwRop As Long) As Long
Public Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Public Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Public Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Public 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
现在我们要为加载图片作好准备
Dim Pict1 As StdPicture
Set Pict1 = LoadPicture("MyPict.jpg")
创造表面:
Dim TDesc As DDSurfaceDesc2
TDesc.lFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH
TDesc.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN
TDesc.lHeight = CLng((Pict1.Height * 0.001) * 567 / Screen.TwipsPerPixelY)
TDesc.lWidth = CLng((TPict.Width * 0.001) * 567 / Screen.TwipsPerPixelX)
Set Surf = DDraw.CreateSurface(TDesc)
Dim SurfDC As Long, PictDC As Long
SurfDC = Surf.GetDC
PictDC = CreateCompatibleDC(0)
SelectObject Pict1.Handle, PictDC
我们将使用Dcs和Bitblt或StretchBlt来把Pic1复制到表面
只复制成一样大小:
BitBlt SurfDC, 0, 0, TDesc.lWidth, TDesc.lHeight, PictDC, 0, 0, vbSrcCopy

现在复制并且改变图片大小
StretchBlt SurfDC, 0, 0, StretchWidth, StretchHeight, PictDC, 0, 0, TDesc.lWidth, TDesc.lWidth, vbSrcCopy
释放DC:
Surf.ReleaseDC SurfDC
DelecteDC PictDC
Set Pict1 = Nothing

下一步该怎么作呢?下载SurfUtil。Bas,我将利用这一技巧来创造并加载图片
注意:有时StretchBlt不能正常显示GIF,那就是我添加了BitBlt这个函数的原因

.
特殊效果
你会懒的不想利用C/C++或汇编来创造一个动态连接库,并用那个动态连接库来完成特殊效果吗?
就让blitter(我不认识,也查不出来,可能是glitter,闪光)光栅操作(ROP)为你工作!
我的格言是让最小的努力为你带来最大的报酬!
在Vb中有不少可使用的ROP常数:
vbSrcPaint -逻辑或操作,源和目标图片,做一个模拟的Alpha混合效果。
vbSrcAnd - 逻辑与操作,灰度效果。
vbSrcInvert - 逻辑异或操作。
vbSrcErase - 翻转目标图片,然后再和源图片进行逻辑与操作
vbSrcCopy - 把原图片直接复制到目标图片上,在圆满的替换掉?(replacing it completely)
vbDstInvert -翻转目标图片,全部忽略原图片。
vbNotSrcCopy -翻转源图片,然后在直接复制到目标上,又是圆满的替换掉?(replacing it completely)
vbNotSrcErase - 对源图片和目标图片进行逻辑或操作,在翻转。
现在我们利用这些ROP有两个执行销毁的方法。我们能够使用APIBitBlt这个函数,,也可以使用DirectX的BltFx函数,只有用户的显卡能够使用硬件来支持ROP而不是软件模拟ROP,BltFx才能够工作,否则BltFx将会失败!无论怎样,BltFx失败时,API的BitBlt还是能够调用,在软件中,一切就绪时,BitBlt就决不会失败!
(深思熟虑:当使用BltFx时,它可以完美的将表面放到显存以使其借助显卡的Blitter(和上面的一样,不认识也查不出来)来快速访问,而当另外使用API函数BitBlt时,它却完美的将其置放到系统内存,否则数据将会被从显存中修改后带来,然后它将回到显存,没有效率啊!)
容易的知道确认用户的显卡是否硬件支持特殊的ROP是件很好的事,我写了一个简单的函数来确认:
Private Function TestROP(ByRef surfBack As DirectDrawSurface7, lngROP As Long) As Boolean

Dim objBltFx As DDBLTFX
Dim rectTemp As RECT
Dim surfTemp As DirectDrawSurface7
Dim udtDDSD As DDSURFACEDESC2

'创造一个临时的表面
udtDDSD.lFlags = DDSD_HEIGHT Or DDSD_WIDTH
udtDDSD.lHeight = 1
udtDDSD.lWidth = 1
Set surfTemp = mdd.CreateSurface(udtDDSD)

'设置BltFx的ROP操作代码
objBltFx.lROP = lngROP

'源和目标矩形
rectTemp.Right = 1
rectTemp.Bottom = 1

'测试BltFx 的能力
If surfBack.BltFx(rectTemp, surfTemp, rectTemp, DDBLT_ROP Or DDBLT_WAIT, objBltFx) <> 0 Then
TestROP = False
Else
TestROP = True
End If

End Function
简单的传递这个函数 一个参考到你的当前后备缓存和你感兴趣的ROP常数,它将把用户硬件是否支持特殊的ROP告诉你给你,它执行一个BltFx 的blit例子并且测试错误然后返回错误。
(注:这一段如果你懂就可不必看我写的,有一些英文短语不容易翻译成中文)
Simply pass this function a reference to your current backbuffer and the ROP constant you're interested in and it will inform you of the user's hardware capabilities. It does this by performing a sample BltFx blit and examining the error code returned.
现在我门知道如果我们希望的ROP已经被支持,我们就能往下继续并且执行我们的blit,如果ROP以被支持,我们也能像这样使用BltFx:


objBltFx.lROP = lngROP
msurfBack.BltFx rectDest, surfDisplay, rectSource, DDBLT_ROP Or DDBLT_WAIT, objBltFx
objBltFx的定义类型是DDBLTFX,必须和我们希望执行(被储存在lngROP中)的ROP一起被加载。一次,我们有我们的DDBLTFX类型填充,我们就能使用后备缓存的BltFx方法,传递源和目标矩形、我们期望的表面、一点常数(DDBLT_ROP和DDBLT_WAIT)和objBltFx,常数DDBLT_ROP是必须的,它将通知BltFx我们想使用DDBLTFX结构的成员lROP。If we're forced to use the BitBlt API, it can be handled in this fashion:


'锁主表面的DCs
lngDestDC = msurfBack.GetDC
lngSrcDC = surfDisplay.GetDC

'Do the fancy old-fashioned blit
BitBlt lngDestDC, intX, intY, intWidth, intHeight, lngSrcDC, 0, 0, lngROP

'释放DCs
surfDisplay.ReleaseDC lngSrcDC
msurfBack.ReleaseDC lngDestDC
首先我们需要得到源(被blitte的表面,和上文的blitter有关,可能是DirectX的专有名词)和目标(后备缓存)的DC,一旦我们全部拥有,我们把它们作为BitBlt的参数,再和妖怪定义(?sprite的确是妖怪)和我们期望的ROP(lngROP)向前,然后必须释放DC,以免电脑当掉。
(First we need to acquire the source (surface to be blitted) and destination (backbuffer) DCs. Once we have them, we feed this data into the BitBlt call, along with our sprite dimensions and the ROP we desire (lngROP). Afterward we MUST release the DCs, lest we freeze the computer)

现在你得到了它!一点魔法般的函数,能让你在你的程序或游戏中使用这些漂亮的光栅,把源代码下载下来,来看看所有不同的ROP的效果吧!
So there you have it! A few magic little functions and you can employ nifty raster operations in your games and programs. Download the source to see all of the different ROPs in action.

推荐的DirectInput Mouse处理
这里有很多你看起来比较熟悉的东东,那时因为我学到的大部分关于在DirectX7中使用鼠标是来自Lucky的DirectInput教程,我仅仅超过教程的一点点,在这个教程中你间看到如何“剪下”鼠标当它正从屏幕上离开的时候,怎样将“Hot Spots”(关于Hot Spots请参考人民邮电出版社的《Visual Basic 6.0 和Windows API 讲座第十五章597页》)添加到鼠标的指针上,把鼠标指针放到DX的表面上的确很好,它首先能帮你阅读Lucky的DirectInput教程,如果你还没有,我不想解释你在这里能学到什么。

你将注意到当你在Windows中移动鼠标时,你将会注意到你把它只能移到底部或右部,直到坐标只剩一个像素(鼠标的X,Y坐标只剩一个像素)。如果你试着去在DX中用鼠标指针的任何一部分来画你的鼠标指针(甚至是不显示的部分),当 离开屏幕/DX表面时,似乎要忽略那条线,那是因为你不能在表面外画任何东东,不可能发生这种情况。所以我们需要把我们看不到,在屏幕外的部分剪下来。

事实上,在Windows中,你若真的做了以上多提到的,将会隐藏你鼠标的“Hot Spot”,你的鼠标指针同样会停止上下左右的移动,其实,HotSpot就是你鼠标指针在Windows上的一个像素,你的鼠标指针对Windows而言,不是32×32像素,它只是一个单一的像素,Windows仅仅将其围绕这那个像素来画鼠标指针,以使你看起来像个鼠标指针,仅仅看起来像,如果你的鼠标的HotSpot大小是32×32像素,那么你将会更有可能同时点击不止一个的按钮,如果按钮都彼此很近,那么换而言之,一个HotSpot就是鼠标指针中最重要的一个像素,其余的就当是陪衬,例如箭头,钢笔的顶端,或改变窗体大小的指针的中间就是它的HotSpot。

OK,让我们试着从一个鼠标指针中得到HotSpot。
你将需要这些变量和声明:
 

'检测和剪切鼠标
Declare Function IntersectRect Lib "user32" (lpDestRect As RECT, lpSrc1Rect As RECT, lpSrc2Rect As RECT) As Long

'声明为DirectX7的变量!
Global DXMouse As New DirectX7
'The DirectInput object! DOUBLE-DUH!
Global DXMouseInput As DirectInput
'Input Device object we'll use for the mouse
Global DXMouseInputDev As DirectInputDevice
'Mouse state type
Global DXMouseState As DIMOUSESTATE


'鼠标移动的速度
Global Const MOUSE_SPEED = 1
'鼠标能移动的矩形范围
Global Mouse_Rect As RECT
'鼠标指针的X(横)坐标
Global DXMouseX As Integer
'鼠标指针的Y(纵) 坐标
Global DXMouseY As Integer
'鼠标左键是否被按下?
Global DXLeftMouseButton As Boolean
'鼠标右键是否被按下?
Global DXRightMouseButton As Boolean

'鼠标HotSpot所在的矩形On Mouse Hot Spot Rectangle
Global HotSpot_Rect As RECT
'鼠标的HotSpot X坐标
Global DXMouseHotX As Integer
'鼠标的HotSpot Y 坐标
Global DXMouseHotY
'是否显示鼠标
Global DXMouseVisible As Boolean

那些就是Lucky对源文件教程的评价,我可不想抄袭Lucky的,就像我前文所说的“我所学到的大部分是关于DirectX7鼠标是来自Lucky的DirectInput教程”,我做了少许关于变量名的改变是因为他的变量名并没有是我感觉到什么。

现在,4个新的东东分别是HotSpot_Rect,DXMouseHotX,DXMouseHotY,和DXMouseVisible。

HotSpot_Rect 就是让我们每每改变RECT时让我们直到的东东:当HotSpot在屏幕上时,他只会有一个像素那么大。
DXMouseHotX和DXMouseHotY是鼠标指针上HotSpot的X 和Y坐标:最左边的顶端被定义为(1,1)
DXMouseVisible仅仅是一个告诉我们如果我们准备将鼠标画到屏幕上的布尔值,因为有时我们并不想这样做
Now comes the Initialization Sub. Again this is the exact same thing in Lucky's Source, but I changed the variable names so we can all understand them.
现在有个初始化的过程,这和Lucky的源代码一样准确(其实就是一样),但是我改变了变量名,所以我们能够更容易的懂得。
Sub Initialize()
'如果我们不能正常的初始化,逮住错误
On Error Resume Next

'创造一个DirectInput部件
Set DXMouseInput = DXMouse.DirectInputCreate()
'将鼠标作为diMouse设备来跟踪
Set DXMouseInputDev = DXMouseInput.CreateDevice("GUID_SysMouse")
'得到鼠标输入设备的唯一性,但只有在前景模式时才有效
 
DXMouseInputDev.SetCommonDataFormat DIFORMAT_MOUSE
DXMouseInputDev.SetCooperativeLevel frmMain.hWnd, DISCL_FOREGROUND Or DISCL_EXCLUSIVE
DXMouseInputDev.Acquire

'初始化鼠标的变量
DXMouseX = 0
DXMouseY = 0
DXLeftMouseButton = False
DXRightMouseButton = False

End Sub

在此我不想解释任何事,你将必须看看Lucky的教程和源代码来为你自己解释
现在,给新的材料加点旧的材料
Sub DXMouseRefresh()
'刷新鼠标变量
'得到当前鼠标的状态
DXMouseInputDev.GetDeviceStateMouse DXMouseState

'强制跟踪不如重新跟踪
If Err.Number <> 0 Then DXMouseInputDev.Acquire

'如果失败,退出此过程
If Err.Number <> 0 Then Exit Sub
On Error GoTo 0

'根据HotSpot支持来校准鼠标X坐标
DXMouseX = DXMouseX + DXMouseState.x * MOUSE_SPEED
If DXMouseX < 0 Then DXMouseX = 0
If DXMouseX > 640 - DXMouseHotX Then DXMouseX = 640 - DXMouseHotX

'根据HotSpot支持来校准鼠标Y坐标
DXMouseY = DXMouseY + DXMouseState.y * MOUSE_SPEED
If DXMouseY < 0 Then DXMouseY = 0
If DXMouseY > 480 - DXMouseHotY Then DXMouseY = 480 - DXMouseHotY

'检测鼠标左键状态
If DXMouseState.buttons(0) <> 0 Then DXLeftMouseButton = True
If DXMouseState.buttons(0) = 0 Then DXLeftMouseButton = False

'检测鼠标右键状态
If DXMouseState.buttons(1) <> 0 Then DXRightMouseButton = True
If DXMouseState.buttons(1) = 0 Then DXRightMouseButton = False

'如果鼠标的图标移出表面外,使它不能显示
'所以,当鼠标靠近边缘时校准鼠标的位置
If DXMouseX <= 608 Then
Let Mouse_Rect.Right = 32
Else
Let Mouse_Rect.Right = 640 - DXMouseX
End If


If DXMouseY <= 448 Then
Let Mouse_Rect.Bottom = 32
Else
Let Mouse_Rect.Bottom = 480 - DXMouseY
End If

HotSpot_Rect.Left = DXMouseX
HotSpot_Rect.Top = DXMouseY
HotSpot_Rect.Right = DXMouseX + 1
HotSpot_Rect.Bottom = DXMouseY + 1

'如果时机成熟,让鼠标显示.
If DXMouseVisible = True Then
DrawMouse
End If

End Sub

 
在调用此过程前,不要忘了设置DxmouseHotX,DXMouseHotY和DXMouseVisible变量。
Wow,那个代码很是不少,你会注意到代码前面的多数部分的一半和Lucky的RefreshMouseState(刷新鼠标状态)很相似,那一部分仅仅改变了对屏幕大小和Hot Spot的支持,在Lucky的教程的源代码中,你把你的鼠标指针移出屏幕外(我不是对Lucky的语言攻击,别搞错了)。若将DXMouseHotX从屏幕的宽上减去,将DXMouseHotY从屏幕高上减去,你不得不承认你把鼠标移到屏幕外了,当DXMouseHotX正对着屏幕的右边时,鼠标会停止向右移动,对左边也是如此。

但是,那仅仅是一部分,我们还是必须“剪”下鼠标指针,否则鼠标指针就不能全部消失,那是为什么出现了If...Then...Else...的原因。那些代码会改变Mouse_Rect.Right和Mouse_Rect.Bottom,那时因为当鼠标移出屏幕时会被改变大小,改变 后的Mouse_Rect的Right和Buttom是当时正确的大小。

鼠标被剪了之后,我们若想移动HotSpot_Rect的位置,我们可以改变他的Left,Top,Right和Bottom来实现。

如果你向让鼠标可见,最后两行就是检查鼠标的可见,如果可见,把它画出来

这是一个小小的过程,而且它使用BltFast来在屏幕上化鼠标,使用正确的剪切方法来剪切Mouse_Rect的大小,我不想太过于详细的解说怎样BltFast才算是完成,你最好在Lucky的教程里找找。


Public Sub DrawMouse()
SurfBack.BltFast DXMouseX, DXMouseY, SurfMain, Mouse_Rect, DDBLTFAST_SRCCOLORKEY Or DDBLTFAST_WAIT
End Sub


SurfBack是我的后备缓存,SurfMain是不可见的表面,SurfMain用来存储鼠标指针图片

 


特别感谢Lucky和他的DirectX教程

编程愉快,

0 0

相关博文

我的热门文章

img
取 消
img