Description

The Editor provides option "Project Settings – Engine – Input – Mouse Properties – Advanced – Use Mouse for Touch" to allow testing virtual joysticks and other touch controls with the mouse. When running the game under PIE on the Selected Viewport, this feature works correctly: the default virtual joystick appears and is usable, and the mouse cursor is kept visible and locked to the viewport while captured. On the other hand, when running under PIE on a New Editor Window, or on Standalone mode, the virtual joystick does not appear and the mouse cursor is incorrectly hidden while captured.

Interestingly, enabling the Debug Camera by pressing the ; (semicolon) hotkey shows the Virtual Joystick, and it is kept visible and usable after disabling the Debug Camera. Also, option "Project Settings – Engine – Input – Mobile – Always Show Touch Interface" can be enabled to force the virtual joystick to appear and be responsive in all cases. However, in all of these situations, the mouse cursor still gets hidden when captured by the viewport, which makes actually using the touch controls nearly impossible.

The only good workaround is passing "-faketouches" in the commandline when running either the Editor or a Packaged Build. This makes the behavior completely as expected in all cases.

Test results:
Repros in mainline CL 41413293
No public tracker found
Repros in all versions from at least 4.24

Steps to Reproduce

1. Create a new blank project (or open any existing project)
2. Go to Project Settings – Engine – Input – Mouse Properties – Advanced. Enable option "Use Mouse for Touch".
3. Start PIE on the Selected Viewport. The virtual joystick appears and is usable. The mouse cursor is kept visible and locked to the viewport.
4. Start PIE on a New Editor Window. The virtual joystick does not appear. The mouse cursor is hidden when captured by the viewport.
5. Start play in Standalone mode. The virtual joystick does not appear. The mouse cursor is hidden when captured by the viewport.
6. Repeat test 4, but during PIE, press the ; (semicolon) key twice to enable and disable the debug camera. The virtual joystick appears and is usable, but the mouse cursor is hidden and not locked to the viewport.
7. Repeat test 4, but before PIE, enable Project Settings – Engine – Input – Mobile – Always Show Touch Interface. The virtual joystick appears and is usable. The mouse cursor is locked to the viewport, but hidden.
8. Repeat test 5, but before running, add "-faketouches" to Editor Preferences – Level Editor – Play – Play in Standalone Game – Additional Launch Parameters. Everything works correctly as in test 3.

Callstack

The ill behavior and its workaround have been investigated. On file [SlateApplication.cpp], function "FSlateApplication::IsFakingTouchEvents()" is queried for most decisions that need to be made, and it returns "bIsFakingTouch || bIsGameFakingTouch". The first boolean is enabled when passing "-faketouches" in the commandline. The second is enabled by calling "FSlateApplication::SetGameIsFakingTouchEvents(true)". The problem occurs because, in the affected use-cases, FSlateApplication::SetGameIsFakingTouchEvents(true) is called AFTER important decisions have already been made using FSlateApplication::IsFakingTouchEvents().

The PIE case has been further analyzed. Both "Selected Viewport" and "New Editor Window" modes start with the following call chain:

UEditorEngine::StartQueuedPlaySessionRequest()
UEditorEngine::StartQueuedPlaySessionRequestImpl()
UEditorEngine::StartPlayInEditorSession()
UEditorEngine::CreateNewPlayInEditorInstance()
UEditorEngine::OnLoginPIEComplete_Deferred()
UEditorEngine::CreateInnerProcessPIEGameInstance()

Everything from now on takes place inside UEditorEngine::CreateInnerProcessPIEGameInstance(). In "Selected Viewport" mode, condition SlatePlayInEditorSession.DestinationSlateViewport.IsValid() evaluates to true, which triggers the following call chain:

SLevelViewport::StartPlayInEditorSession()
FSlateApplication::RegisterGameViewport()
FSlateApplication::ActivateGameViewport()
SViewport::OnViewportActivated()
FSceneViewport::OnViewportActivated()
UGameViewportClient::ReceivedFocus()
if (bUseMouseForTouch)
FSlateApplication::SetGameIsFakingTouchEvents(true)

Afterwards, in both PIE modes, we have the following call chain:

UGameInstance::StartPlayInEditorGameInstance()
ULocalPlayer::SpawnPlayActor()
UWorld::SpawnPlayActor()
APlayerController::SetPlayer()
APlayerController::InitInputSystem()
APlayerController::CreateTouchInterface()
SVirtualJoystick::ShouldDisplayTouchInterface()
FSlateApplication::IsFakingTouchEvents()
UWorld::BeginPlay()
AGameModeBase::StartPlay()
AGameStateBase::HandleBeginPlay()
AWorldSettings::NotifyBeginPlay()
AActor::DispatchBeginPlay()
APlayerController::BeginPlay()
FSlateApplication::IsFakingTouchEvents()

Here, SVirtualJoystick::ShouldDisplayTouchInterface() can be "fooled" by setting "bAlwaysShowTouchInterface", but FSlateApplication::IsFakingTouchEvents() only returns the expected result in the "Selected Viewport" mode because of the earlier call chain. As seen above, this affects SVirtualJoystick, making it not appear, and APlayerController, making it think that the mouse cursor must be hidden.

Finally, in "New Editor Window" mode, FSlateApplication::SetGameIsFakingTouchEvents(true) is called too late by the following chain (still inside UEditorEngine::CreateInnerProcessPIEGameInstance()):

FSlateApplication::RegisterGameViewport()
FSlateApplication::ActivateGameViewport()
SViewport::OnViewportActivated()
FSceneViewport::OnViewportActivated()
UGameViewportClient::ReceivedFocus()
if (bUseMouseForTouch)
FSlateApplication::SetGameIsFakingTouchEvents(true)

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-264645 in the post.

0
Login to Vote

Unresolved
ComponentUE - Gameplay - Input
Affects Versions5.5
Target Fix5.7
CreatedApr 1, 2025
UpdatedApr 18, 2025
View Jira Issue