版本:VS2015 语言:C++
最近简直忙死了。晚上抽空把博客写了。
这几天也是深深感觉到自己能力的不足,最近貌似要开3D项目了,比起在老代码上爬滚,还是想写自己的新代码啊。
嗯嗯,Windows编程还是得继续看、日语得继续学,然后是cocos3d啊。看来真得把周末玩的时候贡献出来了。善始善终,把空轨sc白金后开始撸上面的东西。
进入正题。
想要使用Direct,第一步当然是导包,先使用以下链接把需要的包放到工程底下:
链接:http://pan.baidu.com/s/1eS5F3Ce 密码:gwny
这是我整个的工程目录,其中lib放lib资源,include放头文件。嗯,好了,这样完毕之后就是要在工程里面导入了。
如果看过第一节Windows编程博客的话,应该对这个东西了如指掌了:
1.在VC++目录中添加include头文件目录;
2.在链接器——常规——附加库目录中添加lib目录;
3.在链接器——输入——附加依赖项中添加两个lib:dxguid.lib和ddraw.lib。
然后就正式进入代码。看过前一节的玩家可能会在想我之前要说的T3D控制台程序去哪了?
嗯,现在给大家:
// 主函数,程序入口
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// 创建窗口类
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; //窗口的样式:改变宽度刷新、改变高度刷新、分配设备描述表、双击信息
wndclass.lpfnWndProc = WindowProc; //回调函数
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //任务栏上的图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //光标的读取
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //窗口背景
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = TEXT("MyFirstWindow"); //窗口的名字
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); //应用上的图标
if (!RegisterClassEx(&wndclass))
return 0;
// 创建窗口,上面的窗口类是一个模版,可以根据上面的模版创建多个窗口,但请注意第二个参数
HWND hwnd = CreateWindowEx(NULL,//WS_EX_TOPMOST, //窗口特性,注释里设置为永远在最上方显示
TEXT("MyFirstWindow"), //窗口名称,一定要和窗口类的lpszClassName对应
TEXT("我与DDraw的第一次"), //标题
WS_POPUP | WS_VISIBLE, //无边框样式配合下面的尺寸实现全屏显示
0, 0, //左上角坐标
SCREEN_WIDTH, SCREEN_HEIGHT,
NULL, //父窗口句柄,如果是桌面则为NULL
NULL, //菜单窗口句柄
hInstance, //应用程序实例
NULL //高级特性
);
if (!hwnd) //创建失败返回
return 0;
main_window_handle = hwnd;
MSG msg; //消息缓存
srand(GetTickCount()); //随机一个种子
Game_Init(); //游戏初始化
// 进入主循环
while (true)
{
DWORD start_time = GetTickCount(); //获取当前时间
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) //有消息事件,注意最后一个参数,如果设置为PM_NOREMOVE的话不会销毁消息队列中的消息
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg); //转译消息
DispatchMessage(&msg); //将消息发送给WindowProc函数处理
}
else //没有消息
{
//游戏主循环
Game_Main();
// 延时代码,锁定30帧
while ((GetTickCount() - start_time) < 33);
}
}
Game_Shutdown(); //游戏结束
return msg.wParam;
}
没错就是在之前循环的框架上家了三个函数:Game_Init、Game_Main、Game_Shutdown,所以我就没有说。
我现在的内容是第六章了,第五章的内容实际上就是一些关于COM(Direct设计原理)的解释,就跳过了。
然后来看看第六章的代码,注意把代码放在主函数的上面:
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) //判断当前的按键是否被按下
HWND main_window_handle = NULL; //当前窗口
LPDIRECTDRAW7 lpdd = NULL; //Direct7对象,下称d7
LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; //主显示表面指针
DDSURFACEDESC2 ddsd; //主显示表面的描述
LPDIRECTDRAWPALETTE lpddpal = NULL; //调色板
PALETTEENTRY palette[256]; //调色板数据
int SCREEN_WIDTH = 640; //显示宽度
int SCREEN_HEIGHT = 480; //显示高度
int SCREEN_BPP = 32; //色深,现在的机子只能设置为32位,书上可能还是8位的
// 游戏初始化
int Game_Init(void* params = NULL)
{
// 基础设置
if (FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL))) //获取d7对象
return 0;
if (FAILED(lpdd->SetCooperativeLevel(main_window_handle,
//DDSCL_NORMAL
DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT
))) //跟windows协作等级设置为全屏,这是最常用的参数
return 0;
if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0))) //设置显示模式,如果设置为8位会直接出错
return 0;
// 开始创建显示主界面
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS; //表明ddsCaps是个有效成员
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; //表明该界面是主界面
lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL); //根据界面描述创建主界面
// 开始创建调色板
for (int color = 1; color < 255; ++color) //随机颜色
{
palette[color].peRed = rand() % 256;
palette[color].peGreen = rand() % 256;
palette[color].peBlue = rand() % 256;
palette[color].peFlags = PC_NOCOLLAPSE; //不需要系统的协助
}
palette[0].peRed = 0; //将黑色设置为0
palette[0].peGreen = 0;
palette[0].peBlue = 0;
palette[0].peFlags = PC_NOCOLLAPSE;
palette[255].peRed = 255; //将白色设置为255
palette[255].peGreen = 255;
palette[255].peBlue = 255;
palette[255].peFlags = PC_NOCOLLAPSE;
lpdd->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE, //创建调色板
palette,
&lpddpal,
NULL);
lpddsprimary->SetPalette(lpddpal); //主界面与调色板关联
return 1;
}
// 游戏结束
int Game_Shutdown(void* params = NULL)
{
// 释放初始化时创建的对象
if (NULL != lpddpal)
{
lpddpal->Release();
lpddpal = NULL;
}
if (NULL != lpddsprimary)
{
lpddsprimary->Release();
lpddsprimary = NULL;
}
if (NULL != lpdd) //d7对象不为空的情况下释放
{
lpdd->Release();
lpdd = NULL;
}
return 1;
}
// 游戏主循环
int Game_Main(void* params = NULL)
{
// 判断是否要退出
if (KEYDOWN(VK_ESCAPE))
PostMessage(main_window_handle, WM_CLOSE, 0, 0);
// 清空主界面的描述
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
// 开始绘制
lpddsprimary->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL); //加锁,绘制的代码需要放在加锁之间
int mempitch = ddsd.lPitch; //屏幕横向的宽度,y(第y行)乘以该参数就是指向具体的第y行第1个像素
UCHAR *video_buffer = (UCHAR*)ddsd.lpSurface; //屏幕的缓冲区
for (int index = 0; index < 1000; ++index) //随机显示颜色
{
UCHAR color = rand() % 256;
int x = rand() % 640;
int y = rand() % (480<<2); //本来只要%480就行了,但是因为我们色深设置为了32位,所以得进行调整
video_buffer[x + y*(mempitch>>2)] = color; //这边也是一样
}
lpddsprimary->Unlock(NULL); //解锁
return 1;
}
// 消息处理函数,相当于cocos中的callback
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM IParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
DefWindowProc(hwnd, msg, wParam, IParam); //自动处理其他的消息
break;
}
return (1);
}
看起来很复杂,但是和窗口的创建一样,写一写留个印象就OK。最后结果:
和例子上的结果是一模一样的,就是无法设置8位色深和残念(就你还用8位色深!)。
总结:
窗口三步走:1.窗口类;2.窗口对象;3.事件处理。
DDraw五步走:1.d7对象;2.与Windows关联;3.显示模式;4.创建显示主界面;5.创建调色板。
用的时候记得清空描述,并且要加Lock
本来打算Windows这块停一下,但是想着这些都是基础,现在不多花点时间看,还有什么时候有时间?