起因:
寻常的一天我依然在被大数据推送各种各样的教学视频,它们大多数都是培训机构骗钱的垃圾,但是也有不少富有诚意的介绍知识的作品,就是这一天前我看到了一个介绍OpenGL和DirectX的视频,萌生了进一步了解的想法,于是在b站上搜索一个介绍DirectX的视频,没想到那个老外开场就讲Win32创建窗口,讲得头头是道而且很有知识密度,飞快地就给我把创建窗口的过程给过过了。
然而最重要的是,他会指引你在微软官方文档 msdn 中查阅资料来学习。
配合着这些资料我掌握了Win32API的基础
DirectX3D 11 这个视频听不下去了是因为C++编程的技巧还没上来
一个超级啰嗦的Win32视频 巴不得揉碎了塞我嘴里,知识密度太低了,但是很细,也应该按照他的目录来学,静态库后面没学了,感觉用不上但是不知道自己错过了什么。
学了一些东西之后就想要把想法输出出来了,恰好之前玩游戏遇到一个**轮盘赌的场景。于是一个简单的小游戏雏形就有了:通过两个按钮猜硬币,猜对了就切换 CG
学习过程中解决的问题:
在客户区画图片
用到了 LoadBitmap()
函数,值得一提的是直接把 PNG 图片加入资源不可行,必须要用 BMP 格式,使用格式工厂转换后图片的占用内存变为原来的 230% 。
我写的一个画CG的函数,需要传入绘图设备句柄
void Draw_CG(HDC hdc)
{
HBITMAP hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(rc_stages[STAGE]));//指定资源
if (!hBmp) { OutputDebugString(L"LoadBitmap failed!"); return; }
HDC hMemdc = CreateCompatibleDC(hdc);//创建一个内存DC,在虚拟地区绘图
HGDIOBJ nOldBmp = SelectObject(hMemdc, hBmp);//将位图数据送给内存DC
BitBlt(hdc, 0, 0, 1024, 768, hMemdc, 0, 0, SRCCOPY);//将虚拟区域中绘制好的图像成像到窗口中
SelectObject(hMemdc, hBmp);
DeleteObject(hBmp);
DeleteDC(hMemdc);
}
画文本
和画位图是一个原理,应该写到一起。
值得一提的是填的是一个 wchar_t 类型的字符串。
void Draw_Text(HDC hdc)
{
const wchar_t text[] = L"这里填字符串";
RECT rc;
rc = { WD_WIDTH - BT_WIDTH - 70, 200,WD_WIDTH-10,250 };
DrawText(hdc, text, sizeof(text)/sizeof(wchar_t) - 1, &rc, DT_LEFT | DT_TOP);
}
画按钮,处理按钮事件
根据 MSDN 的文档,画按钮和画窗口一样,也要用 CreateWindow()
函数,处理事件则是要给他一个菜单 ID ,最后会在消息处理机制函数中得到一个 WM_COMMAND 消息,它的 wParam 参数用 LOWORD 函数处理后会变成我们想要的菜单 ID。在 switch 中处理就好。
刷新图片的方法
由于是在客户区用hdc绘图设备绘图,向导创建的代码只能在 WM_PAINT 消息时处理重绘的任务。然而这个消息的发送只在窗口大小改变和重现时,为了让他执行强制刷新,我们要先无效化客户区,然后自动就会发送 WM_PAINT 了。
RECT rect = { 0, 0, 1024, 768 };//一个结构体用来指定客户区区域
InvalidateRect(hWnd, &rect,TRUE);//无效化这个客户区,这时会发送WM_PAINT消息使其重画
UpdateWindow(hWnd);//不知道为什么去了这行也行
调试技术
使用 ostringstream ,这是C++的一个字符集操作模板类,定义在 sstream.h 头文件中。 ostringstream 类通常用于执行C风格的串流的输出操作,格式化字符串,避免申请大量的缓冲区,替代 sprintf 。
然而使用它需要一个把 string 转换成 LPWCSTR 的函数:
LPCWSTR stringToLPCWSTR(std::string orig)
{
wchar_t* wcstring = 0;
try
{
size_t origsize = orig.length() + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
if (orig == "")
{
wcstring = (wchar_t*)malloc(0);
mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
}
else
{
wcstring = (wchar_t*)malloc(sizeof(wchar_t) * (orig.length() - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
}
}
catch (std::exception e)
{
}
return wcstring;
}
然后我们就可以这么写了
std::ostringstream oss;
oss << "now stage = " << STAGE << '\n';//在控制台中查看想调试的变量
OutputDebugString(stringToLPCWSTR(oss.str()));
在上面的那个老外的视频里,他给出了一个可以实时输出触发的消息类型的模块,有兴趣的可以去看看那个视频win32部分
编写窗口程序的流程
我的评价是直接用 Visual Studio 自动创建代码,然后改一改就行
创建、注册窗口类
会创建一个 WNDCLASS 的结构体变量,然后根据文档填上它的参数。
自动创建的话会是一个 WNDCLASSEXW 的加强版
然后使用 RegisterClass()
函数注册窗口类
创建、显示窗口
HWND hWnd = CreateWindowW(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, WD_WIDTH, WD_HEIGHT, //位置,宽高
nullptr,
nullptr,
hInstance,
nullptr);
创建按钮
直接抄的文档里面的示例代码改了改
HWND hwndButton1 = CreateWindowW(
L"BUTTON", // Predefined class; Unicode assumed
L"猜正面", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
WD_WIDTH - BT_WIDTH - 40, // x position
WD_HEIGHT - 2 * BT_HEIGHT - 30 - 100, // y position
BT_WIDTH, // Button width
BT_HEIGHT, // Button height
hWnd, // Parent window
(HMENU)IDB_LEFT, // 用这个参数来设置控件的ID,这个宏是自己定义的,照猫画虎
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
显示并刷新窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
消息处理机制
你需要编写一个函数,把它填到注册窗口类里面,自动创建的代码已经帮我们写好了
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//static WindowsMessageMap mm;
//OutputDebugString(stringToLPCWSTR( mm(message, lParam, wParam)));//外置调试
roll_coin();
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
//处理两个按钮的点击事件,注意按钮是和菜单的事件一起的
case IDB_LEFT:
{
Button_event(hWnd, 1);
break;
}
case IDB_RIGHT:
{
Button_event(hWnd, 0);
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
Draw_CG(hdc);
Draw_Text(hdc);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
其他
无非就是一个随机数然后切换CG的机制,非常的简单。但是生成的伪随机数可能导致容易连续获胜或者连续输。
关于游戏美术资源:提取自兰斯1重制版,版权属于AliceSoft株式会社
成品效果:
被毙了,悲
代码
感觉传上来有点苕皮:下载地址