零基础游戏编程:如何用Direct3D来处理俄罗斯方块的显示问题?

发布时间:19-12-0212:44

废话不多说,这一节的目的,是把游戏显示到我们前面创建的Windows窗口中。用脑子一想,不是很简单吗?不就是把图片显示在窗口里面吗?这样想没错,但是程序员不应该这样想。因为程序员知道,电脑是笨蛋,我们觉得容易,电脑可能完全不明白我们要说什么。所以,想要显示游戏的话,还是要一步一个脚印慢慢走。

一、游戏是如何显示的?

用过电脑的你可能觉得,显示一张图片不是很容易吗?在游戏中不应该也是很容易的吗?其实,这是错觉。

我们在使用电脑的时候,是有大量应用软件帮助我们方便使用的。比方说显示一张照片,我们之所以认为它简单,主要是因为应用软件已经把复杂的图片显示处理过了,我们只需要双击图片就可以了。然而,现在我们要自己写游戏,游戏就是一个程序,是不能借助其他软件的,也就是说,我们要自己想办法把图片显示在窗口中。

再说清楚一点,我们需要直接和显示设备打交道,直接给显示设备发送指令,让它把我们希望的东西显示出来。

在开头的几节课中我们已经讲过,Windows系统的封装性很强,它已经把我们和硬件分割开了。Windows为了解决游戏编程的问题,专门开发了DirectX,好让我们游戏编程的时候更简单。

这个,我们在前面已经说过了,在这里,我们专门“科普化”地讲一下,什么是DirectX。

像DirectX用到的COM技术我们就不扯了,毕竟太过于专业化,我们作为零基础的学生,只要理解DirectX的工作原理就可以了。

DirectX是组件对象模型(COM),是一种分离于代码本身的东西。从第1节课到现在,为了让大家好理解,我都把函数比喻成“芯片”。实际上,函数是程序本身的内容,是程序员自己写的内容,是整个代码的一部分,比喻成“芯片”是有点夸张成分的。而COM技术,完全就是一种芯片,因为它独立于代码。不管是任何人还是任何语言的代码,都能够使用这个芯片。我们再刨深一点点,COM技术的这个内容,是提前编译过的东西,相当于是一个半成品了。

如果仅仅是这一个功能,COM技术还不会这么厉害,这个技术的厉害之处,是它异常强大的版本兼容性

我承认,版本兼容性这个词是我造的,但是,想要把如此复杂的概念说给零基础的读者听,我只能自己造词了。

什么是版本兼容性呢?就是这个技术,能兼容第1代到当前这一代的所有功能,包括相同功能的不同版本。你可能不理解,我们举个例子。

比方说,停车场为了智能化管理,给每个车主发放了管理卡,靠这个卡,车主就能很方便进入停车场。一开始还好,可是后来发现,这个智能化管理系统有漏洞,容易被人钻空子,所以,停车场升级了管理系统,也就是2.0系统。由于升级了系统,导致第一代的卡无法使用了,所以,他们主动找车子换卡。可是,依旧有很多车主无法找到。当某一天车主回来的时候,发现自己的卡无法使用的时候,又是一通麻烦。

而这个版本兼容性的意思是说,不管这个管理系统升级多少次,任何版本下的卡片都是可以使用的。

版本兼容性曾经是编程的难题,因为我们真的很难保留不同版本下相同的处理方法。比方说简单的1+1程序,以前版本是那样算的,而现在版本是这样算的,势必就会造成,以前写的程序到了现在就无法使用了。

而微软想要让Windows变得和DOS一样对游戏编程友好,这个难题是不得不面对的。而微软果然不负众望,不愧于当时最厉害的计算机公司,不愧于当时最顶级的计算机技术,直接创造了COM技术。有了这个技术后,版本兼容性的问题就解决了。

现在的的DirectX已经越来越强大了,当前的版本是DirectX 12,不过,DirectX 10.0的功能已经非常非常强大了,而我们编写这个俄罗斯方块小游戏,使用DirectX 9.0已经绰绰有余了。除非是编写3A游戏,我们真的不必要用到DirectX 12,因为DirectX有版本兼容性,即使是在DirectX 12的环境下,DirectX 1.0写出来的游戏,依旧是可以运行的。

那么,DirectX的工作原理是什么呢?

其实非常简单,DirectX就是将所有的硬件模拟成了一个一个的接口,我们操作这些接口就相当于直接操作那些硬件了。比方说显卡、声卡、键盘和鼠标、网络等等,都已经被DirectX模拟成了一个一个的接口了。比方说,关于显示的接口统称为DirectX Graphics,关于声卡的接口统称为DirectX Audio。也就是说,我们通过这些DirectX的接口,就可以直接操控硬件了。

二、推荐使用Direct3D

我们现在要把俄罗斯方块这个游戏显示在窗口中,用到的当然是DirectX Graphics,而DirectX Graphics是一个统称,里面包含的东西非常多,有关于显示方面的接口都包含在里面。我们用不了那么多功能,只需要用里面Direct3D中的接口就行了。实际上,DirectX Graphics中是有DirectDraw这一组接口的,这组接口是专门用于二维游戏开发的,其中的功能其实非常强大,足够我们写俄罗斯方块了。可我并不建议用它,因为游戏的发展方向是3D,DirectDraw已经快被淘汰了。之所以还有这些接口,就是为了“版本兼容性”而已。而且,Direct3D中的功能非常多,它是处理三维游戏的,比二维游戏复杂多了,用这么一个强大的组件来处理二维游戏,简直就是小儿科一样。所以,我们就用Direct3D来写俄罗斯方块游戏吧。

三、将两张图片素材载入

由于DirectX和一般的功能模块不一样,它的使用过程也是不太一样的。不过,所有的DirectX的组件使用过程是类似的,只要懂得了任何一个,其他的组件也就能很快学会了。我们是打算用Direct3D来处理显示问题的,所以就从它先入手吧。

首先的第一步,是让编译器知道,我们的这个程序将要用到Direct3D这个组件。通过前面的基础知识,你应该知道,告诉编译器是通过包含文件来告诉的,也就是添加下面的几个包含文件到std.h这个文件中:

#include <d3d9.h>

#include <d3d9types.h>

#include <d3dx9tex.h>

第一个包含文件中,是所有关于Direct3D的接口内容;第二个包含文件中,是Direct3D中需要用到的大量类型或者是结构;第三个包含文件我们可以不包含,因为在我们的俄罗斯方块游戏中并没有用到纹理方面的接口,但是我建议,把它包含进去。其实,这三个文件相当于连体婴,要包含就一起包含了,Direct3D的程序中,基本上都要用到这些内容。我们的俄罗斯方块是因为简单,才没有用纹理功能。

还有,Direct3D一般被称为D3D,所以,下面就简称D3D了。

其它的内容只需要包含一个头文件就可以了,比如我们要用Windows的功能,只需要把Windows.h这个文件包含进来就行了,可是,DirectX是特殊的组件,仅仅包含一个头文件是无法真正使用它的。想要真正使用它,还需要添加链接库的支持,具体的方法,是在“项目-属性”中找到配置属性,能看到其中有链接器的设置,在下一级目录中找到“输入”,就能看到右边看到一个附加依赖项。我们需要把DirectX的库文件添加到这里面来。

单击附加依赖项右边的省略号,就能出现下面的对话框,在上面的输入框中把D3D的库文件添加进去就可以了。这里一共能看到6个库文件,也是俄罗斯方块全部需要添加的库文件。而对于D3D来讲,我们只需要添加前面三个库文件,也就是dxguid.lib、d3d9.lib和d3dx9.lib。

有了这个基础动作之后,我们就可以添加代码了。首先是在Tetris.h文件中添加一个成员函数,用于D3D这个组件的初始化工作:

void CreateD3D9(void);

由于初始化完成后,数据都保存在成员变量中,我们完全不需要函数的返回值和函数的参数。

一般而言,我们把D3D的初始化过程称呼为D3D的创建过程,也就是说,我们不是直接使用D3D组件接口的,而是要在我们自己的程序中(也就是俄罗斯方块程序中)重新创建一个特属于俄罗斯方块的D3D组件。在创建D3D组件的过程中,需要用到一些变量,而这些变量非常重要,干脆就写到成员变量中去吧。我们直接在Windows的那些成员变量后面添加就可以了,记住,成员变量的属性是private。D3D有关的成员变量如下:

//d3d9变量

IDirect3D9 *D3D9;//主D3D9

IDirect3DDevice9 *D3D9Device;//D3D9设备指针

D3DLOCKED_RECT RC9;//D3D9表面锁定得到的指针,可理解为屏幕直接操作

IDirect3DSurface9 *D3D9Surface;//D3D9主表面指针

IDirect3DSurface9 *BGSurface;//背景表面

IDirect3DSurface9 *FKSurface;//小方块表面

int Depth;

总共7个成员变量,具体什么用,我们后面慢慢讲解。

由于是C++类的成员变量,在类的创建过程中(也就是WinMain函数中创建Tetris类的语句中,就是那个用了new的语句),这些成员变量的内存空间是随机分配的,也就是说,这些成员变量的内存空间中会有一些不可预测的数据。为了防止这些数据出问题,在类的创建过程中,需要把这些成员变量数据清零。我们可以单独写一个初始化清零的成员函数,但是没这个必要,因为C++类是有构造函数的。想要清零,只需要把赋值语句写到构造函数中就可以了:

返回顶部