游戏网站模,商城网站布局,小说分销平台,怎么制作小游戏这次为程序添加鼠标事件和键盘事件 当检测到鼠标事件和键盘事件的信息时#xff0c;捕获其信息并将信息传送到需要信息的对象处理。为此#xff0c;需要一个可以分派信息的对象#xff0c;这个对象能够正确的把信息交到正确的对象。 实现思路#xff1a; 要实现以上的功能捕获其信息并将信息传送到需要信息的对象处理。为此需要一个可以分派信息的对象这个对象能够正确的把信息交到正确的对象。 实现思路 要实现以上的功能需要几个对象 事件分派器EventDispatcher负责将 BaseEvent 分派给 EventListener 对象 事件监听器EventListener这只是一个接口类接受 BaseEvent 的对象真正的处理在它的子类中实现 事件BaseEvent储存用户数据事件信息载体 假设我要分派一个 BaseEvent 那么我应该将 BaseEvent 分派给哪个监听器 EventListener 可以在 BaseEvent 上添加一个 ID通过这个 ID 将 BaseEvent 分派到对应 ID 的监听器。 有这样一个场景有 A、B、C、D 四个监听器需要把 ID 为 5 的 BaseEvent 分派给 A、B 监听器而 C、D 监听器不需要接受这个 BaseEvent。 这时可以创建一个映射表存储有 ID 和 监听器之间的联系信息 typedef std::mapint, std::listEventListener* ListenerGroup; A、B 需要监听 ID 为 5 的 BaseEvent就把 A、B 注册到这个表中表中就有了 5-A、B 这样的信息。事件分派器就能根据这个表将 ID 为 5 的 BaseEvent 分派到需要监听这个 BaseEvent 的监听器 A 和 B。对于 C、D 监听器只能监听到对应 ID 的 BaseEvent实现思路就这样。 代码实现 BaseEvent 结构如下 struct BaseEvent{int nEventID; /* 事件 ID */int nParams[MAX_EVENT_PARAM]; /* 自定义参数 */void* pUserData; /* 用户数据 */}; nParams 用来储存几个自定义参数对于其他数据就用 void 指针储存需要时转换一下就可以了。 事件分派器有两个属性分别是 事件池 和 ID-监听器表事件池主要是用来储存所有要分派的事件 std::listBaseEvent vEventPool;ListenerGroup listenerGroup; 接下来是监听器的实现 class DLL_export EventListener{friend class EventDispatcher;public:EventListener();virtual ~EventListener() {}protected:void appendListener(int eventID, EventListener* listener);void removeListener(int eventID, EventListener* listener);virtual void handleEvent(const BaseEvent event) 0;private:static unsigned int nIDCounter;unsigned int nID;}; 主要有三个函数用于将监听器注册到 ID-监听器表和从 ID-监听器表中移除监听器最后一个是处理 BaseEvent 的函数这是一个抽象函数表示在子类中实现处理函数。 将监听器注册到表中需要一个监听器要监听的 BaseEvent ID 以及监听器本身 void EventListener::appendListener(int eventID, EventListener* new_listener){auto listenerList pDispatcher-listenerGroup.find(eventID);/* 事件 ID 没有监听列表为 ID 创建监听列表添加 eListener */if ( listenerList pDispatcher-listenerGroup.end() ) {std::listEventListener* newListenerList;newListenerList.push_back(new_listener);pDispatcher-listenerGroup.insert(std::make_pair(eventID, newListenerList));}else {/* 如果监听列表中没有监听器添加监听器到列表中 */std::listEventListener*::iterator listener_it;for ( listener_it listenerList-second.begin(); listener_it ! listenerList-second.end(); listener_it ) {if ( (*listener_it)-nID new_listener-nID ) return;}if ( listener_it listenerList-second.end() ) {listenerList-second.push_back(new_listener);}}} 先判断该 ID 的 BaseEvent 是否有一张表了如果没有就新建表然后将监听器添加到表中。 将监听器中表中移除 void EventListener::removeListener(int eventID, EventListener* listener){auto listenerList pDispatcher-listenerGroup.find(eventID);if ( listenerList pDispatcher-listenerGroup.end() ) return;/* 从监听列表中移除监听器 */for ( auto it listenerList-second.begin(); it ! listenerList-second.end(); it ) {if ( (*it)-nID listener-nID ) {listenerList-second.erase(it);break;}}/* 移除空监听列表 */if ( listenerList-second.empty() ) {pDispatcher-listenerGroup.erase(listenerList);}} 如果要分派一个 BaseEvent先将其添加到分派器中 void EventDispatcher::dispatchEvent(const BaseEvent event){/* 只是暂时添加事件到事件池中并没有立即分派事件避免递归分派错误 */vEventPool.push_back(event);} 这里没有立即将 BaseEvent 交给对应的监听器处理是因为如果处理函数中有将 BaseEvent 添加到事件分派器中的操作会发生递归错误。所以就将 BaseEvent 添加到一个事件池中稍后在函数 flushEvent 中统一分派 void EventDispatcher::flushEvent(){if ( vEventPool.empty() ) return;/* 分派事件池中的所有事件 */for ( auto event : vEventPool ) {this-realDispatchEvent(event);}vEventPool.clear();} 分派每一个 BaseEvent需要找到其对应的监听表再交给表中的监听器处理 void EventDispatcher::realDispatchEvent(const BaseEvent event){auto listenerList_it listenerGroup.find(event.nEventID);if ( listenerList_it ! listenerGroup.end() ) {std::listEventListener* listenerList listenerList_it-second;for ( auto listener_it : listenerList ) {listener_it-handleEvent(event);}}} 以上就实现了一个事件分派模块费如此大的一番功夫是为了让它不仅仅分派鼠标和键盘事件还可以分派其他需要的事件。 鼠标事件和键盘事件处理 为鼠标事件和键盘事件分别定义事件 ID enum EventType { ET_UNKNOWN, /* 未知事件 */ET_MOUSE, /* 鼠标事件 */ET_KEY /* 按键事件 */}; 先实现鼠标事件的处理定义一个鼠标监听器类继承于事件监听器 class DLL_export MouseEventListener : public EventListener{public:MouseEventListener();virtual ~MouseEventListener();virtual void mouseMove(const MouseEvent event) {}virtual void mousePress(const MouseEvent event) {}virtual void mouseRelease(const MouseEvent event) {}virtual void mouseDoubleClick(const MouseEvent event) {}virtual void mouseWheel(const MouseEvent event) {}void handleEvent(const BaseEvent event);}; 在构造函数和析构函数中主要是注册监听器到事件分派器和从事件分派器中移除监听器 MouseEventListener::MouseEventListener(){this-appendListener(EventType::ET_MOUSE, this);}MouseEventListener::~MouseEventListener(){this-removeListener(EventType::ET_MOUSE, this);} 鼠标事件分别有按键按下、释放、双击、鼠标移动和滚轮滑动等动作 enum EventAction{ACT_MOVE, /* 移动 */ACT_PRESS, /* 按压 */ACT_RELAESE, /* 释放 */ACT_DUBBLE_CLICK, /* 双击 */ACT_SCROLL /* 滚动 */}; 以及按钮类型左键、右键和中键 enum ButtonType { LEFT_BUTTON, /* 鼠标左键 */RIGHT_BUTTON, /* 鼠标右键 */MIDDLE_BUTTON /* 鼠标中键 */}; 对于一个鼠标事件需要的数据信息如下 /* 鼠标事件 */struct MouseEvent{EventAction eventAction;ButtonType buttonType;int nDelta;int nX, nY;}; 动作类型、按钮类型、滚轮滚动数据和坐标数据。 为了捕捉窗口程序的鼠标信息定义一个窗口信息处理类 //------------------------------------------------------------------// WinMsgHandle// 窗口信息处理//------------------------------------------------------------------class WinMsgHandle{public:WinMsgHandle();void handleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);private:BaseEvent baseEvent;KeyEvent keyEvent;MouseEvent mouseEvent;}; 函数 handleMessage 主要捕捉窗口信息 void WinMsgHandle::handleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){baseEvent.nEventID ET_UNKNOWN;/* 鼠标事件信息 */if ( msg WM_MOUSEMOVE msg WM_MBUTTONDBLCLK || msg WM_MOUSEWHEEL ) {switch ( msg ) {case WM_LBUTTONDOWN:mouseEvent.buttonType ButtonType::LEFT_BUTTON;mouseEvent.eventAction EventAction::ACT_PRESS;break;case WM_LBUTTONUP:mouseEvent.buttonType ButtonType::LEFT_BUTTON;mouseEvent.eventAction EventAction::ACT_RELAESE;break;case WM_LBUTTONDBLCLK:mouseEvent.buttonType ButtonType::LEFT_BUTTON;mouseEvent.eventAction EventAction::ACT_DUBBLE_CLICK;break;case WM_MBUTTONDOWN:mouseEvent.buttonType ButtonType::MIDDLE_BUTTON;mouseEvent.eventAction EventAction::ACT_PRESS;break;case WM_MBUTTONUP:mouseEvent.buttonType ButtonType::MIDDLE_BUTTON;mouseEvent.eventAction EventAction::ACT_RELAESE;break;case WM_MBUTTONDBLCLK:mouseEvent.buttonType ButtonType::MIDDLE_BUTTON;mouseEvent.eventAction EventAction::ACT_DUBBLE_CLICK;break;case WM_RBUTTONDOWN:mouseEvent.buttonType ButtonType::RIGHT_BUTTON;mouseEvent.eventAction EventAction::ACT_PRESS;break;case WM_RBUTTONUP:mouseEvent.buttonType ButtonType::RIGHT_BUTTON;mouseEvent.eventAction EventAction::ACT_RELAESE;break;case WM_RBUTTONDBLCLK:mouseEvent.buttonType ButtonType::RIGHT_BUTTON;mouseEvent.eventAction EventAction::ACT_DUBBLE_CLICK;break;case WM_MOUSEMOVE:mouseEvent.eventAction EventAction::ACT_MOVE;break;case WM_MOUSEWHEEL:mouseEvent.eventAction EventAction::ACT_SCROLL;mouseEvent.nDelta ( short ) HIWORD(wParam);break;}mouseEvent.nX ( short ) LOWORD(lParam);mouseEvent.nY ( short ) HIWORD(lParam);baseEvent.nEventID ET_MOUSE;baseEvent.pUserData mouseEvent;EventDispatcher::getInstance()-dispatchEvent(baseEvent);}} 主要是获取鼠标事件数据 MouseEvent然后将数据附加到 BaseEvent 上设置其 ID 为 鼠标事件ID——ET_MOUSE最后由事件分派器分派 BaseEvent。 当鼠标事件监听器处理 BaseEvent 时需要获取 MouseEvent 数据然后根据按钮类型和动作类型调用相应函数 void MouseEventListener::handleEvent(const BaseEvent event){if ( event.nEventID ! EventType::ET_MOUSE event.pUserData ) return;MouseEvent* mouseEvent static_castMouseEvent*(event.pUserData);switch ( mouseEvent-eventAction ) {case Simple2D::ACT_MOVE: this-mouseMove(*mouseEvent); break;case Simple2D::ACT_PRESS: this-mousePress(*mouseEvent); break;case Simple2D::ACT_RELAESE: this-mouseRelease(*mouseEvent); break;case Simple2D::ACT_SCROLL: this-mouseWheel(*mouseEvent); break;case Simple2D::ACT_DUBBLE_CLICK: this-mouseDoubleClick(*mouseEvent); break;}} 当然这些函数都没有具体的实现具体的实现由子类完成。 对于键盘事件只有两个按键动作按压和释放及事件的数据结构体 /* 按键事件 */struct KeyEvent{EventAction eventAction;bool keys[256];KeyType keyType;}; bool 类型的按键数组 keys 储存哪一个按键被按下的信息当同时有多个按键按压时也可以检测。而 KeyType 就记录了当前按压的按键类型这里并不包括键盘上的所有按键只包含字母键、数字键和其它常用按键。 /** VK_0 - VK_9 are the same as ASCII 0 - 9 (0x30 - 0x39)* 0x40 : unassigned* VK_A - VK_Z are the same as ASCII A - Z (0x41 - 0x5A)*/enum KeyType{Key_Unknown,Key_Space 0x20,Key_Prior,Key_Next,Key_End,Key_Home,Key_Left,Key_Up,Key_Right,Key_Down,Key_Select,Key_Print,Key_Execute,Key_Snapshot,Key_Insert,Key_Delete,Key_Help,/* 主键盘上的数字键 */Key_0 0x30,Key_1,Key_2,Key_3,Key_4,Key_5,Key_6,Key_7,Key_8,Key_9,Key_A 0x41,Key_B,Key_C,Key_D,Key_E,Key_F,Key_G,Key_H,Key_I,Key_J,Key_K,Key_L,Key_M,Key_N,Key_O,Key_P,Key_Q,Key_R,Key_S,Key_T,Key_U,Key_V,Key_W,Key_X,Key_Y,Key_Z,/* 小键盘上的数字 */Key_NumPad_0 0x60,Key_NumPad_1,Key_NumPad_2,Key_NumPad_3,Key_NumPad_4,Key_NumPad_5,Key_NumPad_6,Key_NumPad_7,Key_NumPad_8,Key_NumPad_9,Key_F1 0x70,Key_F2,Key_F3,Key_F4,Key_F5,Key_F6,Key_F7,Key_F8,Key_F9,Key_F10,Key_F11,Key_F12,Key_F13,Key_F14,Key_F15,Key_F16,Key_F17,Key_F18,Key_F19,Key_F20,Key_F21,Key_F22,Key_F23,Key_F24,}; 键盘事件监听器定义 class DLL_export KeyEventListener : public EventListener{public:KeyEventListener();virtual ~KeyEventListener();virtual void keyPress(const KeyEvent event) {}virtual void keyRelease(const KeyEvent event) {}void handleEvent(const BaseEvent event);}; 对于按键信息的捕捉和鼠标事件一样在 handleMessage 函数中这里只截取了键盘事件 void WinMsgHandle::handleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){baseEvent.nEventID ET_UNKNOWN;/* 键盘按键事件信息 */if ( msg WM_KEYDOWN || msg WM_KEYUP ) {keyEvent.eventAction (msg WM_KEYDOWN) ? EventAction::ACT_PRESS : EventAction::ACT_RELAESE;keyEvent.keyType keyMap(( UINT ) wParam);keyEvent.keys[( UINT ) wParam] (msg WM_KEYDOWN) ? true : false;baseEvent.nEventID ET_KEY;baseEvent.pUserData keyEvent;EventDispatcher::getInstance()-dispatchEvent(baseEvent);}} 和鼠标事件一样获取按键数据 KeyEvent然后附加到 BaseEvent 中设置其 ID 为 ET_KEY最后由分派器分派事件。按键事件监听器处理 BaseEvent 时根据动作类型调用相应函数其函数有子类实现。 void KeyEventListener::handleEvent(const BaseEvent event){if ( event.nEventID ! EventType::ET_KEY event.pUserData ) return;KeyEvent* keyEvent static_castKeyEvent*(event.pUserData);switch ( keyEvent-eventAction ) {case Simple2D::ACT_PRESS: this-keyPress(*keyEvent); break;case Simple2D::ACT_RELAESE: this-keyRelease(*keyEvent); break;}} 最后在窗口的 proc 函数中 /* 处理鼠标和按键事件 */if ( self ) {self-winMsgHandle.handleMessage(wnd, msg, wParam, lParam);} 主循环中分派所有事件 if ( PeekMessage(msg, 0, 0, 0, PM_REMOVE) ) {TranslateMessage(msg);DispatchMessage(msg);EventDispatcher::getInstance()-flushEvent();} 新建一个测试类继承与鼠标事件监听器和按键事件监听器实现监听器中的函数输出到输出窗口 class EventTest : public MouseEventListener, public KeyEventListener
{
public://void mouseMove(const MouseEvent event)//{// log(mouse move);// log(x:%d - y:%d, event.nX, event.nY);//}void mousePress(const MouseEvent event){if ( event.buttonType ButtonType::LEFT_BUTTON ) {log(left button press);}else if ( event.buttonType ButtonType::MIDDLE_BUTTON ) {log(middle button press);}else if ( event.buttonType ButtonType::RIGHT_BUTTON ) {log(right button press);}log(x:%d - y:%d, event.nX, event.nY);}void mouseRelease(const MouseEvent event){log(mouse release);log(x:%d - y:%d, event.nX, event.nY);}void mouseDoubleClick(const MouseEvent event){log(mouse double click);log(x:%d - y:%d, event.nX, event.nY);}void mouseWheel(const MouseEvent event){log(mouse wheel);log(delta: %d, event.nDelta);}void keyPress(const KeyEvent event){if ( event.keys[KeyType::Key_A] event.keys[KeyType::Key_S] ) {log(同时按下 AS);}}void keyRelease(const KeyEvent event){if ( event.keyType KeyType::Key_NumPad_1 ) {log(释放键 1);}}
}; 运行程序的结果 源码下载http://pan.baidu.com/s/1skOmP21转载于:https://www.cnblogs.com/ForEmail5/p/6882998.html