Következzen az Application osztály implementálása.
Hozzunk létre egy új fájlt, a neve legyen ugyan az, csak ne header legyen, hanem C++ file (.cpp)
Hozzunk létre egy új fájlt, a neve legyen ugyan az, csak ne header legyen, hanem C++ file (.cpp)
Includeoljuk a megfelelő headert (nekem sApplication.h)
#include "sApplication.h"
Ezután kezdjük a névtér beállításával, ami legyen ugyan az, mint a headerben lévő!
namespace Scream
{
namespace Base
{
}
}
Én úgy szervezem a kódot, hogy a konstruktor elé írom a konstans és statikus változókat, illetve metódusokat, tehát az én szematikám szerint következzen ez. Kezdőérték adása kötelező!
bool Application::m_Active = false;
Most pedig a fentebbi WndProc, ami nem más, mint az üzenetkezelő függvényünk. A windows ablak üzeneteket küldözget, amiből néhányat nem árt lekezelni (például nekünk az ablak "aktívsága" és a kilépés)
LRESULT CALLBACK Application::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_ACTIVATEAPP:
if (wParam == TRUE)
{
m_Active = true;
}
else if (wParam == FALSE)
{
m_Active = false;
}
break;
default:
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
A WM_CLOSE üzenet Alt+F4 lenyomására (vagy ha olyan ablakot készítünk, akkor a jobb felső X lenyomásakor) érkezik. Szeretnénk bezárni az ablakot? Küldjünk az ablakkezelőnek egy kilépési kérést. Amint "ráér", végre is hajtja.
Jöhet a konstruktor és a destruktor. A konstruktorban kezdőértéket állítok be, ez később, más osztályok esetén is így lesz, és javaslom is ezt a módot. (például null-ra állítani a pointereket, stb.)
Application::Application(const std::string& p_Title)
{
m_Title = p_Title;
m_Run = false;
m_Hwnd = 0;
}
Application::~Application()
{
UnregisterClass("MainWindow", m_Wnd.hInstance);
}
Az UnregisterClass metódusra később reflektálok.
Következzen a SetParameters metódus
void Application::SetParameters(const int& p_Width,
const int& p_Height, const bool& p_Fullscreen)
{
//set values
m_Width = p_Width;
m_Height = p_Height;
m_Fullscreen = p_Fullscreen;
//update states
if (m_Fullscreen)
{
m_WindowStyle = WS_POPUP;
m_X = 0;
m_Y = 0;
}
else
{
m_WindowStyle = WS_CAPTION;
int px = GetSystemMetrics(SM_CXSCREEN);
int py = GetSystemMetrics(SM_CYSCREEN);
if (m_Width > px)
m_Width = px;
if (m_Height > py)
m_Height = py;
m_X = (px - m_Width) / 2;
m_Y = (py - m_Height) / 2;
RECT window;
window.left = m_X;
window.top = m_Y;
window.right = m_X + m_Width;
window.bottom = m_Y + m_Height;
AdjustWindowRectEx(&window, m_WindowStyle, false, WS_POPUP);
m_X = window.left;
m_Y = window.top;
m_Width = (window.right - window.left);
m_Height = (window.bottom - window.top);
}
}
Nagyon egyszerű: átadjuk az értékeket, és beállítjuk az ablakunk stílusát (msdn-en szét lehet nézni, hogy milyenek vannak, ha ezek nem felelnek meg nekünk)
Ha nem teljes képernyős ablakot szeretnénk, akkor viszont címsor is kell nekünk, így viszont nem a megadott (mondjuk 800x600) méret fog kelleni, hanem valamivel nagyobb. Erre való az AdjustWindowRect metódus, ami átállítja a RECT értékét, így egy valamivel nagyobb ablakot fogunk kapni.
A Start metódusban hozzuk létre az ablakunkat
bool Application::Start()
{
//set all value to null
memset(&m_Wnd, 0, sizeof(WNDCLASSEXA));
//set wndclassex parameters
m_Wnd.cbSize = sizeof(WNDCLASSEXA);
m_Wnd.style = (CS_HREDRAW | CS_VREDRAW);
m_Wnd.lpfnWndProc = (WNDPROC)WndProc;
m_Wnd.hInstance = GetModuleHandle(0);
m_Wnd.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
m_Wnd.lpszClassName = "MainWindow";
//register window
if (!RegisterClassExA(&m_Wnd))
{
Helpers::Logger::GetInstance()->Write("FAILED to register window!");
return false;
}
//creating hwnd
m_Hwnd = CreateWindowA("MainWindow",m_Title.c_str(),m_WindowStyle,m_X,m_Y,
m_Width,m_Height,HWND_DESKTOP,0,m_Wnd.hInstance,0);
if (!m_Hwnd)
{
Helpers::Logger::GetInstance()->Write("FAILED to create window!");
return false;
}
return true;
}
Fel is kommenteztem, de azért nézzük át. A fenti headerben lévő m_Wnd értékeit beállítjuk. Ez adja majd meg az ablaknak a megfelelő paramétereket. Megpróbáljuk regisztrálni ezt az ablakot (ennek az ellenpárja van a destruktorban!). Ha sikerült, akkor létre is hozzuk, ami egy HWND típussal tér vissza, ami nem más, mint egy azonosító az ablakunkra. A device és az input létrehozásánál is fog még nekünk kelleni.
Ha minden sikerült, akkor már rendben is vagyunk.
(megj.: írtam egy helper osztályt, amivel Log-fájlt lehet készíteni. Nem a legszebb, csak össze lett röffentve, így nem is publikálnám)
A Show metódust azért szedtem külön, mert szebb az a megoldás, hogy előbb létrehozzuk az ablakot, aztán a render device-ot és társait, és csak azután jelenítjük meg. Itt tehát nem is csinálunk mást, csak kiadjuk a parancsot, hogy tegye láthatóvá az ablakunkat, illetve elindítjuk a futást, legalábbis a mi változónk szerint.
void Application::Show()
{
//show window
ShowWindow(m_Hwnd, SW_SHOWDEFAULT);
SetActiveWindow(m_Hwnd);
SetForegroundWindow(m_Hwnd);
//start running
m_Run = true;
}
A Stop metódus ennek az ellenpárja, a változónkat false-ra állítja, illetve elrejti az ablakot.
void Application::Stop()
{
//stop running
m_Run = false;
//hide window
ShowWindow(m_Hwnd, SW_HIDE);
}
A Restart abban az esetben kell nekünk, ha átméretezzük az ablakot. Nem is igazán újraindítás ez, mert az azonosítónk is megmarad, viszont ezzel a névvel könnyebb később azonosítani.
void Application::Restart()
{
//hide fullscreen window
if (!m_Fullscreen)
ShowWindow(m_Hwnd, SW_HIDE);
//set style
SetWindowLong(m_Hwnd, GWL_STYLE, m_WindowStyle);
//set position and size
SetWindowPos(m_Hwnd, 0, m_X, m_Y, m_Width, m_Height, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
}
Ezt a metódust meg nyilván majd a SetParameters(...) után fogjuk meghívni. Átállítjuk a méretet, a pozíciót, de akár a stílust is.
Következzen ennek az osztálynak az utolsó függvénye, az IsRuning.
bool Application::IsRuning()
{
MSG msg;
ZeroMemory(&msg, sizeof(msg));
if (m_Run)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//exit
if (msg.message == WM_QUIT)
{
m_Run = false;
goto _end;
}
return true;
}
_end:
DestroyWindow(m_Hwnd);
return false;
}
A fentebb említett WndProc üzenetkezelő metódusnak itt "adjuk át" az üzeneteket. Van egy MSG struktúránk, ami magát az üzenetet tartalmazza majd. Ha fut a programunk (azaz az m_Run igaz), akkor megnézzük, hogy érkezett-e üzenet. Ha igen, akkor "lefordítjuk" a program számára értelmezhető üzenetté, majd elküldjük az üzenetkezelő függvényünknek (WndProc). Ha esetleg az üzenetünk egy WM_QUIT (amit ugye a PostQuitMessage() küld), akkor megszakítjuk a futást, és "brutálisan" eltávolítjuk az ablakunkat (DestroyWindow)
Nagyon röviden, tömören ennyi lenne az alap ablak létrehozása, lehet még szépítgetni, hozzászólni, ha esetleg valahol hibát talál valaki, és így tovább. :)
Ha valaki egy kicsit többet szeretne róla olvasni, az keresse fel a játékfejlesztés.hu oldalt! ;)
Apró sample, ha látni is akarjuk, hogy mit csináltunk:
A Tester projekten belül a main függvénybe írjuk a következőket (természetesen include szükséges)
Scream::Base::Application* app = new Scream::Base::Application("Tester title");
app->SetParameters(800,600,false);
app->Start();
//itt lesz a device és a többi komponens létrehozása
app->Show();
while (app->IsRuning())
{
//itt lesz a fő ciklusunk tartalma
}
delete app;
app = 0;
Ajánlott bejegyzések:
A bejegyzés trackback címe:
Kommentek:
A hozzászólások a vonatkozó jogszabályok értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a Felhasználási feltételekben és az adatvédelmi tájékoztatóban.