Help us understand the problem. What is going on with this article?

UnrealC++ でキーをアサインする方法、VR(Vive)版

More than 1 year has passed since last update.

Unreal Engine 4 (UE4) その2 Advent Calendar 2018 の23日目です
https://qiita.com/advent-calendar/2018/ue4_zwei

UnrealC++のキーアサイン機能

C++erにとってエディターで各ボタンの名称を管理することが面倒な場合もあると思います。
そういった場合への対策で、C++コード内で管理したい人も多いと思います。
けどこういうUE4のこの手の資料って意外と見つからないんですよね。
使用する関数名を知ってれば簡単に見つかるんですけど、
知らないと見つけるのが困難という何とも悩ましい仕組みです。
なのでリマインドの意味も含めて纏めてみました。

※注意点
サンプルでは日本語コメントが書かれていますが
実際にUE4でコンパイルしようとすると日本語コメントでエラーが出る場合があります。
場合によりコメントアウトされてる日本語が原因でエラーが出ます。(大切なことなので2度)
最近のバージョンは分かりませんが基本的には日本語コメントは削除するようお願いします。

基本的な方法

ボタンの基本としては、
1、アクションを登録する。
2、ボタンに対する関数を指定する。

軸の基本としては
1、軸を登録する。
2、軸を動かしたときに実行する関数を指定する。

になります。
この2通りの方法ですべてのボタン、軸を扱えます。

尚、SetupPlayerInputComponent内で事を済ませる必要がありますので注意が必要です。
簡単な例

sample.cpp
// Called to bind functionality to input
void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//アクションを登録して
UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("RTrigger", EKeys::MotionController_Right_Trigger));
//実行する関数を指定する。
InputComponent->BindAction("RTrigger", IE_Pressed, this, &UInputManager::btpRTrigger);

//軸を登録して
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("RPadX", EKeys::MotionController_Right_Thumbstick_X, 1.0f));
//実行する関数を指定する。
InputComponent->BindAxis("RPadX", this, &UInputManager::axRPadX);
}

管理の外部化

アクターコンポーネントで管理を外部化します。
入力関係は凄くかさばるので分離しておく方が良いでしょう。

InputManager.h
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "InputManager.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMyEvent);

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class QUIITA_API UInputManager : public UActorComponent
{
    GENERATED_BODY()

public: 
    // Sets default values for this component's properties
    UInputManager();

protected:
    // Called when the game starts
    virtual void BeginPlay() override;

public: 
    // Called every frame
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, 
        FActorComponentTickFunction* ThisTickFunction) override;

    void SetupInput(class UInputComponent* InputComponent);

    //ボタン押した、ボタン離したイベント用のデリゲート
    //BP側へも公開され、関数を実行できます。
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPRTapDown;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPRTapUp;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPRTapRight;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPRTapLeft;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPRThoulder;

    UPROPERTY(BlueprintAssignable)
        FMyEvent evPLTapDown;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPLTapUp;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPLTapRight;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPLTapLeft;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPLThoulder;

    UPROPERTY(BlueprintAssignable)
        FMyEvent evPRTrigger;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evRRTrigger;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPLTrigger;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evRLTrigger;

    UPROPERTY(BlueprintAssignable)
        FMyEvent evPRGrip;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evRRGrip;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evPLGrip;
    UPROPERTY(BlueprintAssignable)
        FMyEvent evRLGrip;

    //ボタンを押された時に発生させるイベントの関数
    //bt = ボタン
    //p = push , r = release
    //を示します。
    void  btpRTrigger();
    void  btpRPadLeft();
    void  btpRPadTop();
    void  btpRPadBottom();
    void  btpRPadRight();
    void  btpRThoulder();
    void  btpRGrip();

    void  btpLTrigger();
    void  btpLPadLeft();
    void  btpLPadTop();
    void  btpLPadBottom();
    void  btpLPadRight();
    void  btpLThoulder();
    void  btpLGrip();

    void  btrRTrigger();
    void  btrRPadLeft();
    void  btrRPadTop();
    void  btrRPadBottom();
    void  btrRPadRight();
    void  btrRThoulder();
    void  btrRGrip();

    void  btrLTrigger();
    void  btrLPadLeft();
    void  btrLPadTop();
    void  btrLPadBottom();
    void  btrLPadRight();
    void  btrLThoulder();
    void  btrLGrip();

    void axRPadX(float axis);
    void axRPadY(float axis);

    void axLPadX(float axis);
    void axLPadY(float axis);

    //各ボタンの現在状態を保持します。
    UPROPERTY(BlueprintReadOnly)
        bool m_RTrigger = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_RPadLeft = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_RPadTop = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_RPadBottom = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_RPadRight = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_RThoulder = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_RGrip = false;

    UPROPERTY(BlueprintReadOnly)
        bool m_LTrigger = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_LPadLeft = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_LPadTop = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_LPadBottom = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_LPadRight = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_LThoulder = false;
    UPROPERTY(BlueprintReadOnly)
        bool m_LGrip = false;

    UPROPERTY(BlueprintReadOnly)
        FVector2D m_RpadInput;
    UPROPERTY(BlueprintReadOnly)
        FVector2D m_LpadInput;
};

Cpp側のコード

InputManager.cpp
// Fill out your copyright notice in the Description page of Project Settings.

#include "InputManager.h"
#include "classes/GameFramework/PlayerInput.h"
#include "Runtime/Engine/Classes/GameFramework/PlayerController.h"
#include "Classes/Components/InputComponent.h"

// Sets default values for this component's properties
UInputManager::UInputManager()
{
    // Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
    // 余分なTickを発生させないためfalseへ変更します。
    PrimaryComponentTick.bCanEverTick = false;
    // ...
}

// Called when the game starts
void UInputManager::BeginPlay()
{
    Super::BeginPlay();
    // ...
}


// Called every frame
void UInputManager::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    // ...
}

void UInputManager::SetupInput(class UInputComponent* InputComponent)
{
    // アクションと名称を登録
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("RTrigger", EKeys::MotionController_Right_Trigger));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("RPadLeft", EKeys::MotionController_Right_FaceButton4));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("RPadTop", EKeys::MotionController_Right_FaceButton1));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("RPadBottom", EKeys::MotionController_Right_FaceButton3));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("RPadRight", EKeys::MotionController_Right_FaceButton2));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("RThoulder", EKeys::MotionController_Right_Shoulder));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("RGrip", EKeys::MotionController_Right_Grip1));

    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("LTrigger", EKeys::MotionController_Left_Trigger));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("LPadLeft", EKeys::MotionController_Left_FaceButton4));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("LPadTop", EKeys::MotionController_Left_FaceButton1));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("LPadBottom", EKeys::MotionController_Left_FaceButton3));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("LPadRight", EKeys::MotionController_Left_FaceButton2));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("LThoulder", EKeys::MotionController_Left_Shoulder));
    UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("LGrip", EKeys::MotionController_Left_Grip1));

    // 使用する軸と名称を登録する。
    UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("RPadX", EKeys::MotionController_Right_Thumbstick_X, 1.0f));
    UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("RPadY", EKeys::MotionController_Right_Thumbstick_Y, 1.0f));

    UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("LPadX", EKeys::MotionController_Left_Thumbstick_X, 1.0f));
    UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("LPadY", EKeys::MotionController_Left_Thumbstick_Y, 1.0f));

    // 実行する関数を指定する。
    InputComponent->BindAction("RTrigger", IE_Pressed, this, &UInputManager::btpRTrigger);
    InputComponent->BindAction("RPadLeft", IE_Pressed, this, &UInputManager::btpRPadLeft);
    InputComponent->BindAction("RPadTop", IE_Pressed, this, &UInputManager::btpRPadTop);
    InputComponent->BindAction("RPadBottom", IE_Pressed, this, &UInputManager::btpRPadBottom);
    InputComponent->BindAction("RPadRight", IE_Pressed, this, &UInputManager::btpRPadRight);
    InputComponent->BindAction("RThoulder", IE_Pressed, this, &UInputManager::btpRThoulder);
    InputComponent->BindAction("RGrip", IE_Pressed, this, &UInputManager::btpRGrip);

    InputComponent->BindAction("LTrigger", IE_Pressed, this, &UInputManager::btpLTrigger);
    InputComponent->BindAction("LPadLeft", IE_Pressed, this, &UInputManager::btpLPadLeft);
    InputComponent->BindAction("LPadTop", IE_Pressed, this, &UInputManager::btpLPadTop);
    InputComponent->BindAction("LPadBottom", IE_Pressed, this, &UInputManager::btpLPadBottom);
    InputComponent->BindAction("LPadRight", IE_Pressed, this, &UInputManager::btpLPadRight);
    InputComponent->BindAction("LThoulder", IE_Pressed, this, &UInputManager::btpLThoulder);
    InputComponent->BindAction("LGrip", IE_Pressed, this, &UInputManager::btpLGrip);

    InputComponent->BindAction("RTrigger", IE_Released, this, &UInputManager::btrRTrigger);
    InputComponent->BindAction("RPadLeft", IE_Released, this, &UInputManager::btrRPadLeft);
    InputComponent->BindAction("RPadTop", IE_Released, this, &UInputManager::btrRPadTop);
    InputComponent->BindAction("RPadBottom", IE_Released, this, &UInputManager::btrRPadBottom);
    InputComponent->BindAction("RPadRight", IE_Released, this, &UInputManager::btrRPadRight);
    InputComponent->BindAction("RThoulder", IE_Released, this, &UInputManager::btrRThoulder);
    InputComponent->BindAction("RGrip", IE_Released, this, &UInputManager::btrRGrip);

    InputComponent->BindAction("LTrigger", IE_Released, this, &UInputManager::btrLTrigger);
    InputComponent->BindAction("LPadLeft", IE_Released, this, &UInputManager::btrLPadLeft);
    InputComponent->BindAction("LPadTop", IE_Released, this, &UInputManager::btrLPadTop);
    InputComponent->BindAction("LPadBottom", IE_Released, this, &UInputManager::btrLPadBottom);
    InputComponent->BindAction("LPadRight", IE_Released, this, &UInputManager::btrLPadRight);
    InputComponent->BindAction("LThoulder", IE_Released, this, &UInputManager::btrLThoulder);
    InputComponent->BindAction("LGrip", IE_Released, this, &UInputManager::btrLGrip);

    // 軸を動かしたときに実行する関数を指定
    InputComponent->BindAxis("RPadX", this, &UInputManager::axRPadX);
    InputComponent->BindAxis("RPadY", this, &UInputManager::axRPadY);
    InputComponent->BindAxis("LPadX", this, &UInputManager::axLPadX);
    InputComponent->BindAxis("LPadY", this, &UInputManager::axLPadY);
}

void UInputManager::btpRTrigger()
{
    m_RTrigger = true;
    evPRTrigger.Broadcast();
}
void UInputManager::btpRPadLeft()
{
    m_RPadLeft = true;
    evPRTapLeft.Broadcast();
}
void UInputManager::btpRPadTop()
{
    m_RPadTop = true;
    evPRTapUp.Broadcast();
}
void UInputManager::btpRPadBottom()
{
    m_RPadBottom = true;
    evPRTapDown.Broadcast();
}
void UInputManager::btpRPadRight()
{
    m_RPadRight = true;
    evPRTapRight.Broadcast();
}
void UInputManager::btpRThoulder() 
{ 
    m_RThoulder = true; 
    evPRThoulder.Broadcast();
}
void UInputManager::btpRGrip()
{
    m_RGrip = true;
    evPRGrip.Broadcast();
}

void UInputManager::btpLTrigger()
{
    m_LTrigger = true;
    evPLTrigger.Broadcast();
}
void UInputManager::btpLPadLeft()
{
    m_LPadLeft = true;
    evPLTapLeft.Broadcast();
}
void UInputManager::btpLPadTop()
{
    m_LPadTop = true;
    evPLTapUp.Broadcast();
}
void UInputManager::btpLPadBottom()
{
    m_LPadBottom = true;
    evPLTapDown.Broadcast();
}
void UInputManager::btpLPadRight()
{
    m_LPadRight = true;
    evPLTapRight.Broadcast();

}
void UInputManager::btpLThoulder() 
{ 
    m_LThoulder = true;
    evPLThoulder.Broadcast();
}
void UInputManager::btpLGrip()
{
    m_LGrip = true;
    evPLGrip.Broadcast();
}

void UInputManager::btrRTrigger()
{
    m_RTrigger = false;
    evRRTrigger.Broadcast();
}
void UInputManager::btrRPadLeft() { m_RPadLeft = false; }
void UInputManager::btrRPadTop() { m_RPadTop = false; }
void UInputManager::btrRPadBottom() { m_RPadBottom = false; }
void UInputManager::btrRPadRight() { m_RPadRight = false; }
void UInputManager::btrRThoulder() { m_RThoulder = false; }
void UInputManager::btrRGrip()
{
    m_RGrip = false;
    evRRGrip.Broadcast();
}

void UInputManager::btrLTrigger()
{
    m_LTrigger = false;
    evRLTrigger.Broadcast();
}
void UInputManager::btrLPadLeft() { m_LPadLeft = false; }
void UInputManager::btrLPadTop() { m_LPadTop = false; }
void UInputManager::btrLPadBottom() { m_LPadBottom = false; }
void UInputManager::btrLPadRight() { m_LPadRight = false; }
void UInputManager::btrLThoulder() { m_LThoulder = false; }
void UInputManager::btrLGrip()
{
    m_LGrip = false;
    evRLGrip.Broadcast();
}

void UInputManager::axRPadX(float axis)
{
    m_RpadInput.X = axis;
}
void UInputManager::axRPadY(float axis)
{
    m_RpadInput.Y = axis;
}

void UInputManager::axLPadX(float axis)
{
    m_LpadInput.X = axis;
}
void UInputManager::axLPadY(float axis)
{
    m_LpadInput.Y = axis;
}

Pawn or Character側の処理

普通にアクターコンポーネントを登録するだけですが
UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
の行が重要です。
アクターコンポーネントを登録する際、この行が無くて一見動くように見えますがちょいちょいバグったりします。
(PawnなのにCharacterなのはちょっとミスったところなので気にしないで下さい。)

MyPawn.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyPawn.generated.h"

UCLASS()
class QUIITA_API AMyPawn : public ACharacter
{
    GENERATED_BODY()
public:
    // Sets default values for this pawn's properties
    AMyPawn();
protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    // 入力管理用のアクターコンポーネントを保持
    UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
        class UInputManager *m_InputManager;
public: 
    // Called every frame
    virtual void Tick(float DeltaTime) override;

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

};

MyPawn.h
#include "MyPawn.h"
#include "InputManager.h"

// Sets default values
AMyPawn::AMyPawn()
{
    // Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    // いつもの初期化用コード
    m_InputManager = this->CreateDefaultSubobject<UInputManager>("InputManager");
    this->AddOwnedComponent(m_InputManager);
}

// Called when the game starts or when spawned
void AMyPawn::BeginPlay()
{
    Super::BeginPlay();
}

// Called every frame
void AMyPawn::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

// Called to bind functionality to input
void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    // ここでアクターコンポーネントの初期化関数を実行
    m_InputManager->SetupInput(InputComponent);
}

nano06126728
UE4いじってます。 時々Unityもやります。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away