How to bind to MotionController event in C++?

All the information I can find about binding to input events in C++ seems to require me to manually name a bunch of axes and actions in the project settings. But I don’t want to do that, I just want to bind to events that are already there! Specifically, I am doing this for VR, so I need to bind to the MotionController input events.

How can I do this? Is it possible to bind to events without manually naming things? Do I really have to do an approach like described in this question? https://answers.unrealengine.com/questions/139056/input-keyobard-binding-in-c.html

Here is how I got it working. I used this link as a reference to all the Vive motion controller bindings.


Add Dependencies to Your Build.cs File

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "SteamVR", "SteamVRController", "HeadMountedDisplay" });

I added “SteamVR”, “SteamVRController”, “HeadMountedDisplay” to the already existing PublicDependencyModuleNames.AddRange arguments.


In /Config/DefaultInput.ini you have to add your action mappings

; Vive Mappings
+ActionMappings=(ActionName="MenuButtonLeft",Key=MotionController_Left_Shoulder,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="LeftTouchpadPress",Key=MotionController_Left_Thumbstick,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="LeftTouchpadTouch",Key=Steam_Touch_0,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="LeftGrip",Key=MotionController_Left_Grip1,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="LeftTrigger",Key=MotionController_Left_Trigger,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="MenuButtonRight",Key=MotionController_Right_Shoulder,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="RightTouchpadPress",Key=MotionController_Right_Thumbstick,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="RightTouchpadTouch",Key=Steam_Touch_1,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="RightGrip",Key=MotionController_Right_Grip1,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+ActionMappings=(ActionName="RightTrigger",Key=MotionController_Right_Trigger,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+AxisMappings=(AxisName="LeftTriggerAnalog",Key=MotionController_Left_TriggerAxis,Scale=1.000000)
+AxisMappings=(AxisName="LeftTouchpadX",Key=MotionController_Left_Thumbstick_X,Scale=1.000000)
+AxisMappings=(AxisName="LeftTouchpadY",Key=MotionController_Left_Thumbstick_Y,Scale=1.000000)
+AxisMappings=(AxisName="RightTriggerAnalog",Key=MotionController_Right_TriggerAxis,Scale=1.000000)
+AxisMappings=(AxisName="RightTouchpadX",Key=MotionController_Right_Thumbstick_X,Scale=1.000000)
+AxisMappings=(AxisName="RightTouchpadY",Key=MotionController_Right_Thumbstick_Y,Scale=1.000000)

Just copy and paste that from the link. Then in your player/character header .h file you add your functions. I didn’t do all of them for this example, instead I just used the trigger pressed and released:


Define Event Functions in Header

UFUNCTION()
virtual void MotionControlLeftTriggerPressed();

UFUNCTION()
virtual void MotionControlLeftTriggerReleased();

UFUNCTION()
virtual void MotionControlRightTriggerPressed();
	
UFUNCTION()
virtual void MotionControlRightTriggerReleased();

You can name those functions whatever you want because next we are adding them to the player source .cpp file.


Create Functions in Source

// Left Trigger Press
void APlayer_VR::MotionControlLeftTriggerPressed()
{
	UE_LOG(LogTemp, Warning, TEXT("Left trigger is PRESSED"));
}

// Left Trigger Release
void APlayer_VR::MotionControlLeftTriggerReleased()
{
	UE_LOG(LogTemp, Warning, TEXT("Left trigger is RELEASED"));
}

// Right Trigger Press
void APlayer_VR::MotionControlRightTriggerPressed()
{
	UE_LOG(LogTemp, Warning, TEXT("Right trigger is PRESSED"));
}

// Right Trigger Release
void APlayer_VR::MotionControlRightTriggerReleased()
{
	UE_LOG(LogTemp, Warning, TEXT("Right trigger is RELEASED"));
}

Last but not least, while still in the source .cpp file for your player you have to add the bindings in the SetupPlayerInputComponent function. It should be there automatically when you set up your player class but if not add this to the header file.


Override Input Component Function

// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;

And then back in the .cpp add your bindings under that function:

void APlayer_VR::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	Super::SetupPlayerInputComponent(InputComponent);
	
	//Motion controller bindings
	InputComponent->BindAction("LeftTrigger", EInputEvent::IE_Pressed, this, &APlayer_VR::MotionControlLeftTriggerPressed);
	InputComponent->BindAction("LeftTrigger", EInputEvent::IE_Released, this, &APlayer_VR::MotionControlLeftTriggerReleased);
	InputComponent->BindAction("RightTrigger", EInputEvent::IE_Pressed, this, &APlayer_VR::MotionControlRightTriggerPressed);
	InputComponent->BindAction("RightTrigger", EInputEvent::IE_Released, this, &APlayer_VR::MotionControlRightTriggerReleased);
}

Add Motion Controllers to Pawn

I’m sure that if you at the point of adding all this stuff you’ve already added the motion controllers to the constructor but if you haven’t make sure you define those in the .h

// Left motion controller
UPROPERTY(EditDefaultsOnly)
class UMotionControllerComponent* LeftMotionController;

//Right motion controller
UPROPERTY(EditDefaultsOnly)
class UMotionControllerComponent* RightMotionController;

And then add the variables in the constructor of the .cpp file and attach them to the root component.

// Add root component.
USceneComponent* RootSceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
RootComponent = RootSceneComponent;

// Add motion controllers
LeftMotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("LeftMotionController"));
LeftMotionController->Hand = EControllerHand::Left;
LeftMotionController->SetupAttachment(RootComponent);
RightMotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("RightMotionController"));
RightMotionController->Hand = EControllerHand::Right;
RightMotionController->SetupAttachment(RootComponent);

Then just continue to add more of the buttons in following the same pattern and add what happens when the buttons are interacted with to the individual functions that you created. I have them set up to write a quick entry in the log in visual studio just so I can tell they are working but you could also write to screen or make a sound or animate something…whatever you want.

This is fantastic info, thanks for this! I will give this a try later today and report back with how it goes.

I literally just figured this out last night. I had found your question a few days ago and saved it so I could get this info on the net. I have barely played with it but the functions do write to the screen log when I click the triggers so its working. I might add some more info or organize it a bit when I have a chance to play with my code a little more.

Hope it helps you out!

I found out it was important to also set the hand of the motion controller when you create them so I added that to the code. It’s under the last section where I add the motion controllers. Before I did this they were both returning the exact same location in the world.

Also note, you’ll need to add ------ #include “HeadMountedDisplay.h” and #include “MotionControllerComponent.h” in your character header file.

This is a useful link .

Thank you for this! Just a heads up for those using a newer version of Unreal (in my case, 4.20), you should map movement for the Vive controllers using BindAxis (instead of BindAction) and “RightTriggerAnalogue” (instead of “RightTrigger”).

i.e.

PlayerInputComponent->BindAction("RightTrigger", EInputEvent::IE_Pressed, this, &APlayer::MotionControlRightTriggerPressed);

becomes

PlayerInputComponent->BindAxis("RightTriggerAnalog", this, &APlayer::MoveForward);