Toggle bShowMouseCursor (or SetShowMouseCursor) at runtime

Hello, everyone!
I’m working on a UE v5.3.2 C++ & Blueprint project (mostly the former).
I’m currently experiencing some issues with the UI. The worst of it all is the function SetShowMouseCursor or, in general, the way the porperty that shows the cursor is handled. I’m unable to toggle the cursor visibility at runtime correctly, no matter what I try to make it work.
Here is the best solution (so far) I’ve implemented:

  • Created a PlayerController that switches between my PlayerCharacter and a Pawn that I use specifically for the UI (a workaround for another issue);
  • Emitted a delegate that broadcasts from the Pawn class everytime it registers a mouse movement (the most effective way was to create an Input Action and bind it to the mouse XY axes);
  • Put the delegate subscription in the controller’s BeginPlay function, which calls another function that handles the visibility of the mouse cursor;
// HybridController.h

// ...

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HybridController")
TSubclassOf<AUIPawn> PawnClass;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HybridController")
TSubclassOf<ACustomCharacter> CharacterClass;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HybridController")
AUIPawn* SpawnedPawn;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HybridController")
ACustomCharacter* SpawnedCharacter;

// ...

------------------------------------------------------------------------
// HybridController.cpp

void AHybridController::BeginPlay()
{
    Super::BeginPlay();

    if (PawnClass)
    {
        FActorSpawnParameters SpawnParams;
        SpawnParams.Owner = this;
        SpawnParams.Instigator = GetPawn();

        FVector ActorSpawnLocation = GetPawn() ? GetPawn()->GetActorLocation() + FVector(200, 0, 0) : FVector(0, 0, 200);
        FRotator SpawnRotation = FRotator::ZeroRotator;

        SpawnedPawn = GetWorld()->SpawnActor<AUIPawn>(PawnClass, ActorSpawnLocation, SpawnRotation, SpawnParams);
        SpawnedPawn->OnToggleShowCursor.AddDynamic(this, &AHybridController::ToggleShowCursor);
    }

    // ...
}

// ...

void AHybridController::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    if (bUIMode && SpawnedPawn)
    {
        SpawnedPawn->DelegateTick(DeltaTime);
    }
    else if (!bUIMode && SpawnedCharacter)
    {
        SpawnedCharacter->DelegateTick(DeltaTime);
    }
}

void AHybridController::ToggleShowCursor(bool bShow)
{
    GEngine->AddOnScreenDebugMessage(2, 1.0f, FColor::Green, FString::Printf(TEXT("TOGGLE CALLED!")), true);
    if (bShowMouseCursor != bShow) SetShowMouseCursor(bShow);
}

------------------------------------------------------------------------
// UIPawn.h

// ...
private:
    float MouseVisibleTimer = 0.f;
    bool bMouseVisible = false;

------------------------------------------------------------------------
// UIPawn.cpp

// ...

void AUIPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    if (UEnhancedInputComponent* Input = Cast<UEnhancedInputComponent>(PlayerInputComponent))
    {
        // ...
        Input->BindAction(IA_Mouse, ETriggerEvent::Started, this, &AUIPawn::HandleMouseMove);
        Input->BindAction(IA_Mouse, ETriggerEvent::Completed, this, &AUIPawn::EndMouseMove);
    }
}

// ..

void AUIPawn::DelegateTick(float DeltaTime) 
{
    if (MouseVisibleTimer > 0.f)
    {
        MouseVisibleTimer -= DeltaTime;
    }
    else if (bMouseVisible)
    {
        MouseVisibleTimer = 0.f;
        bMouseVisible = false;
        OnToggleShowCursor.Broadcast(false);
    }
}

// ...

void AUIPawn::HandleMouseMove()
{
    bMouseVisible = true;
    OnToggleShowCursor.Broadcast(true);
}

void AUIPawn::EndMouseMove()
{
    MouseVisibleTimer = UIConstants::CursorVisibilityDuration; // 5.0f
}

Despite the efforts, this implementation hides the cursor when MouseVisibleTimer becomes <= 0 (desired behaviour), but takes around 1-2 seconds to make it visible again after i start moving the mouse (IF it makes it visible AT ALL).
The worst thing is, the test log gets printed EXACTLY when it should (as soon as the mouse starts moving and 5 seconds after the last movement was detected).

Another thing worth mentioning is that both these classes have blueprint children.
Also, I used the “Standalone Game” and the “Selected Viewport” symulation modes.

Am I missing something? Is it possible that Blueprint override something that I missed?
Any help would be appreciated.

Well, first of all - Am I get it right, that you searching ways to hide cursor after a certain amount of time; and “wake” it back on any mouse move?

If so, and console logs tells everything is good - maybe you should try test in Standalone Game, since this mode gives more accurate result. Better ones is to build project.

1 Like

First of all, thank you for your answer!
You understood it correctly.
Second, sadly I already tried using Standalone Game and it does not work. About building the project, i always use the “build solution“ option in Visual Studio and never use Live Coding from the Unreal Editor. Despite all this, the “waking up” of the cursor takes some time (usually 1-2 seconds) IF it even works (sometimes i have to “restart“ moving the mouse to trigger it because the first delegate call gets ignored).

EDIT#1: I just realized that you might mean “actually making a stantalone version of the game” by building it. I’m going to try and keep you updated.

Edit#2: The delay still stands, but it’s now a fraction of a second and the cursor shows every time. Not the bug-free solution that i was hoping for, but it’s the best result i’ve got. Thank you again for your help!

Edit#3: After some more testing, now the cursor disappears after a second click on any widget, appearing again after another click, no matter how much I move it. After that, I can no longer focus on the buttons with my mouse cursor. It is only after a third click that the situation restores to normal. Then, if i click again, it’s back to square 1. Should I just report it as an Unreal bug? This thing has been killing me for months!

Okey, this might be just a thought, but what is an IA_Mouse? Axis2D value type? If so, try to make it simple - without delegatetick for example. And bind action on ETriggerEvent::Triggered also. Just to be sure about “delay bug”.

Or, u can try to make custom cursor widget and manage visibilty on it.

Also I might note (after solving another issues with cursor visibility) that mouse cursor aren’t use straightforward logic as u could think of it first. There are some sort of unintuitive behavior under the hood, like mouse cursor is still captured, even hided, events are proceed (or not proceed?), and etc.

1 Like

Correct, it is an Axis2D type Input Action.
I tried binding it to an ETriggerEvent::Triggered, but not without removing the DelegateTick.
In a couple days I’m giving this a try.
Maybe, adding a custom widget as a “fake cursor” might even do it as a workaround.
Thanks, if this turns out to be the solution, I’ll mark your answer as the solution (I really hope to do so).
Thank you so much, I really appreciate it!

1 Like