一、基本流程
1、初始化游戏资源
1)、游戏所需要的资源
(1)
hwnd
:用上一节我们定义的句柄。
(2)HDC
类型的句柄,因此我们定义全局变量g_hdc
,它需要与窗口绑定一起,我们可以使用GetDC(hwnd)
来绑定,可以这么写。
HDC g_hdc;
g_hdc = GetDC(hwnd);
ps:浅一点来说两个都是句柄,但是hwnd是来表示窗口的,HDC更多的是关于图像绘制的。
此外,模板并未使用GetDC
,它在窗口过程函数WndProc
使用的是BeginPaint
和EndPaint
获取设备句柄,但是GetDC
更简单,我建议使用这种方法。
模板自带:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
我们用一个专门的函数实现初始化资源
BOOL Game_Init(HWND hwnd) {
g_hdc = GetDC(hwnd);
/*开始绘制*/
Game_Paint(hwnd);
return true;
}
其中
Game_Paint
也是我们自己创造的绘图函数,待会会实现。
另外,为了以防万一,Game_Init
函数用的是布尔型,用来检测初始化是否失败,我们可以在主函数中这么写:
// 执行应用程序初始化:
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hwnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, WIND_WIDTH, WIND_HEIGHT, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
if (!hwnd)
{
return FALSE;
}
if (!Game_Init(hwnd)) {
MessageBox(hwnd, L"ERROR", L"消息窗口", 0);
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MODSDEMO));
其中
MessageBox
是消息框函数,定义如下:
MessageBox(
HWND hWnd, //句柄
LPCTSTR lpText, //显示的文本
LPCTSTR lpCaption, //消息框的标题,也可取各类参数
UINT uType //指定对话框的内容和行为,各类参数可以查阅资料
)
使用如图:
2、绘制游戏
1)、Rectangle函数
我们都知道,俄罗斯方块,少不了正方形,恰好C++内置函数
Rectangle
能够实现这个功能,此外还有圆,椭圆等函数,读者可自行查找定义如下:
WINGDIAPI BOOL WINAPI Rectangle(
_In_ HDC hdc, //句柄
_In_ int left, //矩形左上角x坐标
_In_ int top, //矩形左上角x坐标
_In_ int right, //矩形右上角x坐标
_In_ int bottom //矩形右上角y坐标
);
假设我们要在
(0,0)
位置绘制一个边长25
的矩形,我们可以这么写:
Rectangle(g_hdc,0,0,25,25);
2)、windows的2D动画技术
我们都指知道,大部分的2D其实都是一个NPC准备多张图片,然后快速播放,让我们以为它是在‘动’,其实就是几张图片循环播放而已。
因此我们也能在此使用定时器SetTimer
来控制播放的速度,定义如下:
SetTimer(
_In_opt_ HWND hWnd, //句柄
_In_ UINT_PTR nIDEvent,//定时器标识符,可以随便选数字
_In_ UINT uElapse, //多久绘制一次,单位ms
_In_opt_ TIMERPROC lpTimerFunc);//回调函数,我们不需要,设置为NULL
我们可以这么使用它:
SetTimer(hwnd, 1, 500, NULL);//500ms绘制一次
使用完之后我们要销毁它:
KillTimer(hwnd, 1);//1就是创造定时器的标识符
现在我们就要修改窗口过程函数了,让它接受信息的时候500ms重新绘制一下窗口:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_TIMER:
Game_Paint(hwnd);
break;
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;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
你可以测试一下:
VOID Game_Paint(HWND hwnd) {
srand((unsigned)time(NULL));//随机种子
Rectangle(g_hdc, rand() % WIND_WIDTH, rand() % WIND_HEIGHT, rand() % WIND_WIDTH + 50, rand() % WIND_HEIGHT+50);
return;
}
过程函数不变
ps:使用随机函数的时候记得加上头文件time.h
,因为我们要使用srand((unsigned)time(NULL))
;来更新随机数种子,不然都是伪随机数,就是每次都是一样的数
运行如图
此外,你还可以使用画刷
HBRUSH
改变矩形颜色,需要和下面所讲的函数一起使用:
HBRUSH Brush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
//注意,参数是RGB三原色,每个取值为0~255.
3)、SelectObject函数
SelectObject 把一个对象(位图、画笔、画刷等)选入指定的设备描述表。新的对象代替同一类型的老对象,绘图的时候我们必须要用此函数,一般来说我们都是这样使用的:
HGDIOBJ SelectObject(
HDC hdc,
HGDIOBJ hgdiobj // 对象,比如上述的Brush
);
使用方法:
VOID Game_Paint(HWND hwnd) {
srand((unsigned)time(NULL));//随机种子
HBRUSH Brush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
SelectObject(g_hdc, Brush);
Rectangle(g_hdc, rand() % WIND_WIDTH, rand() % WIND_HEIGHT, rand() % WIND_WIDTH + 50, rand() % WIND_HEIGHT+50);
return;
}
运行如下:
此外,我们绘制的模板一般都是这么写:
1.创建位图HBITMAP
,画刷等对象HBRUSH
obj
。
2.SelectObject(句柄,obj)
;
3.绘制.。
All Code:
#include "framework.h"
#include "ModsDemo.h"
#include <time.h>
#define WIND_WIDTH 800
#define WIND_HEIGHT 680
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
HDC g_hdc, g_mdc; //句柄
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
BOOL Game_Init(HWND hwnd); //游戏资源初始化
VOID Game_Paint(HWND hwnd); //绘制游戏
VOID Game_Clean(HWND hwnd); //释放资源
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_MODSDEMO, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hwnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, WIND_WIDTH, WIND_HEIGHT, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
if (!hwnd)
{
return FALSE;
}
if (!Game_Init(hwnd)) {
MessageBox(hwnd, L"ERROR", L"消息窗口", 0);
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MODSDEMO));
MSG msg = { 0 };
// 主消息循环:
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MODSDEMO));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MODSDEMO);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_TIMER:
Game_Paint(hWnd);
break;
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;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
BOOL Game_Init(HWND hwnd) {
g_hdc = GetDC(hwnd);
/*定时播放动画,与过程函数WM_TIMER对应*/
SetTimer(hwnd, 1, 100, NULL);
/*开始绘制*/
Game_Paint(hwnd);
return true;
}
VOID Game_Paint(HWND hwnd) {
srand((unsigned)time(NULL));//随机种子
HBRUSH Brush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
SelectObject(g_hdc, Brush);
Rectangle(g_hdc, rand() % WIND_WIDTH, rand() % WIND_HEIGHT, rand() % WIND_WIDTH + 50, rand() % WIND_HEIGHT + 50);
return;
}
VOID Game_Clean(HWND hwnd)
{
ReleaseDC(hwnd, g_mdc);
ReleaseDC(hwnd, g_hdc);
KillTimer(hwnd, 1);
return;
}
欢迎大家指正