UE4 输入系统详解一、 UE4如何获取win系统输入消息

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);
}

UE4 输入系统详解一、 UE4如何获取win系统输入消息_第1张图片

轴输入

轴输入指的是鼠标的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 (const auto& IndexPathPair :User.GetWidgetsUnderPointerLastEventByIndex())
	{
	for (const TWeakPtr<SWidget>& WidgetPtr : IndexPathPair.Value.Widgets)
				{
					if (TSharedPtr<SWidget> Widget = WidgetPtr.Pin())
					{
						Widget->OnFinishedPointerInput();
				}
			}
		}
	}
}

2、会有不同的Widget来响应轴输入,
本次测试环境是在游戏场景窗口中滑动鼠标。
所以会在视口SViewport响应轴输入。

void SViewport::OnFinishedPointerInput()
{
	///代码省略…
	///视图的呈现和I/O实现的接口,(当前环境即是SceneViewport)
	TSharedPtr<ISlateViewport> PinnedInterface = ViewportInterface.Pin();
	if (PinnedInterface.IsValid())
	{
		PinnedInterface->OnFinishedPointerInput();
	}
}

3、跳转到场景视口SceneViewport

void FSceneViewport::OnFinishedPointerInput()
{
	///当这个帧的输入完成时从slate调用,并且我们应该处理任何累积的鼠标数据。
	ProcessAccumulatedPointerInput();
}

void FSceneViewport::ProcessAccumulatedPointerInput()
{
	///代码省略…
	if (NumMouseSamplesX > 0 || NumMouseSamplesY > 0)
	{
		const float DeltaTime = FApp::GetDeltaTime();
		ViewportClient->InputAxis( this, 0, EKeys::MouseX, MouseDelta.X, DeltaTime, NumMouseSamplesX );
		ViewportClient->InputAxis( this, 0, EKeys::MouseY, MouseDelta.Y, DeltaTime, NumMouseSamplesY );
	}
}

其中NumMouseSamplesX 和NumMouseSamplesY 是在OnMouseMove修改的。

FReply FSceneViewport::OnMouseMove( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent )
{
	///代码省略…
	++NumMouseSamplesX;
	++NumMouseSamplesY;
}

OnMouseMove的流程和按键输入一样,结合上面的按键输入看下图
UE4 输入系统详解一、 UE4如何获取win系统输入消息_第2张图片
4、跳到游戏视口的接口:UGameViewportClient。

bool UGameViewportClient::InputAxis(FViewport* InViewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad)
{
///代码省略…
	bResult = TargetPlayer->PlayerController->InputAxis(Key, Delta, DeltaTime, NumSamples, bGamepad);
}

5、跳到角色控制器APlayerController

bool APlayerController::InputAxis(FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad)
{
	bool bResult = false;
	
	if (PlayerInput)
	{
		bResult = PlayerInput->InputAxis(Key, Delta, DeltaTime, NumSamples, bGamepad);
	}

	return bResult;
}

6、跳到角色输入:UPlayerInput
UPlayerInput:: InputAxis
后面会对UPlayerInput做详细分析。

FEngineLoop Tick() FSlateApplication FinishedInputThisFrame() ForEachUser() SViewport OnFinishedPointerInput() FSceneViewport ProcessAccumulatedPointerInput() UGameViewportClient InputAxis() APlayerController UPlayerInput* PlayerInput; InputAxis() UPlayerInput InputAxis()
链接
会有不同的窗口来响应轴输入
场景视口
游戏视口
FEngineLoop::Tick
FSlateApplication ::FinishedInputThisFrame
SViewport ::OnFinishedPointerInput
FSceneViewport::OnFinishedPointerInput
FSceneViewport::ProcessAccumulatedPointerInput
UGameViewportClient::InputAxis
APlayerController::InputAxis
UPlayerInput::InputAxis

你可能感兴趣的:(UE4,输入系统,游戏开发)