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. Open the Third Person template project. Enable the "Use Mouse For Touch" project setting or editor preference.
  2. PIE in the current viewport. Notice that the mobile touch joystick is on screen, as expected.
  3. Change to PIE in a new window, or "Standalone" mode.
  4. Play

Result: The virtual on screen joystick is not there, even though the project setting/editor preference is set to true.

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

Fixed
ComponentUE - Gameplay - Input
Affects Versions5.5
Target Fix5.7
Fix Commit44072392
CreatedApr 1, 2025
ResolvedJul 15, 2025
UpdatedJul 21, 2025
View Jira Issue