I'm writing a C++ game engine, and I have events such as KeyboardEvent, MouseEvent, and WindowEvent. However, after reading some of "Game Coding Complete, 4th Edition" by Mike McShaffry (which I know is a bit dated), its event system focuses on things like ObjectMoved, ObjectCreated, ObjectDestroyed, and specific actions like GuardPickedNose, meanwhile input is handled through callbacks/forwarding/delegation (I'm unsure what the correct terminology is) and it's not just this book though the event chapter on gameprogrammingpatterns.com also discusses using events for things like tutorials and combat. So it does have me thinking maybe I am using events for the wrong things? is there a reason why you wouldn't want these things as events?
For a bit of context, here is the code from the Game Coding Complete GitHub. The first block of code is how they handle input, and the second block of code is the event system.
class IKeyboardHandler
{
public:
virtual bool VOnKeyDown(const BYTE c)=0;
virtual bool VOnKeyUp(const BYTE c)=0;
};
DXUTSetCallbackMsgProc( GameCodeApp::MsgProc );
LRESULT CALLBACK GameCodeApp::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext )
{
switch (uMsg)
{
case WM_SYSKEYDOWN:
{
if (wParam == VK_RETURN)
{
*pbNoFurtherProcessing = true;
return g_pApp->OnAltEnter();
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
case WM_CLOSE:
{
if (g_pApp->m_bQuitting)
{
result = g_pApp->OnClose();
}
else
{
*pbNoFurtherProcessing = true;
}
break;
}
case WM_KEYDOWN:
case WM_KEYUP:
case WM_CHAR:
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case MM_JOY1BUTTONDOWN:
case MM_JOY1BUTTONUP:
case MM_JOY1MOVE:
case MM_JOY1ZMOVE:
case MM_JOY2BUTTONDOWN:
case MM_JOY2BUTTONUP:
case MM_JOY2MOVE:
case MM_JOY2ZMOVE:
{
if (g_pApp->m_pGame)
{
BaseGameLogic *pGame = g_pApp->m_pGame;
AppMsg msg;
msg.m_hWnd = hWnd;
msg.m_uMsg = uMsg;
msg.m_wParam = wParam;
msg.m_lParam = lParam;
for(GameViewList::reverse_iterator i=pGame->m_gameViews.rbegin(); i!=pGame->m_gameViews.rend(); ++i)
{
if ( (*i)->VOnMsgProc( msg ) )
{
result = true;
break;
}
}
}
break;
}
}
return result;
}class EvtData_New_Actor : public BaseEventData
{
ActorId m_actorId;
GameViewId m_viewId;
public:
static const EventType sk_EventType;
EvtData_New_Actor(void)
{
m_actorId = INVALID_ACTOR_ID;
m_viewId = gc_InvalidGameViewId;
}
explicit EvtData_New_Actor(ActorId actorId, GameViewId viewId = gc_InvalidGameViewId)
: m_actorId(actorId),
m_viewId(viewId)
{
}
virtual void VDeserialize(std::istrstream& in)
{
in >> m_actorId;
in >> m_viewId;
}
virtual const EventType& VGetEventType(void) const
{
return sk_EventType;
}
virtual IEventDataPtr VCopy(void) const
{
return IEventDataPtr(GCC_NEW EvtData_New_Actor(m_actorId, m_viewId));
}
virtual void VSerialize(std::ostrstream& out) const
{
out << m_actorId << " ";
out << m_viewId << " ";
}
virtual const char* GetName(void) const
{
return "EvtData_New_Actor";
}
const ActorId GetActorId(void) const
{
return m_actorId;
}
GameViewId GetViewId(void) const
{
return m_viewId;
}
};
class IEventManager
{
public:
enum eConstants { kINFINITE = 0xffffffff };
explicit IEventManager(const char* pName, bool setAsGlobal);
virtual ~IEventManager(void);
virtual bool VAddListener(const EventListenerDelegate& eventDelegate, const EventType& type) = 0;
virtual bool VRemoveListener(const EventListenerDelegate& eventDelegate, const EventType& type) = 0;
virtual bool VTriggerEvent(const IEventDataPtr& pEvent) const = 0;
virtual bool VQueueEvent(const IEventDataPtr& pEvent) = 0;
virtual bool VThreadSafeQueueEvent(const IEventDataPtr& pEvent) = 0;
virtual bool VAbortEvent(const EventType& type, bool allOfType = false) = 0;
virtual bool VUpdate(unsigned long maxMillis = kINFINITE) = 0;
static IEventManager* Get(void);
};