Implementing touch controls for games.

by on

In the search for my struggles with the built-in touch interface for UnrealEngine's virtual joystick, I didn't find any articles that fixed the issue of being able to click a button and interface other UMG components while having the virtual joystick displayed while running the game level at the same time.

Mostly the recommendation is to disable the virtual joystick when you do menu input, but what if the game isn't paused?

The user is pretty much stuck until they are done using the menu.

There is a component called NativeWidgetHost, which lets you put Slate widget into a UMG blueprint with minimal fuss.

So you want to add one of these to your blueprint, give it a name and make sure that the Is Variable box is checked.

In this instance, we named it TouchControls.

Back in your C++ Classes in the Content Browser, Create a new subclass of UserWidget, if you haven't already.

In the header for the newly subclasses UserWidget add in:


#include "UMG/Public/Components/NativeWidgetHost.h"

 

As an include above your generated.h include.

And in your public: section add the following.

 

UPROPERTY(BlueprintReadOnly, meta = (BindWidget))
UNativeWidgetHost* TouchControls = nullptr;

 

Compile and then reparent your User Widget blueprint that you created the NativeWidgetHost in earlier to this subclass of UserWidget.

Now we can access the TouchControls variable from C++.

Next step is to create a reference to the widget and add it to the viewport, I recommend the GameInstance, but you can do that wherever you like.

In your PlayerController class, hopefully, you have a custom C++ version already if not go ahead and subclass PlayerController.

You could also override the function in Blueprints, but I'm not going to cover that.

We're going to add to the header:


protected:
    virtual void CreateTouchInterface() override;

public:
    void EnableTouchInterface();
    void DisableTouchInterface();

and in the .cpp:

 


void AMyPlayerController::CreateTouchInterface()
{
    ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player);

    // do we want to show virtual joysticks?
    if (LocalPlayer && LocalPlayer->ViewportClient && SVirtualJoystick::ShouldDisplayTouchInterface())
    {
        // in case we already had one, remove it
        if (VirtualJoystick.IsValid())
        {
            Cast<ULocalPlayer>(Player)->ViewportClient->RemoveViewportWidgetContent(VirtualJoystick.ToSharedRef());
        }

        if (CurrentTouchInterface == nullptr)
        {
            // load what the game wants to show at startup
            FSoftObjectPath DefaultTouchInterfaceName = GetDefault<UInputSettings>()->DefaultTouchInterface;

            if (DefaultTouchInterfaceName.IsValid())
            {
                // activate this interface if we have it
                CurrentTouchInterface = LoadObject<UTouchInterface>(NULL, *DefaultTouchInterfaceName.ToString());
            }
        }

        if (CurrentTouchInterface)
        {
            // create the joystick 
            VirtualJoystick = SNew(SVirtualJoystick);
        }
    }
}

 

void AMyPlayerController::EnableTouchInterface()
{
    ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player);

    // do we want to show virtual joysticks?
    if (LocalPlayer && LocalPlayer->ViewportClient && SVirtualJoystick::ShouldDisplayTouchInterface())
    {
        UMyGameInstance* instance = GetGameInstance<UMyGameInstance>();
        if (VirtualJoystick.IsValid())
        {
            instance->HUD->TouchControls->ReleaseSlateResources(true);
        }

        if (CurrentTouchInterface)
        {
            VirtualJoystick = SNew(SVirtualJoystick);
            instance->HUD->TouchControls->SetContent(VirtualJoystick.ToSharedRef());
            // create the joystick 
           
            ActivateTouchInterface(CurrentTouchInterface);
            instance->HUD->TouchControls->SetVisibility(ESlateVisibility::Visible);
        }
    }
}


void AMyPlayerController::DisableTouchInterface()
{
    ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player);

    // do we want to show virtual joysticks?
    if (LocalPlayer && LocalPlayer->ViewportClient && SVirtualJoystick::ShouldDisplayTouchInterface())
    {
        UMyGameInstance* instance = GetGameInstance<UMyGameInstance>();
        // in case we already had one, remove it
        if (VirtualJoystick.IsValid())
        {
            instance->HUD->TouchControls->ReleaseSlateResources(true);
            instance->HUD->TouchControls->SetVisibility(ESlateVisibility::Hidden);
        }
    }
}

 

So now, you can enable and disable the virtual joystick from being visible, and in the UMG blueprint, you can adjust the Z-Order of the NativeWidgetHost, and the virtual joystick will respect the Z-Ordering.

The astute reader will point out that we did not show you how to declare a reference or create the widget from code in the Game Instance, that wasn't what this post is about and perhaps we'll cover that in the future. It's pretty simple and there are a ton of people that have already covered it.

I hope you've found this helpful, we'll be posting more as we progress here.


Comments 0
No comments have been left yet.