首页 > 代码库 > 第二章:创建框架和窗体
第二章:创建框架和窗体
没有翻译第一章是由于第一章仅仅介绍了怎样设置IDE。这方面网上文章非常多,我就没有翻译,直接从第二章開始。
以下是原文链接。翻译有不正确的地方请朋友们指正。
当中有一个WinMain方法用来作为程序的入口。还有system类,封装了所有程序,并在WinMain方法中调用。在system类中包括了用来处理opengl系统的opengl类,处理用户输入的input类和处理OpenGL图形的graphics类。以下是这个框架的结构图:
//////////////////////////////////////////////////////////////////////////////// // Filename: main.cpp //////////////////////////////////////////////////////////////////////////////// #include "systemclass.h" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdshow) { SystemClass* System; bool result; // Create the system object. // 创建system对象 System = new SystemClass; if(!System) { return 0; } // Initialize and run the system object. // 初始化并执行system对象 result = System->Initialize(); if(result) { System->Run(); } // Shutdown and release the system object. // 关闭并释放system对象 System->Shutdown(); delete System; System = 0; return 0; }
我们创建了system类的对象然后对它进行初始化。假设没有出错就调用system对象的Run方法。Run方法将循环执行所有程序代码。
Run方法执行结束后我们将关闭并清理system对象。我们将所有的程序逻辑都放在system类中。这样就保证了WinMain方法很简洁。
以下是system类的头文件。
//////////////////////////////////////////////////////////////////////////////// // Filename: systemclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _SYSTEMCLASS_H_ #define _SYSTEMCLASS_H_
/////////////////////////////// // PRE-PROCESSING DIRECTIVES // /////////////////////////////// #define WIN32_LEAN_AND_MEAN
/////////////////////// // MY CLASS INCLUDES // /////////////////////// #include "openglclass.h" #include "inputclass.h" #include "graphicsclass.h"
我们在WinMain方法里调用的Initialize、Shutdown和Run方法都在这里定义。同一时候这里也定义了将被用到的私有方法。同一时候,我们将处理系统窗体消息的MessageHandler方法也放在这个类里。最后。另一些私有变量m_OpenGL、m_Input和m_Graphics分别指向opengl、输入和图像的3个对象。
//////////////////////////////////////////////////////////////////////////////// // Class name: SystemClass //////////////////////////////////////////////////////////////////////////////// class SystemClass { public: SystemClass(); SystemClass(const SystemClass&); ~SystemClass(); bool Initialize(); void Shutdown(); void Run(); LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM); private: bool Frame(); bool InitializeWindows(OpenGLClass*, int&, int&); void ShutdownWindows(); private: LPCWSTR m_applicationName; HINSTANCE m_hinstance; HWND m_hwnd; OpenGLClass* m_OpenGL; InputClass* m_Input; GraphicsClass* m_Graphics; }; ///////////////////////// // FUNCTION PROTOTYPES // ///////////////////////// static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ///////////// // GLOBALS // ///////////// static SystemClass* ApplicationHandle = 0; #endif
//////////////////////////////////////////////////////////////////////////////// // Filename: systemclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "systemclass.h"
本来这个类不须要这些函数。可是某些编译器会自己主动帮你生成他们。所以我宁愿放两个空函数在这。
这样做的原因是我并不信任析构函数。
某些windows函数,比方ExitThread(),周所周知在类的析构时没有调用而引发了内存泄漏。你也能够调用这些方法的安全版本号。可是我会在windows编程上多加小心。
SystemClass::SystemClass(const SystemClass& other) { } SystemClass::~SystemClass() { }
这种方法同一时候创建和初始化了程序用来处理用户输入和渲染的输入和图形对象。
bool SystemClass::Initialize() { int screenWidth, screenHeight; bool result; // Initialize the width and height of the screen to zero. screenWidth = 0; screenHeight = 0; // Create the OpenGL object. m_OpenGL = new OpenGLClass; if(!m_OpenGL) { return false; } // Create the window the application will be using and also initialize OpenGL. // 创建程序用的窗体并初始化OpenGL result = InitializeWindows(m_OpenGL, screenWidth, screenHeight); if(!result) { MessageBox(m_hwnd, L"Could not initialize the window.", L"Error", MB_OK); return false; } // Create the input object. This object will be used to handle reading the input from the user. m_Input = new InputClass; if(!m_Input) { return false; } // Initialize the input object. m_Input->Initialize(); // Create the graphics object. This object will handle rendering all the graphics for this application. m_Graphics = new GraphicsClass; if(!m_Graphics) { return false; } // Initialize the graphics object. result = m_Graphics->Initialize(m_OpenGL, m_hwnd); if(!result) { return false; } return true; }
同一时候它也负责关闭窗体并清理和窗体相关联的句柄。
void SystemClass::Shutdown() { // Release the graphics object. if(m_Graphics) { m_Graphics->Shutdown(); delete m_Graphics; m_Graphics = 0; } // Release the input object. if(m_Input) { delete m_Input; m_Input = 0; } // Release the OpenGL object. if(m_OpenGL) { delete m_OpenGL; m_OpenGL = 0; } // Shutdown the window. ShutdownWindows(); return; }
void SystemClass::Run() { MSG msg; bool done, result; // Initialize the message structure. ZeroMemory(&msg, sizeof(MSG)); // Loop until there is a quit message from the window or the user. done = false; while(!done) { // Handle the windows messages. if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } // If windows signals to end the application then exit out. if(msg.message == WM_QUIT) { done = true; } else { // Otherwise do the frame processing. result = Frame(); if(!result) { done = true; } } } return; }
然后我们调用图形对象来渲染一帧。
后面我们还会在此加入很多其它的代码
bool SystemClass::Frame() { bool result; // Check if the user pressed escape and wants to exit the application. if(m_Input->IsKeyDown(VK_ESCAPE)) { return false; } // Do the frame processing for the graphics object. result = m_Graphics->Frame(); if(!result) { return false; } return true; }
LRESULT CALLBACK SystemClass::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) { switch(umsg) { // Check if a key has been pressed on the keyboard. case WM_KEYDOWN: { // If a key is pressed send it to the input object so it can record that state. m_Input->KeyDown((unsigned int)wparam); return 0; } // Check if a key has been released on the keyboard. case WM_KEYUP: { // If a key is released then send it to the input object so it can unset the state for that key. m_Input->KeyUp((unsigned int)wparam); return 0; } // Any other messages send to the default message handler as our application won‘t make use of them. default: { return DefWindowProc(hwnd, umsg, wparam, lparam); } } }
通过调用该方法,返回能够在程序全局使用的screenWidth和screenHeight。我们使用默认的设置创建了一个简单的没有边框的窗体。
这种方法能够通过FULL_SCREEN变量来决定程序是窗体模式还是全屏。
假设它的值为true将创建全屏窗体。假设为false将在桌面中部创建一个800x600的窗体。FULL_SCREEN变量是在graphicsclass.h文件头部的一个能够随时设置值的全局变量。(后面的不懂)
bool SystemClass::InitializeWindows(OpenGLClass* OpenGL, int& screenWidth, int& screenHeight) { WNDCLASSEX wc; DEVMODE dmScreenSettings; int posX, posY; // Get an external pointer to this object. ApplicationHandle = this; // Get the instance of this application. m_hinstance = GetModuleHandle(NULL); // Give the application a name. m_applicationName = L"Engine"; // Setup the windows class with default settings. wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = m_hinstance; wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); wc.hIconSm = wc.hIcon; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = m_applicationName; wc.cbSize = sizeof(WNDCLASSEX); // Register the window class. RegisterClassEx(&wc);
// Create a temporary window for the OpenGL extension setup. m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, m_applicationName, m_applicationName, WS_POPUP, 0, 0, 640, 480, NULL, NULL, m_hinstance, NULL); if(m_hwnd == NULL) { return false; } // Don‘t show the window. ShowWindow(m_hwnd, SW_HIDE); // Release the temporary window now that the extensions have been initialized. DestroyWindow(m_hwnd); m_hwnd = NULL;
This is where we proceed with the regular window creation.
// Determine the resolution of the clients desktop screen. screenWidth = GetSystemMetrics(SM_CXSCREEN); screenHeight = GetSystemMetrics(SM_CYSCREEN); // Setup the screen settings depending on whether it is running in full screen or in windowed mode. if(FULL_SCREEN) { // If full screen set the screen to maximum size of the users desktop and 32bit. memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); dmScreenSettings.dmSize = sizeof(dmScreenSettings); dmScreenSettings.dmPelsWidth = (unsigned long)screenWidth; dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight; dmScreenSettings.dmBitsPerPel = 32; dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; // Change the display settings to full screen. ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN); // Set the position of the window to the top left corner. posX = posY = 0; } else { // If windowed then set it to 800x600 resolution. screenWidth = 800; screenHeight = 600; // Place the window in the middle of the screen. posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth) / 2; posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2; } // Create the window with the screen settings and get the handle to it. m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, m_applicationName, m_applicationName, WS_POPUP, posX, posY, screenWidth, screenHeight, NULL, NULL, m_hinstance, NULL); if(m_hwnd == NULL) { return false; } // Bring the window up on the screen and set it as main focus. ShowWindow(m_hwnd, SW_SHOW); SetForegroundWindow(m_hwnd); SetFocus(m_hwnd); // Hide the mouse cursor. ShowCursor(false); return true; }
void SystemClass::ShutdownWindows() { // Show the mouse cursor. ShowCursor(true); // Fix the display settings if leaving full screen mode. if(FULL_SCREEN) { ChangeDisplaySettings(NULL, 0); } // Remove the window. DestroyWindow(m_hwnd); m_hwnd = NULL; // Remove the application instance. UnregisterClass(m_applicationName, m_hinstance); m_hinstance = NULL; // Release the pointer to this class. ApplicationHandle = NULL; return; }
LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam) { switch(umessage) { // Check if the window is being closed. case WM_CLOSE: { PostQuitMessage(0); return 0; } // All other messages pass to the message handler in the system class. default: { return ApplicationHandle->MessageHandler(hwnd, umessage, wparam, lparam); } } }
input对象用键盘数组保存每个输入值的状态。
我们能够通过查询来推断某个键是否被按下。以下是头文件:
//////////////////////////////////////////////////////////////////////////////// // Filename: inputclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _INPUTCLASS_H_ #define _INPUTCLASS_H_ //////////////////////////////////////////////////////////////////////////////// // Class name: InputClass //////////////////////////////////////////////////////////////////////////////// class InputClass { public: InputClass(); InputClass(const InputClass&); ~InputClass(); void Initialize(); void KeyDown(unsigned int); void KeyUp(unsigned int); bool IsKeyDown(unsigned int); private: bool m_keys[256]; }; #endif
Inputclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: inputclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "inputclass.h" InputClass::InputClass() { } InputClass::InputClass(const InputClass& other) { } InputClass::~InputClass() { } void InputClass::Initialize() { int i; // Initialize all the keys to being released and not pressed. for(i=0; i<256; i++) { m_keys[i] = false; } return; } void InputClass::KeyDown(unsigned int input) { // If a key is pressed then save that state in the key array. m_keys[input] = true; return; } void InputClass::KeyUp(unsigned int input) { // If a key is released then clear that state in the key array. m_keys[input] = false; return; } bool InputClass::IsKeyDown(unsigned int key) { // Return what state the key is in (pressed/not pressed). return m_keys[key]; }
所有的图形方法都被封装在这个类中。用来设置全屏和窗体模式的全局变量放在了文件头部。眼下类的方法留空。以后的教程会进行填充。
//////////////////////////////////////////////////////////////////////////////// // Filename: graphicsclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _GRAPHICSCLASS_H_ #define _GRAPHICSCLASS_H_ /////////////////////// // MY CLASS INCLUDES // /////////////////////// #include "openglclass.h"
///////////// // GLOBALS // ///////////// const bool FULL_SCREEN = false; const bool VSYNC_ENABLED = true; const float SCREEN_DEPTH = 1000.0f; const float SCREEN_NEAR = 0.1f; //////////////////////////////////////////////////////////////////////////////// // Class name: GraphicsClass //////////////////////////////////////////////////////////////////////////////// class GraphicsClass { public: GraphicsClass(); GraphicsClass(const GraphicsClass&); ~GraphicsClass(); bool Initialize(OpenGLClass*, HWND); void Shutdown(); bool Frame(); private: bool Render(); private: }; #endif
本章仅仅是为了构造框架。
//////////////////////////////////////////////////////////////////////////////// // Filename: graphicsclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "graphicsclass.h" GraphicsClass::GraphicsClass() { } GraphicsClass::GraphicsClass(const GraphicsClass& other) { } GraphicsClass::~GraphicsClass() { } bool GraphicsClass::Initialize(OpenGLClass* OpenGL, HWND hwnd) { return true; } void GraphicsClass::Shutdown() { return; } bool GraphicsClass::Frame() { return true; } bool GraphicsClass::Render() { return true; }
//////////////////////////////////////////////////////////////////////////////// // Filename: openglclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _OPENGLCLASS_H_ #define _OPENGLCLASS_H_ ////////////// // INCLUDES // ////////////// #include <windows.h> //////////////////////////////////////////////////////////////////////////////// // Class name: OpenGLClass //////////////////////////////////////////////////////////////////////////////// class OpenGLClass { public: OpenGLClass(); OpenGLClass(const OpenGLClass&); ~OpenGLClass(); private: }; #endif
Openglclass.h
//////////////////////////////////////////////////////////////////////////////// // Filename: openglclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "openglclass.h" OpenGLClass::OpenGLClass() { } OpenGLClass::OpenGLClass(const OpenGLClass& other) { } OpenGLClass::~OpenGLClass() { }
窗体显示后。按ESC键退出。
第二章:创建框架和窗体