Engine Architecture - Where should I put my INPUT object?
Language: C/C++
Compiler/Editor: MSVS 2010
Graphics: DirectX 9.0c
OS: Windows XP
When school starts up again this year, I will be working on a game project with three other students. I'm attempting to architect our engine to avoid globals and be as modular as possible. I've come up with a basic idea of how I would like to structure the engine but I'm at a bit of a loss as what to do with the INPUT class, it just doesn't quite seem to fit in my design. The plan is to get keyboard input through the window messages WM_KEYDOW and WM_KEYUP in the WinProc function rather than using DirectInput. I've created an abstract SYSTEM class to act as the footprint for all the other engine components: WINDOW, GRAPHICS, INPUT, PHYSICS, AI, SOUND, ect. Here is my pseudo-code:
system.h
Code:
.
.
.
class SYSTEM
{
private:
public: SYSTEM();
virtual SYSTEM();
virtual void Load() = 0;
virtual void Initialize() = 0;
virtual void Update() = 0;
virtual void Unload() = 0;
};
window.h
Code:
.
.
.
class WINDOW : public SYSTEM
{
private:
public: WINDOW();
~WINDOW();
void Load();
void Initialize();
void Update();
void Unload();
};
input.h
Code:
.
.
.
enum KEY_STATE { NO_STATE, RELEASED, HELD, PRESSED = 4};
class INPUT : public SYSTEM
{
private: char key_[256];
public: INPUT();
~INPUT();
void Load();
void Initialize();
void Update();
void Unload();
};
window.cpp
Code:
.
.
.
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
case WM_CLOSE: PostQuitMessage(0);
return 0;
case WM_KEYDOWN: //Update Input
break;
case WM_KEYUP: //Update Input
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
WINDOW::WINDOW()
{
}
WINDOW::~WINDOW()
{
}
.
.
.
It makes sense to me for INPUT to inherit SYSTEM because INPUT "Is-a" SYSTEM. However, I need to somehow call INPUTS' Update() function in the WinProc() function which is in the Window.cpp file but is not a part of the WINDOW class. I've come up with a few ideas:
1. Make some friend functions in the INPUT class:
input.h
Code:
.
.
.
enum KEY_STATE { NO_STATE, RELEASED, HELD, PRESSED = 4};
class INPUT : public SYSTEM
{
private: char key_[256];
public: INPUT();
~INPUT();
void Load();
void Initialize();
void Update();
void Unload();
friend void UpdateKeyPress(WPARAM key);
friend void UpdateKeyRelease(WPARAM key);
friend bool IsKeyPressed(int key);
friend bool IsKeyHeld(int key);
friend bool IsKeyReleased(int key);
};
.
.
.
input.cpp
Code:
.
.
.
INPUT *g_input = NULL; //ewww, a global.
INPUT::INPUT()
{
g_input = this;
}
.
.
.
void UpdateKeyPress(WPARAM key)
{
//will have coditional to determin if key PRESSED or HELD
g_input->key_[key] = PRESSED;
}
void UpdateKeyRelease(WPARAM key)
{
//update function will reset a RELEASED keys to NO_STATE
g_input->key_[key] = RELEASED;
}
bool IsKeyPressed(int key) { return g_input->key_[key] & PRESSED; }
bool IsKeyHeld(int key) { return g_input->key_[key] & HELD; }
bool IsKeyReleased(int key) { return g_input->key_[key] & RELEASED; }
window.cpp
Code:
#include "Input.h"
.
.
.
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
case WM_CLOSE: PostQuitMessage(0);
return 0;
case WM_KEYDOWN: UpdateKeyPress(wParam);
break;
case WM_KEYUP: UpdateKeyRelease(wParam);
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
WINDOW::WINDOW()
{
}
WINDOW::~WINDOW()
{
}
.
.
.
This works but I feel like I'm breaking some coding rules. Shouldn't friend functions only be used to avoid globals or public data members? Had to make a global INPUT pointer at top of input.cpp to make this work. This however makes it so that all I have to do is include "Input.h" in any file to get access to the functions that tell weather a key is pressed, held, or released; and, keys could be forced to be pushed, held, or released with the "UpdateKey..." functions.
2. Option two is to just make a static INPUT object in the SYSTEM class. But I feel like I'm breaking some rule here too. Haven't tried this and I think I would get a compile error. SYSTEM "Has-a" INPUT object that "Is-a" SYSTEM? hmm...
3. Don't make INPUT inherit SYSTEM at all and include it in SYSTEM. Doing this though would mean that I would have to write input related functions in the SYSTEM class, but all the SYSTEM components would have access to input.
Code:
.
.
.
class SYSTEM
{
private: INPUT input_;
public: SYSTEM();
~SYSTEM();
void UpdateKeyPress(WPARAM key);
void UpdateKeyRelease(WPARAM key);
bool IsKeyPressed(int key);
bool IsKeyHeld(int key);
bool IsKeyReleased(int key);
};
All of these solutions are, in my opinion, ugly and I'm out of ideas. Any advice on how to handle input or restructure my code would be appreciated. Maybe I should look into using function pointers somehow?