What happens is that when the game starts, the mouse cursor is shown, but not locked to screen. If I move it off the game screen and back again, then it becomes hidden within the game window. If I press the right mouse button while the cursor is visible, it is hidden, but not shown again when I release the right mouse button.
I changed DefaultInput.ini to have:
DefaultViewportMouseLockMode=LockInFullscreen
No difference.
I changed DefaultInput.ini to have:
DefaultViewportMouseLockMode=LockOnCapture
No difference.
I am a bit frustrated because earlier with, as far as I can remember, identical settings the cursor was initially locked to the game screen, just not after I pressed the right mouse button.
I have tried the code below, but it made no difference.
I have checked the code in Engine/Private/Slate/SceneViewport.cpp and can see what it is meant to be doing. The code all looks sensible at a brief glance. It just is not doing it.
In C++ you could restrict cursor position to geometry (screen / player screen / widget space),
by snapping the cursor to the edges or center of the geometry on Tick.
In the past (and possibly currently) some engine code related to the viewport, player input mode and cursor visibility setting have been misbehaving, leading to all sorts of broken behaviors. I have put my UI framework online for free which deals with it all, you can browse through it and see if there is anything else you need.
For example, I don’t use “SetShowMouseCursor” anywhere random for it’s bugged (and misleading, as it influences other behaviors).
Controlled by the HUD, I found this to work in my environment without bad behavior:
void UHUDCorePlayerControllerComponent::ActivateInputMode_Implementation(E_PlayerControllerInputModes InInputMode) {
APlayerController* PC = Cast<APlayerController>(GetOwner());
if (!IsValid(PC)) {
UE_LOG(LogUIAdditionsPlugin, Error, TEXT("Can't activate the input mode, because this component's owner is not a valid APlayerController."));
return;
}
switch (InInputMode) {
case (E_PlayerControllerInputModes::Game): {
//UWidgetBlueprintLibrary::SetInputMode_GameOnly(this);
FInputModeGameOnly InputModeStruct;
PC->SetInputMode(InputModeStruct);
PC->SetShowMouseCursor(false);
UE_LOG(LogUIAdditionsPlugin, Verbose, TEXT("Activated input mode: Game, SetShowMouseCursor: false"));
break;
}
case (E_PlayerControllerInputModes::GameAndUI): {
//UWidgetBlueprintLibrary::SetInputMode_GameAndUIEx(this, nullptr, EMouseLockMode::LockAlways, false);
// Prevent engine behavior that prevents widgets from responding to single click events: https://answers.unrealengine.com/questions/420047/an-lmb-event-is-only-triggered-with-a-double-click.html
//UGameplayStatics::SetViewportMouseCaptureMode(this, EMouseCaptureMode::NoCapture);
/** Workaround:
* bShowMouseCursor does not, like the name suggest, just alter 'cursor visibility'. It influences input behavior and misbehaves when set true combined with "Input Mode Game / Game + UI" (as configured in UWidgetBlueprintLibrary).
* The SceneViewPort releases focus if you release a mouse button in mode "GameAndUI" when bShowMouseCursor == true.This results in loss of mouse input axis data or loss of controls if not countered.Why that is implemented that way is unclear. As a workaround calling ActivateInputModeGameAndUI after mouse button release should be enough to counter that problem.
*/
FInputModeGameAndUI InputModeStruct;
InputModeStruct.SetLockMouseToViewportBehavior(EMouseLockMode::LockAlways);
InputModeStruct.SetHideCursorDuringCapture(false);
PC->SetInputMode(InputModeStruct);
PC->SetShowMouseCursor(true);
UE_LOG(LogUIAdditionsPlugin, Verbose, TEXT("Activated input mode: GameAndUI, SetShowMouseCursor: true"));
break;
}
case (E_PlayerControllerInputModes::UI): {
//UWidgetBlueprintLibrary::SetInputMode_UIOnlyEx(this, nullptr, EMouseLockMode::LockAlways);
FInputModeUIOnly InputModeStruct;
InputModeStruct.SetLockMouseToViewportBehavior(EMouseLockMode::LockAlways);
PC->SetInputMode(InputModeStruct);
PC->SetShowMouseCursor(true);
UE_LOG(LogUIAdditionsPlugin, Verbose, TEXT("Activated input mode: UI, SetShowMouseCursor: true"));
break;
}
}
InputMode = InInputMode;
}
I am using a software cursor to hide and show properly. A software cursor comes with a slight movement delay, but when you want to set the cursor position on tick, it won’t jump around like a hardware cursor does.
I let my HUD decide (based on context (is a menu open, is X happening)) how to manage input mode and cursor behavior. This centralizes the challenge to one solver and prevents users from manually calling bugged engine methods:
void AHUDCore::UpdateInputMode() {
APlayerController* PC = GetOwningPlayerController();
if (!IsValid(PC)) {
UE_LOG(LogUIAdditionsPlugin, VeryVerbose, TEXT("Can not update the current input mode without a valid owning player controller."));
return;
}
UHUDCorePlayerControllerComponent* HUDCorePCComponent = PC->FindComponentByClass<UHUDCorePlayerControllerComponent>();
if (!IsValid(HUDCorePCComponent)) {
UE_LOG(LogUIAdditionsPlugin, Error, TEXT("To update the input mode, the PlayerController requires a valid component of type HUDCorePlayerControllerComponent."));
return;
}
if (GetIsAnyPlayerViewportHUDMenuVisible() || GetIsAnyPlayerScreenHUDMenuVisible()) {
HUDCorePCComponent->ActivateInputMode(E_PlayerControllerInputModes::UI);
UE_LOG(LogUIAdditionsPlugin, VeryVerbose, TEXT("PlayerScreenHUD or PlayerViewportHUD has visible menu panels and desires the cursor to be unfrozen. Input mode set to 'UI'."));
SetFreezeCursorToCenterOfScreen(false);
}
else if (GetIsAnyPawnHUDMenuVisible()) {
HUDCorePCComponent->ActivateInputMode(E_PlayerControllerInputModes::GameAndUI);
UE_LOG(LogUIAdditionsPlugin, VeryVerbose, TEXT("PawnHUD has visible menu panels and desires the cursor to be unfrozen. Input mode set to 'Game + UI'."));
SetFreezeCursorToCenterOfScreen(false);
}
else {
HUDCorePCComponent->ActivateInputMode(E_PlayerControllerInputModes::Game);
UE_LOG(LogUIAdditionsPlugin, VeryVerbose, TEXT("No Sub HUD has a visible menu, the cursor does not have to be unfrozen. Input mode set to 'Game'."));
if (GetPawnDesiresCenteredWorldCursor()) {
UE_LOG(LogUIAdditionsPlugin, VeryVerbose, TEXT("The pawn desires the cursor to be frozen to center of screen (World Cursor Modifier Component). This is allowed in input mode 'Game'."));
SetFreezeCursorToCenterOfScreen(true);
}
else {
UE_LOG(LogUIAdditionsPlugin, VeryVerbose, TEXT("Unfreezing cursor from center of screen."));
SetFreezeCursorToCenterOfScreen(false);
}
}
}