UE4 输入系统详解一、 UE4如何获取win系统输入消息
UE4版本:4.253
按键输入
1、当我们按下键盘时输入时,FEngineLoop::Tick()里的每个tick执行的PumpMessages函数输送按键消息
WindowsPlatformApplicationMisc.cpp
void FWindowsPlatformApplicationMisc::PumpMessages(bool bFromMainLoop)
{
//{…忽略代码}
WinPumpMessages();
//{…忽略代码}
}
static void WinPumpMessages()
{
{
MSG Msg;
while( PeekMessage(&Msg,NULL,0,0,PM_REMOVE) )
{
TranslateMessage( &Msg );
DispatchMessage( &Msg );
}
}
}
PeekMessage,读取消息,非阻塞式获取输入消息。
TranslateMessage,转换消息,将虚拟键消息转换成字符消息。
DispatchMessage,发送消息,该函数调度一个消息给窗口程序。
2、然后FWindowsApplication在AppWndProc处接受Windows传进的message;
LRESULT CALLBACK FWindowsApplication::AppWndProc(HWND hwnd, uint32 msg, WPARAM wParam, LPARAM lParam)
{
ensure( IsInGameThread() );
return WindowsApplication->ProcessMessage( hwnd, msg, wParam, lParam );
}
3、ProcessMessage()根据Message的类型对Message分类处理然后执行DeferMessage()。
4、DeferMessage()检查消息是否需要延迟处理;延迟处理则放入处理延迟处理列表TArray DeferredMessages;否则立即执行ProcessDeferredMessage()。
5、ProcessDeferredMessage()根据Message类型调用MessageHandler的不同方法开始处理事件,如鼠标双击OnMouseDoubleClick (),W键按下OnKeyDown(),则分别执行:
MessageHandler->OnMouseDoubleClick( CurrentNativeEventWindowPtr,EMouseButtons::Left );
MessageHandler->OnKeyDown( ActualKey, CharCode, bIsRepeat );
6、转到FSlateApplication
FSlateApplication根据不同按键的做不同的处理,如按下W键则执行OnKeyDown(),然后各自找出当前所有FWidgetPath、然后对于每个FWidgetPath里的Widget生成FReply响应,并执行Widget里面对应的处理事件的方法。就是当我们在场景中按下w是向前走,但在聊天那里按下w则是输入字符”w”。
bool FSlateApplication::OnKeyDown( const int32 KeyCode, const uint32 CharacterCode, const bool IsRepeat )
{
FKey const Key = FInputKeyManager::Get().GetKeyFromCodes( KeyCode, CharacterCode );
FKeyEvent KeyEvent(Key, PlatformApplication->GetModifierKeys(), GetUserIndexForKeyboard(), IsRepeat, CharacterCode, KeyCode);
return ProcessKeyDownEvent( KeyEvent );
}
7、当在编辑器里的场景(PIE)中输入W键时,FSceneViewport会响应输入事件。
FReply FSceneViewport::OnKeyDown( const FGeometry& InGeometry, const FKeyEvent& InKeyEvent )
{
//{…}
//省略了判断代码
if (!ViewportClient->InputKey(FInputKeyEventArgs(this, InKeyEvent.GetUserIndex(), Key, InKeyEvent.IsRepeat() ? IE_Repeat : IE_Pressed, 1.0f, false)))
{
CurrentReplyState = FReply::Unhandled();
}
}
8、本次我是在场景中输入W键(控制角色向前移动),转到FSceneViewportWidget里面对应的处理事件。
然后执行FViewportClient::InputKey(),被派生类UGameViewportClient重写。
bool UGameViewportClient::InputKey(const FInputKeyEventArgs& EventArgs)
{
//{…}
//省略代码
bResult = TargetPlayer->PlayerController->InputKey(EventArgs.Key, EventArgs.Event, EventArgs.AmountDepressed, EventArgs.IsGamepad());
}
9、把按键信息发给PlayerController,然后交给UPlayerInput处理。
bool APlayerController::InputKey(FKey Key, EInputEvent EventType, float AmountDepressed, bool bGamepad)
{
//省略代码
bResult = PlayerInput->InputKey(Key, EventType, AmountDepressed, bGamepad);
}
轴输入
轴输入指的是鼠标的X轴和Y轴的滑动(PS:按键也是有轴输入的)。
1、当我们按下键盘时输入时,FEngineLoop::Tick()里的每个tick执行FSlateApplication::FinishedInputThisFrame()函数处理输入累积值
void FSlateApplication::FinishedInputThisFrame()
{
///代码省略…
///判断是否有主动捕获的Slate,有的话则遍历主动捕获的Widget处理累积值
if (User.HasAnyCapture())
{
for (const TSharedRef<SWidget>& Captor : User.GetCaptorWidgets())
{
Captor->OnFinishedPointerInput();
}
}
///遍历每个指针所在的最后一个Widget处理累积值
else
{
for (