前言
之前是读取XML文件,目的是将对窗体的描述信息单独存放,方便修改,这次开始对窗体进行封装。
多提一件事:楼道里2、3层上贴着“禁止吸烟”,4、5层贴着“禁止乱扔烟头”,结果2、3层一地砂砾,4、5层常闻叹息,大家都是聪明人,尤其是贴字的人,上下都有交代。
正文
之前生成窗体的方法是面向过程的,现在将它封装成对象,难点只有一个,就是窗体处理方法需要静态,但也是有解决办法的。
封装方法推荐参考链接:http://blog.csdn.net/norains/article/details/1840482,(共四论)
下文只是笔者的实现方法。
class GameWindow
{
public:
GameWindow(HINSTANCE hInstance);
~GameWindow();
void Run();
static const wstring className;
private:
void InitWindow();
//非静态,真正的处理方法。
LRESULT WindowProcess(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
//静态
static LRESULT CALLBACK WndProcess(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HINSTANCE hInstance = NULL;
HWND hWnd = NULL;
double left;
double top;
double width;
double height;
wstring title;
};
//以下cpp文件:
wstring GameWindow::className = L"GameWindow";
GameWindow::GameWindow(HINSTANCE hInstance)
{
this->hInstance = hInstance;
left = 0;
top = 0;
width = 800;
height = 600;
InitWindow();
}
GameWindow::~GameWindow()
{
UnregisterClass(className.c_str(), hInstance);
}
void GameWindow::Run()
{
ShowWindow(hWnd, SW_SHOW);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
}
}
}
void GameWindow::InitWindow()
{
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProcess;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.hInstance = hInstance;
wc.lpszClassName = L"GameWindow";
RegisterClassEx(&wc);
hWnd = CreateWindow(L"GameWindow", L"", WS_POPUP, 0, 0, 800, 600, 0, 0, hInstance, 0);
SetWindowLong(hWnd, GWLP_USERDATA, (LONG)this);//将当期窗体的指针与窗体句柄关联起来,使用时取出。
}
//静态方法
LRESULT GameWindow::WndProcess(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
//取出方法:第二个参数和平台有关,64位:GWLP_USERDATA 、32位:GWL_USERDATA
GameWindow* gameWindow=(GameWindow*)GetWindowLong(hwnd, GWLP_USERDATA);
if (gameWindow)
{
return gameWindow->WindowProcess(hwnd, msg, wParam, lParam);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
//真正的处理函数。
LRESULT GameWindow::WindowProcess(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_KEYDOWN:
case WM_LBUTTONDOWN:
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
现在处理函数中没有内容,为了拓展性,需要把针对每个事件的处理方法从外部传入。
实现方法大概是将在外部写好的函数的指针传入,笔者介绍一种方法。
引入头文件:
#include
使用function模板类,模板中间的类型和个数和要声明的函数对应,比如
function<void()>//可以对应 void F1(){}
function<bool(void*,int)> //可以对应 bool F2(void*,int){return false;}
使用方法:function类的对象=方法的名称
function 类的使用方法可以参考链接:https://www.cnblogs.com/kex1n/p/7072124.html,
首先给方法定义新名称,让它更像事件:
typedef function<void()> HandlerEvent;
typedef function<void(void*,int)> HandlerUIEvent;
//然后给GameWindow类添加属性:
//开始事件
HandlerEvent OnBeginEvent = nullptr;
//运行事件
HandlerEvent OnRunEvent = nullptr;
//结束事件
HandlerEvent OnEndEvent = nullptr;
//获得焦点
HandlerEvent OnActivate = nullptr;
//失去焦点
HandlerEvent OnInActivate = nullptr;
//左键点击
HandlerUIEvent OnLeftMouseDown = nullptr;
//左键弹起
HandlerUIEvent OnLeftMouseUp = nullptr;
//右键点击
HandlerUIEvent OnRightMouseDown = nullptr;
//右键弹起
HandlerUIEvent OnRightMouseUp = nullptr;
//鼠标移动
HandlerUIEvent OnMouseOver = nullptr;
//中键滚动
HandlerUIEvent OnMouseWheel = nullptr;
//获得字符
HandlerUIEvent OnGetChar = nullptr;
//键盘点击
HandlerUIEvent OnKeyDown = nullptr;
处理函数就可以银不同条件触发不同事件:
LRESULT GameWindow::WindowProcess(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_ACTIVATE:
{
if (LOWORD(wParam) == WA_INACTIVE)
{
if (OnInActivate)
{
OnInActivate();
}
}
else
{
if (OnActivate)
{
OnActivate();
}
}
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_CHAR://不经过输入法获得字符
case WM_IME_CHAR://经过输入法获得字符
{
if (OnGetChar)
{
OnGetChar(this, wParam);
}
return 0;
}
case WM_KEYDOWN:
{
if (OnKeyDown)
{
OnKeyDown(this, wParam);
}
return 0;
}
case WM_LBUTTONDOWN:
{
if (OnLeftMouseDown)
{
OnLeftMouseDown(this, wParam);
}
return 0;
}
case WM_RBUTTONDOWN:
{
if (OnRightMouseDown)
{
OnRightMouseDown(this, wParam);
}
return 0;
}
case WM_LBUTTONUP:
{
if (OnLeftMouseUp)
{
OnLeftMouseUp(this, wParam);
}
return 0;
}
case WM_RBUTTONUP:
{
if (OnRightMouseUp)
{
OnRightMouseUp(this, wParam);
}
return 0;
}
case WM_MOUSEMOVE:
{
if (OnMouseOver)
{
OnMouseOver(this, wParam);
}
return 0;
}
case WM_MOUSEWHEEL:
{
if (OnMouseWheel)
{
OnMouseWheel(this, GET_WHEEL_DELTA_WPARAM(wParam));
}
return 0;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
开始运行结束事件也可以添加到Run函数中。
为窗体类补充一点方法:
//此方法可以同时设置位置的大小,也可以将两个功能分开,实现方法都是同一个。
void SetPositionAndSize(double left,double top,double width, double height))
{
SetWindowPos(hWnd, nullptr, left, top, width, height, SWP_NOZORDER);
}
//设置标题。
void SetTitle(wstring title){SetWindowText(hWnd, title.c_str());}
窗体封装基本完成。
在main方法中生成窗体代码如下:
#include "GameWindow.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
GameWindow* gameWindow1;
gameWindow1=new GameWindow(hInstance);
gameWindow1->SetTitle(L"game1");
gameWindow1->SetPosition(300, 300);
gameWindow1->SetSize(1200, 800);
gameWindow1->Run();
delete gameWindow1;
return 0;
}
多次双击可执行文件,有的允许生成多个,多个程序同时运行,有的只允许生成一个,之后点击只会让首次生成的程序重新获得焦点。
默认程序可以生成多个,但要是想实现只允许生成一个程序,方法可以参考链接:https://www.cnblogs.com/herbertchina/p/5717241.html,
//在创建窗体钱前加上判断:
bool canMultiWindow = false;
if(!canMultiWindow)
{
wstring title = L"窗体标题";
HANDLE hMutex = CreateMutex(NULL, TRUE, title.c_str());
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
HWND hWnd = FindWindow(GameWindow::className.c_str(), title.c_str());
SetForegroundWindow(hWnd);
if (hMutex)
{
ReleaseMutex(hMutex);
}
return 0;
}
}
结束语
感觉到本篇文章为止都和游戏没什么关系,都是一些零散的内容,感觉很枯燥。这也没有办法,等再有几篇大概就能出现界面之类了吧!