关闭

DirectX学习手记(-)

标签: null游戏编程windowsapplicationcallback
2408人阅读 评论(2) 收藏 举报
分类:
					DirectX学习手记(-)
			       		HappyFire 2002/8/2

	题记:玩了很多的游戏,不禁萌发了自己做游戏的念头,于是7月份从网上收集了N多资料。7月20几号在家开始了闭关式的学习,
一直到昨天,我的第一个地图类封装完毕,并乘胜追击到凌晨3点,做好了地图编辑器的0.9版。早上起来觉得有点累(才睡了几个小时,
呵呵),于是把这些天的学习经历回忆一下,权当是休息。这个过程是一个从对游戏编程一无所知到略识其理得过程,我想对于像我这样
的初学者应该有所帮助吧,至少可以少走些弯路。


一. 初识DirectX
	在放假之前,我拜读了金点时空softboy的《圣剑英雄传--英雄救美制作手札》一文。此文简述了RPG游戏的基本原理,通俗易懂,
看完此文使人觉得游戏制作并非遥不可及,强烈推荐!但此文并没有提到有关DirectX编程的方法。所以做游戏,还要先过DirectX关。
于是我花了一天的时间,通读了老王翻译的《DirectX中文手册》,并分析了几个例程,在加上一个多星期的编程经历,总算有了一点感觉。

1.基础中的基础
	既然我们讨论的是Windows平台下的游戏编程,当然要对Windows编程有所了解。我们使用的是Win32 SDK(API)编程。至于为什么
不用MFC? MFC是微软对API的封装(其实何止是封装),它适用于开发有统一程序架构和界面的大型商业软件,但其复杂的机制影响了速度,
这对于游戏编程是难以忍受的,因此基于C/C++的Win32API是我们必然的选择。(也听说有用VB开发的游戏,但那毕竟不是主流)。那么对
于Win32 API要掌握到什么程度呢?我想只要知道WinMain、窗口类、消息循环、窗口回调函数,能编个简单的HelloWorld就行了,至于GDI
只要稍作了解,而Windows的控件基本上用不到。这方面的文章很多,找几篇看看就行了。当然游戏编程不同于一般的编程,所以这其中也
有些变化,在下面我会提到的。

2.第一个DirectDraw例程
	学DirectX当然要先学习它最基本也是最重要的部分DirectDraw。DirectDraw是微软提供的Windows平台下高效的与硬件无关的图形
引擎,它提供对显存的直接访问...好了,闲话少说,来看看我的第一个例程(这是在手册上的第一个例程的基础上修改而成的):

//-----------------------------------
//工程:helloDX
//文件:helloDX.cpp
//内容:第一个DirectDraw应用程序
//-----------------------------------

#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>

//全局变量
LPDIRECTDRAW lpDD; //DirectDraw对象
LPDIRECTDRAWSURFACE lpDDSPrimary ; //DirectDraw主表面
LPDIRECTDRAWSURFACE lpDDSBack ; //后台缓冲表面

char szMsg1[]="我的第一个DirectDraw程序" ;
char szMsg2[]="按ESC退出" ;

BOOL bActive = TRUE ;
HWND hwnd ;

//函数声明
LRESULT CALLBACK WinProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) ;
BOOL InitWindow ( HINSTANCE hInstance, int nCmdShow ) ;
BOOL InitDDraw ( void ) ;	//初始化DirectX
void FreeDDraw ( void ) ;	//释放DirectX对象
void MainLoop ( void ) ;	//游戏主循环

//-------------------------------------------------------
//函数:WinMain()
//功能:Win32应用程序入口函数.进行初始化工作,处理消息循环
//-------------------------------------------------------
int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,
					LPSTR lpCmdLine, int nCmdShow )
{
	MSG msg ;

	//初始化主窗口
	if ( !InitWindow(hInstance,nCmdShow) )
		return FALSE ;

	//初始化DirectDraw环境,并实现DirectDraw功能
	if ( !InitDDraw() ){
		MessageBox ( GetActiveWindow(), "初始化DirectDraw过程中出错!", "Error", MB_OK ) ;
		FreeDDraw () ;
		DestroyWindow ( GetActiveWindow() ) ;
		return FALSE ;
	}

	while(1){
		if ( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ){	//如果有消息就处理消息
			if ( !GetMessage(&msg, NULL, 0, 0 ) ) 
				return msg.wParam;
			TranslateMessage ( &msg ); 
			DispatchMessage ( &msg );
		}
		else if(bActive){	//如果程序处于激活状态,进入游戏主循环
			
			MainLoop(); 
		}
		//等待消息
		else WaitMessage();
	}
	return msg.wParam ;
}

//--------------------------------------
//函数:InitWindow()
//功能:创建主窗口
//--------------------------------------
static BOOL InitWindow ( HINSTANCE hInstance, int nCmdShow )
{
//	HWND hwnd ; //窗口句柄
	WNDCLASS wc ; //窗口类结构

	//填充窗口类结构
	wc.style = 0 ;
	wc.lpfnWndProc = WinProc ;
	wc.cbClsExtra = 0 ;
	wc.cbWndExtra = 0 ;
	wc.hInstance = hInstance ;
	wc.hIcon = LoadIcon ( hInstance, IDI_APPLICATION ) ;
	wc.hCursor = LoadCursor ( NULL, IDC_ARROW ) ;
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH) ;
	wc.lpszMenuName = NULL ;
	wc.lpszClassName = "dxHello" ;

	//注册窗口类
	RegisterClass ( &wc ) ;
	//创建主窗口
	hwnd = CreateWindowEx ( 0, "dxHello", "", WS_POPUP, 
		0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
		NULL, NULL, hInstance, NULL ) ;

	if ( !hwnd )
		return FALSE ;
	ShowWindow ( hwnd, nCmdShow ) ;
	UpdateWindow ( hwnd ) ;

	return TRUE ;
}

//--------------------------------------------------
//函数:WinProc()
//功能:处理主窗口消息
//--------------------------------------------------
LRESULT CALLBACK WinProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	
	
	switch ( message ){
	
	case WM_ACTIVATEAPP:
		bActive = wParam;
		break;
	
	case WM_KEYDOWN: //击键消息
		switch ( wParam ){
		case VK_ESCAPE:
			PostMessage ( hWnd, WM_CLOSE, 0, 0 ) ;
			break ;
		}
		break ;

	case WM_SETCURSOR:
		SetCursor ( NULL ) ;
		return TRUE ;

	case WM_DESTROY: //退出消息
		FreeDDraw () ;
		PostQuitMessage ( 0 ) ;
		break ;
	}
	
	//调用缺省消息处理过程
	return DefWindowProc ( hWnd, message, wParam, lParam ) ;
}

//----------------------------------------------------------------
//函数: InitDDraw()
//功能: 初始化DirectDraw环境并实现其功能.包括:创建DirectDraw对象,
//设置显示模式,创建主表面
//----------------------------------------------------------------
BOOL InitDDraw (void)
{
	DDSURFACEDESC ddsd ; //表面描述
	DDSCAPS ddscaps ;
//	HDC hdc ; //设备环境句柄

	//创建DirectDraw对象
	if ( DirectDrawCreate(NULL,&lpDD,NULL)!=DD_OK )
		return FALSE ;
	//取得独占和全屏模式
	if ( lpDD->SetCooperativeLevel(GetActiveWindow(),DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN)!=DD_OK )
		return FALSE ;
	//设置显示模式
	if ( lpDD->SetDisplayMode(640,480,8)!=DD_OK)
		return FALSE ;
	//填充主表面信息
	ddsd.dwSize = sizeof(ddsd) ;
	ddsd.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT ;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX ;
	ddsd.dwBackBufferCount = 1 ;
	//创建主表面对象
	if ( lpDD->CreateSurface(&ddsd,&lpDDSPrimary,NULL)!=DD_OK )
		return FALSE ;
	//提取后台缓存表面指针
	ddscaps.dwCaps = DDSCAPS_BACKBUFFER ;
	if ( lpDDSPrimary->GetAttachedSurface ( &ddscaps, &lpDDSBack )!=DD_OK )
		return FALSE ;

	
	

	return TRUE ;
}

//-------------------------------------------------------
//函数:FreeDDraw()
//功能:释放所有的DirectDraw对象
//-------------------------------------------------------
void FreeDDraw(void)
{
	if ( lpDD!=NULL ){
		if ( lpDDSPrimary!=NULL ){
			lpDDSPrimary->Release() ;
			lpDDSPrimary = NULL ;
		}
		lpDD->Release() ;
		lpDD = NULL ;
	}
}


//-------------------------------------------------------
//函数:MainLoop()
//功能:游戏主循环
//-------------------------------------------------------
void MainLoop (void)
{
	static int i = 0;
	HDC	hdc ;

	//后台缓冲表面上的操作
	if ( lpDDSBack->GetDC(&hdc)==DD_OK ){
		SetBkColor ( hdc, RGB(0+i,255-i,0+i) ) ;
		SetTextColor ( hdc, RGB( 0+i,0+i,255-i) ) ;
		TextOut ( hdc, 220, 200, szMsg1, lstrlen(szMsg1) ) ;
		TextOut ( hdc, 280, 220, szMsg2, lstrlen(szMsg2) ) ;
		lpDDSBack->ReleaseDC (hdc) ;
		i+=10 ;
		if ( i>255)		i=0 ;
	}
	if (lpDDSPrimary->Flip(NULL,0)!=DD_OK){		//一经Flip,两个表面的指针互换!lpDDSPrimary指向后台表面,所以
		FreeDDraw() ;				//你就看到刚才在后台表面上写的字了,而lpDDSBack指向了原来的前台主表面
		PostQuitMessage(0);			//把它掉到后台进行操作	
	
	}

}

3.例程详解

初次看这个程序,可能会觉得有点复杂,先让我们看看这个程序能做什么。在VC++6.0中建一个名helloDX
的Win32 Application工程,新建一个空白的cpp文件helloDX.cpp,把这断代码粘贴上去。先别急着运行,还要把DirectDraw库添加到工程中。
方法是在菜单project中选Setting,打开project setting对话框,在右面的link选择卡里面的对象/库模块里添加ddraw.lib,确定就行了。
下面运行程序,你看到了什么?

我们先分析一下这个程序的结构,首先它是一个典型的Win32 API程序,有WinMain和WinProc两大块组成。但稍微有点不同的是,它对于消息
循环的处理采用了Peek方式:
while(1){
		if ( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ){	//如果有消息就处理消息
			if ( !GetMessage(&msg, NULL, 0, 0 ) ) 
				return msg.wParam;
			TranslateMessage ( &msg ); 
			DispatchMessage ( &msg );
		}
		else if(bActive){	//如果程序处于激活状态,进入游戏主循环
			
			MainLoop(); 
		}
		//等待消息
		else WaitMessage();
	}
函数PeekMessage判断消息队列中是否有消息,如果有就处理消息,如果没有再判断程序是否激活,如果激活就进入游戏主循环,如果没有激
活就什么都不做,等待消息产生。这个循环不断进行下去,直到窗口收到WM_QUIT消息。在进入这个循环之前,我们初始化了窗口(注册窗口
类,建立窗口),注意在建立窗口时我们指定窗口样式为WS_POPUP,这样就不会出现窗口边框标题栏什么的了。而且窗口的大小指定为显示
器屏幕的大小,因为我们要实现的是全屏的DirectDraw编程。初始化窗口之后我们初始化了DirectDraw环境并建立了DirectDraw对象(下面
将详细说明),这样程序就可以使用DirectDraw进行图形输出了。

下面介绍我们的主角DirectDraw。你一定注意到了程序开头的全局变量声明:
LPDIRECTDRAW lpDD; //DirectDraw对象
LPDIRECTDRAWSURFACE lpDDSPrimary ; //DirectDraw主表面
LPDIRECTDRAWSURFACE lpDDSBack ; //后台缓冲表面
这个LPDIRECTDRAW就是DirectDraw对象的类型了(我们姑且把它作为一个类型好了,其实它是一个COM接口,这个我也不怎么懂:)),程序中
至少有一个LPDIRECTDRAW类型的对象,它是建立其他对象的基础,两个LPDIRECTDRAWSURFACE类型的对象被称为是DirectDraw表面。lpDD对应
着你的显卡,lpDDSPrimary,lpDDSBack这样的对象对应着显存(或主存)中的一块区域。建立DirectDraw表面的时候可以指定它的类型,这里
的lpDDSPrimary是主表面,也是前台你能看到的表面(它被显示到屏幕上),lpDDSBack是后台缓冲表面,它是附带于lpDDSPrimary的。当然
也可以创建不带后台缓冲表面的主表面(手册的第一个例程就是),也可以创建带多个后台缓冲的主表面。在这里我介绍最最常用的带一个缓
冲的主表面。建立主表面之前,我们必须先建立DirectDraw对象,就是这里的lpDD,请看InitDDraw函数的内容。函数DirectDrawCreate(NULL,
&lpDD,NULL)创建了一个LPDIRECTDRAW对象,这个函数的具体用法可以查相关资料我就不在赘述了。建立lpDD后还要设置DirectDraw的环境,
具体就是这里的取得独占和全屏模式和设置显示模式,注意SetDisplayMode的第三个参数8不是颜色8色,而是色深度为8位,即256色。然后是
建立主表面,先要填写一个DDSURFACEDESC表面描述结构体,
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX 指出要建立的表面是主表面,且能够filp(后面再说),
是带缓冲的复杂表面; ddsd.dwBackBufferCount = 1 指明缓冲表面数为1。一般都是这样填的,具体的可以查资料。填好后就可以通过lpDD
的CreateSurface方法创建主表面了。然后是后台表面...这里的代码怎么有点不同?我们不是通过填写 DDSURFACEDESC结构体,在通过
CreateSurface创建后台表面的!因为后台缓冲表面是同主表面联系在一起的,主表面创建好了它就自动创建好了,我们要作的是获取它的指针
以便去应用它!使用lpDDSPrimary->GetAttachedSurface,明白这里的AttachedSurface了吧!

至此所有的表面创建完毕,可以用来图形操作了。(顺便提一下,还有一种表面叫离屏表面,它上面的东西不会被直接显示到屏幕上,它的
作用是存放图像,然后把图像复制到后台表面上,即Blt,呵呵,以后再说...)

我们在什么地方用DirectDraw呢?当然是在游戏模块中,在这个程序中是MainLoop(MainLoop每个游戏都有,这是最简单的了)。在MainLoop
里面我们先是获取后台表面的设备环境句柄,然后用Windows的GDI写了几个字。再然后运用主表面的Flip方法,把前后台表面的指针互换,
这样你就看到刚才写的字了。由于MainLoop是不断循环执行的,而且我们通过静态变量i改变了字符的颜色,因此你看到的就是不断变色的字符
了。关于Flip一定要理解,其实就是指针互换,因此非常快!经过Flip,lpDDSPrimary指向后台表面,所以你就看到刚才在后台表面上写的字
了,而lpDDSBack指向了原来的前台主表面把它掉到后台进行操作(换个颜色在写一遍),然后再Flip回去,你再看到的字颜色就变了。明白
了吧? 再多说几句,我们设置了lpDD为全屏模式,只有在这种模式下才能使用Flip,如果是窗口模式就只能Blt(Blt就是复制啊)了。

在程序结束之前不要忘了释放DirectDraw对象和表面对象,这里放在FreeDDraw()里面的,注意我没有释放后台表面,因为它随着主表面释放
了。




0
0
查看评论

综合评价书籍4--MATLAB GUI设计学习手记

引用格式:罗华飞.MATLAB GUI设计学习手记[M].北京:北京航空航天大学出版社,2014. 相关资源下载(配套光盘源程序):http://download.csdn.net/detail/snicolashe/9625615
  • snicolashe
  • snicolashe
  • 2016-09-08 23:52
  • 819

说说本人学习DirectX之路

决定不继续学DIRECTX/DXUT了,所以在这里总结一下,留一些经验,留一些遗憾...
  • davied9
  • davied9
  • 2016-07-13 09:41
  • 2648

DirectX学习手记(二)

DirectX学习手记(二) happyfire 2002/8/11此回说说怎样用DirectDraw向表面上贴图,包括创建离屏表面,设置调色板,载入位图到表面,透明色,页面丢失等。二. 用DirectDraw贴图 先让我们回忆一下上一回的内容。为了初始化DirectDraw我们首先创建了一个D...
  • n5
  • n5
  • 2002-11-28 09:13
  • 2543

Directx :一些学习网站 和 博客

1: http://www.cnblogs.com/mikewolf2002/category/351011.html  这个是关于dx11的教程 很棒 每个实例都有yuanma
  • cq361106306
  • cq361106306
  • 2014-09-23 13:25
  • 1549

DirectX11与游戏编程学习路线自拟

最近学习了龙书(DX11版本)简要的翻了翻,对于初学者还是有很大的难度。 因此,制定了下关于学习游戏编程以及游戏引擎的路线。学习使用游戏引擎学习游戏引擎之前需要了解一下如何使用游戏引擎我打算从Unity3d入手,先了解游戏引擎所具备的功能,对于游戏引擎有初步的了解。 目标:实现demo,可以做出...
  • qq_23225317
  • qq_23225317
  • 2016-12-02 18:46
  • 1455

OPENGL学习笔记(六)

如何使用OPENGL分配顶点缓存对象,并绘制一个白色的三角形 添加Mesh类: class Vertex {   public:      Vertex(const glm::vec3& pos)   ...
  • u012181898
  • u012181898
  • 2016-11-06 06:54
  • 515

两天学会DirectX 3D之入门

环境配置以及背景知识环境 Windows 8.1 64bit VS2013 Microsoft DirectX SDK (June 2010) NVDIA Geforce GT755环境的配置参考 VS2008整合DirectX9.0开发环境一些背景知识 DirectX的和应用层与硬件层的...
  • qp120291570
  • qp120291570
  • 2015-07-05 11:32
  • 2848

FPGA笔记(二)

#对FPGA的一些理解 经过一周对FPGA的了解,对Verilog语法已经有一定的了解了,自己也写了些程序,但是感觉自己写出来的硬件描述语言程序不是那个味儿,就像一段式状态机跟三段式状态机相比一样,在我这所有的逻辑都放在一个always进程中,虽然目的都能达到,但是这么下去,越大的运用中会带来越大的...
  • Struggle_For_M
  • Struggle_For_M
  • 2016-05-19 14:26
  • 615

Directx9.0 学习教程1 - 第一个程序

在图书馆选了N本书,还是觉得直接讲代码的书更适合我,一个完整的列子加上些许注释,比那些一上来就是一大段一大段的文字阐述的教程容易懂的多。  因此我的笔记也开始用这种方式记录。 写在开头:  dx(我对directx简称) dx的版本主要有9 和11这两种系列。 API和功能差异还...
  • cq361106306
  • cq361106306
  • 2014-09-25 13:01
  • 14569

DirectX11 学习笔记1 - 第一个程序

为了加快学习速度,和使程序更加容易理解。  我把sampler tutorial里面的一个例子 的固定代码和经常改动的代码经过简单的类的封装了一下。 以后学习的时候就可以只在另一个文件写render渲染代码和fx着色器代码了 D3DBase.h  基本框架代码 #inclu...
  • cq361106306
  • cq361106306
  • 2014-10-01 22:20
  • 5867
    个人资料
    • 访问:716613次
    • 积分:9395
    • 等级:
    • 排名:第2314名
    • 原创:212篇
    • 转载:12篇
    • 译文:0篇
    • 评论:339条
    博主链接
    最新评论
    同道中人