2D游戏引擎制作:窗体封装和唯一窗体运行

窗体封装和唯一窗体运行


前言

  之前是读取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;
    }
}

结束语

  感觉到本篇文章为止都和游戏没什么关系,都是一些零散的内容,感觉很枯燥。这也没有办法,等再有几篇大概就能出现界面之类了吧!

你可能感兴趣的:(2D游戏引擎制作)