那么首先让我们从最熟悉的Windows开始我们的图形编程支线任务。
想象一下在现实生活当中,如果我们想要画画,需要准备一些什么东西。在计算机领域,这个过程也是很相似的。因为当计算机从CUI(就是文字界面)转向GUI的时候,拓荒GUI的那些先驱们,也是参考着生活进行设计和编程的。
显然,我们首先需要有一块画布。在Windows系统当中,就是需要创建一个窗体。相信看这篇文章的很多人都写过Windows应用,而且是GUI应用。用Visual Studio生成一个windows应用,会自动生成创建这个窗体的代码。甚至,如果使用UWP,那么窗口对我们来说更像一张网页。然而这不是我想要介绍的方法。
我想要介绍的是使用windows系统API,也就是俗称win32 API(64位windows的API仍然称为win32 API)的方式创建窗口。之所以这么做,是为了最小限度的依赖Visual Studio以及相关的库,也是为了便于与其它系统的类似API作个比较。
好了,让我们开始。首先依然是做个记录点:
[tim@iZ625ivhudwZ GameEngineFromScratch]$ git checkout -b article_7
Switched to a new branch 'article_7'
然后新建一个Platform目录,用于存放和平台相关的代码。
[tim@iZ625ivhudwZ GameEngineFromScratch]$ mkdir Platform
[tim@iZ625ivhudwZ GameEngineFromScratch]$ cd Platform
在Platform下面新建Windows目录,用于存放Windows平台相关代码:
[tim@iZ625ivhudwZ Platform]$ mkdir Windows
[tim@iZ625ivhudwZ Platform]$ cd Windows
新建helloworld_win.c,敲入如下代码:
// include the basic windows header file
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = _T("WindowClass1");
// register the window class
RegisterClassEx(&wc);
// create the window and use the result as the handle
hWnd = CreateWindowEx(0,
_T("WindowClass1"), // name of the window class
_T("Hello, Engine!"), // title of the window
WS_OVERLAPPEDWINDOW, // window style
300, // x-position of the window
300, // y-position of the window
500, // width of the window
400, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
// enter the main loop:
// this struct holds Windows event messages
MSG msg;
// wait for the next message in the queue, store the result in 'msg'
while(GetMessage(&msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
}
// return this part of the WM_QUIT message to Windows
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch(message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
} break;
}
// Handle any messages the switch statement didn't
return DefWindowProc (hWnd, message, wParam, lParam);
}
好了,让我们来用Clang对它进行编译。注意这是一个Windows程序,所以你需要在Windows里面编译它。并且,因为我们用到了一些Windows平台的接口,需要链接相关的库:
C:UsersTim.AzureADSourceReposGameEngineFromScratchPlatformWindows>clang -l user32.lib -o helloengine_win.exe helloengine_win.c
编译成功的话,会出现一个helloengine_win.exe
C:UsersTim.AzureADSourceReposGameEngineFromScratchPlatformWindows>dir
驱动器 C 中的卷是 OS
卷的序列号是 38A2-CBDD
C:UsersTim.AzureADSourceReposGameEngineFromScratchPlatformWindows 的目录
2017/08/21 08:20 <DIR> .
2017/08/21 08:20 <DIR> ..
2017/08/21 08:10 3,163 helloengine_win.c
2017/08/21 08:21 73,216 helloengine_win.exe
2 个文件 76,379 字节
2 个目录 885,621,440,512 可用字节
执行它,我们就可以看到我们的窗体啦!
接下来再玩些别的酷酷的东西。在Linux上面交叉编译它(把代码Commit/Push到Github上面,然后在Linux上面把代码再Pull下来。不会的网上搜一下Github/Git教程。)
[tim@iZ625ivhudwZ GameEngineFromScratch]$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 5 (delta 1), reused 5 (delta 1), pack-reused 0
Unpacking objects: 100% (5/5), done.
From github.com:netwarm007/GameEngineFromScratch
264a4aa..5587a7d article_7 -> origin/article_7
Updating 264a4aa..5587a7d
Fast-forward
Platform/Windows/{helloworld_win.c => helloengine_win.c} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename Platform/Windows/{helloworld_win.c => helloengine_win.c} (97%)
[tim@iZ625ivhudwZ GameEngineFromScratch]$ cd Platform/Windows/
[tim@iZ625ivhudwZ Windows]$ ls
helloengine_win.c
[tim@iZ625ivhudwZ Windows]$ docker run -it --rm -v $(pwd):/usr/src tim03/mingw64
docker@691b5f941825:/$ cd /usr/src
docker@691b5f941825:/usr/src$ ls
helloengine_win.c
docker@691b5f941825:/usr/src$ x86_64-w64-mingw32-gcc -o helloengine_win.exe helloengine_win.c
docker@691b5f941825:/usr/src$ ls
helloengine_win.c helloengine_win.exe
docker@691b5f941825:/usr/src$ exit
exit
这个程序,当然,这样直接是没有办法在Linux上执行的,因为它是一个Windows程序。但是,我们可以使用wine来执行它。wine是一个在Linux上面模拟Windows系统接口的执行环境,可以用了执行Windows程序。当时开发wine的主要目的,就是在Linux上面跑只有Windows版本的游戏。
不过需要注意的是,要执行图形界面程序,我们必须在Linux的桌面环境下(也就是不能在TTY环境下)。切换到桌面环境(不会的请自己在网上搜索Linux基本教程),打开终端仿真窗口,执行下面的命令(如果没有安装wine,请用yum或者apt命令安装):
tim@iZuf6iup3mphicesefdwajZ:~/src/GameEngineFromScratch/Platform/Windows$ wine helloengine_win.exe