新手光能
大家写游戏时总要监听用户做出了什么操作,这叫事件监听
监听键盘事件时,大家用GetAsyncKeyState,但是假如焦点不在控制台上,它也照样处理。
监听鼠标事件时,大家用GetAsyncKeyState+GetCursorPos,但是如果鼠标不在控制台窗口内,GetCursorPos照样捕获;如果鼠标点击了另一个窗口,GetAsyncKeyState照样处理
经过一番百度后,我找到了最终的解决方法:控制台事件接管,用ReadConsoleInput
函数原型:
WINBASEAPI WINBOOL WINAPI ReadConsoleInputA(
HANDLE hConsoleInput, //in
PINPUT_RECORD lpBuffer, //out
DWORD nLength, //in
LPDWORD lpNumberOfEventsRead); //out
我们暂时不做Unicode编程,统一用WINAPI函数后缀A的,当然,省略默认为后缀A,有办法改变这种默认行为(呃呃,有点跑题)
好,回归正题,我们解释一下参数
[in] HANDLE hConsoleInput
对于单缓冲区程序,默认为GetStdHandle(STD_INPUT_HANDLE)
[out] PINPUT_RECORD lpBuffer
首先定义一个INPUT_RECORD类型的数组,把数组名传进来
[in] DWORD nLength
数组的大小,有助于防止函数进行数组越界
[out] PDWORD lpNumberOfEventReads
实际读入的事件个数,得先定义一个DWORD类型的变量,这个参数这里填'&'加上变量名
微软文档的实例:
#include <windows.h>
#include <stdio.h>
HANDLE hStdin;
DWORD fdwSaveOldMode;
VOID ErrorExit(LPSTR);
VOID KeyEventProc(KEY_EVENT_RECORD);
VOID MouseEventProc(MOUSE_EVENT_RECORD);
VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD);
int main(VOID)
{
DWORD cNumRead, fdwMode, i;
INPUT_RECORD irInBuf[128];
int counter=0;
// Get the standard input handle.
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (hStdin == INVALID_HANDLE_VALUE)
ErrorExit("GetStdHandle");
// Save the current input mode, to be restored on exit.
if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
ErrorExit("GetConsoleMode");
// Enable the window and mouse input events.
fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
if (! SetConsoleMode(hStdin, fdwMode) )
ErrorExit("SetConsoleMode");
// Loop to read and handle the next 100 input events.
while (counter++ <= 100)
{
// Wait for the events.
if (! ReadConsoleInput(
hStdin, // input buffer handle
irInBuf, // buffer to read into
128, // size of read buffer
&cNumRead) ) // number of records read
ErrorExit("ReadConsoleInput");
// Dispatch the events to the appropriate handler.
for (i = 0; i < cNumRead; i++)
{
switch(irInBuf[i].EventType)
{
case KEY_EVENT: // keyboard input
KeyEventProc(irInBuf[i].Event.KeyEvent);
break;
case MOUSE_EVENT: // mouse input
MouseEventProc(irInBuf[i].Event.MouseEvent);
break;
case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
ResizeEventProc( irInBuf[i].Event.WindowBufferSizeEvent );
break;
case FOCUS_EVENT: // disregard focus events
case MENU_EVENT: // disregard menu events
break;
default:
ErrorExit("Unknown event type");
break;
}
}
}
// Restore input mode on exit.
SetConsoleMode(hStdin, fdwSaveOldMode);
return 0;
}
VOID ErrorExit (LPSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
// Restore input mode on exit.
SetConsoleMode(hStdin, fdwSaveOldMode);
ExitProcess(0);
}
VOID KeyEventProc(KEY_EVENT_RECORD ker)
{
printf("Key event: ");
if(ker.bKeyDown)
printf("key pressed\n");
else printf("key released\n");
}
VOID MouseEventProc(MOUSE_EVENT_RECORD mer)
{
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
printf("Mouse event: ");
switch(mer.dwEventFlags)
{
case 0:
if(mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
{
printf("left button press \n");
}
else if(mer.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
{
printf("right button press \n");
}
else
{
printf("button press\n");
}
break;
case DOUBLE_CLICK:
printf("double click\n");
break;
case MOUSE_HWHEELED:
printf("horizontal mouse wheel\n");
break;
case MOUSE_MOVED:
printf("mouse moved\n");
break;
case MOUSE_WHEELED:
printf("vertical mouse wheel\n");
break;
default:
printf("unknown\n");
break;
}
}
VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD wbsr)
{
printf("Resize event\n");
printf("Console screen buffer is %d columns by %d rows.\n", wbsr.dwSize.X, wbsr.dwSize.Y);
}
另外,我结合实例做了一个API。
#ifndef CONSOLEEVENT_H_
#define CONSOLEEVENT_H_
#include<windef.h>
#include<winbase.h>
#include<wincon.h>
#include<stdio.h>
short UnitSize=128;
INPUT_RECORD input_buffer[32768];
VOID ErrorExit(LPCSTR);
VOID (*KeyEvtProc)(KEY_EVENT_RECORD);
VOID (*MouseEvtProc)(MOUSE_EVENT_RECORD);
VOID (*ResEvtProc)(WINDOW_BUFFER_SIZE_RECORD);
void Listen()
{
DWORD cNumRead;
if (!ReadConsoleInput(
GetStdHandle(STD_INPUT_HANDLE), // input buffer handle
input_buffer, // buffer to read into
UnitSize, // size of read buffer
&cNumRead)) // number of records read
ErrorExit("ReadConsoleInput");
// Dispatch the events to the appropriate handler.
for(int i=0;i<cNumRead;i++)
{
switch(input_buffer[i].EventType)
{
case KEY_EVENT: // keyboard input
if(KeyEvtProc) KeyEvtProc(input_buffer[i].Event.KeyEvent);
break;
case MOUSE_EVENT: // mouse input
if(MouseEvtProc) MouseEvtProc(input_buffer[i].Event.MouseEvent);
break;
case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
if(ResEvtProc) ResEvtProc(input_buffer[i].Event.WindowBufferSizeEvent);
break;
case FOCUS_EVENT: // disregard focus events
case MENU_EVENT: // disregard menu events
break;
default:
ErrorExit("Unknown event type");
break;
}
}
}
VOID ErrorExit(LPCSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
// SetConsoleMode(GdtStdHandle(STD_INPUT_HANDLE),fdwSaveOldMode);
ExitProcess(0);
}
#endif
把它加到头文件后,using namespace std;前,
使用方法见WP文档(等写完文档后把链接发出来)
王牌工作室官方在2022-03-25 13:39:35追加了内容
那个,我封装的那个要改成这样
#ifndef CONSOLEEVENT_H_
#define CONSOLEEVENT_H_
#include<windef.h>
#include<winbase.h>
#include<wincon.h>
#include<stdio.h>
short UnitSize=128;
INPUT_RECORD input_buffer[32768];
VOID ErrorExit(LPCSTR);
VOID (*KeyEvtProc)(KEY_EVENT_RECORD);
VOID (*MouseEvtProc)(MOUSE_EVENT_RECORD);
VOID (*ResEvtProc)(WINDOW_BUFFER_SIZE_RECORD);
void InitConsoleEvent()
{
DWORD fdwMode=ENABLE_WINDOW_INPUT|ENABLE_MOUSE_INPUT;
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),fdwMode);
}
void Listen()
{
DWORD cNumRead;
if (!ReadConsoleInput(
GetStdHandle(STD_INPUT_HANDLE), // input buffer handle
input_buffer, // buffer to read into
UnitSize, // size of read buffer
&cNumRead)) // number of records read
ErrorExit("ReadConsoleInput");
// Dispatch the events to the appropriate handler.
for(int i=0;i<cNumRead;i++)
{
switch(input_buffer[i].EventType)
{
case KEY_EVENT: // keyboard input
if(KeyEvtProc) KeyEvtProc(input_buffer[i].Event.KeyEvent);
break;
case MOUSE_EVENT: // mouse input
if(MouseEvtProc) MouseEvtProc(input_buffer[i].Event.MouseEvent);
break;
case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
if(ResEvtProc) ResEvtProc(input_buffer[i].Event.WindowBufferSizeEvent);
break;
case FOCUS_EVENT: // disregard focus events
case MENU_EVENT: // disregard menu events
break;
default:
ErrorExit("Unknown event type");
break;
}
}
}
VOID ErrorExit(LPCSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
// SetConsoleMode(GdtStdHandle(STD_INPUT_HANDLE),fdwSaveOldMode);
ExitProcess(0);
}
#endif
王牌工作室官方在2022-03-26 15:09:25追加了内容
新的1.4版本的ConsoleEvent和文档一起发出
源码:https://gitee.com/wp-studio/code-base/blob/master/ConsoleEvent.h
文档:https://gitee.com/wp-studio/repository/blob/master/WangpaiConsoleEventApiDunuments.docx
王牌工作室官方在2022-03-26 15:11:29追加了内容
1.4版本得感谢@薛乘志,是他给我提的建议