上一节:https://blog.csdn.net/z736248591/article/details/122658701

搭建引擎框架

回顾

上一章节,我们创建了一个空的窗口,这一章节,我们搭建引擎框架来封装窗口显示和渲染。

框架介绍

首先来介绍四个最重要的全局类:

  • Director:负责控制整个游戏。
  • Graphics:负责处理游戏的渲染部分。
  • Input:负责处理用户的所有输入。
  • Audio:负责全局管理所有的针对音频的操作。

本节主要介绍Graphics渲染部分。

创建

创建Graphics类的头文件和源文件。

声明

Graphics.h内引入Direct2D的头文件。

#include <windows.h>
#include <d2d1_3.h>
#include <wincodec.h>

d2d1_3为Windows上Direct2D 1.3版本的头文件,具体可以查看MSDN上更新的内容。

引入STL头文件。

#include <memory>
#include <string>
#include <iostream>

链接Direct2D的库文件。

#pragma comment(lib, "d2d1.lib")

声明类成员属性。

class Graphics
{
    public:
    static void Init(int width, int height,const std::wstring &title = L"");
    static void Render(std::shared_ptr<Scene> scene);
    static void Dispose();
    private:
    inline static HWND hwnd = NULL;
    inline static ID2D1Factory* d2dFactory = NULL;
    inline static ID2D1HwndRenderTarget* renderTarget = NULL;
    static void InitWindows(int width, int height,const std::wstring& title = L"");
    static void InitD2D();
};

初始化

需要对渲染初始化,主要的任务为创建窗口和初始化Direct2D。

void Graphics::Init(int width, int height, const std::wstring& title)
{
    InitWindows(width, height, title);
    InitD2D();
}

窗口创建为上一节的内容,这里不再一一介绍。

LRESULT CALLBACK WindowProc(
	HWND hwnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam
);
void Graphics::InitWindows(int width, int height, const std::wstring& title)
{
    HINSTANCE hInstance;
    hInstance = GetModuleHandle(NULL);
    WNDCLASS wndClass{};
    // 设置应用图标
    wndClass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wndClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wndClass.lpfnWndProc = WindowProc;
    // 设置类名字
    wndClass.lpszClassName = L"SGE";
    wndClass.hInstance = hInstance;
    // 指定唯一一个style
    wndClass.style = CS_OWNDC;

    // 注册
    RegisterClass(&wndClass);

    HWND hwnd = CreateWindow(
        L"SGE",							// 类名
        L"The Seed Game Engine",		// 窗口名
        WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE,
        38,
        20,
        width,
        height,
        NULL,
        NULL,
        hInstance,
        NULL);

    if (FAILED(hwnd))
    {
        std::wcout << L"创建窗口出错!" << std::endl;
        throw;
    }
    Graphics::hwnd = hwnd;

    // 重调窗口大小
    RECT rc;
    GetClientRect(hwnd, &rc);
    MoveWindow(hwnd, 38, 20, width + width - rc.right, height + height - rc.bottom, true);
}

初始化Direct2D。

void Graphics::InitD2D()
{
    HRESULT hr;

在Debug模式下打开调试可视层。

D2D1_FACTORY_OPTIONS options{};
#if defined(_DEBUG)
		// 启动Debug可视化层
		options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif

初始化D2D1Factory类,以后操作均要通过这个类来创建D2D资源对象。

hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dFactory);

if (FAILED(hr))
{
    std::wcout << L"创建D2D工厂失败。" << std::endl;
    throw;
}

创建绘制渲染区域。

RECT rc;
GetClientRect(hwnd, &rc);
hr = d2dFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(),D2D1::HwndRenderTargetProperties(hwnd,D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)),&renderTarget);
if (FAILED(hr))
{
    std::wcout << L"创建渲染区域失败!" << std::endl;
    throw;
}

渲染

Render函数负责渲染内容,scene参数为场景类对象,由场景类来组织内容,具体的实现见下一节。

void Graphics::Render(std::shared_ptr<Scene> scene)
{

准备开始绘制

renderTarget->BeginDraw();

只有调用了BeginDraw才能开始绘图。

使用黑色背景色来清空绘图区域

static D2D1::ColorF blackgroundColor = D2D1::ColorF::Black;
renderTarget->Clear(blackgroundColor);

绘制内容

scene->Render();

结束绘制

HRESULT hr = renderTarget->EndDraw();
if (FAILED(hr))
{
    std::wcout << L"绘图错误!" << std::endl;
    throw;
}

销毁

当游戏结束运行的时候需要销毁之前申请的资源。

这里定义一个安全释放资源的宏。

#define SAFE_RELEASE(P) if(P){P->Release() ; P = NULL ;}

销毁所有申请的资源。

void Graphics::Dispose()
{
    SAFE_RELEASE(renderTarget);
    SAFE_RELEASE(d2dFactory);
}

完整代码

Graphics.h

#pragma once
#include <windows.h>
#include <d2d1_3.h>
#include <wincodec.h>

#include <memory>
#include <string>
#include <iostream>
#include "Scene.h"

#pragma comment(lib, "d2d1.lib")

namespace sge {
	class Graphics
	{
	public:
		static void Init(int width, int height,const std::wstring &title = L"");
		static void Render(std::shared_ptr<Scene> scene);
		static void Dispose();
	private:
		inline static HWND hwnd = NULL;
		inline static ID2D1Factory* d2dFactory = NULL;
		inline static ID2D1HwndRenderTarget* renderTarget = NULL;
		static void InitWindows(int width, int height,const std::wstring& title = L"");
		static void InitD2D();
	};
}

Graphics.cpp

#include "Graphics.h"

LRESULT CALLBACK WindowProc(
	HWND hwnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam
);

#define SAFE_RELEASE(P) if(P){P->Release() ; P = NULL ;}

namespace sge {
	void Graphics::Init(int width, int height, const std::wstring& title)
	{
		InitWindows(width, height, title);
		InitD2D();
	}
	void Graphics::Render(std::shared_ptr<Scene> scene)
	{
		renderTarget->BeginDraw();

		// 清空绘图区域
		static D2D1::ColorF blackgroundColor = D2D1::ColorF::Black;
		renderTarget->Clear(blackgroundColor);

		scene->Render();

		HRESULT hr = renderTarget->EndDraw();
		if (FAILED(hr))
		{
			std::wcout << L"绘图错误!" << std::endl;
			throw;
		}
	}

	void Graphics::Dispose()
	{
		SAFE_RELEASE(renderTarget);
		SAFE_RELEASE(d2dFactory);
	}

	void Graphics::InitWindows(int width, int height, const std::wstring& title)
	{
		HINSTANCE hInstance;
		hInstance = GetModuleHandle(NULL);
		WNDCLASS wndClass{};
		// 设置应用图标
		wndClass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
		wndClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
		wndClass.lpfnWndProc = WindowProc;
		// 设置类名字
		wndClass.lpszClassName = L"SGE";
		wndClass.hInstance = hInstance;
		// 指定唯一一个style
		wndClass.style = CS_OWNDC;

		// 注册
		RegisterClass(&wndClass);

		HWND hwnd = CreateWindow(
			L"SGE",							// 类名
			L"The Seed Game Engine",		// 窗口名
			WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE,
			38,
			20,
			width,
			height,
			NULL,
			NULL,
			hInstance,
			NULL);

		if (FAILED(hwnd))
		{
			std::wcout << L"创建窗口出错!" << std::endl;
			throw;
		}
		Graphics::hwnd = hwnd;

		// 重调窗口大小
		RECT rc;
		GetClientRect(hwnd, &rc);
		MoveWindow(hwnd, 38, 20, width + width - rc.right, height + height - rc.bottom, true);
	}

	void Graphics::InitD2D()
	{
		HRESULT hr;
		D2D1_FACTORY_OPTIONS options{};
#if defined(_DEBUG)
		// 启动Debug可视化层
		options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
		hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dFactory);

		if (FAILED(hr))
		{
			std::wcout << L"创建D2D工厂失败。" << std::endl;
			throw;
		}

		// 获取绘制区域
		RECT rc;
		GetClientRect(hwnd, &rc);
		hr = d2dFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(),
			D2D1::HwndRenderTargetProperties(
				hwnd,
				D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)),
			&renderTarget);
		if (FAILED(hr))
		{
			std::wcout << L"创建渲染区域失败!" << std::endl;
			throw;
		}
	}
}

总结

本节主要介绍了游戏引擎框架最主要的四个类,以及渲染部分的编写。下一节会继续介绍剩余未编写的部分。

更多推荐