Learning notes of Chapter 4 of WINDOWS game programming from scratch

GDI

GDI has some basic functions and graphical definitions. In short, it is a whole composed of some data structures, functions and macros. All kinds of functions cannot escape a concept - device environment DC

4.2 equipment environment

A device environment is a place in the program where you can draw. That is, the "customer area".

In GDI image output, we only need to care about the type of device environment.

To determine the type of device environment, you can create a handle of the corresponding type. (I have to say that the handle of C + + is well designed)

4.2.2 get HDC (device environment handle)

  1. Using BeginPaint and EndPaint

    The prototypes of these two functions can be found in Microsoft documents:

    HDC BeginPaint(
      [in]  HWND          hWnd,
      [out] LPPAINTSTRUCT lpPaint
    );
    
    BOOL EndPaint(
      [in] HWND              hWnd,
      [in] const PAINTSTRUCT *lpPaint
    );
    

    Window handle and an lpPaint

    An example:

    HDC g_hdc; // Note the g of the Hungarian nomenclature_ Prefixes denote global variables
    ...
        case WM_PAINT:
    		g_hdc = BeginPaint(hwnd, &paintStruct);
    		Game_Paint();
    		EndPaint(hwnd, &paintStruct);
    		ValidateRect(hwnd, NULL);
    		break;
    ...
    
  2. Directly use GetDC and release DC

    HDC g_hdc;
    g_hdc = GetDC(hWnd);
    ...
    ReleaseDC(hWnd, g_hdc);
    

4.3

4.4

Write a general framework of GDI program, which is just a framework. Copy and modify the main in GameCore in the previous section to get:

//-------------------[procedure description]-------------------
// Program name: GDIdemoCore
// Learn from light ink orz
// Created by Eke, February 2022
// Description: realize the core program required for GDI game development
//--------------------------------------------------

//-----------------[header document]---------------------
// Description: header file that the program depends on
//--------------------------------------------------
#include <Windows.h>

//-----------------[macro definition]--------------------
// Description: defines auxiliary macros
//--------------------------------------------------
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
constexpr auto WINDOW_TITLE = L"Who's going to fly on the slide? I!"; // In the new version, constexpr is safer than define

//----------------[global function declaration]----------------
// Description: global function declaration
//--------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL Game_Init(HWND hwnd); // Resource initialization
VOID Game_Paint(HWND hwnd); // Drawing code writing
BOOL Game_CleanUp(HWND hwnd); // Resource cleanup

//----------------[global variable declaration]----------------
// Description: global variable declaration
//--------------------------------------------------
HDC g_hdc = NULL;

//----------------[WinMain() function]-----------------
// Description: program main entrance
//--------------------------------------------------
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) // Need to_ In_, _In_opt_  Add according to the function prototype, otherwise a warning will be given But why?
{
	// Trilogy
	// 1. Design (Oh 11 paras!)
	WNDCLASSEX wndClass = { 0 }; // Define a window class
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = (HICON)::LoadImage(NULL, L"favicon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // Try the brush without RED\BLUE, qwq
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";

	// 2. Register
	if (!RegisterClassEx(&wndClass)) return -1;

	// 3. Create
	HWND hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOW_TITLE,
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);

	// 4. Move, show and update
	MoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	if (!Game_Init(hwnd))
	{
		MessageBox(hwnd, L"Resource initialization failed", L"news", 0);
		return FALSE;
	}

	// 5. msg loop
	MSG msg = { 0 };
	while (msg.message != WM_QUIT);
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	// 6. unregister
	UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);
	return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT paintStruct;

	switch (message)
	{
	case WM_PAINT:
		g_hdc = BeginPaint(hwnd, &paintStruct);
		Game_Paint(hwnd);
		EndPaint(hwnd, &paintStruct);
		ValidateRect(hwnd, NULL);
		break;
	case WM_KEYDOWN:
		if (wParam == VK_ESCAPE) DestroyWindow(hwnd);
		break;
	case WM_DESTROY:
		Game_CleanUp(hwnd);
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
	return 0;
}

BOOL Game_Init(HWND hwnd)
{
	g_hdc = GetDC(hwnd);
	Game_Paint(hwnd);
	ReleaseDC(hwnd, g_hdc);
	return TRUE;
}

VOID Game_Paint(HWND hwnd)
{

}

BOOL Game_CleanUp(HWND hwnd)
{
	return TRUE;
}

ok. Keep it for standby

4.5 GDI basic geometric drawing

A seemingly unimportant section.

After browsing, I found some similar interface functions. Not in your notes yet.

(there is no difficulty)

Mainly to find out the brush and brush:

Brush is used to draw lines, shapes, outlines and colors;

Brushes are used to create solid shapes and render text

In other words, the brush determines the color and shape, and the brush "Embroiders" the figure drawn by the brush

4.6 random number

Nothing to see, familiar.

After being familiar with the random, write a sample program GDIdemo1, a demonstration program for the use of brushes and brushes.

There is also the playing of background music, but instead of using the PlaySound() function, MCI:

//-------------------[procedure description]-------------------
// Program name: GDIdemoCore
// Learn from light ink orz
// Created by Eke, February 2022
// Description: realize the core program required for GDI game development
//--------------------------------------------------

//-----------------[header document]---------------------
// Description: header file that the program depends on
//--------------------------------------------------
#include <Windows.h>
#include <time.h>
#include <mmsystem.h>
#pragma comment (lib, "winmm.lib") / / link to winmm library

//-----------------[macro definition]--------------------
// Description: defines auxiliary macros
//--------------------------------------------------
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
constexpr auto WINDOW_TITLE = L"Who's going to fly on the slide? I!"; // In the new version, constexpr is safer than define

//----------------[global function declaration]----------------
// Description: global function declaration
//--------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL Game_Init(HWND hwnd); // Resource initialization
VOID Game_Paint(HWND hwnd); // Drawing code writing
BOOL Game_CleanUp(HWND hwnd); // Resource cleanup

//----------------[global variable declaration]----------------
// Description: global variable declaration
//--------------------------------------------------
HDC g_hdc = NULL;
HPEN g_hPen[7] = { 0 };
HBRUSH g_hBrush[7] = { 0 };
int g_iPenStyle[7] = { PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFRAME };
int g_iBrushStyle[6] = { HS_VERTICAL, HS_HORIZONTAL, HS_CROSS, HS_DIAGCROSS, HS_FDIAGONAL, HS_BDIAGONAL };

//----------------[WinMain() function]-----------------
// Description: program main entrance
//--------------------------------------------------
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) // Need to_ In_, _In_opt_  Add according to the function prototype, otherwise a warning will be given Why?
{
	// Trilogy
	// 1. Design (Oh 11 paras!)
	WNDCLASSEX wndClass = { 0 }; // Define a window class
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = (HICON)::LoadImage(NULL, L"favicon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // Try the brush without RED\BLUE, qwq
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";

	// 2. Register
	if (!RegisterClassEx(&wndClass)) return -1;

	// 3. Create
	HWND hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOW_TITLE,
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);

	// 4. Move, show and update
	MoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	/* In this book, PlaySound function is used to play background music. Unfortunately, this function is only supported wav format 
	   So I use mci to play sound, which is powerful and supports mp3 */
	mciSendString(TEXT("open Make a flower fire.mp3 alias BackMusic"), NULL, 0, NULL); // Turn on music
	mciSendString(L"play BackMusic repeat", NULL, 0, NULL); // Start playing

	if (!Game_Init(hwnd))
	{
		MessageBox(hwnd, L"Resource initialization failed", L"news", 0);
		return FALSE;
	}

	// 5. msg loop
	
	MSG msg = { 0 };
	while (msg.message != WM_QUIT);
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	mciSendString(L"stop BackMusic", NULL, 0, NULL); // stop playing
	mciSendString(L"close BackMusic", NULL, 0, NULL); // Turn off music

	// 6. unregister
	UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);
	return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT paintStruct;

	switch (message)
	{
	case WM_PAINT:
		g_hdc = BeginPaint(hwnd, &paintStruct);
		Game_Paint(hwnd);
		EndPaint(hwnd, &paintStruct);
		ValidateRect(hwnd, NULL);
		break;
	case WM_KEYDOWN:
		if (wParam == VK_ESCAPE) DestroyWindow(hwnd);
		break;
	case WM_DESTROY:
		Game_CleanUp(hwnd);
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
	return 0;
}

BOOL Game_Init(HWND hwnd)
{
	g_hdc = GetDC(hwnd);
	srand((unsigned)time(NULL));
	for (int i = 0; i <= 6; ++i) {
		g_hPen[i] = CreatePen(g_iPenStyle[i], 1, RGB(rand() % 256, rand() % 256, rand() % 256));
		if (i == 6) {
			g_hBrush[i] = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
		}
		else {
			g_hBrush[i] = CreateHatchBrush(g_iBrushStyle[i], RGB(rand() % 256, rand() % 256, rand() % 256));
		}
	}

	Game_Paint(hwnd);
	ReleaseDC(hwnd, g_hdc);
	return TRUE;
}

VOID Game_Paint(HWND hwnd)
{
	int y = 0;

	for (int i = 0; i <= 6; ++i) {
		y = (i + 1) * 70;

		SelectObject(g_hdc, g_hPen[i]); // Choose a good brush Who is the subject chosen here? g_hdc is our DC
		MoveToEx(g_hdc, 30, y, NULL); // move cursor to (30, y)
		LineTo(g_hdc, 100, y); // from pos to (100, y)
	}

	/* now y = 420 */
	int x1 = 120, x2 = 190;

	// OK, the brush is ready. Now you need to fill the drawn rectangle with the brush
	for (int i = 0; i <= 6; ++i) {
		SelectObject(g_hdc, g_hBrush[i]);
		Rectangle(g_hdc, x1, 70, x2, y);
		x1 += 90, x2 += 90;
	}
}

BOOL Game_CleanUp(HWND hwnd)
{
	for (int i = 0; i <= 6; ++i) {
		DeleteObject(g_hPen[i]);
		DeleteObject(g_hBrush[i]);
	}
	return TRUE;
}

Effect of a run:

(there should be DAOKO and Ba Ye's BGM on the flower fire...)

4.7 text

The most commonly used text output function: TextOut

Learn about your advanced output function: DrawText

This function writes formatted text in the specified rectangle and formats the text

SetTextColor: sets the color value of the text Note that its parameters are only DC handle and text color (God, doesn't that mean a device environment can only have one color)

SetBkMode: sets the text background mode Usually set to TRANSPARENT. If this function is not called, the default text background is white.

CreateFont: font creation function After the font is created, use SelectObject to select it into the device environment.

The second example program: we only modified the GamePaint function

VOID Game_Paint(HWND hwnd)
{
	HFONT hFont = CreateFont(30, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, L"Microsoft YaHei "); // Create a font
	SelectObject(g_hdc, hFont);
	SetBkMode(g_hdc, TRANSPARENT);

	wchar_t text1[] = L"Fly to ride the storm of the times and become the eye of the typhoon tonight!";
	wchar_t text2[] = L"このWindにFlyびToって,Typhoon tonightのorderとなって!";
	wchar_t text3[] = L"------King GNU <Flying boat";

	SetTextColor(g_hdc, RGB(50, 255, 50));
	TextOut(g_hdc, 30, 150, text1, wcslen(text1));
	
	SetTextColor(g_hdc, RGB(50, 50, 255));
	TextOut(g_hdc, 30, 200, text2, wcslen(text2));

	SetTextColor(g_hdc, RGB(255, 150, 50));
	TextOut(g_hdc, 500, 250, text3, wcslen(text3));

	DeleteObject(hFont);
}

Operation effect:

4.8 bitmap

Bitmap drawing four steps:

  1. Load bitmap object from file (give bitmap handle)
  2. Establish a memory DC compatible with window DC
  3. Select bitmap object
  4. Map Paste the contents of the memory DC into the window DC

Load bitmap

Use the LoadImage function Its function is to load a picture into a bitmap handle

Establish compatible DC

Use the CreateCompatibleDC function Its function is to create a memory DC compatible with window DC

The memory DC is released after use. Instead of ReleaseDC(), it calls DleteDC(). Please refer to the following link:

Difference between DeleteDC() and ReleaseDC() - vranger - blog Park (cnblogs.com)

Select bitmap object

SelectObject().

Map

You must be familiar with mapping functions, such as BitBlt(), TransparentBlt(), StrentchBlt()

Next, add these four steps to GameInit(), GamePaint(), and you're done. Don't forget to release. Release in game_ In cleanup()

BOOL Game_Init(HWND hwnd)
{
	g_hdc = GetDC(hwnd);

	// STEP1
	g_hBitmap = (HBITMAP)LoadImage(NULL, L"11.bmp", IMAGE_BITMAP, 800, 600, LR_LOADFROMFILE);

	// STEP2
	g_mdc = CreateCompatibleDC(g_hdc);

	Game_Paint(hwnd);
	ReleaseDC(hwnd, g_hdc);
	return TRUE;
}
VOID Game_Paint(HWND hwnd)
{
	// STEP3
	SelectObject(g_mdc, g_hBitmap);
	// STEP4
	BitBlt(g_hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, g_mdc, 0, 0, NOTSRCCOPY);


	HFONT hFont = CreateFont(30, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, L"Microsoft YaHei "); // Create a font
	SelectObject(g_hdc, hFont);
	SetBkMode(g_hdc, TRANSPARENT);

	wchar_t text1[] = L"Fly to ride the storm of the times and become the eye of the typhoon tonight!";
	wchar_t text2[] = L"このWindにFlyびToって,Typhoon tonightのorderとなって!";
	wchar_t text3[] = L"------King GNU <Flying boat";

	SetTextColor(g_hdc, RGB(50, 255, 50));
	TextOut(g_hdc, 30, 150, text1, wcslen(text1));
	
	SetTextColor(g_hdc, RGB(50, 50, 255));
	TextOut(g_hdc, 30, 200, text2, wcslen(text2));

	SetTextColor(g_hdc, RGB(255, 150, 50));
	TextOut(g_hdc, 500, 250, text3, wcslen(text3));

	DeleteObject(hFont);
}
BOOL Game_CleanUp(HWND hwnd)
{
	DeleteObject(g_hBitmap);
	DeleteDC(g_mdc);
	return TRUE;
}

The third example program:

summary

It seems that up to now, it is a series of interfaces + functions. I think that's not the case with GDI development?

Keywords: Windows Game Development

Added by ukalpa on Sat, 05 Feb 2022 09:34:43 +0200