最近找到了一个比较好玩的东西,日本DirectX的教材,有很多本,最简单的最符合新手的「ゲームプログラミング入門 第2版」(游戏编程入门 第2版),没有找到资源,倒是找到了也算是DirectX的新手教材「はじめての3Dゲーム」(第一个3D游戏),那就有意思了,因为据说日本人的代码风格应该算是很严谨规范的,看看好不好玩。(这篇文章是我刚下到资源后看了会写的,过去快2个月了,共192页的PDF也看到175多页了,其实相当于看完了,后面全是讲通信对战什么的,没有代码,没有案例。给我的印象也就是日本人也不是很严谨呀。)
PDF加源码:下载地址
书比较老,估计会跟《Programming.Role.Playing.Games》一样,有些代码估计都不能用,先看看再说。第1章千篇一律都是DirectX的环境搭建,而且还是VC6++的版本搭建,这个就不多说了。题外话,书本作者说是为了让新手快速入门,所以有些地方讲错就不要在意,还有就是要多会使用复制和粘贴代码功能。我这边也说一句,我不会去翻译书本,第一费时间,第二语文功底太差(曾经做过游戏汉化,被人吐槽过)。
第2章就有意思了,用DirectX在应用程序中显示一个坦克的图片,不像其他书本,一开始只介绍Window初始化,或者DirectX初始化,这些其实一开始还是比较繁琐的枯燥的工作,而且还是比较难理解的,有些人死脑筋就会卡在这很久然后就放弃,我还真见到过几个这样的人。
其实只要记住Window初始化就这么几步,注册窗口类(RegisterClassEx),创建窗口对象(CreateWindowEx),显示窗口(ShowWindow),消息循环。消息循环中注意游戏开发不要用GetMessage方法,要用PeekMessage方法,是因为GetMessage方法是阻塞函数。
其实DX9的初始化相对于DX10/11来说,简直不要太简单,就这么几步:创建一个IDirect3D9指针对象,再根据IDirect3D9指针对象创建IDirect3DDevice9指针对象,初始化就完成了。
书本源码:
#include <windows.h>
#include <d3dx9.h>
LPDIRECT3D9 pD3d;
LPDIRECT3DDEVICE9 pDevice;
LPDIRECT3DTEXTURE9 pTexture;
LPD3DXSPRITE pSprite;
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HRESULT InitD3d(HWND);
VOID DrawSprite();
//
//INT WINAPI WinMain( HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR szStr,INT iCmdShow)
//アプリケーションのエントリー関数
// 程序的入口函数
INT WINAPI WinMain( HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR szStr,INT iCmdShow)
{
HWND hWnd=NULL;
MSG msg;
// ウィンドウの初期化
// Window的初始化
static char szAppName[] = "Chapter2" ;
WNDCLASSEX wndclass ;
wndclass.cbSize = sizeof (wndclass) ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInst ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
RegisterClassEx (&wndclass) ;
hWnd = CreateWindow (szAppName,szAppName,WS_OVERLAPPEDWINDOW,
0,0,640,480,NULL,NULL,hInst,NULL) ;
ShowWindow (hWnd,SW_SHOW) ;
UpdateWindow (hWnd) ;
// ダイレクト3Dの初期化関数を呼ぶ
// 调用Direct3D初始化函数
if(FAILED(InitD3d(hWnd)))
{
return 0;
}
// メッセージループ
// 消息循环
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
DrawSprite();
}
}
return (INT)msg.wParam ;
}
//
//LRESULT CALLBACK WndProc(HWND hWnd,UINT iMsg,WPARAM wParam,LPARAM lParam)
// ウィンドウプロシージャ関数
// 窗口回调函数,用于处理消息
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMsg,WPARAM wParam,LPARAM lParam)
{
switch(iMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
switch((CHAR)wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
break;
}
return DefWindowProc (hWnd, iMsg, wParam, lParam) ;
}
//
//HRESULT InitD3d(HWND hWnd)
//ダイレクト3Dの初期化関数
// Direct3D初始化函数
HRESULT InitD3d(HWND hWnd)
{
// 「Direct3D」オブジェクトの作成
// 创建Direct3D对象
if( NULL == ( pD3d = Direct3DCreate9( D3D_SDK_VERSION ) ) )
{
MessageBox(0,"Direct3Dの作成に失敗しました","",MB_OK);
return E_FAIL;
}
// 「DIRECT3Dデバイス」オブジェクトの作成
// 创建Direct3D设备对象
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.BackBufferFormat =D3DFMT_UNKNOWN;
d3dpp.BackBufferCount=1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed = TRUE;
if( FAILED( pD3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_MIXED_VERTEXPROCESSING,
&d3dpp, &pDevice ) ) )
{
MessageBox(0,"HALモードでDIRECT3Dデバイスを作成できません\nREFモードで再試行します",NULL,MB_OK);
if( FAILED( pD3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hWnd,
D3DCREATE_MIXED_VERTEXPROCESSING,
&d3dpp, &pDevice ) ) )
{
MessageBox(0,"DIRECT3Dデバイスの作成に失敗しました",NULL,MB_OK);
return E_FAIL;
}
}
//「テクスチャオブジェクト」の作成
// 创建texture
if(FAILED(D3DXCreateTextureFromFileEx(pDevice,"Sprite.bmp",100,100,0,0, D3DFMT_UNKNOWN,
D3DPOOL_DEFAULT,D3DX_FILTER_NONE,D3DX_DEFAULT,
0xff000000,NULL,NULL,&pTexture)))
{
MessageBox(0,"テクスチャの作成に失敗しました","",MB_OK);
return E_FAIL;
}
// 「スプライトオブジェクト」の作成
// 创建sprite
if(FAILED(D3DXCreateSprite(pDevice,&pSprite)))
{
MessageBox(0,"スプライトの作成に失敗しました","",MB_OK);
return E_FAIL;
}
return S_OK;
}
//
//VOID DrawSprite()
//スプライトを描画する関数
// 绘制Sprite函数
VOID DrawSprite()
{
pDevice->Clear( 0, NULL, D3DCLEAR_TARGET,D3DCOLOR_XRGB(100,100,100), 1.0f, 0 );
if( SUCCEEDED( pDevice->BeginScene() ) )
{
RECT rect={0,0,100,100};
D3DXVECTOR2 vec2Scale(1.0,1.0);
D3DXVECTOR2 vec2RotationCenter(1.0,1.0);
D3DXVECTOR2 vec2Position(270,180);
pSprite->Draw(pTexture,&rect,&vec2Scale,&vec2RotationCenter,0,&vec2Position,0xffffffff);
pDevice->EndScene();
}
pDevice->Present( NULL, NULL, NULL, NULL );
}
自己修改了下一些老旧的API,主要是DrawSprite函数中的接口,可以编译运行,运行环境是DX9加VS2017。ID3DXSprite这个之前没怎么用过,也就搜了一下,了解了一下,具体的可以百度和Google一下。PS:最近在看另外一本书,书上说BeginScene和EndScene这两个方法是用在3D上渲染的,也就是底层调用DrawPrimitive函数绘制图元才需要的,也就是点,线和三角面。自己测了一下,好像是这样的,这个案例我也测过,必须要有BeginScene和EndScene这两个方法,也就是说ID3DXSprite底层应该也是用DrawPrimitive函数绘制图元来实现的,正不正确就不清楚了。
自己修改后的代码:
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "legacy_stdio_definitions.lib")
#pragma comment(lib, "DxErr.lib")
#define WIN32_LEAN_AND_MEAN
#define SAFE_RELEASE(p) { if(p) { p->Release(); p = nullptr; } }
#include <Windows.h>
#include <d3dx9.h>
#include <DxErr.h>
#define DEFAULT_WINDOW_TITLE TEXT("DirectX_09_Chapter02")
#define DEFAULT_SCREEN_WIDTH 640
#define DEFAULT_SCREEN_HEIGHT 480
IDirect3D9* d3d9;
IDirect3DDevice9* device;
ID3DXSprite* sprite;
IDirect3DTexture9* texture;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT InitD3D(HWND hwnd);
HRESULT InitSprite();
void Render();
void RenderSprite();
void Release();
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wc;
wc.cbClsExtra = 0;
wc.cbSize = sizeof(wc);
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = wc.hIcon;
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = DEFAULT_WINDOW_TITLE;
wc.lpszMenuName = nullptr;
wc.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, TEXT("RegisterClassEx function failed"), TEXT("Error"), MB_OK | MB_ICONERROR);
return 0;
}
auto hwnd = CreateWindowEx(WS_EX_APPWINDOW,
wc.lpszClassName,
wc.lpszClassName,
WS_OVERLAPPEDWINDOW,
0, 0,
DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT,
NULL,
NULL,
hInstance,
nullptr);
if (!hwnd)
{
MessageBox(NULL, TEXT("CreateWindowEx function failed"), TEXT("Error"), MB_OK | MB_ICONERROR);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
auto result = InitD3D(hwnd);
if (FAILED(result))
return 0;
MSG msg;
memset(&msg, 0, sizeof(msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Render();
}
Release();
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
{
switch ((char)wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
}
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
HRESULT InitD3D(HWND hwnd)
{
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
if (!d3d9)
{
MessageBox(NULL, TEXT("Direct3DCreate9 function failed"), TEXT("Error"), MB_OK | MB_ICONERROR);
return E_FAIL;
}
D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(d3dpp));
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed = true;
auto result = d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
if (FAILED(result))
{
DXTRACE_ERR_MSGBOX(TEXT("The HAL mode of Direct3D device create failed,\ntry to create the REF mode"), result);
result = d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hwnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
if (FAILED(result))
{
DXTRACE_ERR_MSGBOX(TEXT("Direct3D device create failed"), result);
return E_FAIL;
}
}
result = InitSprite();
if (FAILED(result))
return E_FAIL;
return S_OK;
}
HRESULT InitSprite()
{
auto result = D3DXCreateSprite(device, &sprite);
if (FAILED(result))
{
DXTRACE_ERR_MSGBOX(TEXT("D3DXCreateSprite function failed"), result);
return E_FAIL;
}
result = D3DXCreateTextureFromFileEx(device, TEXT("Sprite.bmp"), 100, 100, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_FILTER_NONE, D3DX_DEFAULT, 0xff000000, nullptr, nullptr, &texture);
if (FAILED(result))
{
DXTRACE_ERR_MSGBOX(TEXT("D3DXCreateTextureFromFileEx function failed"), result);
return E_FAIL;
}
return S_OK;
}
void Render()
{
auto color = D3DCOLOR_COLORVALUE(0.5f, 0.5f, 0.5f, 1.0f);
device->Clear(0, nullptr, D3DCLEAR_TARGET, color, 1.0f, 0);
device->BeginScene();
RenderSprite();
device->EndScene();
device->Present(nullptr, nullptr, NULL, nullptr);
}
void RenderSprite()
{
sprite->Begin(D3DXSPRITE_ALPHABLEND);
RECT rect = { 0, 0, 100, 100 };
D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 position(270.0f, 180.0f, 0.0f);
auto color = D3DCOLOR_COLORVALUE(1.0f, 1.0f, 1.0f, 1.0f);
sprite->Draw(texture, &rect, ¢er, &position, color);
sprite->End();
}
void Release()
{
SAFE_RELEASE(texture);
SAFE_RELEASE(sprite);
SAFE_RELEASE(device);
SAFE_RELEASE(d3d9);
}
源码下载:下载地址